Ciência e Tecnologia

Servidor Falhando Mas Sem Cair

Trabalho em sistemas resilientes há muitos anos. Na década de 1990 como um Engenheiro Distinto na Sun Microsystems, ajudei alguns dos primeiros sites da Internet através de suas dores de crescimento precoces e entrei para o eBay em 2004 com o título Distinguished Availability Engineer. Em 2007, a Netflix me contratou para ajudá-los a construir serviços de streaming de vídeo escaláveis e resilientes e em 2010 liderei sua transição para uma arquitetura baseada em nuvem na AWS. Nos últimos dez anos, venho construindo e falando sobre a Chaos Engineering,arquiteturas de nuvem multi-zonas e multi-regiões, e modernizando práticas de desenvolvimento.

À medida que os aplicativos se movem online e a automação digital se estende para controlar mais do mundo físico ao nosso redor, as falhas de software têm um impacto crescente nos resultados dos negócios e na segurança. Precisamos desenvolver sistemas mais resistentes, e isso não pode ser deixado como uma preocupação operacional. Os engenheiros precisam arquitetar a resiliência no código de aplicação, e a operabilidade é um dos atributos mais importantes de um sistema resiliente. A experiência do operador precisa ser clara e responsiva, especialmente durante uma falha. Temos visto muitos exemplos de pequenos problemas iniciais aumentando, pois códigos e procedimentos mal projetados e testados falham de maneiras que ampliam o problema e tiram todo o sistema.

O que podemos fazer quanto a isso? Para começar, é uma responsabilidade compartilhada entre suas equipes técnicas construir e operar sistemas que sejam observáveis, controláveis e resistentes. Com a integração de funções das práticas de DevOps e a automação fornecida pelos provedores de nuvem, precisamos adaptar conceitos comuns e terminologia que já existem no design de sistemas resilientes para arquiteturas nativas da nuvem.

O que seu sistema deve fazer quando algo de que depende falha? Há três resultados comuns. Poderia parar até que o que falhou seja restaurado; poderia contornar a falha e continuar com funcionalidade reduzida; ou poderia cair e causar um fracasso ainda maior! Infelizmente, para muitos sistemas hoje, o terceiro resultado é o padrão, seja porque eles não foram arquitetados para sobreviver ou, mais criticamente, não foram testados regularmente para provar que as soluções arquitetadas funcionavam como pretendido.

Há muitos tipos de falha em pensar, mas vou me concentrar no que normalmente é chamado de recuperação de desastres ou continuidade de negócios. Muitas organizações têm uma estratégia de data center de backup e um plano para falhar no backup se houver uma falha em seu data center principal. No entanto, quando pergunto às pessoas com que frequência exercem sua capacidade de failover de aplicativos, elas normalmente parecem envergonhadas. Muitas organizações nunca testaram um failover, e algumas que fazem isso apenas como parte de um processo de auditoria anual demorado e doloroso. Minha próxima pergunta é se eles algum dia desligaram um data center inteiro de uma vez, então todos os aplicativos têm que falhar juntos. A maioria das pessoas está horrorizada com a ideia de tentar isso como um teste. As pessoas que levam isso a sério tiveram que acontecer com eles quando um desastre real causou um fracasso.

Eu chamo esse estado de coisas de “teatro de disponibilidade”. Você passou pelos movimentos e atuou em um cenário de recuperação de desastres, mas apesar de gastar muito na produção, não é real. O que você tem é um conto de fadas: “Era uma vez, em teoria, se tudo funciona perfeitamente, temos um plano para sobreviver aos desastres que pensamos com antecedência.” Na prática, é mais provável que seja um pesadelo.

 

Quando os failovers falham

 

O Instituto Uptime registra informações públicas sobre paralisações. Seu relatório 2018-19 destacou uma tendência: “Grandes paralisações registradas publicamente são agora mais propensas a serem causadas por problemas de TI e rede do que há um ano ou dois anos, quando os problemas de energia eram uma causa maior.” A forma como desenvolvemos e operamos sistemas é fundamental, então o que podemos fazer para melhorar a situação? Para começar, devemos aprender com pessoas que têm estudado falhas e construído sistemas mais resilientes por décadas, adotar sua terminologia (em vez de inventar novas palavras para conceitos antigos) e adaptar suas ideias aos problemas de manter as aplicações funcionando.

