Gostaria de ser notificado sobre novos conteúdos?
Por Becky Weiss e Mike Furr
Na Amazon, os serviços que criamos precisam cumprir metas de disponibilidade extremamente altas. Isto significa que nós precisamos pensar com cuidado sobre as dependências tomadas por nossos sistemas. Nós projetamos nossos sistemas para se manterem resilientes mesmo quando tais dependências estiverem danificadas. Neste artigo, definiremos um padrão que costumamos chamar de estabilidade estática para atingir esse nível de resiliência. Nós mostraremos como aplicamos esse conceito para zona de disponibilidade, um bloco de construção da infraestrutura chave na AWS e, assim, uma dependência base sobre a qual todos os nossos serviços são estruturados.
Em um projeto estável estatisticamente, o sistema como um todo se mantém trabalhando mesmo quando uma dependência fica comprometida. Talvez o sistema não veja qualquer informação atualizada (como elementos novos, apagados ou modificados) que sua dependência deveria ter enviado. Todavia, tudo que estava sendo feito antes do comprometimento da dependência continuará a trabalhar apesar de tal comprometimento. Nós descreveremos como nós planejamos o Amazon Elastic Compute Cloud (EC2) para ser estável estaticamente. Então nós forneceremos duas arquiteturas de exemplo estaticamente estável que consideramos útil para projetar sistemas regionais altamente disponíveis no topo da zona de disponibilidade.
Finalmente, nós nos aprofundaremos em algumas filosofias de projeto por trás do Amazon EC2, incluindo como sua arquitetura fornece independência de sua zona de disponibilidade no nível do software. Além disso, discutiremos algumas das compensações que virão com a construção de um serviço construído com essa arquitetura.
Estabilidade estática
• Localiza uma interface de rede fora da sub-rede VPC.
• Prepara volumes do Amazon Elastic Block Store (EBS).
• Gera credenciais da função do AWS Identity and Access Management (IAM).
• Instala regras de grupos de segurança inseguros.
• Armazena os resultados em datastores de vários serviços de downstream.
• Propaga as configurações necessárias para o servidor na VPC e na borda da rede, conforme o caso.
• Lê e grava dos volumes do Amazon EBS.
• E por aí vai.
• É comum para o plano de dados operar em um volume maior (geralmente pela ordem de magnitude) do que seu plano de controle. Dessa forma, é melhor mantê-los separados, assim cada um pode ser ampliado de acordo com suas dimensões de escala relevantes.
• Nós descobrimos durante os anos que um plano de controle do sistema tende a ter mais partes móveis do que seu plano de dados, logo é estatisticamente mais provável que seja comprometido sem nenhuma razão específica.
Padrões de estabilidade estática
Nesta seção, nós introduziremos dois padrões de alto nível que usamos na AWS para projetar sistemas de alta disponibilidade usando a estabilidade estática. Cada padrão é aplicável em seu próprio conjunto de situações, mas ambos se aproveitam da abstração da zona de disponibilidade.
No caso de um comprometimento da zona de disponibilidade, a arquitetura apresentada no diagrama anterior não exige ação. As instâncias do EC2 na zona de disponibilidade comprometidas começarão a apresentar falhas nas verificações de integridade e o Application Load Balancer irá desviar a rota delas. De fato, o serviço do Elastic Load Balancing é projetado de acordo com esse princípio. Ele forneceu capacidade de balanceamento de carga suficiente para suportar um comprometimento da zona de disponibilidade sem precisar elevá-la.
Nós também usamos esse padrão mesmo quando não há load balancer no serviço HTTPS. Por exemplo, uma frota de instâncias do EC2 que processa mensagens de uma fila do Amazon Simple Queue Service (SQS) também pode seguir esses padrões. As instâncias são implantadas em um grupo de Auto Scaling entre diversas zonas de disponibilidade, apropriadamente provisionadas excessivamente. No caso de uma zona de disponibilidade ficar comprometida, o serviço não faz nada. As instâncias comprometidas param seu trabalho e as outras compensam essa deficiência.
Como foi o caso no exemplo stateless, ativo-ativo, quando a zona de disponibilidade com o nó primário fica comprometida, o serviço stateful não faz nada com a infraestrutura. Para serviços que usam o Amazon RDS, o RDS gerenciará o failover e reapontará o nome do DNS para um primário novo na zona de disponibilidade em funcionamento. Esse padrão também se aplica para outras configurações ativo em espera, mesmo se elas não usam um banco de dados relacional. Em particular, nós o aplicamos a sistemas com uma arquitetura em cluster que tenha um nó principal. Nós implantamos esses clusters entre as zonas de disponibilidade e elegemos um novo nó principal de um candidato a espera em vez de lançar um substituto "a tempo".
O que esses dois padrões têm em comum é que ambos já provisionaram a capacidade que precisam no caso de um comprometimento da zona de disponibilidade muito antes de um comprometimento real. Em nenhum desses casos o serviço toma qualquer dependência deliberada do plano de controle, tal como fornecer novas infraestruturas ou fazer modificações, em resposta ao comprometimento da zona de disponibilidade.
Detalhes internos: estabilidade estática dentro do Amazon EC2
Na seção final desse artigo nós iremos nos aprofundar na resiliência das arquiteturas da zona de disponibilidade, cobrindo algumas das formas que seguimos os princípios de independência da zona de disponibilidade no Amazon EC2. Entender alguns desses conceitos é útil quando nós criamos um serviço que não apenas precisa ser altamente disponível, mas também precisa fornecer infraestrutura para que outros sejam altamente disponíveis. O Amazon EC2, como fornecedor de infraestrutura de baixo nível da AWS, é a infraestrutura que aplicações podem usar para se tornar altamente disponíveis. Há vezes em que outros sistemas também podem querer adotar tal estratégia.
Nós seguimos o princípio da independência na zona de disponibilidade no Amazon EC2 em nossas práticas de implantação. No Amazon EC2, um software é implantado nos servidores físicos hospedando instâncias do EC2, dispositivos de borda, resolvedores de DNS, componentes do plano de controle na instância do EC2, caminho de lançamento e muitos outros componentes dos quais as instâncias do EC2 dependem. Essas implantações seguem um calendário de implantação zonal. Isso significa que duas zonas de disponibilidade na mesma região receberão uma dada implantação em dias diferentes. Na AWS, nós usamos implantações graduais. Por exemplo, nós seguimos a melhor prática (independentemente do tipo de serviço que queremos implantar) de primeiro implantar um one-box, depois 1/N de servidores, etc. Todavia, no caso específico de serviços como aqueles do Amazon EC2, nossas implantações vão um passo além e são alinhadas deliberadamente para se alinharem com os limites da zona de disponibilidade. Dessa forma, um problema com uma implantação afeta uma zona de disponibilidade e é revertida e corrigida. Ele não afeta outras zonas de disponibilidade, que continuam funcionando normalmente.
Outra forma de usarmos o princípio de zonas de disponibilidade independentes quando criamos no Amazon EC2 é projetar todos os fluxos de pacotes para ficarem na zona de disponibilidade em vez de cruzar os limites. Este segundo ponto - que o tráfico da rede se mantém local na zona de disponibilidade - precisa ser explorado em detalhes. É uma ilustração interessante de como nós pensamos de forma diferente quando criamos um sistema regional, altamente disponível que é um consumidor de zonas de disponibilidade independentes (isso é, usa as garantidas da independência da zona de disponibilidade como fundação para criar um serviço de alta disponibilidade), em oposição a quando nós fornecemos infraestrutura independente da zona de disponibilidade para outros que os permitirão criar para serem altamente disponíveis.
O diagrama a seguir ilustra um serviço externo altamente disponível, apresentado em laranja, que depende de outro, interno, mostrado em verde. Um projeto simples trata os dois serviços como consumidores de zonas de disponibilidade EC2 independentes. Cada um dos serviços laranjas e verdes é coberto por um Application Load Balancer, e cada serviço tem uma frota bem provisionada de hosts de back-end espalhados entre três zonas de disponibilidade. Um serviço regional altamente disponível chama outro serviço regional altamente disponível. Esse é um projeto simples, e para muitos dos serviços que nós criamos, um bom projeto.
Suponha, porém, que o serviço em verde é um serviço básico. Isso é, suponha que ele é destinado não apenas para ser altamente disponível, mas também para servir como bloco de criação para fornecer independência da zona de disponibilidade. Nesse caso, nós podemos projetá-lo como três instâncias de um serviço local para a zona, em que nós seguimos as práticas de implantação conhecidas na zona de disponibilidade. O diagrama a seguir ilustra o projeto em que um serviço regional altamente disponível chama um serviço zonal altamente disponível.
As razões pelas quais nós projetamos nossos serviços de criação de bloco para serem independente da zona de disponibilidade vêm de uma aritmética simples. Digamos que uma zona de disponibilidade esteja comprometida. Para falhas evidentes, o Application Load Balancer executará automaticamente um failover dos nós afetados. Todavia, nem todas as falhas são óbvias. Podem haver falhas cinzas, tais como bugs no software, que o load balancer não será capaz de ver na verificação de integridade e lidar de modo limpo.
No exemplo anterior, em que um serviço regional altamente disponível chama outro serviço regional altamente disponível, se uma requisição é enviada pelo sistema, com algumas suposições simples a chance da requisição evitar uma zona de disponibilidade comprometida é de 2/3 * 2/3 = 4/9. Isso é, a requisição tem uma chance bem baixa de passar sem cruzar com o evento. Em contrate, se nós criamos o serviço em verde para ser zonal como no exemplo atual, os hosts no serviço laranja podem chamar o endpoint verde na mesma zona de disponibilidade. Com essa arquitetura, as chances de evitar a zona de disponibilidade comprometida são de 2/3. Se N serviços são parte desse caminho de chamada, esses números se generalizam para (2/3)^N para N serviços regionais contra o ainda constante 2/3 para N serviços zonais.
Por esse motivo nós criamos o gateway Amazon EC2 NAT como serviço zonal. O gateway NAT é um recurso do Amazon EC2 que permite o tráfico de internet de saída de uma sub-rede privada e aparece não como um gateway regional, de amplo VPC, mas como um recurso zonal, que clientes iniciam separadamente por zona de disponibilidade como apresentado no diagrama a seguir. O gateway NAT fica no caminho da conectividade da internet para o VPC e dessa forma é parte do plano de dados de qualquer instância do EC2 naquele VPC. Se houver um comprometimento de conectividade de uma zona de disponibilidade, nós queremos manter o mesmo dentro dessa zona de disponibilidade em vez de deixá-lo se espalhar para as demais zonas. No fim, nós queremos que um cliente que criou uma arquitetura similar com a mencionada anteriormente nesse artigo (ou seja, provisionando uma frota pelas três zonas de disponibilidade com capacidade suficiente em duas para manter a carga completa) para saber que outras zonas de disponibilidade não serão de forma alguma afetadas por qualquer comprometimento em andamento na zona de disponibilidade. A única forma de nós fazermos isso é garantir que todos os componentes fundamentais - como o gateway NAT - realmente ficarão dentro da zona de disponibilidade.
Essa escolha trás o custo de complexidade adicional. Para nós na Amazon EC2, a complexidade adicional vem na forma de gerenciamento zonal, não regional, dos ambientes de serviço. Para os clientes do gateway NAT, a complexidade adicional vem na forma de diversos gateways NAT e tabela de rota para uso em diferentes zonas de disponibilidade do VPC. A complexidade adicional é apropriada porque o gateway NAT é um serviço básico, parte do plano de dados do Amazon EC2 que deve fornecer garantias de disponibilidade zonal.
Há mais uma consideração que nós gostaríamos de fazer quando criamos serviços que são independentes da zona de disponibilidade, que é a durabilidade do dado. Embora cada uma das arquiteturas zonais descritas anteriormente mostre a pilha inteira contida em uma única zona de disponibilidade, nós replicamos qualquer estado difícil entre diversas zonas de disponibilidade para fins de recuperação de desastres. Por exemplo, nós tipicamente armazenamos cópias de segurança de bancos de dados periódicos no Amazon S3 e mantemos réplicas de leitura dos armazenamentos de dados além dos limites da zona de disponibilidade. Essas réplicas não são necessárias para o funcionamento da zona de disponibilidade primária. Em vez disso, elas garantem que nós armazenemos dados críticos de nossos clientes - ou negócios - em diversos locais.
Quando projetamos uma arquitetura orientada para o serviço que irá rodar na AWS, nós aprendemos a usar um desses dois padrões ou uma combinação de ambos:
• O padrão simples: regional chama regional. Geralmente é a melhor escolha para serviços voltados para o ambiente externo e também apropriado para muitos dos serviços internos. Por exemplo, quando criamos serviços de aplicação de alto nível na AWS, tais como o Amazon API Gateway e tecnologias sem servidor da AWS, nós usamos esse padrão para fornecer alta disponibilidade mesmo frente a comprometimentos da zona de disponibilidade.
• O padrão mais complexo: regional chama zonal ou zonal chama regional. Quando projetamos componentes internos, e em alguns casos externos, de plano de dados no Amazon EC2 (por exemplo, dispositivos de rede ou outras infraestruturas que ficam diretamente no caminho de dados críticos), nós seguimos o padrão de independência da zona de disponibilidade e usamos instâncias que são separadas em silos nas zonas de disponibilidade, assim o tráfico de rede se mantém em sua própria zona de disponibilidade. Este padrão não apenas ajuda a manter os comprometimentos isolados na zona de disponibilidade mas também tem características de custo de tráfico favoráveis no AWS.
Conclusão
Neste artigo, nós discutimos algumas estratégias simples que usamos na AWS para tomar dependências de forma bem sucedida nas zonas de disponibilidade. Nós aprendemos que a chave para a estabilidade estática é antecipar comprometimentos antes que eles ocorram. Se um sistema roda em uma frota escalável horizontalmente do tipo ativo-ativo, ou se é um padrão ativo em espera, nós podemos usar as zonas de disponibilidade para atingir níveis altos de disponibilidade. Nós implantamos nossos sistemas para que toda a capacidade que será necessária no caso de um comprometimento já esteja provisionada e pronta para ser fornecida. Finalmente, nós aprofundamos em como o Amazon EC2 usa conceitos de estabilidade estática para manter as zonas de disponibilidade independentes umas das outras.
Sobre os autores
Becky Weiss é engenheira-chefe sênior na Amazon Web Services. Ela se concentra no Identity and Access Management na AWS e, geralmente, no fornecimento de controles de segurança flexíveis, abrangentes e com autoridade para os clientes de nuvem. No passado, ela trabalhou no Amazon Virtual Private Cloud (por exemplo, em redes) e AWS Lambda e também trabalhou no AWS Professional Services para ajudar clientes das empresas a proteger com êxito seus ambientes na AWS. Beck também é uma das maiores fãs da AWS e, em seu tempo livre, cria todo o tipo de coisas úteis e inúteis na AWS. Antes de trabalhar para a AWS, Becky trabalhou para a Microsoft no Windows e no Windows Phone.
Mike Furr é engenheiro-chefe na Amazon Web Services. Ele entrou na Amazon em 2009, após terminar seu doutorado em ciências da computação na University of Maryland, College Park. Durante o tempo em que está trabalhando na Amazon, ele trabalhou no Virtual Private Cloud, Direct Connect e na pilha de medição e faturamento da AWS. Agora ele está focando seu tempo no EC2, onde ajuda as equipes a chegar nas nuvens.