- Eles operam mais de 2.800 serviços em uma arquitetura MSA e obtêm muito valor com isso
- Mas essa arquitetura também traz dificuldades. Uma delas é fazer mudanças de biblioteca em todos os serviços
- Em geral, é preciso escolher duas entre três opções: bibliotecas atualizadas, versões consistentes de bibliotecas e baixo esforço de upgrade
Estratégia de migração centralizada
- Na Monzo, eles acreditam que uma abordagem de migração conduzida de forma centralizada permite alcançar, em alto nível, os três atributos acima
- Em vez de passar a responsabilidade da migração para os donos dos serviços, eles preferem que um único time conduza a migração
- Isso ajuda a evitar um alto overhead de coordenação (que leva a migrações lentas) e o risco de interrupção do projeto (que leva a inconsistências)
- Para permitir que um único time conclua a migração em um prazo razoável, eles dependem fortemente de:
- escolhas tecnológicas centrais (por exemplo, alto nível de consistência, uso de monorepo)
- uso extensivo de automação (por exemplo, ferramentas de deploy em massa de serviços, verificações automatizadas de rollback)
Migração do OpenTracing SDK para o OpenTelemetry SDK
- Recentemente, eles colocaram a estratégia da Monzo em prática com um projeto de transição do OpenTracing SDK para o OpenTelemetry SDK
- Todos os serviços exportam dados de tracing para o Jaeger. Antes, isso era feito via OpenTracing e Jaeger Go SDK
- Essas bibliotecas agora estão obsoletas, e a comunidade se consolidou em torno do OpenTelemetry
- Para estabelecer a base de melhorias no sistema de tracing, eles primeiro quiseram substituir as bibliotecas obsoletas pelo OpenTelemetry SDK
Princípios da migração
- Migração centralizada e transparente para os donos dos serviços. Eles preferem estratégias que possam ser executadas centralmente por um único time para minimizar overhead de coordenação e reduzir o risco de interrupção da migração
- Sem downtime. A maioria das migrações envolve serviços críticos para funções centrais do banco, então downtime não é aceitável
- Roll forward gradual, rollback rápido. Em mudanças de grande escala, é preciso conseguir fazer roll forward gradualmente para reduzir o raio de impacto caso haja problemas. Mas também deve ser possível fazer rollback de tudo rapidamente, se necessário
- Regra 80/20 para automação. Em migrações em larga escala, normalmente uma grande parte das mudanças segue um template comum. Essas mudanças podem ser automatizadas com facilidade. Para casos de uso mais únicos, a automação tem retorno decrescente, e costuma ser mais eficiente resolver caso a caso. Para evitar surpresas desagradáveis e facilitar o acompanhamento do progresso, é melhor classificar previamente as mudanças necessárias nessas duas categorias
Estratégia de migração
- Na Monzo, há uma série de etapas de migração usadas de forma sistemática
1. Planejamento e alinhamento
- Migrações trazem riscos significativos. Além de impactarem uma grande quantidade de serviços, o time que executa a migração pode ter pouco (ou nenhum) contexto sobre os serviços que está migrando
- Eles buscam consistência entre os serviços, mas sempre há exceções, e querem detectar essas surpresas o mais cedo possível
- Por isso, é muito importante que o processo de planejamento seja transparente e que todos os engenheiros tenham a chance de contribuir
- Para isso, existem dois processos:
- Propostas: na Monzo, eles escrevem muitas. Praticamente tudo acaba sendo compartilhado em um único canal do Slack onde qualquer pessoa da empresa pode opinar
- Revisão de arquitetura: para as mudanças maiores, eles fazem reuniões síncronas de revisão de arquitetura para aprofundar áreas específicas mais controversas ou arriscadas. O objetivo não é obter aprovação ou assinatura, mas avançar de forma significativa o estado do design para acelerar o projeto
2. Encapsular a biblioteca antiga
-
Em vez de instalar a nova biblioteca e atualizar o código dos serviços para chamá-la, eles decidiram primeiro encapsular a biblioteca antiga
- Isso permite interceptar chamadas à biblioteca subjacente e decidir, com base em configuração dinâmica, qual implementação usar. Assim, é possível fazer roll forward e rollback facilmente sem precisar redeployar todos os serviços
- A nova biblioteca tinha tipos/funções significativamente diferentes. Atualizar todos os call sites exigiria muito esforço e, em alguns casos, os benefícios da nova API eram pequenos. Ao encapsular a biblioteca antiga, nesses casos eles puderam manter uma interface parecida com a da biblioteca anterior, facilitando a atualização dos call sites
-
Outros benefícios de encapsular bibliotecas:
- é possível instrumentar com a própria biblioteca de telemetria
- é possível oferecer uma interface mais opinativa
3. Atualizar os call sites
-
O uso dessa biblioteca seguia um padrão comum:
- havia um pequeno número de funções/tipos referenciados várias vezes em todo o codebase
- depois vinha uma longa cauda de funções/tipos referenciados em apenas alguns lugares
-
Eles trataram cada caso de forma diferente:
- para o pequeno conjunto de funções/tipos referenciados em muitos lugares, automatizaram o máximo possível. Para essa biblioteca, dependeram principalmente de
goplsegorenamepara fazer refatorações automatizadas - para lidar com a longa cauda de funções/tipos referenciados em poucos lugares, adotaram uma abordagem manual, caso a caso. Em alguns casos, migraram manualmente. Em outros, perceberam que era possível fazer a mesma coisa usando uma API mais comum já existente e, então, passaram a usá-la. Isso significou que esses casos não precisavam mais ser tratados como especiais, com o benefício adicional de manter a API da biblioteca wrapper pequena e opinativa
- para o pequeno conjunto de funções/tipos referenciados em muitos lugares, automatizaram o máximo possível. Para essa biblioteca, dependeram principalmente de
-
Além de encapsular a biblioteca antiga, eles também bloquearam a criação de novas dependências da biblioteca anterior. Isso foi feito adicionando uma verificação de CI usando semgrep
4. Encapsular a nova biblioteca
- Depois que a biblioteca antiga foi encapsulada, eles puderam começar a adicionar a nova biblioteca por trás da biblioteca wrapper
- No início, a nova implementação ficou desativada por configuração. Isso significa que não eram esperadas mudanças de comportamento e que eles podiam continuar mesclando gradualmente alterações na branch principal
5. Deploy em massa de serviços
- Antes de começar a ativar a nova implementação, era necessário garantir que todos os serviços em execução pudessem suportá-la
- Em outros tipos de mudança de biblioteca, pode-se fazer deploy de apenas um subconjunto dos serviços com a nova funcionalidade por vez. Mas, no caso da biblioteca de tracing, se um serviço tivesse sido migrado para usar a nova biblioteca, então todos os serviços que ele pudesse chamar (durante a transição) também precisariam suportar a nova funcionalidade
- Para gerenciar um grande número de deploys de serviços, eles construíram uma ferramenta de deploy em massa que permite enviar mudanças de biblioteca para todos os serviços como jobs em lote assíncronos
- Para mitigar o impacto de possíveis deploys incorretos:
- usam verificações automatizadas de rollback
- fazem deploy primeiro nos serviços menos críticos. Todos os serviços recebem uma tag de "camada", e a ferramenta de deploy em massa usa isso para priorizar os deploys menos arriscados
6. Controle do rollout via configuração
- O problema da ferramenta de deploy em massa é que ela é relativamente lenta. O que eles realmente querem evitar é fazer deploy em todos os serviços e depois descobrir que há um problema na nova biblioteca sem conseguir voltar rapidamente
- Por isso, em vez de ativar a nova implementação via deploy, eles fazem deploy da capacidade de ativá-la por meio do sistema de configuração
- Comparado a um deploy normal, a vantagem de usar o sistema de configuração aqui é a velocidade. Todos os serviços atualizam a configuração a cada 60 segundos, então é possível reverter rapidamente, se necessário
- Isso também oferece muito mais controle sobre quando a nova implementação é usada. Por exemplo, é possível ativá-la apenas para um conjunto específico de usuários ou para uma porcentagem aleatória de requisições
- Nesse caso, eles escolheram fazer o rollout apenas para endpoints de API de propriedade do próprio time, ativando com probabilidades gradualmente crescentes
7. Limpeza
- Quando a transição para a nova implementação está completa, eles fazem o trabalho satisfatório de remover a implementação antiga da biblioteca wrapper
Superpoderes de migração
- Esse tipo de migração centralizada é possível graças a escolhas tecnológicas fundamentais feitas na Monzo e às ferramentas nas quais eles continuam investindo
- Tecnologia consistente: todos os serviços foram escritos em Go e usam a mesma versão da biblioteca antiga. Isso torna muito mais fácil automatizar mudanças. Por exemplo, basta usar uma única ferramenta de refatoração, em vez de uma por linguagem
- Monorepo: todo o código dos serviços está em um único monorepo, o que torna muito mais fácil fazer refatorações em larga escala em um único commit. Também permite aplicar globalmente, via verificações de CI, o uso de determinadas bibliotecas, ajudando a manter a consistência
- Deploy em massa: quando há muitos componentes implantáveis, é necessário um processo automatizado de deploy para enviar mudanças de biblioteca
- Serviço de configuração leve e flexível: o processo de deploy é seguro, mas lento (alguns minutos por deploy). É preciso um processo mais leve e flexível para ativar/desativar rapidamente novas funcionalidades em muitos serviços de uma vez
Conclusão
- No passado, eles tentaram distribuir as migrações, mas isso invariavelmente levava a migrações incompletas e a muito esforço de coordenação
- É por isso que a Monzo prefere fortemente migrações centralizadas. Um único time precisa arcar com um custo relativamente alto, mas, no geral, isso exige menos esforço e aumenta bastante a chance de manter a consistência
- Essa abordagem cria um ciclo virtuoso:
- o time que executa a migração tem forte incentivo para investir em ferramentas de automação da migração
- isso também ajuda a manter consistência técnica (o que facilita construir ferramentas)
- ainda assim, eles continuam pragmáticos quanto ao nível de automação, aplicando a regra 80/20
- Além das ferramentas nas quais a Monzo continua investindo, essa abordagem só é possível por causa de algumas escolhas tecnológicas centrais feitas no início
- principalmente pelo fato de usarem um conjunto de tecnologias opinativo e restrito
Ainda não há comentários.