Se olharmos para o tipo de “problemas de TI e rede…” que estão causando paralisações, são falhas de sistemas complexos. Publicado em 1984, o livro De Charles Perrow, Normal Accidents, foi inspirado no acidente de Three Mile Island em 1979, onde um colapso nuclear resultou de uma interação inesperada de múltiplas falhas em um sistema complexo. O evento foi um exemplo de acidente normal porque foi “inesperado, incompreensível, incontrolável e inevitável”.

 

“Desastres e fatalidades são excepcionais e não fazem parte da distribuição de possíveis resultados que a maioria das pessoas considera e modelam porque são inerentemente inaceitáveis.”

 

Por que desastres são inesperados? O livro de Todd Conklin de 2017, Workplace Fatalities: Failure to Predict, aponta que desastres e fatalidades são excepcionais e não fazem parte da distribuição de possíveis resultados que a maioria das pessoas considera e modelam porque são inerentemente inaceitáveis. Temos um exemplo muito atual no momento. Não acho que muitas organizações tinham “pandemia global” em seu plano de negócios para 2020, por isso se encaixa no padrão — inesperado, incompreensível, incontrolável e inevitável. Também vimos que os governos que tinham um plano bem testado esperavam a pandemia, entendiam melhor, eram capazes de controlá-la e evitavam as piores consequências.

O conceito de usar failover para aumentar a resiliência é que deve haver mais de uma maneira de ter sucesso e uma maneira de alternar entre eles. É relativamente fácil adicionar redundância a um sistema, verificar se a capacidade extra existe e justificar o custo extra de redundância contra o custo da falha. É muito mais difícil construir e testar a capacidade de falhar com sucesso. Para que um sistema seja verdadeiramente resiliente, o switch failover precisa ser muito mais confiável do que as alternativas entre as que está alternando; caso contrário, falhas do switch em si dominam a confiabilidade geral do sistema. Uma configuração de sistema que inclui failover é mais complexa e tem mais oportunidades de falhar do que um sistema mais simples. É por isso que a melhor prática é chegar a um nível maduro de excelência operacional em um sistema mais simples antes de adicionar mecanismos de failover.

Infelizmente, o mecanismo de comutação de failover e os processos de gerenciamento relacionados são muitas vezes as partes menos testadas do sistema, e é por isso que os sistemas geralmente entram em colapso quando tentam falhar. Para corrigir isso, precisamos passar de processos de teste de recuperação de desastres personalizados, dolorosos e anuais, para resiliência automatizada e continuamente testada. A razão pela qual isso é possível agora é que a computação em nuvem fornece automação consistente, juntamente com técnicas e ferramentas de engenharia do caos, que nos permitem padronizar e productizar recursos de failover.

Quais são as técnicas de engenharia do caos? Definidos simplesmente são experimentos para garantir que o impacto da falha seja mitigado. Você precisa executar experimentos que introduzem falhas, para mostrar que seu sistema pode lidar com essas falhas sem causar problemas visíveis ao usuário.

Quando você olha para um sistema complexo através das lentes dos engenheiros do caos, você pode pensar nele como elos em uma cadeia. Uma corrente é tão forte quanto o elo mais fraco, então as paralisações são muitas vezes desencadeadas pela única coisa que você esqueceu ou não tinha conseguido consertar ainda. A outra maneira de pensar sobre isso é uma corda com muitos fios, alguns desgastados e alguns quebrados. Os muitos fios de uma corda formam uma margem de capacidade, mas se você não prestar atenção ao número de fios que estão quebrados, você eventualmente “deriva parao fracasso ” como Sydney Dekker discutiu em seu livro de mesmo nome.

Dessa forma de pensar, o último fio que quebrou quando a falha ocorreu não é a “causa do fracasso”. A negligência sistemática do cabo desgastado ao longo do tempo é a causa. Para ser atual novamente, um raio pode desencadear um incêndio florestal específico, mas evitar que um raio não tenha impedido que um incêndio fosse provocado por outra coisa. Se a floresta está seca e cheia de árvores mortas por causa da mudança climática, então essa é uma causa sistemática de incêndios florestais.

