Citymobil - um manual para melhorar a disponibilidade em meio ao crescimento dos negócios para startups. Parte 3



Este é o próximo artigo da série que descreve como estamos aumentando nossa disponibilidade de serviços no Citymobil (você pode ler as partes anteriores aqui e aqui ). Em outras partes, falarei sobre os acidentes e interrupções em detalhes. Mas primeiro, deixe-me destacar algo sobre o que eu deveria ter falado no primeiro artigo, mas não o fiz. Eu descobri sobre isso a partir dos comentários dos meus leitores. Este artigo me dá a chance de corrigir essa falha irritante.

1. Prólogo


Um leitor me fez uma pergunta muito justa: "O que há de tão complicado no backend do serviço de carona?" Essa é uma boa pergunta. No verão passado, eu me fiz essa pergunta antes de começar a trabalhar na Citymobil. Eu estava pensando: "isso é apenas um serviço de táxi com seu aplicativo de três botões". Quão difícil isso pode ser? Tornou-se um produto de alta tecnologia. Para esclarecer um pouco sobre o que estou falando e sobre o que é uma grande coisa tecnológica, vou falar sobre algumas instruções de produtos na Citymobil:

  • Preços. Nossa equipe de preços lida com o problema do melhor preço de pedalada em todos os momentos e momentos. O preço é determinado pela previsão de equilíbrio de oferta e demanda com base em estatísticas e em alguns outros dados. Tudo isso é feito por um serviço complicado e em constante desenvolvimento, baseado no aprendizado de máquina. Além disso, a equipe de preços lida com a implementação de vários métodos de pagamento, encargos extras ao concluir uma viagem, estornos, cobrança, interação com parceiros e motoristas.
  • Ordens de expedição. Qual carro completa o pedido do cliente? Por exemplo, uma opção de escolher o veículo mais próximo não é a melhor em termos de maximização de várias viagens. A melhor opção é combinar carros e clientes para maximizar o número de viagens, considerando a probabilidade desse cliente específico cancelar seu pedido nessas circunstâncias específicas (porque a espera é muito longa) e a probabilidade desse motorista específico cancelar ou sabotar o pedido ( por exemplo, porque a distância é muito grande ou o preço é muito pequeno).
  • Geo. Tudo sobre pesquisa e sugestão de endereços, pontos de coleta, ajustes da hora estimada de chegada (nossos parceiros de fornecimento de mapas nem sempre fornecem informações precisas sobre o ETA com margem para tráfego), aumento da precisão da geocodificação direta e reversa, aumento da precisão do ponto de chegada do carro. Há muitos dados, muitas análises, muitos serviços baseados em aprendizado de máquina.
  • Antifraude A diferença no custo da viagem para um passageiro e um motorista (por exemplo, em viagens curtas) cria um incentivo econômico para invasores que tentam roubar nosso dinheiro. Lidar com fraudes é um pouco semelhante a lidar com spam de e-mail - tanto a precisão quanto a retirada são muito importantes. Precisamos bloquear o número máximo de fraudes (recall), mas, ao mesmo tempo, não podemos aceitar bons usuários por fraudes (precisão).
  • A equipe de incentivos ao motorista supervisiona o desenvolvimento de tudo o que pode aumentar o uso de nossa plataforma pelos motoristas e a lealdade dos motoristas devido a diferentes tipos de incentivos. Por exemplo, complete viagens X e ganhe dinheiro Y extra. Ou compre um turno para Z e dirija sem comissão.
  • Aplicativo de driver back-end. Lista de pedidos, mapa de demanda (mostra um driver para onde maximizar seus lucros), alterações de status, sistema de comunicação com os drivers e muitas outras coisas.
  • Back-end do aplicativo do cliente (essa é provavelmente a parte mais óbvia e é o que as pessoas costumam chamar de "back-end de táxi"): colocação de pedidos, informações sobre o status do pedido, movimento de pequenos carros no mapa, dicas de back-end etc.

Esta é apenas a ponta do iceberg. Há muito mais funcionalidade. Há uma enorme parte subaquática do iceberg por trás do que parece ser uma interface bastante simples.

E agora vamos voltar aos acidentes. Seis meses de registro do histórico de acidentes resultaram na seguinte classificação:

  • versão ruim: 500 erros internos do servidor;
  • versão ruim: sobrecarga de banco de dados;
  • infeliz interação manual de operação do sistema;
  • Ovos de pascoa;
  • razões externas;
  • versão ruim: funcionalidade corrompida.

Abaixo, detalharei as conclusões que tiramos sobre nossos tipos de acidentes mais comuns.

2. Versão incorreta: 500 erros internos do servidor


Nosso back-end é escrito principalmente em PHP - uma linguagem interpretada de tipo fraco. Lançaríamos um código que falhou devido ao erro no nome da classe ou função. E esse é apenas um exemplo quando ocorre um erro 500. Também pode ser causado por erro lógico no código; ramo errado foi liberado; pasta com o código foi excluída por engano; artefatos temporários necessários para teste foram deixados no código; a estrutura das tabelas não foi alterada de acordo com o código; os scripts cron necessários não foram reiniciados ou parados.

