Na True Engineering, configuramos o processo de entrega contínua de atualizações nos servidores do cliente e queremos compartilhar essa experiência.
Primeiro, desenvolvemos um sistema online para o cliente e o implantamos em nosso próprio cluster Kubernetes. Agora, nossa solução de alta carga foi transferida para a plataforma do cliente, para a qual configuramos um processo de Continuous Deployment totalmente automático. Com isso, aceleramos o time-to-market – a entrega de alterações no ambiente de produção.
Neste artigo, abordaremos todas as etapas do processo de Continuous Deployment (CD) ou entrega de atualizações na plataforma do cliente:
- como esse processo começa,
- sincronização com o repositório Git do cliente,
- compilação do backend e frontend,
- implantação automática do aplicativo em ambiente de teste,
- implantação automática em produção.
Durante o processo, compartilharemos detalhes da configuração.

1. Início do CD
O Continuous Deployment começa quando o desenvolvedor publica as alterações na branch de lançamento do nosso repositório Git.
Nosso aplicativo é baseado em uma arquitetura de microserviços e todos os seus componentes são armazenados em um único repositório. Com isso, todos os microserviços são compilados e instalados, mesmo que apenas um deles tenha sido alterado.
Organizamos o trabalho através de um único repositório por várias razões:
- Facilidade de desenvolvimento – o aplicativo está em constante desenvolvimento, portanto, é possível trabalhar com todo o código de uma vez.
- Pipeline CI/CD unificado, que garante que o aplicativo, como um sistema único, passe por todos os testes e seja entregue no ambiente de produção do cliente.
- Eliminamos confusão de versões – não precisamos manter um mapa de versões dos microserviços e descrever a configuração de cada microserviço em scripts Helm.
2. Sincronização com o repositório Git do código-fonte do cliente
As alterações feitas são automaticamente sincronizadas com o repositório Git do cliente. Nele, está configurada a compilação do aplicativo, que é acionada após a atualização da branch, e o deployment em produção. Ambos os processos ocorrem em seu ambiente a partir do repositório Git.
Não podemos trabalhar diretamente com o repositório do cliente, pois precisamos de nossos próprios ambientes para desenvolvimento e testes. Para isso, usamos nosso próprio repositório Git — que está sincronizado com o repositório Git deles. Assim que o desenvolvedor publica alterações no ramo correspondente do nosso repositório, o GitLab envia imediatamente essas alterações para o cliente.