Aplicando esse pensamento às aplicações que precisam falhar sem cair, precisamos construir e manter alguma margem de segurança, para que pequenas falhas individuais não aumentem e causem desastres. Os conceitos de “defesa em profundidade” e “controles e equilíbrios” são úteis, e precisam ser aplicados a todas as camadas do sistema. No entanto, eles precisam ser testados com frequência para ter certeza de que as verificações e a margem de segurança estão presentes e tendo o efeito desejado.

 

Engenharia de um mundo mais seguro

 

As características de um sistema resistente podem ser divididas em quatro camadas:

 

Equipe experiente – Use “dias de jogo” para entender como o sistema se comporta quando está gerenciando falhas e saber como observar e controlar rapidamente os problemas.

Aplicações robustas – Foram testadas usando ferramentas de teste de injeção de falhas e caos.

Tecido de comutação confiável – Uma estrutura de aplicação que compensa as falhas ao roteamento ao seu redor

Fundação de serviços redundantes – Serviços automatizados redundantes que mantêm cuidadosamente o isolamento para que as falhas sejam independentes

Se estamos tentando tornar nossos acidentes normais menos “inesperados, incompreensíveis, incontroláveis e inevitáveis”, então podemos começar fazendo uma análise de risco. Entender quais acidentes são possíveis deve resultar em menos acidentes inesperados. Podemos aumentar a observância e, em particular, melhorar a experiência do usuário da operadora durante acidentes para torná-los menos incompreensíveis. Então podemos ver como tornar o sistema mais controlável durante um acidente. Finalmente, como os acidentes são inevitáveis,a capacidade mais geral que podemos desenvolver é a velocidade de detecção e resposta para minimizar seu impacto.

Eu vi isso no meu tempo na Netflix. Construímos um sistema que se tornou bastante robusto com o tempo, e a maioria das coisas normais que dão errado estavam sendo tratadas, então tivemos muito poucos incidentes de impacto do cliente. Infelizmente, os incidentes que tivemos foram aqueles que não tínhamos visto antes. Nos livramos dos incidentes fáceis e comuns e ficamos com incidentes infrequentes que foram inesperados, incompreensíveis, incontroláveis e inevitáveis.

Para garantir que todos praticassem o que fazer durante um incidente e para pegar problemas rapidamente, a Netflix aumentou a frequência de testes de failover do dia do jogo de uma vez por trimestre para cada duas semanas. Dessa forma, poderíamos pegar novos problemas cedo, e fortalecer o linchamento do nosso sistema resiliente, funcionários experientes,tornando-os mais familiarizados com acidentes.

 

Uma Teoria dos Acidentes: Aviões, Trens e Mísseis Nucleares

 

À medida que procuro novas maneiras de ajudar a evitar que falhas se tornem quedas, tenho explorado a Análise de Processos Teóricos do Sistema (STPA), que faz parte de um conjunto mais amplo de técnicas chamada System Theoretic Accident Model and Processes (STAMP). Isso é descrito no livro Engenharia a Mundo Mais Seguro por Nancy G. Leveson do MIT.

As técnicas foram refinadas de muitos projetos de análise de riscos, incluindo sistemas usados para sistemas de reabastecimento a bordo, controle de tráfego aéreo dos EUA e sistemas de lançamento de mísseis nucleares. O STPA é baseado em um diagrama de controle funcional do sistema e nas restrições de segurança e requisitos para cada componente no projeto. Um diagrama de controle comum que podemos usar para sistemas de TI é dividido em três camadas: o plano de dados que é a própria função de negócio, o sistema de controle que gerencia essa função de negócio e os operadores humanos que vigiam o sistema de controle.

O foco é entender as conexões entre os componentes e como eles são afetados por falhas. Em um diagrama de “caixas e fios”, a maioria das pessoas se concentra em especificar as caixas e seus modos de falha e são menos precisas sobre as informações que fluem entre as caixas. Com o STPA, há um foco nos fios, o que as informações de controle fluem através deles, o que acontece se esses fluxos forem afetados e os modelos que consomem as informações e impulsionam as ações de controle.

