Uma história de lançamento que afetou tudo

Uma história de lançamento que afetou tudo
Inimigos da Realidade por 12f-2

No final de abril, enquanto os Caminhantes Brancos sitiavam Winterfell, algo mais interessante aconteceu conosco: fizemos um lançamento incomum. Em princípio, estamos constantemente lançando novos recursos em produção (como todo mundo). Mas este era diferente. A escala era tal que quaisquer erros potenciais que pudéssemos cometer afetariam todos os nossos serviços e usuários. Com isso, implementamos tudo conforme o planejado, dentro do período de inatividade planejado e anunciado, sem consequências para as vendas. O artigo é sobre como conseguimos isso e como qualquer pessoa pode repetir em casa.

Não vou agora descrever as decisões arquitetônicas e técnicas que tomamos ou contar como tudo funciona. São antes notas à margem sobre como ocorreu um dos lançamentos mais difíceis, que observei e no qual estive diretamente envolvido. Não reivindico completude ou detalhes técnicos; talvez eles apareçam em outro artigo.

Antecedentes + que tipo de funcionalidade é essa?

Estamos construindo uma plataforma em nuvem Soluções em nuvem Mail.ru (MCS), onde atuo como diretor técnico. E agora é hora de adicionar IAM (Gerenciamento de Identidade e Acesso) à nossa plataforma, que fornece gerenciamento unificado de todas as contas de usuários, usuários, senhas, funções, serviços e muito mais. Por que é necessário na nuvem é uma questão óbvia: todas as informações do usuário são armazenadas nela.

Normalmente, essas coisas começam a ser construídas logo no início de qualquer projeto. Mas historicamente as coisas têm sido um pouco diferentes no MCS. MCS foi construído em duas partes:

  • Openstack com seu próprio módulo de autorização Keystone,
  • Hotbox (armazenamento S3) baseado no projeto Mail.ru Cloud,

em torno do qual surgiram novos serviços.

Essencialmente, tratava-se de dois tipos diferentes de autorização. Além disso, usamos alguns desenvolvimentos separados do Mail.ru, por exemplo, um armazenamento geral de senhas do Mail.ru, bem como um conector openid auto-escrito, graças ao qual o SSO (autorização ponta a ponta) foi fornecido no painel Horizon de máquinas virtuais (UI OpenStack nativa).

Fazer o IAM para nós significou conectar tudo em um único sistema, completamente nosso. Ao mesmo tempo, não perderemos nenhuma funcionalidade ao longo do caminho, mas criaremos uma base para o futuro que nos permitirá refiná-la de forma transparente, sem refatorá-la, e escalá-la em termos de funcionalidade. Também no início, os usuários tinham um modelo para acesso aos serviços (RBAC central, controle de acesso baseado em funções) e algumas outras pequenas coisas.

A tarefa acabou não sendo trivial: python e perl, vários back-ends, serviços escritos de forma independente, várias equipes de desenvolvimento e administradores. E o mais importante, existem milhares de usuários ativos no sistema de produção de combate. Tudo isto tinha de ser escrito e, o mais importante, implementado sem vítimas.

O que vamos lançar?

Resumindo, em cerca de 4 meses preparamos o seguinte:

  • Criamos vários novos daemons que agregaram funções que anteriormente funcionavam em diferentes partes da infraestrutura. O restante dos serviços recebeu um novo back-end na forma desses demônios.
  • Escrevemos nosso próprio armazenamento central de senhas e chaves, disponível para todos os nossos serviços, que podem ser modificados livremente conforme necessário.
  • Escrevemos 4 novos backends para Keystone do zero (usuários, projetos, funções, atribuições de funções), que, de fato, substituíram seu banco de dados e agora atuam como um repositório único para nossas senhas de usuários.
  • Ensinamos todos os nossos serviços Openstack a recorrer a um serviço de política de terceiros para obter suas políticas, em vez de ler essas políticas localmente em cada servidor (sim, é assim que o Openstack funciona por padrão!)

