Atualização do processo de CI / CD: preparação e planejamento

imagem

Em 2020, provavelmente é bastante difícil encontrar um projeto em uma descrição de pilha que não tenha uma das seguintes palavras: IaC, microsserviços, kubernetes, docker, aws / azure / gcloud, blockchain, ML, VR e assim por diante. E isso é ótimo! O progresso não pára. Estamos crescendo, nossos projetos estão crescendo conosco, aparecem ferramentas mais convenientes e funcionais que resolvem os problemas modernos.

Olá. Então, eu queria começar este artigo. Mas então revi algumas coisas, conversei com meus colegas e percebi que estaria errado. Ainda existem projetos com mais de 15 anos de idade, com gerentes e participantes que são crentes antigos, e, portanto, esses projetos têm uma pilha de tecnologia antiga que é bastante difícil de manter em um zoológico existente. E, por alguma razão, este projeto não pode ser atualizado globalmente (o cliente é iniciante, não há atualização, o projeto é muito grande e a migração está atrasada ou todos estão satisfeitos com tudo), e você precisa apoiá-lo. Ainda mais desagradável quando um projeto semelhante ainda está sendo desenvolvido ativamente. É como uma bola de neve. O cliente e o público exigem recursos, o código exige entrega, os servidores exigem atenção e cuidado ... Mas o bitpack - então, em geral, deixou de suportar mercúrio. Apenas esse caso é proposto para consideração.

O que será considerado: a conversão de mercurial-git, a mudança do CI do CruiseControl.NET para o TeamCity, da implantação do git para o Octopus, com uma pequena descrição de todo o processo.

O texto saiu muito, então será dividido em partes separadas para facilitar a percepção. Haverá um índice.
Parte 1: o que é, por que não é, como planejar, um pouco de festa. Eu chamaria essa parte de quase técnica.
Parte 2: cidade da equipe.
Parte 3: implantação do polvo.
Parte 4: nos bastidores. Momentos desagradáveis, planos para o futuro, possivelmente FAQ. Provavelmente, também pode ser chamado de quase técnico.

Eu não chamaria isso de guia para repetição por muitas razões: imersão insuficiente nos processos do projeto devido à falta de tempo, prática insuficiente de tais coisas, um enorme monólito no qual todos os subprojetos estão intimamente entrelaçados e um monte de outras nuances que o fazem queimar, admirar, aturar eles , mas em nenhum caso não, por favor. E também, devido aos recursos do projeto (é único), algumas etapas serão personalizadas exclusivamente para este caso.

Introdução clássica


Tínhamos um repositório mercurial, mais de 300 Brunches (open!), Ccnet, outro ccnet + git (para implantações) e um host inteiro de módulos de projeto com nossas próprias configurações e clientes individuais, quatro ambientes e um host inteiro de pools no IIS, além de cmd scripts, SQL, mais de quinhentos domínios, duas dúzias de compilações e desenvolvimento ativo além disso. Não que tudo isso fosse necessário, mas funcionou, e foi inconveniente e longo. A única coisa que me preocupou foi a presença de outros projetos que exigem minha atenção. Nada no processo de trabalhar com tarefas dessa magnitude é mais perigoso do que a falta de concentração e interrupção.

Eu sabia que, mais cedo ou mais tarde, teria que prestar atenção a outras tarefas, e é por isso que gastei muito tempo estudando a infraestrutura existente para que, pelo menos posteriormente, não houvesse problemas não resolvidos.

Infelizmente, não posso fornecer uma descrição completa do projeto, devido à NDA, portanto, pontos técnicos gerais serão considerados. Todos os nomes relacionados ao projeto também serão pintados. Peço desculpas pela mancha preta nas capturas de tela.

Um dos principais recursos do projeto é que ele possui vários módulos que possuem um núcleo, mas diferem em suas configurações. Existem também módulos com uma abordagem "especial", projetada para revendedores e clientes especialmente grandes. Um módulo pode servir mais de um cliente. Um cliente deve ser entendido como uma organização ou grupo de pessoas separado que obtém acesso a um módulo específico. Cada cliente obtém acesso em seu próprio domínio, tem seu próprio design e suas próprias configurações padrão exclusivas. O cliente é identificado pelo domínio que ele usa.

O esquema geral desta parte do projeto pode ser representado da seguinte forma:


Como você pode ver, o núcleo é usado da mesma maneira em todos os lugares, e isso pode ser usado.

