Ambientes por Branch com OpenShift Next Gen | iMasters
Hoje, na Coderockr, utilizamos Pull Requests e Code Reviews como uma ferramenta de qualidade nos nossos desenvolvimentos, e tem garantido resultados nesse sentido.
Mas mesmo com esse processo, eventualmente temos de lidar com alguns problemas como, por exemplo, funções que interferem umas nas outras depois de aprovadas, permitir que os Testers possam avaliar as melhorias, e garantir que todos as mudanças feitas na branch principal possam ser enviadas para produção.
Esses problemas podem ser reduzidos, ou até eliminados, se, mesmo antes de aprovar os PRs, os Testers conseguissem trabalhar sobre essas melhorias e só serem repassadas para a branch principal após a aprovação deles.
Desse modo, o fonte principal não só passou pelo Review de outros desenvolvedores, como foi testado pela equipe de QA, dando ainda mais confiança a ele. Mas subir ambientes de homologação para cada um dos PRs, automaticamente ou sobre demanda, não é um problema trivial, envolve subir máquinas, garantir que esteja rodando a versão atualizada, liberar portas etc.
Uma forma que encontramos para resolver esse problema é utilizando um cluster Kubernetes (ou a versão da Red Hat, o OpenShift), pois essas ações são bem simples de realizar com ele e ainda mais fáceis se forem automatizadas.
Agora, vou explicar como montar um exemplo simples, um para o GitLab e outro para o GitHub, integrando com o OpenShift, da Getup Cloud.
Obs: sobre uma introdução ao Kubernetes/OpenShift pode ver aqui. O cliente de linha de comando pode ser baixado neste link.
A primeira experiência que fizemos foi com o GitLab, principalmente pela integração que ele traz com o Kubernetes, e as outras ferramentas que ele oferece que acabaram cobrindo todo o escopo do problema.
O que queremos montar é um ambiente por branch/PR que deve ser facilmente criado e destruído. Para demonstrar, criei um repositório no GitLab com uma aplicação bem simples que apenas retorna uma página estática, mas é o suficiente para o objetivo.
Primeiramente, criei a base da aplicação usando Docker, a mesma gera uma página com o conteúdo acima. O que vale destacar nesse primeiro momento é que já configurei um processo de CI simples:
Nesse CI, eu construo o contêiner da aplicação para cada commit feito e guardo no registro do próprio GitLab por branch. Dessa forma, tenho uma versão do meu contêiner para cada uma das branchs que forem criadas e vou atualizando essas versões automaticamente a cada alteração.
Obs: Fonte completo até aqui.
Nesse momento, não tenho nenhum deploy, seja de ambiente de teste, produção ou por branch. Então, vamos adicionar um processo de deploy no OpenShift para o ambiente de produção e testes, sendo que o ambiente de testes é atualizado automaticamente para os commits na master e o de produção apenas quando um usuário disparar o deploy.
Para fazer isso, primeiramente, temos de configurar a integração entre o OpenShift e o GitLab. Para isso, vamos em Settings > Integrations e procuramos Kubernetes nas opções. O GitLab irá solicitar algumas informações sobre o ambiente, qual o Namespace, o URL da API do Kubernetes e uma forma de autenticação, que pode ser um Service Token, ou um CA Bundle.
Dessa forma, vou criar um novo Namespace, como fazer isso vai depender do seu vendor de Kubernetes, no caso da Getup Cloud, basta ir em https://portal.getupcloud.com/projects e criar um novo projeto, o nome do projeto será o Namespace.
Uma vez com o Namespace, podemos criar um novo Service Token para ser usado no CI do GitLab. No caso, para criar um Service Token é necessário criar uma ServiceAccount e dar permissões a mesma, e então, pegar o Service Token dela. O script abaixo realiza essas operações:
Agora que temos o token gerado, basta adicionar essas informações no GitLab.
Certo, agora o GitLab consegue conversar com o OpenShift. Podemos, então, alterar nossas regras de CI para criar duas novas etapas: staging e production, que irão realizar o deploy dos nossos ambientes padrão, sendo que staging será disparada automaticamente por commits na master e production ficará como manual.
A variável KUBE_DOMAIN vai ajudar a deixar o nosso processo de deploy mais simples. Basicamente, nós colocamos nela o domínio base que o OpenShift usa para expor as rotas dele. No caso da Getup, seria "getup.io". A CI_ENVIRONMENT_URL vai completar a KUBE_DOMAIN e serve para informar o k8s/deploy qual endereço ele deve expor o ambiente, ele deve sempre terminar com o KUBE_DOMAIN e deve ser igual à URL da chave environment, pois é por essa chave que o GitLab sabe onde os ambientes estão expostos.
As etapas de staging e production irão fazer o deploy dos nossos ambientes e, como comentei antes, o ambiente de staging terá deploy automático para todo commit na master, enquanto production irá esperar uma ação do usuário. No mais, as duas etapas são iguais, mudando apenas a URL que estão sendo expostas. Estou usando a imagem lucassabreu/openshift-k8s-cli que é basicamente um ubuntu com o oc instalado.
O script k8s/deploy está abaixo e ele basicamente se autentica contra a API do OpenShift usando o Service Token que criamos antes. Ele destrói a aplicação antiga e executa o deploy de uma nova.
Vale ressaltar que é importante marcar os componentes do ambiente com app=$CI_ENVIRONMENT_SLUG, pois é assim que o GitLab consegue encontrá-los e lhe retornar status sobre eles.
Também estou usando um truque de "templating" com o YAML, que define os ambientes para poder inserir as variáveis de cada ambiente nele. Existem outras ferramentas mais avançadas, como o Helm, mas para o meu exemplo, templating com sed é o suficiente.
Agora, depois que do commit das alterações, o GitLab faz o build, o deploy da staging e production (manual); podemos ver na área Environments, do GitLab, que os ambientes estão rodando. Ele inclusive traz alguns comandos para facilitar a vida: link para a URL do ambiente, terminal dentro do Pod e opção de Re-deploy.
Obs: Fonte completo até agora.
Basicamente precisamos de duas novas etapas no nosso CI: uma para subir o ambiente para um branch e outro para destruir esse ambiente para evitar consumir recursos sem necessidade.
Para isso, fiz as seguintes alterações nos .gitlab-ci.yml:
Basicamente, adicionei as duas novas etapas. O review basicamente faz a mesma coisa que staging, mas usa um nome de ambiente dinâmico baseado na branch; e tem um enviroment:on_stop que basicamente indica o que fazer quando a branch for removida.
Na etapa stop_review, executo alguns comandos para eliminar o ambiente quando for chamada, é importante deixar essa como manual para que ela não apague sozinha o ambiente quando terminar as outras etapas.
Os comandos da etapa stop_review precisam estar definidos diretamente no .gitlab-ci.yml, pois quando essa etapa for executada é possível que a branch e commits dela não existam mais, é também por esse motivo que informamos a variável GIT_STRATEGY como NO, evitando que sequer seja checado se a branch/commit de origem existem.
Agora quando crio uma nova branch, automaticamente é criado um novo ambiente para a mesma no OpenShift.
Para testar, criei a branch a-change e fiz a seguinte alteração:
Assim que dei o git push, começou o deploy do novo ambiente r/a-change, logo que terminou pude verificar na área de ambientes do GitLab que estava rodando, e tem as mesmas operações disponíveis que os outros, mais a opção de parada (stop_review):
Obs: Fontes com essas alterações.
Após essas alterações, podemos implementar a regra de merge apenas após testes da equipe de QA, sem interferência de outras atividades que foram aplicadas no meio do caminho e permitindo um controle melhor sobre o que está pronto para ir para a produção.
O artigo acabou ficando bem grande apenas para falar do processo no GitLab, por isso vou criar um segundo sobre como fazer isso no GitHub.