Deploy, release the kraken!
Tags: programming, productivity, architecture
Published: 2021-11-09 14:33:48

Chegou a hora de mandar para produção o trabalho dos últimos dias ou do dia! É só jogar para cima do time de operações e deu, né? Claro que não, é hora de vermos e ajustarmos uma esteira de entrega que atenda os requisitos do produto. É um trabalho em conjunto entre operação e desenvolvimento, ambos precisam conhecer sobre o sistema, a infraestrutura, a escalabilidade, o código. Essa troca de informações é essencial para um crescimento escalável do sistema.

![Deploy!](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mh69i2gdz7kkoncd2qsl.gif)

Hoje em dia as opções de deploy são praticamente infinitas: kubernetes, diferentes PaaS, máquinas virtuais, docker, infraestrutura serverless, etc. Escolha a que tem melhor custo-benefício para o produto. Evite fazer escolhas da moda ou só por quê você quer aprender algo. Entenda os pontos de falha da infra escolhida – é impossível entender todos, mas saiba que eles existem e que vão eventualmente acontecer. Redes vão se particionar, pacotes vão se perder, DNS vai demorar para atualizar, VMs vão morrer, armazenamento será corrompido, configurações ficarão fora de sincronismo. O software e a esteira de entrega devem levar todos esses pontos em consideração.

**Não faça deploys manualmente.** Entenda manualmente como qualquer prática que dependa do ambiente local de desenvolvimento ou conhecimento que só existe na cabeça do desenvolvedor. Garanta uma esteira de entrega completa que permita reprodutibilidade do build e testes do sistema. Queremos evitar o clássico problema "funciona na minha máquina" então reprodutibilidade é uma peça chave de qualquer esteira de entrega.

## Estratégias

Discuta e planeje estratégias de entrega do software, levando em conta pelo menos os seguintes pontos:
- Os modelos de dados são compatíveis?
- Como iremos atuar em caso de problemas?
- É simples voltar para a versão anterior?
- Temos uma linha base das métricas pra conseguir detectar anomalias?

Durante o desenvolvimento, pense nas alterações nos modelos de dados e modificações nas assinaturas de funções. A maioria dos sistemas atualmente são distribuídos e na maioria das estratégias de deploy as instâncias novas começam a trabalhar antes de desligarmos todas as antigas. Será que remover um atributo vai quebrar as units antigas? Se quebrar, como iremos voltar para a versão anterior em caso de falha? Tente manter a retrocompatibilidade dos modelos de dados. No caso de APIs que são consumidas por outros sistemas, não quebre o modelo até que todos consigam atualizar. Dê preferência para criar novas rotas com os modelos novos se a atualização for totalmente incompatível — isso acontece, é normal.

Defina um plano de ação simples, que todos no time saibam executar, para quando tivermos problemas não gastarmos tempo tentando lembrar aquele comando mágico ou a sequência correta de passos. Se possível, tenha um ambiente que permita exercitar essas ações. Um plano de ação simples é voltar para a versão anterior, basta garantir retrocompatibilidade nas alterações que fizermos ao sistema.

As métricas do sistema vão definir uma linha base do seu comportamento ao longo do tempo. Se familiarize com isso para entender melhor quando algo foge do padrão. As métricas vão fugir do padrão quando temos algum evento especial, por exemplo Black Friday ou a final de um campeonato. Elas também podem fugir da linha base quando subirmos uma alteração com problema, e às vezes só conseguimos ver um problema quando está em produção com uma carga bem mais alta do que no sistema de teste.

Existem diversas estratégias que ajudam nos pontos citados: blue-green, canary deployment, feature flags, rolling update, etc. Estude com o time qual faz mais sentido para o sistema. Particularmente, gosto muito de canários e feature flags. Abaixo irei exemplificar algumas dessas estratégias, são apenas esboços sem detalhes de implementação.

### Blue-Green

![Blue-Green](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b2oeb8dteunex9hbnybh.png)

Uma estratégia simples de deploy é usar Blue-Green, onde criamos em paralelo uma nova infraestrutura completa com a nova versão do sistema. Na figura acima estamos apontando os acessos para o lado green. No próximo deploy iremos recriar o lado blue, daí podemos testar a aplicação utilizando o endereço interno antes de trocarmos o apontamento do endereço principal. Caso tenhamos algum problema, basta voltar o apontamento para o lado green.

Normalmente as bases de dados são as mesmas para ambos os lados, então tome cuidado com migrações nos modelos de dados.

Prós:
- Facilidade de voltar a aplicação

Contra:
- Custo de manter duas infraestruturas em paralelo por um tempo. Em ambientes não cloud isso pode ter um custo considerável.

### Rolling Update

![Rolling Update](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1u4h5e999khobmvymbei.png)

As instâncias da aplicação vão sendo substituídas aos poucos, em um número fixo de instâncias por vez. Somente depois que as novas instâncias estão respondendo corretamente é feita a remoção das instâncias antigas. Na figura temos a instância A como sendo a nova, e a instância 1 marcada para remoção. É a estratégia padrão do [Kubernetes](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy).

Prós:
- Resiliente caso a aplicação falhe

Contra:
- Pode ser demorado para fazer um rollback

### Canary Deployment

![Canary Deployment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/arflzfr27m5fiw9a94oa.png)

A ideia por trás de canários é subir uma aplicação que possa ser sacrificada caso tenha problemas. O nome vem justamente dos canários que os mineiros utilizavam no passado para indicar a qualidade do ar. Quando o canário para de cantar é porque o ar não está respirável e os mineiros devem sair daquele túnel. Aqui é a mesma coisa, colocamos uma pequena carga na nova versão da aplicação e olhamos o seu comportamento. Conforme temos confiança que o comportamento está dentro do esperado vamos aumentando a quantidade de instâncias e o volume servido. Em caso de problemas, basta tirar a nova versão do ar. Minimizamos qualquer tempo de indisponibilidade e sensação de problemas por parte dos usuários. Existem ferramentas que automatizam esse processo de deployment. O livro [The Site Reliability Workbook](https://sre.google/workbook/table-of-contents/) tem um [capítulo inteiro](https://sre.google/workbook/canarying-releases/) dedicado sobre a prática.

Para aplicações que processam dados, deve-se usar [outras formas](https://sre.google/workbook/data-processing/#canarying), mas o princípio é sempre o mesmo: olhar o comportamento das métricas e os resultados gerados e ir trocando de forma incremental as instâncias.

Um amigo gosta de dizer que a única forma de deploy deveria ser canários. A grande maioria dos problemas em sistemas poderiam ser evitados se canários fossem utilizados.

Prós:
- Fácil de detectar problemas
- Minimiza o impacto com os usuários

Contra:
- Complexidade para criar o ambiente de deploy

### Feature Flags

![Feature Flags](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/22p9ayj2y2x76dde94xx.png)

O princípio por trás de feature flags é permitir ligar ou desligar comportamentos do sistema de forma dinâmica. Podemos utilizar alguma das outras estratégias descritas para atualizar as instâncias, e depois controlamos quando vamos habilitar a funcionalidade. O controle pode ser só ligado ou desligado, ou ser probabilístico — 5% das execuções chamam a funcionalidade nova. Normalmente temos métricas associadas à flag para podermos acompanhar o comportamento novo. Também podemos usar esse sistema para controles dinâmicos, por exemplo: desligar um subsistema durante um momento de pico de acessos.

Prós:
- Facilidade de uso

Contra:
- Requer um sistema para gerenciar as flags