Razões pelas quais surgiu a tarefa de revisar e atualizar os processos de CI / CD:

  1. O CruiseControl.NET foi usado como um sistema de compilação. Trabalhar com ele é um prazer duvidoso em princípio. Configurações XML em várias telas, um monte de dependências e configurações de vinculação entre si, falta de soluções modernas para problemas modernos.
  2. Alguns desenvolvedores (principalmente líderes) no projeto devem ter acesso para criar servidores e, às vezes, gostam de alterar as configurações da ccnet que não devem ser alteradas. Adivinha o que acontece a seguir. Você precisa ter um gerenciamento simples e conveniente de direitos no sistema de IC, sem tirar o acesso dos desenvolvedores ao servidor. Simplificando, não há configuração de texto - não há para onde escalar com mãos divertidas.
  3. - … CCNET, git. (. ).
  4. , . , . 
  5. - , — ( ) .
  6. . ? ? ? .
  7. Uso subótimo de recursos e um processo desatualizado de construção e entrega de código.

Figura ao parágrafo 3:


No estágio de planejamento, foi decidido usar o Teamcity como um sistema de construção e o Octopus como um sistema de implantação. A infraestrutura "de ferro" do cliente permaneceu inalterada: servidores dedicados separados para desenvolvimento, teste, preparo e produção, bem como servidores de revenda (principalmente ambientes de produção).

O Cliente recebeu a Prova de Conceito no exemplo de um dos módulos, foi elaborado um plano de ação, realizado trabalho preparatório (instalação, configuração, etc.). Provavelmente não há sentido em descrever isso. Como instalar a equipe da cidade pode ser lido no site oficial. Vou abordar separadamente alguns requisitos formulados para o novo sistema:

  1. Manutenção fácil com todas as consequências (backups, atualizações, resolução de problemas, se houver).
  2. Universalidade. Idealmente, desenvolva um esquema geral segundo o qual todos os módulos sejam montados e entregues e use-o como modelo.
  3. Minimize o tempo para adicionar novas configurações e manutenção de compilação. Clientes são adicionados / removidos. Às vezes, torna-se necessário fazer novas configurações. É necessário que, ao criar um novo módulo, não haja atraso na configuração de sua entrega.

Nesse ponto, lembramos da descontinuação do suporte a repositórios mercuriais com um bitbucket e o requisito foi adicionado para transferir o repositório para o git, preservando as ramificações e o histórico de confirmações.

Preparação: conversão de repositório


Parece que alguém claramente resolveu esse problema diante de nós e não de nós. Você só precisa encontrar uma solução de trabalho pronta. A exportação rápida acabou não sendo tão rápida. Além disso, não funcionou. Infelizmente, não há registros e capturas de tela restantes. O fato é que ele não podia. O Bitbucket não fornece seu próprio conversor (mas poderia). Mais alguns métodos pesquisados, e também por. Decidi escrever meus próprios scripts, de qualquer forma, este não é o único repositório mercurial, será útil no futuro. Os desenvolvimentos iniciais serão apresentados aqui (porque ainda permanecem na mesma forma). A lógica do trabalho é mais ou menos assim:

  1. Tomamos a extensão mercurial hggit como base. 
  2. Temos uma lista de todos os ramos do repositório mercurial. 
  3. Convertemos nomes de ramificações (graças ao mercurial e aos desenvolvedores pelos espaços nos nomes das ramificações, agradecimentos especiais aos tremas e outros caracteres que dão alegria à vida).
  4. Crie marcadores (marcador hg) e empurre para o repositório intermediário. Pelo que? Porque bitbucket e porque você não pode criar um marcador com o mesmo nome que o nome da ramificação (por exemplo, armazenamento temporário). 
  5. No novo repositório (já git), remova o postfix do nome da filial e migre hgignore para gitignore. 
  6. Envie para o repositório principal.