Um retrabalho tão grande requer mudanças grandes, complexas e, o mais importante, síncronas em vários sistemas escritos por diferentes equipes de desenvolvimento. Depois de montado, todo o sistema deverá funcionar.

Como implementar essas mudanças e não estragar tudo? Primeiro decidimos olhar um pouco para o futuro.

Estratégia de implementação

  • Seria possível lançar o produto em várias etapas, mas isso triplicaria o tempo de desenvolvimento. Além disso, por algum tempo teríamos uma dessincronização completa dos dados nos bancos de dados. Você teria que escrever suas próprias ferramentas de sincronização e conviver com vários armazenamentos de dados por muito tempo. E isso cria uma grande variedade de riscos.
  • Tudo o que poderia ser preparado de forma transparente para o usuário foi feito com antecedência. Demorou 2 meses.
  • Permitimos um tempo de inatividade de várias horas - apenas para operações do usuário para criar e alterar recursos.
  • Para o funcionamento de todos os recursos já criados, o tempo de inatividade era inaceitável. Planejamos que, durante a implementação, os recursos funcionassem sem tempo de inatividade e sem afetar os clientes.
  • Para reduzir o impacto sobre nossos clientes caso algo dê errado, decidimos lançar no domingo à noite. Menos clientes gerenciam máquinas virtuais à noite.
  • Alertamos a todos os nossos clientes que durante o período selecionado para implantação a gestão do serviço estará indisponível.

Digressão: o que é um rollout?

<cuidado, filosofia>

Todo especialista em TI pode responder facilmente o que é uma implementação. Você instala o CI/CD e tudo é entregue automaticamente na loja. 🙂

Claro que isso é verdade. Mas a dificuldade é que, com ferramentas modernas de automação de entrega de código, a compreensão da implementação em si é perdida. Como você esquece a epopeia da invenção da roda quando olha para o transporte moderno. Tudo é tão automatizado que muitas vezes a implementação é realizada sem a compreensão do quadro completo.

E a imagem toda é assim. A implementação consiste em quatro aspectos principais:

  1. Entrega de código, incluindo modificação de dados. Por exemplo, suas migrações.
  2. A reversão de código é a capacidade de voltar atrás se algo der errado. Por exemplo, através da criação de backups.
  3. Hora de cada operação de implementação/reversão. Você precisa entender o momento de qualquer operação dos dois primeiros pontos.
  4. Funcionalidade afetada. É necessário avaliar os efeitos positivos esperados e os possíveis efeitos negativos.

Todos esses aspectos devem ser levados em consideração para uma implementação bem-sucedida. Normalmente, apenas o primeiro ou, na melhor das hipóteses, o segundo ponto é avaliado e, então, a implementação é considerada bem-sucedida. Mas o terceiro e o quarto são ainda mais importantes. Qual usuário gostaria que o lançamento demorasse 3 horas em vez de um minuto? Ou se algo desnecessário for afetado durante a implementação? Ou será que o tempo de inatividade de um serviço terá consequências imprevisíveis?

Ato 1..n, preparação para lançamento

A princípio pensei em descrever brevemente nossas reuniões: toda a equipe, suas partes, muitas discussões nos cafés, discussões, testes, brainstorms. Então pensei que seria desnecessário. Quatro meses de desenvolvimento sempre consistem nisso, especialmente quando você não está escrevendo algo que pode ser entregue constantemente, mas sim um grande recurso para um sistema ativo. O que afeta todos os serviços, mas nada deve mudar para os usuários, exceto “um botão na interface web”.

Nossa compreensão de como implementar mudou a cada nova reunião, e de forma bastante significativa. Por exemplo, atualizaríamos todo o nosso banco de dados de faturamento. Mas calculamos o tempo e percebemos que era impossível fazer isso em um tempo de implementação razoável. Levamos quase mais uma semana para fragmentar e arquivar o banco de dados de faturamento. E quando a velocidade de implementação esperada ainda não era satisfatória, encomendamos hardware adicional e mais potente, onde toda a base foi arrastada. Não é que não quiséssemos fazer isso antes, mas a atual necessidade de implementação nos deixou sem opções.