Depois disso, é necessário realizar a montagem. Ela consiste em várias etapas: montagem do backend e frontend, testes e entrega em produção.
3. Montagem do backend e frontend
A montagem do backend e do frontend são duas tarefas paralelas que são executadas no sistema GitLab Runner. A configuração da montagem original está neste mesmo repositório.
.
O GitLab Runner busca o código do repositório necessário, compila e envia o aplicativo Java para o registro Docker. Aqui montamos o backend e o frontend, obtemos imagens Docker, que armazenamos no repositório do lado do cliente. Para gerenciar as imagens Docker, usamos .
Sincronizamos as versões de nossas imagens com a versão do lançamento que será publicada no Docker. Para um funcionamento suave, fizemos algumas configurações:
1. Entre o ambiente de teste e o de produção, os contêineres não são recompilados. Fizemos parametrizações para que o mesmo contêiner possa operar sem recompilação com todas as configurações, variáveis de ambiente e serviços tanto no ambiente de teste quanto em produção.
2. Para atualizar o aplicativo via Helm, é necessário especificar sua versão. Nossa montagem do backend, frontend e a atualização do aplicativo são três tarefas diferentes, portanto, é importante usar sempre a mesma versão do aplicativo em todos os lugares. Para essa tarefa, usamos dados do histórico do Git, uma vez que temos a configuração do cluster K8S e do aplicativo no mesmo repositório Git.
A versão do aplicativo é obtida a partir do resultado da execução do comando
git describe --tags --abbrev=7.
4. Desdobramento automático de todas as alterações no ambiente de teste (UAT)
A próxima etapa neste script de montagem é a atualização automática do cluster K8S. Isso ocorre desde que todo o aplicativo tenha sido montado e todos os artefatos publicados no Docker Registry. Depois disso, a atualização do ambiente de teste é iniciada.
A atualização do cluster é iniciada com . Se algo não sair como planejado, o Helm reverterá automaticamente todas as suas alterações. Não é necessário monitorar seu funcionamento.
Fornecemos, junto com a compilação, a configuração do cluster K8S. Portanto, o próximo passo é atualizar a configuração: configMaps, deployments, services, secrets e quaisquer outras configurações K8S que tenhamos alterado.
Depois disso, o Helm inicia a atualização RollOut do próprio aplicativo no ambiente de teste. Antes que o aplicativo seja implantado no ambiente de produção. Isso é feito para que os usuários possam verificar manualmente as funcionalidades de negócios que ativamos no ambiente de teste.
5. Implantação automática de todas as alterações em Prod
Para implantar a atualização no ambiente de produção, basta pressionar um botão no GitLab — e os contêineres são imediatamente entregues no ambiente de produção.
O mesmo aplicativo pode operar em diferentes ambientes — teste e produção — sem recompile. Usamos os mesmos artefatos, sem alterar nada no aplicativo, e os parâmetros são definidos externamente.
A parametrização flexível das configurações do aplicativo depende do ambiente em que esse aplicativo será executado. Retiramos todas as configurações de ambientes para fora: tudo é parametrizado através da configuração do K8S e parâmetros do Helm. Quando o Helm implanta a compilação no ambiente de teste, são aplicados os parâmetros de teste, e no ambiente de produção — os parâmetros de produção.
O mais desafiador foi parametrizar todos os serviços e variáveis usados que dependem do ambiente e convertê-los em variáveis de ambiente e na descrição/configuração de parâmetros de ambiente para o Helm.
Nas configurações do aplicativo, são usadas variáveis de ambiente. Seus valores são definidos nos contêineres através do K8S configmap, que é modelado usando templates Go. Por exemplo, a definição de uma variável de ambiente para o nome do domínio pode ser feita assim:
APP_EXTERNAL_DOMAIN: {{ (pluck .Values.global.env .Values.app.properties.app_external_domain | first) }}
.Values.global.env – nesta variável está armazenado o nome do ambiente (prod, stage, UAT).
.Values.app.properties.app_external_domain – nesta variável definimos no arquivo .Values.yaml o domínio desejado
Ao atualizar o aplicativo, o Helm cria a partir dos modelos do arquivo configmap.yaml e preenche o valor de APP_EXTERNAL_DOMAIN com o valor necessário, dependendo do ambiente em que a atualização é iniciada. Essa variável já é ajustada no contêiner. O acesso a ela é possível a partir do aplicativo, portanto, em cada ambiente do aplicativo, haverá um valor diferente para essa variável.
Recentemente, o Spring Cloud passou a suportar o K8S, incluindo o trabalho com configMaps: . Enquanto o projeto está em constante desenvolvimento e mudando drasticamente, não podemos usá-lo em produção. Mas estamos monitorando ativamente seu estado e o utilizando em configurações DEV. Assim que estabilizar, mudaremos de usar variáveis de ambiente para ele.
Total
Assim, o Continuous Deployment está configurado e funcionando. Todas as atualizações ocorrem com um simples pressionar de botão. A entrega de mudanças no ambiente de produção é automática. E, o que é importante, as atualizações não interrompem o funcionamento do sistema.

Planos para o futuro: migração automática do banco de dados
Estamos considerando a atualização do banco de dados e a possibilidade de reverter essas mudanças. Duas versões diferentes do aplicativo estão operando ao mesmo tempo: a antiga está em funcionamento, enquanto a nova está sendo lançada. E desligaremos a antiga apenas quando tivermos certeza de que a nova versão está funcionando. A migração do banco de dados deve permitir que ambas as versões do aplicativo funcionem.
Portanto, não podemos simplesmente alterar o nome da coluna ou outros dados. Mas podemos criar uma nova coluna, copiar os dados da coluna antiga para ela e escrever gatilhos que, ao atualizar os dados, também copiarão e atualizarão para a outra coluna. E após o lançamento bem-sucedido da nova versão do aplicativo, após um período de suporte pós-lançamento, podemos remover a coluna antiga e o gatilho que se tornou desnecessário.
Se a nova versão do aplicativo não funcionar corretamente, podemos reverter para a versão anterior, incluindo a versão anterior do banco de dados. Em suma, nossas mudanças permitirão que várias versões do aplicativo funcionem simultaneamente.
Planejamos automatizar a migração do banco de dados através de um job K8S, integrando isso ao processo de CD. E certamente compartilharemos essa experiência no Habr.
Fonte: habr.com
