Aumento do MTBF - Disponibilidade e muito mais: entendendo e melhorando a resiliência de sistemas distribuídos no AWS

Aumento do MTBF

O componente final para melhorar a disponibilidade é aumentar o MTBF. Isso pode se aplicar tanto ao software quanto aos serviços do AWS usados para executá-lo.

Aumentando o MTBF do sistema distribuído

Uma forma de aumentar o MTBF é reduzir os defeitos no software. Há várias maneiras de fazer isso. Os clientes podem usar ferramentas como o Amazon CodeGuru Reviewer para encontrar e corrigir erros comuns. Você também deve realizar análises abrangentes de código por pares, testes unitários, testes de integração, testes de regressão e testes de carga no software antes que ele seja implantado na produção. Aumentar a quantidade de cobertura de código nos testes ajudará a garantir que até mesmo caminhos incomuns de execução de código sejam testados.

A implantação de mudanças menores também pode ajudar a evitar resultados inesperados, reduzindo a complexidade da mudança. Cada atividade oferece a oportunidade de identificar e corrigir defeitos antes que eles possam ser invocados.

Outra abordagem para evitar falhas são os testes regulares. A implementação de um programa de engenharia de caos pode ajudar a testar como sua workload falha, validar procedimentos de recuperação e ajudar a encontrar e corrigir os modos de falha antes que eles ocorram na produção. Os clientes podem usar o AWS Fault Injection Simulator como parte de seu conjunto de ferramentas para experimentos de engenharia do caos.

A tolerância a falhas é outra forma de evitar falhas em um sistema distribuído. Módulos rápidos, novas tentativas com recuo e instabilidade exponenciais, transações e idempotência são técnicas para ajudar a tornar as workloads tolerantes a falhas.

As transações são um grupo de operações que aderem às propriedades ACID. Eles são os seguintes:

  • Atomicidade — Ou todas as ações acontecem ou nenhuma delas acontecerá.

  • Consistência — Cada transação deixa a workload em um estado válido.

  • Isolamento — As transações realizadas simultaneamente deixam a workload no mesmo estado como se tivessem sido executadas sequencialmente.

  • Durabilidade — Depois que uma transação é confirmada, todos os seus efeitos são preservados mesmo no caso de falha na workload.

Novas tentativas com recuo exponencial e instabilidade permitem que você supere falhas transitórias causadas por Heisenbugs, sobrecarga ou outras condições. Quando as transações são idempotentes, elas podem ser repetidas várias vezes sem efeitos colaterais.