Quando um de nós teve dúvidas de que o rollout poderia afetar a disponibilidade de nossas máquinas virtuais, passamos uma semana realizando testes, experimentos, análise de código e recebemos o claro entendimento de que isso não aconteceria em nossa produção, e até as pessoas mais duvidosas concordaram com isso.

Enquanto isso, o pessoal do suporte técnico conduziu seus próprios experimentos independentes para escrever instruções aos clientes sobre os métodos de conexão, que deveriam mudar após o lançamento. Eles trabalharam na experiência do usuário do usuário, prepararam instruções e forneceram consultas pessoais.

Automatizamos todas as operações de implementação possíveis. Cada operação era roteirizada, mesmo as mais simples, e os testes eram executados constantemente. Eles discutiram sobre a melhor maneira de desligar o serviço - omitir o daemon ou bloquear o acesso ao serviço com um firewall. Criamos uma lista de verificação de equipes para cada etapa do lançamento e a atualizamos constantemente. Desenhamos e atualizamos constantemente um gráfico de Gantt para todo o trabalho de implementação, com cronogramas.

E assim ...

O ato final, antes do lançamento

...é hora de lançar.

Como se costuma dizer, uma obra de arte não pode ser concluída, apenas terminada de trabalhar nela. Você tem que fazer um esforço de vontade, entendendo que não vai encontrar tudo, mas acreditando que fez todas as suposições razoáveis, providenciou todos os casos possíveis, fechou todos os bugs críticos e todos os participantes fizeram tudo o que podiam. Quanto mais código você lança, mais difícil é se convencer disso (além disso, todos entendem que é impossível prever tudo).

Decidimos que estávamos prontos para implementar quando estávamos convencidos de que havíamos feito todo o possível para cobrir todos os riscos para nossos usuários associados a efeitos inesperados e tempos de inatividade. Ou seja, tudo pode dar errado, exceto:

  1. Afetar a infraestrutura do usuário (sagrada para nós, mais preciosa),
  2. Funcionalidade: a utilização do nosso serviço após o lançamento deverá ser a mesma de antes.

Lançamento

Uma história de lançamento que afetou tudo
Dois rolam, 8 não interferem

Reduzimos o tempo de inatividade para todas as solicitações dos usuários por 7 horas. Neste momento, temos um plano de implementação e um plano de reversão.

  • A implementação em si leva aproximadamente 3 horas.
  • 2 horas para teste.
  • 2 horas - reserva para possível reversão de alterações.

Foi elaborado um gráfico de Gantt para cada ação, quanto tempo leva, o que acontece sequencialmente, o que é feito em paralelo.

Uma história de lançamento que afetou tudo
Um pedaço de um gráfico de Gantt de implementação, uma das primeiras versões (sem execução paralela). A ferramenta de sincronização mais valiosa

Todos os participantes têm seu papel determinado na implementação, quais tarefas eles realizam e quais são suas responsabilidades. Tentamos tornar cada estágio automático, implementá-lo, revertê-lo, coletar feedback e implementá-lo novamente.

Crônica de eventos

Assim, 15 pessoas vieram trabalhar no domingo, 29 de abril, às 10h. Além dos principais participantes, alguns vieram simplesmente para apoiar a equipe, pelo que lhes agradecemos especialmente.

Também vale a pena mencionar que nosso principal testador está de férias. É impossível implementar sem testes, estamos explorando opções. Uma colega concorda em nos testar nas férias, pelo que recebe imenso agradecimento de toda a equipe.

00:00. Parar
Paramos as solicitações dos usuários, penduramos uma placa dizendo trabalho técnico. O monitoramento grita, mas está tudo normal. Verificamos se nada caiu além do que deveria cair. E começamos a trabalhar na migração.