Existem dois passos principais que formam boas listas de verificação para pensar em seu próprio design. Primeiro, identificar o potencial de controle inadequado do sistema que poderia levar a um estado perigoso. Este estado pode resultar de controle inadequado ou aplicação das restrições de segurança. Para o segundo passo, cada ação de controle potencialmente perigosa é examinada para ver como ela pode ocorrer. Avaliar controles e mecanismos de mitigação, buscando conflitos e problemas de coordenação. Considere como os controles podem se degradar ao longo do tempo, usando técnicas como gerenciamento de mudanças, auditorias de desempenho e revisões de incidentes para anomalias superficiais e problemas com o design do sistema.

Se pegarmos o modelo stpa geral e mapeá-lo para um aplicativo específico, como uma API de serviços financeiros que coleta solicitações de clientes e executa ações, então o controlador humano monitora o throughput do sistema para ter certeza de que está concluindo ações na taxa esperada. O controlador automatizado pode ser um autoescalador que está olhando para a utilização da CPU do processo controlado, aumentando o número de instâncias que estão suportando o tráfego para manter a utilização da CPU entre um nível mínimo fixo e máximo.

Se a utilização da CPU de serviço aumentar e o rendimento cair bruscamente, espera-se que o controlador humano observe e decida o que fazer sobre isso. Os controles disponíveis para eles são alterar os limites do autoescalador, reiniciar o plano de dados ou sistemas de plano de controle, ou reverter para uma versão anterior do código.

Os perigos nesta situação são que o controlador humano poderia fazer algo que o torna pior em vez de melhor. Eles não podiam fazer nada, porque não estão prestando atenção. Eles poderiam reiniciar todas as instâncias de uma só vez, o que pararia o serviço completamente. Eles podem surtar depois de uma grande queda no tráfego causada por muitos clientes que decidem assistir ao Superbowl na TV e tomar uma ação antes que seja necessário. Eles poderiam fazer algo tarde demais, como notar eventualmente depois que o sistema foi degradado por um tempo e aumentar o limite máximo de autoescalador. Eles podem fazer coisas na ordem errada, como reinicialização ou reversão antes de aumentar o autoescalador. Eles poderiam parar muito cedo, aumentando o limite de autoescalado, mas não o suficiente para fazer o sistema funcionar novamente, e ir embora assumindo que está fixo. Eles poderiam passar muito tempo reiniciando o sistema uma e outra vez. A equipe de resposta a incidentes poderia entrar em uma discussão sobre o que fazer, ou várias pessoas poderiam fazer diferentes mudanças ao mesmo tempo. Além disso, é provável que o runbook esteja desatualizado e contenha informações incorretas sobre como responder ao problema no sistema atual. Tenho certeza que muitos leitores já viram esses perigos pessoalmente!

Cada uma das informações flui no sistema também deve ser examinada para ver quais riscos podem ocorrer. Nos fluxos de observabilidade, os perigos típicos são um pouco diferentes dos fluxos de controle. Neste caso, o sensor que relata throughput poderia parar de relatar e ficar preso no último valor visto. Poderia relatar zero rendimento, mesmo que o sistema esteja funcionando corretamente. O valor relatado poderia transbordar numericamente e relatar um valor positivo negativo ou embrulhado. Os dados podem ser corrompidos e reportar um valor arbitrário. As leituras podem ser adiadas por diferentes quantidades para que sejam vistas fora de ordem. A taxa de atualização pode ser definida muito alta para que o sensor ou sistema de entrega métrico não possa acompanhar. As atualizações podem ser adiadas para que o sistema de monitoramento esteja mostrando status desatualizado, e o efeito das ações de controle não seja visto em breve. Isso muitas vezes leva à correção excessiva e oscilação no sistema, que é um exemplo de um problema de coordenação. As leituras dos sensores podem se degradar ao longo do tempo, talvez devido a vazamentos de memória ou atividade de coleta de lixo no caminho de entrega.

A terceira área de foco é pensar nos modelos que compõem o sistema, lembrando a máxima “Todos os modelos estão errados, alguns modelos são úteis”. Um autoescalador contém um modelo simples que decide qual ação de controle é necessária com base na utilização relatada. Esta é uma visão muito simples do mundo, e funciona bem desde que a utilização da CPU seja o principal gargalo. Por exemplo, vamos supor que o código está sendo executado em um sistema com quatro CPUs e uma atualização de software é empurrada para fora que contém alterações em algoritmos de bloqueio que serializam o código para que ele só possa fazer uso de uma CPU. A instância não pode ficar muito mais do que 25% ocupada, então o autoescalador reduzirá para o número mínimo de instâncias, mas o sistema será realmente sobrecarregado com baixo rendimento. Aqui, o autoescalador falha porque seu modelo não explica a situação que está tentando controlar.