Listando comandos de acordo com os pontos:

  1. $ cd /path/to/hg/repo
    $ cat << EOF >> ./.hg/hgrc
    [extensions]
    hggit=
    EOF
    
  2. $ hg branches > ../branches
    
  3. #!/usr/bin/env bash
    hgBranchList="./branches"
    sed -i 's/:.*//g' ${hgBranchList}
    symbolsToDelete=$(awk '{print $NF}' FS=" " ${hgBranchList} > sym_to_del.tmp)
     
    i=0
    while read hgBranch; do
      hgBranches[$i]=${hgBranch}
      i=$((i+1))
    done <${hgBranchList}
     
    i=0
    while read str; do
      strToDel[$i]=${str}
      i=$((i+1))
    done < ./sym_to_del.tmp
     
    for i in ${!strToDel[@]}
    do
      echo ${hgBranches[$i]} | sed "s/${strToDel[$i]}//" >> result.tmp
    done
     
    sed -i 's/[ \t]*$//' result.tmp
    sed 's/^/"/g' result.tmp > branches_hg
    sed -i 's/$/"/g' branches_hg
    sed 's/ /-/g' result.tmp > branches_git
    sed -i 's/-\/-/\//g' branches_git
    sed -i 's/-\//\//g' branches_git
    sed -i 's/\/-/\//g' branches_git
    sed -i 's/---/-/g' branches_git
    sed -i 's/--/-/g' branches_git
    rm sym_to_del.tmp
    rm result.tmp
    
  4. #!/usr/bin/env bash
    gitBranchList="./branches_git"
    hgBranchList="./branches_hg"
    hgRepo="/repos/reponame"
     
    i=0
    while read hgBranch; do
      hgBranches[$i]=${hgBranch}
      i=$((i+1))
    done <${hgBranchList}
     
    i=0
    while read gitBranch; do
      gitBranches[$i]=${gitBranch}
      i=$((i+1))
    done <${gitBranchList}
     
    cd ${hgRepo}
    for i in ${!gitBranches[@]}
    do
      hg bookmark -r "${hgBranches[$i]}" "${gitBranches[$i]}-cnv"
    done
     
    hg push git+ssh://git@bitbucket.org:username/reponame-temp.git
    echo "Done."
    
  5. #!/bin/bash
    # clone repo, run git branch -a, delete remotes/origin words to leave only branch names, delete -cnv postfix, delete default branch string because we can't delete it
    repo="/repos/repo"
    gitBranchList="./branches_git"
    defaultBranch="default-cnv"
    while read gitBranch; do   gitBranches[$i]=${gitBranch};   i=$((i+1)); done < $gitBranchList
    cd $repo
    for i in ${!gitBranches[@]}; do git checkout ${gitBranches[$i]}-cnv; done
    git checkout $defaultBranch
    for i in ${!gitBranches[@]}; do
        git branch -m ${gitBranches[$i]}-cnv ${gitBranches[$i]}
        git push origin :${gitBranches[$i]}-cnv ${gitBranches[$i]}
        git push origin -u ${gitBranches[$i]}
    done
    
  6. #!/bin/bash
    # clone repo, run git branch -a, delete remotes/origin words to leave only branch names, delete -cnv postfix, delete default branch string because we can't delete it
    repo="/repos/repo"
    gitBranchList="./branches_git"
    defaultBranch="default"
    while read gitBranch; do   gitBranches[$i]=${gitBranch};   i=$((i+1)); done < $gitBranchList
    cd $repo
    for i in ${!gitBranches[@]}; do
        git checkout ${gitBranches[$i]}
        sed -i '1d' .hgignore
        mv .hgignore .gitignore
        git add .
        git commit -m "Migrated ignore file"
    done
    

Vou tentar explicar o significado de algumas ações, a saber, o uso de um repositório intermediário. Inicialmente, no repositório após a conversão, os nomes das ramificações contêm o postfix "-cnv". Isso se deve ao recurso de marcador hg. Você precisa remover esse postfix e também criar arquivos gitignore em vez de hgignore. Tudo isso adiciona histórico e, portanto, aumenta o tamanho do repositório (e de maneira irracional). Como outro exemplo, posso citar o seguinte: tente criar um repositório e envie o primeiro commit para 300M com o código nele. Em seguida, adicione-o ao gitignore e empurre-o sem ele. Ele permanecerá na história. Agora tente removê-lo do histórico (git filter-branch). Com um certo número de confirmações, o tamanho do repositório resultante não diminui, mas aumenta. Isso é resolvido pela otimização, mas não pode ser iniciado no bitbucket.Isso é realizado apenas ao importar o repositório. Portanto, todas as operações brutas são realizadas com um intermediário e, em seguida, a importação para uma nova é feita. O tamanho do repositório intermediário na final foi de 1,15G e o tamanho dos 350M resultantes. 

Conclusão


Todo o processo de migração foi dividido em várias etapas, a saber:

  • preparação (demonstração para o cliente usando um exemplo de uma compilação, instalação de software, conversão de repositório, atualização de configurações existentes da ccnet);
  • Configuração de CI / CD para o ambiente de desenvolvimento e seus testes (o sistema antigo permanece funcionando em paralelo);
  • Configurações de CI / CD para os ambientes restantes (em paralelo com o "bom" existente) e testes;
  • desenvolvedor de migração, teste e teste;
  • Migrando domínios de produção e transferência de instâncias antigas do IIS para novas.

A descrição da fase preparatória concluída com êxito está sendo concluída. Algum sucesso grandioso não foi alcançado aqui, exceto que o sistema existente não quebrou e o repositório mercurial no git foi migrado. No entanto, sem uma descrição desses processos, não haverá contexto para partes mais técnicas. A próxima parte descreverá o processo de configuração de um sistema de IC com base na cidade da equipe.

All Articles