Fomos abordando gradualmente esse problema em etapas. As viagens perdidas devido a um lançamento ruim são obviamente proporcionais ao tempo de produção. Portanto, devemos fazer o melhor possível e minimizar o tempo ruim de lançamento na produção. Qualquer alteração no processo de desenvolvimento que reduza o tempo médio de operação ruim da versão, mesmo em 1 segundo, é boa para os negócios e deve ser implementada.

Versão ruim e, de fato, qualquer acidente de produção tem dois estados que chamamos de "estágio passivo" e "estágio ativo". Durante a fase passiva, ainda não estamos cientes de um acidente. O estágio ativo significa que já sabemos. Um acidente começa na fase passiva; com o tempo, entra no estágio ativo - é quando descobrimos e começamos a abordar: primeiro diagnosticamos e depois corrigimos.

Para reduzir a duração de qualquer interrupção, precisamos reduzir a duração dos estágios ativo e passivo. O mesmo vale para uma versão ruim, pois é considerada uma espécie de interrupção.

Começamos a analisar o histórico de solução de problemas de interrupções. Os lançamentos ruins que tivemos quando começamos a analisar os acidentes causaram uma média de 20 a 25 minutos de inatividade (completa ou parcial). O estágio passivo normalmente leva 15 minutos, e o ativo - 10 minutos. Durante o estágio passivo, receberíamos reclamações de usuários que foram processadas por nossa central de atendimento; e após algum limite específico, a central de atendimento reclamava em um bate-papo do Slack. Às vezes, um de nossos colegas reclamava por não conseguir pegar um táxi. A reclamação do colega sinalizaria um problema sério. Depois que uma versão ruim entrou no estágio ativo, iniciamos o diagnóstico do problema, analisando versões recentes, vários gráficos e logs para descobrir a causa do acidente. Ao determinar as causas, reverteríamos se a versão incorreta fosse a mais recente ou executaríamos uma nova implantação com a confirmação revertida.

Esse é o processo de manipulação de versões ruins que foi criado para melhorar.

Etapa passiva: 20 minutos.
Estágio ativo: 10 minutos.

3. Redução passiva de estágio


Antes de tudo, percebemos que, se uma versão ruim fosse acompanhada por 500 erros, poderíamos dizer que ocorreu um problema mesmo sem as queixas dos usuários. Felizmente, todos os 500 erros foram registrados na New Relic (este é um dos sistemas de monitoramento que usamos) e tudo o que precisamos fazer foi adicionar notificações por SMS e URA sobre exceder um número específico de 500 erros. O limiar seria continuamente reduzido com o passar do tempo.

O processo em tempos de acidente seria assim:

  1. Um engenheiro implementa uma liberação.
  2. O lançamento leva a um acidente (quantidade massiva de 500).
  3. Mensagem de texto recebida.
  4. Engenheiros e devops começam a investigar. Às vezes, não imediatamente, mas em 2 a 3 minutos: a mensagem de texto pode demorar, os sons do telefone podem estar desativados; e, é claro, o hábito de reação imediata ao receber esse texto não pode ser formado da noite para o dia.
  5. O estágio ativo do acidente começa e dura os mesmos 10 minutos de antes.

Como resultado, o estágio ativo do tipo de acidente "Versão ruim: 500 erros internos do servidor" seria iniciado 3 minutos após a liberação. Portanto, o estágio passivo foi reduzido de 15 minutos para 3.

Resultado:

Etapa passiva: 3 minutos.
Estágio ativo: 10 minutos.

4. Redução adicional de uma etapa passiva


Embora o estágio passivo tenha sido reduzido para 3 minutos, ainda nos incomodou mais do que o ativo, pois durante o estágio ativo estávamos fazendo algo tentando resolver o problema, e durante o estágio passivo o serviço estava total ou parcialmente inativo. eram absolutamente sem noção.

Para reduzir ainda mais o estágio passivo, decidimos sacrificar 3 minutos do tempo de nossos engenheiros após cada versão. A idéia era muito simples: implantaríamos código e, por três minutos depois, procurávamos 500 erros em New Relic, Sentry e Kibana. Assim que detectássemos um problema, assumiríamos que ele estava relacionado ao código e começamos a solucionar o problema.

Escolhemos esse período de três minutos com base nas estatísticas: algumas vezes, os problemas apareciam nos gráficos em um ou dois minutos, mas nunca em três minutos.

Esta regra foi adicionada ao fazer e não fazer. No começo, nem sempre era seguido, mas com o tempo nossos engenheiros se acostumaram com essa regra, como fizeram com a higiene básica: escovar os dentes de manhã leva um tempo também, mas ainda é necessário.