O outro modelo importante a ser considerado é o modelo mental interno de quem está operando o sistema. Esse modelo é criado por treinamento e experiência com o sistema. O operador recebe um alerta e olha para o throughput e utilização e provavelmente será confundido. Uma correção pode ser aumentar o número mínimo de ocorrências. Se eles também são capazes de ver quando o novo software foi empurrado para fora e que isso se correlaciona com o início do problema, então eles também podem reverter para uma versão anterior como a ação de controle corretivo.

Algumas perguntas poderíamos fazer: o modelo do processo controlado está olhando para as métricas certas e se comportando com segurança? Qual é o fator de constante e amortecimento do tempo para o algoritmo de controle? Vai oscilar, tocar ou demorar muito para responder às entradas? Como espera-se que o controlador humano desenvolva seus próprios modelos do processo controlado e da automação, então entenda o que esperar quando fizer insumos de controle? Como a experiência do usuário é projetada para que o controlador humano seja notificado de forma rápida e precisa com informações suficientes para responder corretamente, mas sem muitos dados para passar ou muitos alarmes falsos?

 

Failover sem desmoronar na prática

 

O exemplo do autoescalador fornece uma introdução fácil aos conceitos, mas o foco desta história é como falhar sem cair, e podemos aplicar STPA a essa situação também. Devemos primeiro considerar os dois padrões de failover que são comuns em arquiteturas de nuvens, zona transversal e região transversal.

As zonas de disponibilidade são semelhantes às situações de failover do datacenter, onde os dois locais estão próximos o suficiente para que os dados possam ser replicados sincronizadamente em uma rede de latência bastante baixa. A AWS limita a distância entre as zonas de disponibilidade para menos de 100 quilômetros, com latência de alguns milissegundos. No entanto, para manter os modos de falha independentes, as zonas de disponibilidade estão a pelo menos dez quilômetros de distância, em diferentes zonas de inundação, com conexões de rede e energia separadas. Os dados que são escritos em um único local são atualizados automaticamente em todas as zonas, e o processo de failover geralmente é automático, de modo que um aplicativo que está em execução em três zonas deve ser capaz de continuar funcionando em duas zonas sem qualquer entrada do operador. Isso mantém a disponibilidade do aplicativo com apenas algumas falhas e tentativas para alguns clientes e nenhuma perda de dados.

O outro padrão é a região transversal. As regiões geralmente estão distantes o suficiente para que a latência seja muito alta para suportar atualizações síncronas. Os failovers geralmente são iniciados manualmente e demoram o suficiente para que muitas vezes sejam visíveis aos clientes.

As configurações de serviço de aplicação e suporte são diferentes nos dois casos, mas a diferença essencial do ponto de vista da análise de riscos é no que quero focar. Presumo que os failovers da zona sejam acionados automaticamente pelo plano de controle, e os failovers da região são acionados manualmente por um operador.

 

 

Em uma situação de failover de zona transversal automatizada, o que é provável que aconteça? Uma corrida de tráfego extra da zona falhada e um trabalho extra de uma tempestade de solicitação-retry entre zonas faz com que as zonas restantes se esforcem e desencadeie uma falha completa do aplicativo. Enquanto isso, o serviço de roteamento que envia tráfego para as zonas e atua como o interruptor de failover também tem uma tempestade de rejulgar e é impactado. Controladores humanos confusos discordam entre si sobre se precisam fazer algo ou não, com inundações de erros, exibem essa realidade por vários minutos e livros de execução desatualizados. O plano de controle de roteamento não informa claramente os humanos se tudo é cuidado, e a zona offline atrasa e quebra outras métricas com uma enxurrada de erros.