Se considerarmos o efeito de um Heisenbug em uma configuração de hardware tolerante a falhas, não nos preocuparíamos, pois a probabilidade de o Heisenbug aparecer tanto no subsistema primário quanto no redundante é infinitesimalmente pequena. (Veja Jim Gray, "Por que os computadores param e o que pode ser feito a respeito? “, junho de 1985, Relatório Técnico Tandem 85.7.) Em sistemas distribuídos, queremos alcançar os mesmos resultados com nosso software.

Quando um Heisenbug é invocado, é fundamental que o software detecte rapidamente a operação incorreta e falhe para que possa ser tentado novamente. Isso é obtido por meio de programação defensiva e validação de entradas, resultados intermediários e saídas. Além disso, os processos são isolados e não compartilham nenhum estado com outros processos.

Essa abordagem modular garante que o escopo do impacto durante a falha seja limitado. Os processos falham de forma independente. Quando um processo falha, o software deve usar “pares de processos” para repetir o trabalho, o que significa que um novo processo pode assumir o trabalho de um que falhou. Para manter a confiabilidade e a integridade da workload, cada operação deve ser tratada como uma transação ACID.

Isso permite que um processo falhe sem corromper o estado da workload, abortando a transação e revertendo as alterações feitas. Isso permite que o processo de recuperação repita a transação a partir de um estado em boas condições e reinicie normalmente. É assim que o software pode ser tolerante a falhas para a Heisenbugs.

No entanto, você não deve tentar tornar o software tolerante a falhas para Bohrbugs. Esses defeitos devem ser encontrados e removidos antes que a workload entre em produção, pois nenhum nível de redundância jamais alcançará o resultado correto. (Veja Jim Gray, "Por que os computadores param e o que pode ser feito a respeito? “, junho de 1985, Relatório Técnico Tandem 85.7.)

A maneira final de aumentar o MTBF é reduzir o escopo do impacto da falha. Usar o isolamento de falhas por meio da modularização para criar contêineres de falhas é a principal maneira de fazer isso, conforme descrito anteriormente em Tolerância e isolamento de falhas. Reduzir a taxa de falhas melhora a disponibilidade. O AWS usa técnicas como divisão de serviços em ambientes de gerenciamento e planos de dados, independência da zona de disponibilidade (AZI), isolamento regional, arquiteturas baseadas em células e fragmentação aleatória para fornecer isolamento de falhas. Esses também são padrões que também podem ser usados pelos clientes do AWS.

Por exemplo, vamos analisar um cenário em que uma workload colocava os clientes em diferentes contêineres de falhas de sua infraestrutura, atendendo no máximo 5% do total de clientes. Um desses contêineres de falhas passa por um evento que aumenta a latência além do tempo limite do cliente em 10% das solicitações. Durante esse evento, para 95% dos clientes, o serviço estava 100% disponível. Para os outros 5%, o serviço parecia estar 90% disponível. Isso resulta em uma disponibilidade de 1 − (5% dos clientes × 10% de suas solicitações) = 99,5% em vez de 10% das solicitações falharem para 100% dos clientes (resultando em uma disponibilidade de 90%).

Regra 11

O isolamento de falhas diminui o escopo do impacto e aumenta o MTBF da workload ao reduzir a taxa geral de falhas.

Aumento da dependência MTBF

O primeiro método para aumentar seu MTBF de dependência do AWS é usar o isolamento de falhas. Muitos serviços do AWS oferecem um nível de isolamento na AZ, o que significa que uma falha em uma AZ não afeta o serviço em outra AZ.

O uso de instâncias EC2 redundantes em várias AZs aumenta a disponibilidade do subsistema. O AZI fornece uma capacidade de economia dentro de uma única região, permitindo que você aumente sua disponibilidade para os serviços do AZI.

No entanto, nem todos os serviços do AWS operam no nível AZ. Muitos outros oferecem isolamento regional. Nesse caso, em que a disponibilidade projetada do serviço regional não oferece suporte à disponibilidade geral necessária para sua workload, você pode considerar uma abordagem multirregional. Cada região oferece uma instanciação isolada do serviço, equivalente à economia.

Existem vários serviços que ajudam a facilitar a criação de um serviço multirregional. Por exemplo:

Este documento não aborda as estratégias de criação de workloads multirregionais, mas você deve avaliar os benefícios de disponibilidade das arquiteturas multirregionais com o custo adicional, a complexidade e as práticas operacionais necessárias para atingir as metas de disponibilidade desejadas.

O próximo método para aumentar o MTBF de dependência é projetar sua workload para ser estaticamente estável. Por exemplo, você tem uma workload que fornece informações do produto. Quando seus clientes fazem uma solicitação de um produto, seu serviço faz uma solicitação a um serviço externo de metadados para recuperar os detalhes do produto. Em seguida, sua workload retorna todas essas informações para o usuário.

No entanto, se o serviço de metadados não estiver disponível, as solicitações feitas por seus clientes falharão. Em vez disso, você pode extrair ou enviar de forma assíncrona os metadados localmente para o seu serviço para serem usados para responder às solicitações. Isso elimina a chamada síncrona para o serviço de metadados do seu caminho crítico.

Além disso, como seu serviço ainda está disponível mesmo quando o serviço de metadados não está, você pode removê-lo como uma dependência no cálculo de disponibilidade. Esse exemplo depende da suposição de que os metadados não mudam com frequência e que veicular metadados obsoletos é melhor do que a falha na solicitação. Outro exemplo semelhante é o serve-stale para DNS, que permite que os dados sejam mantidos no cache além da expiração do TTL e usados para respostas quando uma resposta atualizada não está prontamente disponível.

O método final de aumentar o MTBF da dependência é reduzir o escopo do impacto da falha. Conforme discutido anteriormente, a falha não é um evento binário, há graus de falha. Esse é o efeito da modularização; a falha está contida apenas nas solicitações ou usuários atendidos por esse contêiner.

Isso resulta em menos falhas durante um evento, o que, em última análise, aumenta a disponibilidade da workload geral ao limitar o escopo do impacto.

Reduzindo fontes comuns de impacto

Em 1985, Jim Gray descobriu, durante um estudo na Tandem Computers, que a falha era causada principalmente por duas coisas: software e operações. (Veja Jim Gray, "Por que os computadores param e o que pode ser feito a respeito? “, junho de 1985, Relatório Técnico Tandem 85.7.) Mesmo depois de 36 anos, isso continua sendo verdade. Apesar dos avanços na tecnologia, não há uma solução fácil para esses problemas, e as principais fontes de falha não mudaram. O tratamento de falhas no software foi discutido no início desta seção, portanto, o foco aqui será as operações e a redução da frequência de falhas.

Estabilidade em comparação com os recursos

Se nos referirmos às taxas de falha do gráfico de software e hardware na seçãoDisponibilidade do sistema distribuído, podemos observar que defeitos são adicionados em cada versão do software. Isso significa que qualquer alteração na workload aumenta o risco de falha. Essas mudanças geralmente são coisas como novos recursos, o que fornece um corolário. Workloads com maior disponibilidade favorecerão a estabilidade em relação aos novos recursos. Assim, uma das maneiras mais simples de melhorar a disponibilidade é implantar com menos frequência ou fornecer menos recursos. As workloads implantadas com mais frequência terão inerentemente uma disponibilidade menor do que aquelas que não o fazem. No entanto, as workloads que não adicionam recursos não atendem à demanda do cliente e podem se tornar menos úteis com o tempo.

Então, como continuamos inovando e lançando recursos com segurança? A resposta é padronização. Qual é a maneira correta de implantar? Como você solicita implantações? Quais são os padrões para testes? Quanto tempo você espera entre os estágios? Seus testes de unidade abrangem o suficiente do código do software? Essas são perguntas que a padronização responderá e evitará problemas causados por coisas como não testar a carga, pular estágios de implantação ou implantar muito rapidamente em muitos hosts.

A forma como você implementa a padronização é por meio da automação. Ele reduz a chance de erros humanos e permite que os computadores façam aquilo em que são bons, ou seja, fazer sempre a mesma coisa da mesma maneira. A maneira de unir padronização e automação é definir metas. Objetivos como não fazer alterações manuais, hospedar somente por meio de sistemas de autorização contingente, escrever testes de carga para cada API e assim por diante. A excelência operacional é uma norma cultural que pode exigir mudanças substanciais. Estabelecer e monitorar o desempenho em relação a uma meta ajuda a impulsionar uma mudança cultural que terá um amplo impacto na disponibilidade da workload. O pilar AWS Well-Architected Operational Excellence fornece as melhores práticas abrangentes para a excelência operacional.

Segurança do operador

O outro grande contribuinte para eventos operacionais que introduzem falhas são as pessoas. Humanos cometem erros. Eles podem usar as credenciais erradas, digitar o comando errado, pressionar Enter muito cedo ou perder uma etapa crítica. Realizar ações manuais de forma consistente resulta em erro, resultando em falha.

Uma das principais causas de erros do operador são interfaces de usuário confusas, não intuitivas ou inconsistentes. Jim Gray também observou em seu estudo de 1985 que “as interfaces que pedem informações ao operador ou que ele execute alguma função devem ser simples, consistentes e tolerantes a falhas do operador”. (Veja Jim Gray, "Por que os computadores param e o que pode ser feito a respeito? “, junho de 1985, Relatório Técnico Tandem 85.7.) Essa percepção continua sendo verdadeira hoje. Nas últimas três décadas, existem vários exemplos em todo o setor em que uma interface de usuário confusa ou complexa, a falta de confirmação ou instruções ou até mesmo uma linguagem humana hostil fizeram com que um operador fizesse a coisa errada.

Regra 12

Faça com que seja fácil para os operadores fazerem a coisa certa.

Prevenir sobrecarga

O último colaborador comum de impacto são seus clientes, os usuários reais de sua workload. Workloads bem-sucedidas tendem a ser muito usadas, mas às vezes esse uso supera a capacidade de escalabilidade da workload. Muitas coisas podem acontecer: os discos podem ficar cheios, os pools de threads podem se esgotar, a largura de banda da rede pode ficar saturada ou os limites de conexão do banco de dados podem ser atingidos.

Não há um método à prova de falhas para eliminá-las, mas o monitoramento proativo da capacidade e da utilização por meio de métricas da Operational Health fornecerá avisos antecipados quando essas falhas puderem ocorrer. Técnicas como redução de carga, disjuntores e repetição com recuo exponencial e instabilidade podem ajudar a minimizar o impacto e aumentar a taxa de sucesso, mas essas situações ainda representam falha. O escalonamento automatizado com base nas métricas da Operational Health pode ajudar a reduzir a frequência de falhas devido à sobrecarga, mas pode não ser capaz de responder com rapidez suficiente às mudanças na utilização.

Se você precisar garantir a capacidade continuamente disponível para os clientes, precisará fazer concessões quanto à disponibilidade e ao custo. Uma forma de garantir que a falta de capacidade não leve à indisponibilidade é fornecer uma cota a cada cliente e garantir que a capacidade de sua workload seja escalada para fornecer 100% das cotas alocadas. Quando os clientes excedem sua cota, eles são limitados, o que não é uma falha e não conta para a disponibilidade. Você também precisará acompanhar de perto sua base de clientes e prever a utilização futura para manter a capacidade suficiente provisionada. Isso garante que sua workload não seja direcionada a cenários de falha devido ao consumo excessivo de seus clientes.

Por exemplo, vamos examinar uma workload que fornece um serviço de armazenamento. Cada servidor na workload pode suportar 100 downloads por segundo, os clientes recebem uma cota de 200 downloads por segundo e há 500 clientes. Para poder suportar esse volume de clientes, o serviço precisa fornecer capacidade para 100.000 downloads por segundo, o que requer 1.000 servidores. Se algum cliente exceder sua cota, ele será limitado, o que garante capacidade suficiente para todos os outros clientes. Este é um exemplo simples de uma forma de evitar a sobrecarga sem rejeitar unidades de trabalho.