Como resultado, o estágio passivo foi reduzido para 1 minuto (os gráficos ainda estavam atrasados ​​às vezes). Também reduziu o estágio ativo como um bom bônus. Porque agora um engenheiro enfrentaria o problema preparado e estaria pronto para reverter seu código imediatamente. Mesmo que nem sempre tenha ajudado, já que o problema poderia ter sido causado por uma versão implantada simultaneamente por outra pessoa. Dito isto, o estágio ativo em média foi reduzido para cinco minutos.

Resultado:

Etapa passiva: 1 minuto.
Estágio ativo: 5 minutos.

5. Redução adicional de uma etapa ativa


Ficamos mais ou menos satisfeitos com o estágio passivo de 1 minuto e começamos a pensar em como reduzir ainda mais um estágio ativo. Antes de tudo, concentramos nossa atenção no histórico de interrupções (por acaso é uma pedra angular em um edifício de nossa disponibilidade!) E descobrimos que, na maioria dos casos, não lançamos uma liberação imediatamente, pois não sabemos qual versão devemos escolher: existem muitos lançamentos paralelos. Para resolver esse problema, introduzimos a regra a seguir (e a escrevemos nos prós e contras): logo antes do lançamento, alguém deve notificar todos em um bate-papo do Slack sobre o que você está prestes a implantar e por que; em caso de acidente, deve-se escrever: "Acidente, não implante!" Também começamos a notificar quem não lê o bate-papo sobre os lançamentos via SMS.

Essa regra simples reduziu drasticamente o número de liberações durante um acidente em andamento, diminui a duração da solução de problemas e reduziu o estágio ativo de 5 minutos para 3.

Resultado:

Etapa passiva: 1 minuto.
Estágio ativo: 3 minutos.

6. Redução ainda maior de um estágio ativo


Apesar de termos publicado avisos no bate-papo sobre todos os lançamentos e acidentes, as condições de corrida ainda ocorriam às vezes - alguém postou sobre um lançamento e outro engenheiro estava implantando naquele exato momento; ou ocorreu um acidente, escrevemos sobre isso no bate-papo, mas alguém tinha acabado de implantar seu código. Tais circunstâncias prolongaram a solução de problemas. Para resolver esse problema, implementamos a proibição automática de versões paralelas. Foi uma ideia muito simples: por 5 minutos após cada versão, o sistema de CI / CD proíbe outra implantação para qualquer pessoa, exceto o autor da versão mais recente (para que ela possa reverter ou implantar o hotfix, se necessário) e vários desenvolvedores experientes (em caso de emergência). Mais do que isso, o sistema de CI / CD impede implantações em tempos de acidentes (ou seja, desde o momento em que a notificação sobre o início do acidente chega e até a chegada da notificação sobre o seu término).

Portanto, nosso processo começou assim: um engenheiro implementa uma liberação, monitora os gráficos por três minutos e, depois disso, ninguém pode implantar nada por mais dois minutos. Caso ocorra algum problema, o engenheiro reverte a liberação. Essa regra simplificou drasticamente a solução de problemas e a duração total dos estágios ativo e passivo foi reduzida de 3 + 1 = 4 minutos para 1 + 1 = 2 minutos.

Mas até um acidente de dois minutos foi demais. Por isso, continuamos trabalhando em nossa otimização de processos.

Resultado:

Etapa passiva: 1 minuto.
Estágio ativo: 1 minuto.

7. Determinação e reversão automáticas de acidentes


Estávamos pensando há algum tempo em como reduzir a duração dos acidentes causados ​​por lançamentos ruins. Nós até tentamos nos forçar a olhar para a tail -f error_log | grep 500 tail -f error_log | grep 500 . Mas no final, optamos por uma solução automática drástica.

Em poucas palavras, é uma reversão automática. Temos um servidor da web separado e o carregamos via balanceador 10 vezes menos que o restante de nossos servidores da web. Cada versão seria implantada automaticamente pelos sistemas de CI / CD neste servidor separado (chamamos de pré - produção, mas , apesar do nome, ela recebia carga real dos usuários reais). Em seguida, o script executaria tail -f error_log | grep 500 tail -f error_log | grep 500 . Se em um minuto não houvesse erro 500, o CI / CD implantaria a nova versão em produção em outros servidores da web. Caso houvesse erros, o sistema revertia tudo. No nível do balanceador, todas as solicitações que resultaram em 500 erros na pré-produção seriam reenviadas em um dos servidores Web de produção.

Essa medida reduziu o impacto de 500 erros de liberação para zero. Dito isto, apenas no caso de erros nos controles automáticos, não abolimos nossa regra de observação de gráficos de três minutos. Isso é tudo sobre lançamentos ruins e 500 erros. Vamos para o próximo tipo de acidente.

Resultado:

Etapa passiva: 0 minutos.
Estágio ativo: 0 minutos.



Em outras partes, vou falar sobre outros tipos de interrupções na experiência do Citymobil e entrar em detalhes sobre cada tipo de interrupção; Também vou falar sobre as conclusões que tiramos sobre as interrupções, como modificamos o processo de desenvolvimento, que automação introduzimos. Fique atento!

Source: https://habr.com/ru/post/pt449708/


All Articles