Todo mundo tem um plano de implementação impresso ponto por ponto, todo mundo sabe quem está fazendo o quê e em que momento. Após cada ação, verificamos os tempos para garantir que não os ultrapassamos e que tudo corre conforme o planeado. Quem não participa diretamente do lançamento na fase atual está se preparando com o lançamento de um brinquedo online (Xonotic, charlatães tipo 3) para não incomodar os colegas. 🙂

02:00. Lançado
Uma agradável surpresa: finalizamos o lançamento uma hora antes, devido à otimização de nossos bancos de dados e scripts de migração. O grito geral, “lançado!” Todas as novas funções estão em produção, mas até agora só podemos vê-las na interface. Todos entram em modo de teste, classificam-nos em grupos e começam a ver o que aconteceu no final.

Não deu muito certo, percebemos isso depois de 10 minutos, quando nada está conectado ou funcionando nos projetos dos integrantes da equipe. Sincronização rápida, expressamos nossos problemas, definimos prioridades, dividimos as equipes e iniciamos a depuração.

02:30. Dois grandes problemas versus quatro olhos
Encontramos dois grandes problemas. Percebemos que os clientes não veriam alguns serviços conectados e surgiriam problemas com contas de parceiros. Ambos se devem a scripts de migração imperfeitos para alguns casos extremos. Precisamos consertar isso agora.

Escrevemos solicitações que corrigem isso, com pelo menos 4 olhos. Nós os testamos durante a pré-produção para garantir que funcionam e não quebram nada. Você pode continuar. Ao mesmo tempo, realizamos testes regulares de integração, o que revela mais alguns problemas. Eles são todos pequenos, mas também precisam ser consertados.

03:00. -2 problemas +2 problemas
Os dois grandes problemas anteriores foram corrigidos, e quase todos os menores também. Todos aqueles que não estão ocupados com consertos estão trabalhando ativamente em suas contas e relatando o que encontram. Priorizamos, distribuímos entre as equipes e deixamos os itens não críticos para a manhã.

Executamos os testes novamente e eles descobrem dois novos grandes problemas. Nem todas as políticas de serviço chegaram corretamente, portanto, algumas solicitações de usuários não passam na autorização. Além de um novo problema com contas de parceiros. Vamos nos apressar para olhar.

03:20. Sincronização de emergência
Um novo problema corrigido. Para o segundo, estamos organizando uma sincronização de emergência. Entendemos o que está acontecendo: a correção anterior resolveu um problema, mas criou outro. Fazemos uma pausa para descobrir como fazê-lo corretamente e sem consequências.

03:30. Seis olhos
Entendemos qual deve ser o estado final da base para que tudo corra bem para todos os parceiros. Escrevemos uma solicitação com 6 olhos, lançamos na pré-produção, testamos e lançamos para produção.

04:00. Tudo está funcionando
Todos os testes foram aprovados, nenhum problema crítico é visível. De vez em quando algo na equipe não funciona para alguém, reagimos prontamente. Na maioria das vezes o alarme é falso. Mas às vezes algo não chega ou uma página separada não funciona. Nós sentamos, consertamos, consertamos, consertamos. Uma equipe separada está lançando o último grande recurso: faturamento.

04:30. Ponto sem retorno
Aproxima-se o ponto sem volta, ou seja, o momento em que, se começarmos a reverter, não cumpriremos o tempo de inatividade que nos foi dado. Há problemas com o faturamento, que sabe e registra tudo, mas se recusa teimosamente a dar baixa no dinheiro dos clientes. Existem vários bugs em páginas, ações e status individuais. A funcionalidade principal funciona, todos os testes foram aprovados com sucesso. Decidimos que o lançamento ocorreu, não iremos reverter.

06:00. Aberto para todos na IU
Defeitos consertados. Algumas que não agradam aos usuários ficam para depois. Abrimos a interface para todos. Continuamos trabalhando no faturamento, aguardando o feedback dos usuários e monitorando os resultados.