Em falhas entre zonas, os controladores humanos não devem fazer nada! No entanto, confusos e trabalhando separadamente, eles tentam corrigir diferentes problemas. Algumas de suas ferramentas não são usadas com frequência e são quebradas ou mal configuradas para fazer a coisa errada. Eles eventualmente percebem que o sistema caiu. As primeiras vezes que você tenta isso em um dia de jogo, isso é o que você deve esperar que aconteça. Depois de corrigir os problemas de observabilidade, sintonizar as tempestades de rejulagem, implementar o escopo de solicitação zoneada para evitar chamadas não essenciais de zonas cruzadas e configurar ferramentas de correlação de alerta, você deve finalmente ser capaz de sentar e assistir a automação fazer a coisa certa sem que ninguém perceba.

Se você não tem a excelência operacional no local para operar dias de jogo de falha de zona de sucesso frequentes, então você não deve estar tentando implementar failover de várias regiões. É muito mais complexo, há mais oportunidades de fracasso, e você estará construindo um sistema menos confiável.

O que é provável que aconteça durante uma falha na região? A região falida cria uma enxurrada de erros e alertas, esses atrasos quebram outras métricas, e o plano de controle de roteamento entre regiões não informa claramente os humanos que uma região é inutilizável. Os controladores humanos devem iniciar o failover! Como dito acima para o failover do nível da zona, eles estão confusos e discordam entre si. Na verdade, o problema é pior porque eles têm que decidir se isso é uma falha no nível da zona ou uma falha no nível da região, e responder adequadamente.

Os operadores então decidem iniciar o failover, mas redirecionam o tráfego muito rapidamente, o trabalho extra de uma tempestade de solicitação-retry entre regiões faz com que outras regiões se esforcem e desencadeie uma falha completa do aplicativo. Enquanto isso, o serviço de roteamento também tem uma tempestade de rejuladas e é impactado, de modo que os operadores perdem o controle do processo de failover. Soa familiar? Alguns leitores podem ter flashbacks de TEPT neste momento. Novamente, a única maneira de ter certeza de que um failover funcionará quando você precisar é rodar dias de jogo com frequência suficiente para que os operadores saibam o que fazer. Certifique-se de que as tempestades de rejulga são minimizadas, os alertas de inundações são contidos e correlacionados, e os sistemas de observabilidade e controle são bem testados na situação de failover.

Seus operadores precisam manter constantemente seu modelo mental do sistema. Este modelo é criado pela experiência de operação do sistema e suportado por documentação e treinamento. No entanto, com entrega contínua e uma alta taxa de mudança no aplicativo, documentação e treinamento não estarão atualizados. Quando os failovers entre zonas e regiões são adicionados ao modelo operacional, ele fica muito mais complexo. Uma maneira de reduzir a complexidade é manter padrões consistentes que imponham simetria na arquitetura. Qualquer coisa que torne uma determinada zona ou uma região diferente é um problema, então implante tudo de forma automática e idêntica em cada zona e região. Se algo não é o mesmo em todos os lugares, faça essa diferença o mais visível possível. A arquitetura multi-região da Netflix que implantamos em 2013 continha um total de nove cópias completas de todos os dados e serviços (três zonas por três regiões), e ao fechar zonas e regiões regularmente, qualquer coisa que quebrasse essa simetria seria descoberta e fixa.

Failover é facilitado pelos serviços comuns que a AWS fornece, mas fica muito mais complexo se cada aplicativo tiver uma arquitetura de failover única, e há pouca comunhão em toda a base de clientes da AWS. O Pilar de Confiabilidade do Guia Bem Arquiteto da AWS contém muitos conselhos úteis, e também suporta práticas e idiomas comuns entre contas e clientes, o que socializa um modelo mais consistente entre controladores mais humanos. Essa padronização dos serviços subjacentes e do modelo de operador humano pela AWS é uma grande ajuda para aumentar a confiança de que será possível falhar sem cair.

Por onde você deve começar? Acho que o primeiro passo mais eficaz é começar uma série regular de exercícios do dia do jogo. Treine seu pessoal para trabalhar em conjunto em chamadas de incidentes, obter seus painéis e controles existentes juntos, e você terá uma resposta muito mais rápida e coordenada para o próximo incidente. Crie seus próprios diagramas de controle e pense nas listas de riscos definidas pelo STPA. Gradualmente, trabalhe desde os dias de jogo usando exercícios simulados, para testar ambientes, para aplicações de produção endurecidas consertando as coisas à medida que você vai.

Mostrar mais

Artigos relacionados

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Botão Voltar ao topo