07:00. Problemas com carregamento da API
Fica claro que planejamos um pouco mal a carga em nossa API e testamos essa carga, o que não conseguiu identificar o problema. Como resultado, ≈5% das solicitações falham. Vamos nos mobilizar e procurar o motivo.

O faturamento é teimoso e também não quer funcionar. Decidimos adiar para mais tarde para realizar as mudanças com tranquilidade. Ou seja, nele ficam acumulados todos os recursos, mas as baixas de clientes não passam. Claro, isto é um problema, mas comparado com a implementação geral parece não ter importância.

08:00. Corrigir API
Implementamos uma correção para a carga, as falhas desapareceram. Começamos a ir para casa.

10:00. Todos
Tudo está consertado. O monitoramento é tranquilo e na casa dos clientes a equipe vai dormindo aos poucos. O faturamento permanece, iremos restaurá-lo amanhã.

Depois, durante o dia, houve lançamentos que corrigiram logs, notificações, códigos de retorno e personalizações para alguns de nossos clientes.

Então, o lançamento foi um sucesso! Poderia, claro, ser melhor, mas tiramos conclusões sobre o que não foi suficiente para alcançarmos a perfeição.

No total

Durante 2 meses de preparação ativa para a implementação, foram concluídas 43 tarefas, que duraram de algumas horas a vários dias.

Durante a implementação:

  • demônios novos e alterados - 5 peças, substituindo 2 monólitos;
  • alterações nos bancos de dados - todos os nossos 6 bancos de dados com dados de usuários foram afetados, foram feitos downloads de três bancos de dados antigos para um novo;
  • front-end completamente redesenhado;
  • quantidade de código baixado - 33 mil linhas de código novo, ≈ 3 mil linhas de código em testes, ≈ 5 mil linhas de código de migração;
  • todos os dados estão intactos, nenhuma máquina virtual do cliente foi danificada. 🙂

Boas práticas para uma boa implementação

Eles nos guiaram nesta situação difícil. Mas, de modo geral, é útil segui-los durante qualquer implementação. Mas quanto mais complexa for a implementação, maior será o papel que desempenham.

  1. A primeira coisa que você precisa fazer é entender como a implementação pode ou irá afetar os usuários. Haverá tempo de inatividade? Em caso afirmativo, qual é o tempo de inatividade? Como isso afetará os usuários? Quais são os possíveis melhores e piores cenários? E cubra os riscos.
  2. Planeje tudo. Em cada estágio, você precisa compreender todos os aspectos da implementação:
    • entrega de código;
    • reversão de código;
    • horário de cada operação;
    • funcionalidade afetada.
  3. Experimente os cenários até que todas as fases da implementação, bem como os riscos de cada uma delas, se tornem óbvios. Se tiver alguma dúvida, você pode fazer uma pausa e examinar a fase questionável separadamente.
  4. Cada etapa pode e deve ser melhorada se ajudar nossos usuários. Por exemplo, reduzirá o tempo de inatividade ou removerá alguns riscos.
  5. O teste de reversão é muito mais importante do que o teste de entrega de código. É necessário verificar se com o rollback o sistema retornará ao seu estado original, e confirmar isso com testes.
  6. Tudo o que pode ser automatizado deve ser automatizado. Tudo o que não pode ser automatizado deve ser escrito antecipadamente em uma folha de dicas.
  7. Registre o critério de sucesso. Que funcionalidade deve estar disponível e em que horário? Se isso não acontecer, execute um plano de reversão.
  8. E o mais importante - pessoas. Todos devem estar cientes do que estão fazendo, por que e o que depende de suas ações no processo de implementação.

E em uma frase, com um bom planejamento e elaboração você pode lançar o que quiser sem consequências nas vendas. Mesmo algo que afetará todos os seus serviços em produção.

Fonte: habr.com

Adicionar um comentário