Padrões para aplicativos escalonáveis e resilientes

Last reviewed 2024-03-19 UTC

Neste documento, apresentamos alguns padrões e práticas para criar aplicativos resilientes e escalonáveis, que são duas metas essenciais de muitos exercícios de arquitetura moderna. Um aplicativo bem projetado faz ampliação e redução conforme a maior ou menor demanda, e é resiliente o suficiente para aguentar falhas temporárias de serviço. Criar e operar aplicativos que atendam a esses requisitos exige cuidado no planejamento e design.

Escalonabilidade: ajuste da capacidade para atender à demanda

Escalonabilidade (em inglês) é a medida da capacidade de um sistema em lidar com diferentes quantidades de trabalho adicionando ou removendo recursos. Por exemplo, um aplicativo da Web escalonável é aquele que funciona bem com um usuário ou com muitos usuários e que lida com picos e quedas de tráfego de maneira adequada.

A flexibilidade de ajustar os recursos consumidos por um aplicativo é um dos principais impulsionadores de negócios para a migração para a nuvem. Com o design adequado, é possível reduzir os custos removendo recursos pouco utilizados sem comprometer o desempenho ou a experiência do usuário. Da mesma forma, é possível manter uma experiência do usuário satisfatória durante períodos de alto tráfego adicionando recursos. Assim, seu aplicativo consome somente os recursos necessários para atender à demanda.

O Google Cloud fornece produtos e recursos para ajudar a criar aplicativos escalonáveis e eficientes:

  • As máquinas virtuais do Compute Engine e os clusters do Google Kubernetes Engine (GKE) se integram aos escalonadores automáticos que permitem aumentar ou reduzir o consumo de recursos com base nas métricas definidas por você.
  • A plataforma sem servidor do Google Cloud oferece computação, banco de dados e outros serviços gerenciados que escalonam rapidamente do zero até altos volumes de solicitações, e você paga apenas pelo que usar.
  • Produtos de banco de dados, como o BigQuery, o Spanner e o Bigtable, oferecem desempenho consistente em grandes volumes de dados.
  • O Cloud Monitoring fornece métricas em todos os seus aplicativos e infraestrutura, ajudando a tomar decisões de escalonamento orientadas por dados.

Resiliência: como projetar para aceitar falhas

Um aplicativo resiliente é aquele que continua funcionando, apesar das falhas nos componentes do sistema. A resiliência exige planejamento em todos os níveis da arquitetura. Ela influencia a maneira como você esquematiza a infraestrutura e a rede e como projeta o aplicativo e o armazenamento de dados. A resiliência também se estende às pessoas e à cultura.

Criar e operar aplicativos resilientes é difícil. Principalmente quando se trata de aplicativos distribuídos que podem conter vários níveis de infraestrutura, redes e serviços. Erros e falhas temporárias acontecem, e melhorar a resiliência do seu aplicativo é uma jornada contínua. Com um planejamento cuidadoso, é possível melhorar a capacidade do aplicativo de aguentar falhas. Por meio de uma cultura organizacional e processos adequados, também é possível aprender com falhas para aumentar ainda mais a resiliência do app.

O Google Cloud fornece ferramentas e serviços para ajudar a criar aplicativos altamente disponíveis e resilientes:

  • Os serviços do Google Cloud estão disponíveis em regiões e zonas em todo o mundo, permitindo que você implante o aplicativo para atender melhor às suas metas de disponibilidade.
  • Os grupos de instâncias do Compute Engine e os clusters do GKE podem ser distribuídos e gerenciados nas zonas disponíveis em uma região.
  • Os discos permanentes regionais do Compute Engine são replicados de maneira síncrona nas zonas de uma região.
  • O Google Cloud oferece uma variedade de opções de balanceamento de carga para gerenciar o tráfego do aplicativo, incluindo o balanceamento de carga global que pode direcionar o tráfego para uma região íntegra mais próxima dos usuários.
  • A plataforma sem servidor do Google Cloud inclui produtos gerenciados de computação e banco de dados que oferecem dbalanceamento de carga e redundância integrada.
  • Para ajudar a automatizar a criação e a implantação de aplicativos, o Google Cloud tem compatibilidade com CI/CD por meio de ferramentas nativas e integrações com tecnologias conhecidas de código aberto.
  • O Cloud Monitoring fornece métricas em todos os aplicativos e infraestrutura, ajudando a tomar decisões orientadas por dados sobre o desempenho e a integridade dos aplicativos.

Impulsionadores e restrições

Há vários requisitos e motivações para melhorar a escalonabilidade e a resiliência de um aplicativo. Também pode haver restrições que limitam sua capacidade de atingir as metas de escalonabilidade e resiliência. A importância relativa desses requisitos e restrições varia de acordo com o tipo de aplicativo, o perfil dos usuários e o escalonamento e a maturidade da organização.

Impulsionadores

Para priorizar seus requisitos, considere os impulsionadores de partes diferentes da sua organização.

Impulsionadores de negócios

Os impulsionadores comuns de negócios incluem:

  • Otimização de custos e de consumo de recursos
  • Redução do tempo de inatividade do app
  • Garantia de que é possível atender à demanda dos usuários durante períodos de uso elevado
  • Melhoria da qualidade e da disponibilidade do serviço
  • Garantia de que a confiança e a experiência do usuário serão mantidas durante falhas temporárias
  • Aumento da flexibilidade e agilidade para lidar com as demandas do mercado em constante mudança

Impulsionadores de desenvolvimento

Os impulsionadores comuns de desenvolvimento incluem:

  • Redução do tempo gasto na investigação de falhas
  • Aumento do tempo gasto no desenvolvimento de novos recursos
  • Redução de esforços repetitivos por meio da automação
  • Criação de apps usando os padrões e as práticas mais recentes do setor

Impulsionadores de operações

Os requisitos a serem considerados quando se trata de operações incluem:

  • Redução da frequência de falhas que exigem intervenção humana
  • Aumento da capacidade de recuperação automática após a ocorrência de falhas
  • Redução de esforços repetitivos por meio da automação
  • Redução do impacto da falha sobre qualquer componente específico

Restrições

Restrições podem limitar sua capacidade de aumentar a escalonabilidade e a resiliência do app. Certifique-se de que suas decisões de design não introduzam nem contribuam para essas restrições:

  • Dependência de hardware ou software difíceis de escalonar
  • Dependência de hardware ou software difíceis de operar em uma configuração de alta disponibilidade
  • Dependências entre aplicativos
  • Restrições de licenciamento
  • Falta de habilidades ou experiência nas equipes operacionais e de desenvolvimento
  • Resistência organizacional à automação

Padrões e práticas

O restante deste documento define padrões e práticas para ajudar você a criar aplicativos resilientes e escalonáveis. Esses padrões afetam todas as partes do ciclo de vida do aplicativo, incluindo o design da infraestrutura, a arquitetura do aplicativo, as opções de armazenamento, os processos de implantação e a cultura organizacional.

Três temas são evidentes nos padrões:

  • Automação. A criação de aplicativos escalonáveis e resilientes exige automação. Automatizar as implantações de aplicativo, teste e provisionamento da infraestrutura aumenta a consistência e a velocidade, além de minimizar erros humanos.
  • Acoplamento flexível. Tratar seu sistema como um conjunto de componentes independentes e levemente acoplados proporciona flexibilidade e resiliência. A independência abrange como você distribui fisicamente os recursos, arquiteta o aplicativo e projeta o armazenamento.
  • Design orientado por dados. A coleta de métricas para entender o comportamento do aplicativo é fundamental. As decisões sobre quando escalonar o aplicativo, ou sobre um serviço específico ser íntegro ou não, precisam ser baseadas em dados. As métricas e os registros precisam ser os recursos principais.

Automatização do provisionamento de sua infraestrutura

Crie uma infraestrutura imutável por meio da automatização para melhorar a consistência dos ambientes e aumentar o sucesso das implantações.

Tratamento da infraestrutura como código

A infraestrutura como código (IaC, na sigla em inglês) é uma técnica que incentiva você a tratar a configuração e o provisionamento da infraestrutura da mesma forma como lida com o código do aplicativo. Sua lógica de provisionamento e configuração é armazenada no controle de origem para que seja detectável e possa ter controle de versões e ser auditada. Como ela está em um repositório de código, é possível aproveitar os pipelines de integração e implantação contínuas (CI/CD) para que as mudanças na configuração possam ser testadas e implantadas automaticamente.

Removendo etapas manuais do provisionamento de infraestrutura, a IaC reduz erros humanos e melhora a consistência e a reprodutibilidade dos aplicativos e ambientes. Dessa forma, a adoção da IaC aumenta a resiliência dos seus apps.

O Cloud Deployment Manager permite automatizar a criação e gestão de recursos do Google Cloud com modelos flexíveis. Como alternativa, o Config Connector permite que você gerencie seus recursos usando técnicas e fluxos de trabalho do Kubernetes. O Google Cloud também tem suporte integrado para ferramentas conhecidas de IaC terceirizadas, incluindo Terraform, Chef e Puppet.

Criação de infraestrutura imutável

A infraestrutura imutável é uma filosofia que se baseia nos benefícios da infraestrutura como código. A infraestrutura imutável determina que os recursos nunca sejam modificados após a implantação. Se uma máquina virtual, um cluster do Kubernetes ou uma regra de firewall precisar de atualização, atualize a configuração do recurso no repositório de origem. Depois de testar e validar as alterações, você reimplanta totalmente o recurso usando a nova configuração. Em outras palavras, em vez de ajustar recursos, você os recria.

Criar uma infraestrutura imutável gera reversões e implantações mais previsíveis. Isso também mitiga problemas comuns em infraestruturas mutáveis, como desvios de configuração e servidores floco de neve (em inglês). Assim, a adoção da infraestrutura imutável melhora ainda mais a consistência e a confiabilidade dos seus ambientes.

Design para alta disponibilidade

A disponibilidade é uma medida da fração de tempo que um serviço pode ser usado. A disponibilidade geralmente é usada como um indicador importante da integridade geral do serviço. As arquiteturas altamente disponíveis têm como objetivo maximizar a disponibilidade do serviço, em geral por meio da implantação redundante de componentes. Em termos mais simples, alcançar a alta disponibilidade normalmente envolve a distribuição de recursos de computação, balanceamento de carga e replicação de dados.

Distribuição física de recursos

Os serviços do Google Cloud estão disponíveis em locais do mundo todo. Esses locais estão divididos em regiões e zonas. A maneira como você implanta o aplicativo nessas regiões e zonas afeta a disponibilidade, a latência e outras propriedades do aplicativo. Para mais informações, consulte Práticas recomendadas para a seleção da região do Compute Engine.

Redundância é a cópia de componentes de um sistema para aumentar a disponibilidade geral desse sistema. No Google Cloud, a redundância geralmente é alcançada por meio da implantação do aplicativo ou serviço em várias zonas ou até mesmo em várias regiões. Se houver um serviço em várias zonas ou regiões, ele poderá aguentar melhor as falhas temporárias em uma zona ou região específica. Embora o Google Cloud se esforce tanto quanto possível para evitar essas falhas temporárias, alguns eventos são imprevisíveis e é melhor estar preparado.

Com os grupos de instâncias gerenciadas do Compute Engine, é possível distribuir instâncias de máquina virtual em várias zonas de uma região e gerenciar as instâncias como uma unidade lógica. O Google Cloud também oferece discos permanentes regionais para replicar seus dados automaticamente para duas zonas em uma região.

Da mesma forma, é possível melhorar a disponibilidade e a resiliência dos aplicativos implantados no GKE criando clusters regionais. Um cluster regional distribui componentes, nós e pods do plano de controle do GKE em várias zonas de uma região. Como os componentes do plano de controle são distribuídos, é possível continuar acessando o plano de controle do cluster mesmo durante uma interrupção que envolva uma ou mais zonas, mas não todas.

Preferência a serviços gerenciados

Em vez de instalar, aceitar e operar de forma independente todas as partes da pilha de aplicativos, é possível usar serviços gerenciados para consumir partes dessa pilha como serviços. Por exemplo, em vez de instalar e gerenciar um banco de dados MySQL em máquinas virtuais (VMs), é possível usar um banco de dados MySQL fornecido pelo Cloud SQL. Assim você terá disponível um Contrato de nível de serviço (SLA) e poderá contar com o Google Cloud para gerenciar a replicação de dados, os backups e a infraestrutura subjacente. Ao usar serviços gerenciados, você passa menos tempo gerenciando a infraestrutura e mais tempo melhorando a confiabilidade do seu aplicativo.

Muitos dos serviços de computação, banco de dados e armazenamento gerenciados do Google Cloud oferecem redundância integrada, que pode ajudar você a atingir suas metas de disponibilidade. Muitos desses serviços oferecem um modelo regional, o que significa que a infraestrutura que executa o aplicativo está localizada em uma região específica e é gerenciada pelo Google para estar disponível, de forma redundante em todas as zonas dessa região. Se uma zona ficar indisponível, o aplicativo ou os dados serão exibidos automaticamente de outra zona na região.

Alguns serviços de banco de dados e armazenamento também oferecem disponibilidade multirregional. Isso significa que a infraestrutura que executa o aplicativo está localizada em várias regiões. Os serviços multirregionais podem aguentar a perda de uma região inteira, mas geralmente às custas de uma latência maior.

Balanceamento de carga em cada camada

O balanceamento de carga permite distribuir o tráfego entre grupos de recursos. Ao distribuir o tráfego, você ajuda a garantir que os recursos individuais não fiquem sobrecarregados enquanto outros permanecem inativos. A maioria dos balanceadores de carga também oferece recursos de verificação de integridade para ajudar a garantir que o tráfego não seja roteado para recursos não íntegros ou indisponíveis.

O Google Cloud oferece várias opções de balanceamento de carga. Se o aplicativo for executado no Compute Engine ou no GKE, será possível escolher o tipo mais apropriado de balanceador de carga, dependendo do tipo, da origem e de outros aspectos do tráfego. Para mais informações, consulte a Visão geral do balanceamento de carga e a Visão geral da rede do GKE.

Como alternativa, alguns serviços gerenciados pelo Google Cloud, como o App Engine e o Cloud Run, fazem automaticamente o balanceamento de carga do tráfego.

É prática comum balancear a carga de solicitações recebidas de fontes externas, como de clientes de dispositivos móveis ou da Web. No entanto, o uso de balanceadores de carga entre diferentes serviços ou níveis no aplicativo também pode aumentar a resiliência e a flexibilidade. O Google Cloud oferece balanceamento de carga interno do nível 4 e do nível 7 para essa finalidade.

O diagrama a seguir mostra um balanceador de carga externo distribuindo o tráfego global em duas regiões, us-central1 e asia-east1. Ele também mostra o balanceamento de carga interno distribuindo o tráfego do nível da Web para o nível interno de cada região.

Distribuição de tráfego global nas regiões.

Monitoramento de infraestrutura e apps

Antes de decidir como melhorar a resiliência e a escalonabilidade do aplicativo, você precisa entender o comportamento dele. Ter acesso a um conjunto abrangente de métricas e séries temporais relevantes sobre o desempenho e a integridade do aplicativo pode ajudar a descobrir possíveis problemas antes que eles causem uma falha temporária. Isso também pode ajudar a diagnosticar e resolver uma falha temporária, caso ela venha a ocorrer. O capítulo Como monitorar sistemas distribuídos (em inglês) do livro do Google sobre SRE (em inglês) oferece uma visão geral sobre algumas abordagens de monitoramento.

Além de fornecer informações sobre a integridade do aplicativo, as métricas também podem ser usadas para controlar o comportamento de escalonamento automático dos serviços.

O Cloud Monitoring é a ferramenta de monitoramento integrada do Google Cloud. Essa ferramenta ingere eventos, métricas e metadados, além de fornecer informações por meio de painéis e alertas. A maioria dos serviços do Google Cloud envia métricas (em inglês) automaticamente para o Cloud Monitoring, e o Google Cloud também aceita muitas origens de terceiros. Além disso, o Cloud Monitoring pode ser usado como um back-end para ferramentas de monitoramento de código aberto conhecidas, proporcionando um "painel único" para observar o aplicativo.

Monitoramento em todos os níveis

A coleta de métricas em vários níveis na arquitetura fornece um panorama completo da integridade e do comportamento do aplicativo.

Monitoramento da infraestrutura

O monitoramento no nível da infraestrutura oferece o desempenho e a integridade de referência para o aplicativo. Essa abordagem de monitoramento captura informações como carga da CPU, uso de memória e número de bytes gravados no disco. Essas métricas podem indicar que uma máquina está sobrecarregada ou não está funcionando conforme o esperado.

Além das métricas coletadas automaticamente, o Cloud Monitoring fornece um agente que pode ser instalado para coletar informações mais detalhadas das VMs do Compute Engine, inclusive de aplicativoss de terceiros em execução nessas máquinas.

Monitoramento do aplicativo

Recomendamos que você capture métricas no nível do aplicativo. Por exemplo, pode ser útil medir quanto tempo leva para executar uma consulta específica ou para executar uma sequência relacionada de chamadas de serviço. Você define, por conta própria, essas métricas no nível do aplicativo. Elas capturam informações que as métricas integradas ao Cloud Monitoring não conseguem capturar. As métricas no nível do aplicativo conseguem capturar condições agregadas que refletem mais de perto os fluxos de trabalho principais. Além disso, podem revelar problemas que as métricas de infraestrutura de baixo nível não revelam.

Também recomendamos o uso do OpenTelemetry para capturar as métricas no nível do aplicativo. O OpenTelemetry oferece um único padrão aberto para dados de telemetria. Use o OpenTelemetry para coletar e exportar dados dos seus aplicativos e infraestrutura com foco na nuvem. É possível monitorar e analisar os dados de telemetria exportados.

Monitoramento do serviço

Para aplicativos distribuídos e orientados por microsserviços, é importante monitorar as interações entre os diferentes serviços e componentes nos seus aplicativos. Essas métricas podem ajudar você a diagnosticar problemas como o aumento do número de erros ou a latência entre serviços.

O Istio é uma ferramenta de código aberto que fornece insights e controle operacional sobre a rede de microsserviços. O Istio gera telemetria detalhada para todas as comunicações de serviço e pode ser configurado para enviar as métricas ao Cloud Monitoring.

Monitoramento de ponta a ponta

O monitoramento de ponta a ponta, também conhecido como monitoramento de caixa preta, testa o comportamento visível externamente da forma como um usuário o vê. Esse tipo de monitoramento verifica se um usuário é capaz de concluir ações importantes dentro dos limites definidos. Esse monitoramento de alta granularidade consegue revelar erros ou latência que o monitoramento mais preciso talvez não consiga, além de mostrar a disponibilidade da maneira que é percebida pelo usuário.

Exposição da integridade dos aplicativos

Um sistema altamente disponível precisa ter alguma forma de determinar quais partes do sistema estão íntegras e funcionando corretamente. Se determinados recursos parecerem não íntegros, o sistema poderá enviar solicitações para outro lugar. Normalmente, as verificações de integridade envolvem a extração de dados de um endpoint para determinar o status ou a integridade de um serviço.

A verificação de integridade é uma responsabilidade fundamental dos balanceadores de carga. Ao criar um balanceador de carga associado a um grupo de instâncias de máquina virtual, você também define uma verificação de integridade. Essa verificação define como o balanceador de carga se comunica com as máquinas virtuais para avaliar se determinadas instâncias precisam continuar a receber tráfego. As verificações de integridade do balanceador de carga também podem ser usadas para recuperar automaticamente grupos de instâncias, de forma que máquinas não íntegras sejam recriadas. Se você estiver executando no GKE e fazendo o balanceamento de carga do tráfego externo por meio de um recurso de entrada, o GKE criará automaticamente verificações de integridade adequadas para o balanceador de carga.

O Kubernetes conta com suporte integrado para sondagens de atividade e prontidão. Essas sondagens ajudam o orquestrador do Kubernetes a decidir como gerenciar pods e solicitações dentro do cluster. Se o aplicativo for implantado no Kubernetes, convém expor a integridade do aplicativo a essas sondagens por meio dos endpoints adequados.

Estabelecimento de métricas essenciais

O monitoramento e a verificação de integridade fornecem métricas sobre o comportamento e o status do aplicativo. A próxima etapa é analisar essas métricas para determinar quais são as mais descritivas ou impactantes. As métricas principais variam de acordo com a plataforma em que o aplicativo foi implantado e com o trabalho realizado por ele.

Você provavelmente não encontrará uma métrica que indique se é preciso escalonar ou não o aplicativo, ou que um determinado serviço não está íntegro. Geralmente, os fatores combinados indicam um determinado conjunto de condições. Com o Cloud Monitoring, é possível criar métricas personalizadas que ajudam a capturar essas condições. O livro do Google sobre SRE defende quatro sinais de ouro (em inglês) para monitorar um sistema voltado ao usuário: latência, tráfego, erros e saturação.

Considere também a tolerância aos outliers. Usar um valor médio ou mediano para medir a integridade ou o desempenho talvez não seja a melhor escolha, porque essas medidas podem ocultar desequilíbrios abrangentes. Por isso, é importante considerar a distribuição da métrica. O 99º percentil talvez seja uma medida mais informativa do que a média.

Definição de objetivos de nível de serviço (SLOs)

É possível usar as métricas coletadas pelo sistema de monitoramento para definir objetivos de nível de serviço (SLOs). Os SLOs especificam um nível desejado de desempenho ou de confiabilidade para seu serviço. Os SLOs são um dos principais pilares das práticas de SRE e são descritos em detalhes no capítulo objetivos de nível de serviço do livro SRE, e também no capítulo implementação de SLOs na pasta de trabalho do SRE.

Use o monitoramento de serviços para definir SLOs com base nas métricas do Cloud Monitoring. É possível criar políticas de alerta nos SLOs para saber se há risco de violação de um deles.

Armazenamento das métricas

A curto prazo, as métricas do sistema de monitoramento são úteis para ajudar com verificações de integridade em tempo real ou investigar problemas recentes. O Cloud Monitoring retém suas métricas por várias semanas para melhor atender àqueles casos de uso.

No entanto, também há valor em armazenar as métricas de monitoramento para análises de longo prazo. Ter acesso a um registro histórico pode ajudar você a adotar uma abordagem orientada por dados para refinar a arquitetura do aplicativo. É possível usar dados coletados durante e após uma falha temporária para identificar gargalos e interdependências nos seus aplicativos. Também é possível usar os dados para criar e validar testes significativos.

Os dados históricos também podem ajudar a validar se o aplicativo está alinhado com as metas de negócios durante períodos importantes. Por exemplo, os dados podem ajudar a analisar como o aplicativo faz o escalonamento durante eventos promocionais de alto volume de tráfego ao longo dos últimos trimestres ou anos.

Para mais detalhes sobre como exportar e armazenar suas métricas, consulte a solução de exportação de métricas do Cloud Monitoring.

Determinação do perfil de escalonamento

Você quer que o aplicativo atenda à experiência do usuário e às metas de desempenho sem recursos com excesso de provisionamento.

O diagrama a seguir mostra como uma representação simplificada do escalonamento de um aplicativo é traçada. O aplicativo mantém um nível de valor de referência de recursos e usa o escalonamento automático para responder às mudanças na demanda.

Caracterização do perfil de escalonamento de um aplicativo.

Equilíbrio de custo e experiência do usuário

A decisão de escalonar o aplicativo consiste basicamente em equilibrar o custo com a experiência do usuário. Defina os níveis mínimo e máximo aceitáveis de desempenho. Esses limites variam de aplicativo para aplicativo e, potencialmente, em diferentes componentes ou serviços de um único aplicativo.

Por exemplo, um aplicativo para dispositivos móveis ou Web voltado aos consumidores pode ter metas de latência rigorosas. Pesquisas mostram que mesmo pequenos atrasos podem afetar negativamente a forma como os usuários percebem seu aplicativo, resultando em menos conversões e inscrições. Portanto, é importante garantir que o aplicativo tenha capacidade de serviço suficiente para responder rapidamente às solicitações dos usuários. Nesse caso, os custos mais altos da execução de mais servidores da Web podem ser justificados.

A proporção de custo por desempenho talvez seja diferente para um aplicativo interno que não é essencial para os negócios, em que os usuários provavelmente são mais tolerantes em relação a pequenos atrasos. Portanto, seu perfil de escalonamento pode ser menos agressivo. Nesse caso, manter os custos baixos pode ser mais importante do que otimizar a experiência do usuário.

Definição de recursos de valor de referência

Outro componente importante do perfil de escalonamento é decidir sobre um conjunto mínimo adequado de recursos.

As máquinas virtuais do Compute Engine ou os clusters do GKE geralmente levam algum tempo para escalonar verticalmente, porque novos nós precisam ser criados e inicializados. Portanto, talvez seja necessário manter um conjunto mínimo de recursos, mesmo se não houver tráfego. Novamente, a extensão dos recursos de valor de referência é influenciada pelo tipo de aplicativo e perfil de tráfego.

Por outro lado, tecnologias sem servidor, como o App Engine, Cloud Functions e Cloud Run, foram projetadas para escalonar até zero e iniciar e escalonar rapidamente, mesmo no caso de inicialização a frio. Dependendo do tipo de aplicativo e do perfil de tráfego, essas tecnologias podem proporcionar eficiência para partes do aplicativo.

Configure o escalonamento automático

O escalonamento automático (em inglês) ajuda a escalonar automaticamente os recursos de computação consumidos pelo aplicativo. Normalmente, o escalonamento automático ocorre quando determinadas métricas são excedidas ou as condições são atendidas. Por exemplo, se as latências de solicitação para o nível da Web começarem a exceder um determinado valor, será recomendável adicionar automaticamente mais máquinas para aumentar a capacidade de serviço.

Muitos produtos de computação do Google Cloud têm recursos de escalonamento automático. Os serviços gerenciados sem servidor, como o Cloud Run, Cloud Functions e App Engine, foram projetados para escalonar rapidamente. Esses serviços geralmente oferecem opções de configuração para limitar ou influenciar o comportamento do escalonamento automático, mas, em geral, grande parte do comportamento do escalonador automático está oculto do operador.

O Compute Engine e o GKE oferecem mais opções para controlar o comportamento do escalonamento. Com o Compute Engine, é possível escalonar com base em várias entradas, incluindo as métricas personalizadas do Cloud Monitoring e a capacidade de serviço do balanceador de carga. É possível definir limites mínimos e máximos no comportamento de escalonamento, além de definir uma política de escalonamento automático com vários sinais para lidar com diferentes cenários. Assim como no GKE, você consegue configurar o escalonador automático de cluster para adicionar ou remover nós com base em métricas de carga de trabalho ou pod ou em métricas externas ao cluster.

Recomendamos que você configure o comportamento do escalonamento automático com base nas principais métricas do aplicativo, no seu perfil de custo e no nível de recursos mínimo exigido.

Redução do tempo de inicialização

Para que o escalonamento seja eficaz, ele precisa ser rápido o suficiente para lidar com o aumento da carga, principalmente ao adicionar capacidade de computação ou serviço.

Uso de imagens predefinidas

Se o aplicativo for executado em VMs do Compute Engine, você provavelmente precisará instalar o software e configurar as instâncias para executá-lo. Embora seja possível usar scripts de inicialização para configurar novas instâncias, uma maneira mais eficiente é criar uma imagem personalizada. Uma imagem personalizada é um disco de inicialização configurado com o software e a configuração específicos do aplicativo.

Para mais informações sobre como gerenciar imagens, consulte o artigo Práticas recomendadas de gerenciamento de imagens.

Ao criar a imagem, é possível definir um modelo de instância. Os modelos de instância combinam a imagem do disco de inicialização, o tipo de máquina e outras propriedades. Assim, é possível usar um modelo de instância para criar instâncias de VM individuais ou um grupo de instâncias gerenciadas. Os modelos de instância são uma maneira conveniente de salvar a configuração de uma instância de VM para que você possa usá-la posteriormente para criar novas instâncias de VM idênticas.

Embora a criação de imagens personalizadas e modelos de instâncias possa aumentar a velocidade de implantação, isso também pode elevar os custos de manutenção, já que as imagens talvez precisem ser atualizadas com mais frequência. Para mais informações, consulte os documentos sobre Como balancear a configuração da imagem e a velocidade de implantação.

Contentorização do aplicativo

Uma alternativa à criação de instâncias de VM personalizadas é contentorizar o aplicativo. Um contêiner é um pacote executável, independente e leve de software que inclui tudo o que é necessário para executar um aplicativo: código, ambiente de execução, ferramentas e bibliotecas do sistema e configurações. Com essas características, os aplicativos em contêineres tornam-se mais portáteis, fáceis de implantar e de manter em escala do que as máquinas virtuais. Os contêineres também costumam ser rápidos para iniciar, o que os torna adequados para aplicativos escalonáveis e resilientes.

O Google Cloud oferece vários serviços para executar contêineres de apps. O Cloud Run fornece uma plataforma de computação gerenciada sem servidor para hospedar contêineres sem estado. O ambiente flexível do App Engine hospeda contêineres em uma Platform as Service (PaaS) gerenciada. O GKE fornece um ambiente gerenciado do Kubernetes para hospedar e orquestrar os aplicativos em contêiner. Também é possível executar os contêineres do aplicativo no Compute Engine quando precisar ter controle total sobre o ambiente do contêiner.

Otimização do aplicativo para uma inicialização rápida

Além de garantir que a infraestrutura e o aplicativo possam ser implantados da maneira mais eficiente possível, também é importante assegurar que o aplicativo fique on-line rapidamente.

As otimizações adequadas para o aplicativo variam de acordo com a plataforma de execução e as características do aplicativo. É importante fazer o seguinte:

  • Encontrar e eliminar gargalos ao caracterizar o perfil das seções essenciais do aplicativo invocadas na inicialização.
  • Reduzir o tempo de inicialização inicial ao implementar técnicas como a de inicialização lenta, principalmente de recursos caros.
  • Reduzir as dependências de aplicativos que talvez precisem ser carregados no momento da inicialização.

Preferência às arquiteturas modulares

É possível aumentar a flexibilidade do aplicativo escolhendo arquiteturas que permitem que os componentes sejam implantados, gerenciados e escalonados de maneira independente. Esse padrão também pode melhorar a resiliência eliminando pontos únicos de falha.

Divisão do aplicativo em serviços independentes

Se você desenvolver seu aplicativo como um conjunto de serviços independentes e levemente acoplados, poderá aumentar a flexibilidade dele. Ao adotar um design levemente acoplado, os serviços poderão ser liberados e implantados de maneira independente. Além de muitos outros benefícios, essa abordagem permite que esses serviços usem diferentes conjuntos de tecnologia e sejam gerenciados por equipes diferentes. Essa abordagem levemente acoplada é o tema principal dos padrões de arquitetura, como microsserviços e SOA.

Ao considerar como traçar limites em torno dos serviços, os requisitos de disponibilidade e escalonabilidade são dimensões importantes. Por exemplo, se um determinado componente tiver um requisito de disponibilidade ou um perfil de escalonamento diferente dos seus outros componentes, ele poderá ser um bom candidato para um serviço independente.

Uso de elementos sem estado

Um aplicativo ou serviço sem estado não retém nenhum estado ou dado permanente local. Um modelo sem estado garante que você possa lidar com cada solicitação ou interação com o serviço, independentemente das solicitações anteriores. Esse modelo facilita a escalonabilidade e a recuperação, porque dessa forma o serviço pode crescer, diminuir ou ser reiniciado sem perder os dados necessários para lidar com solicitações ou processos em andamento. A condição sem estado é importante principalmente quando você está usando um escalonador automático, porque as instâncias, nós ou pods que hospedam o serviço podem ser criados e destruídos inesperadamente.

Talvez não seja possível que todos os seus serviços sejam sem estado. Nesse caso, determine quais serviços exigem a condição com estado. Ao garantir uma separação clara de serviços sem estado e com estado, é possível assegurar uma escalonabilidade fácil para serviços sem estado, ao mesmo tempo que adota uma abordagem voltada a serviços com estado.

Gestão da comunicação entre serviços

Um desafio envolvendo arquiteturas de microsserviços distribuídos é gerenciar a comunicação entre serviços. À medida que sua rede de serviços cresce, é provável que as interdependências de serviço também aumentem. Você não quer que a falha de um serviço resulte na falha de outros serviços, o que às vezes é chamado de falha em cascata.

É possível ajudar a reduzir o tráfego para um serviço sobrecarregado ou com falha adotando técnicas como padrão de disjuntor, esperas exponenciais e degradação suave (links em inglês). Esses padrões aumentam a resiliência do aplicativo, seja ao dar aos serviços sobrecarregados uma chance de recuperação ou ao gerenciar estados de erro de modo otimizado. Para mais informações, consulte o capítulo Como lidar com falhas em cascata (em inglês) no livro do Google sobre SRE.

Usar uma malha de serviço pode ajudar você a gerenciar o tráfego nos serviços distribuídos. Uma malha de serviços é um software que une os serviços e ajuda a separar a lógica de negócios da rede. Uma malha de serviço geralmente oferece recursos de resiliência, como novas tentativas de solicitação, failovers e disjuntores.

Uso de tecnologias adequadas de banco de dados e armazenamento

Determinados bancos de dados e tipos de armazenamento são difíceis de escalonar e tornar resilientes. Verifique se as opções do seu banco de dados não restringem a disponibilidade e a escalonabilidade do aplicativo.

Avaliação das necessidades do banco de dados

O padrão de projetar o aplicativo como um conjunto de serviços independentes também se estende aos bancos de dados e armazenamento. Talvez seja adequado escolher diferentes tipos de armazenamento para partes diferentes do aplicativo, o que resulta em um armazenamento heterogêneo.

Os aplicativos convencionais geralmente operam exclusivamente com bancos de dados relacionais. Esses bancos de dados relacionais oferecem funcionalidades úteis, como transações fortes, consistência, integridade referencial e consultas sofisticadas em tabelas. Esses recursos, por sua vez, tornam os bancos de dados relacionais uma boa opção para muitos recursos comuns de aplicativos. No entanto, os bancos de dados relacionais também têm algumas restrições. Eles normalmente são difíceis de escalonar e exigem um gerenciamento cuidadoso em uma configuração de alta disponibilidade. Um banco de dados relacional talvez não seja a melhor escolha para todas as necessidades do banco de dados.

Os bancos de dados não relacionais, geralmente chamados de bancos de dados NoSQL, têm uma abordagem diferente. Embora os detalhes variem de um produto para outro, os bancos de dados NoSQL costumam ignorar alguns recursos de bancos de dados relacionais para favorecer o aumento da disponibilidade e a facilidade de escalonabilidade. Em relação ao Teorema CAP (em inglês), os bancos de dados NoSQL geralmente escolhem a disponibilidade em vez da consistência.

A adequação de um banco de dados NoSQL se baseia no nível de consistência necessário. Se o modelo de dados de um determinado serviço não exige todos os recursos de um RDBMS e pode ser projetado para ser consistente posteriormente, a escolha de um banco de dados NoSQL talvez ofereça mais disponibilidade e escalonabilidade.

No gerenciamento de dados, os bancos de dados relacionais e não relacionais são muitas vezes vistos como tecnologias complementares em vez de concorrentes. Ao usar ambos os tipos de banco de dados estrategicamente, as organizações podem aproveitar os pontos fortes de cada um para alcançar os melhores resultados em armazenamento, recuperação e análise de dados.

Além de uma variedade de bancos de dados relacionais e NoSQL, o Google Cloud também oferece o Spanner, um banco de dados de consistência forte, alta disponibilidade e distribuição global com suporte para SQL. Para mais informações sobre como escolher um banco de dados adequado no Google Cloud, consulte Bancos de dados do Google Cloud.

Implementação de armazenamento em cache

A finalidade principal de um cache é aumentar o desempenho da recuperação de dados ao reduzir a necessidade de acessar a camada de armazenamento subjacente mais lenta.

O armazenamento em cache é compatível com a escalonabilidade aprimorada ao reduzir a dependência do armazenamento baseado em disco. Como as solicitações podem ser exibidas a partir da memória, as latências de solicitação para a camada de armazenamento são reduzidas, geralmente permitindo que o serviço processe mais solicitações. Além disso, o armazenamento em cache pode reduzir a carga dos serviços de downstream do aplicativo, especialmente bancos de dados, ao permitir que outros componentes que interagem com esses serviços possam ser escalonados mais.

O armazenamento em cache também pode aumentar a resiliência ao oferecer suporte a técnicas como a de degradação suave (em inglês). Se a camada de armazenamento subjacente estiver sobrecarregada ou indisponível, o cache poderá continuar a processar solicitações. E, embora os dados retornados do cache possam estar incompletos ou desatualizados, isso pode ser aceitável para determinados cenários.

O Memorystore para Redis oferece um serviço totalmente gerenciado que é alimentado pelo armazenamento de dados na memória do Redis. O Memorystore para Redis fornece acesso de baixa latência e alta capacidade para dados frequentemente acessados. Ele pode ser implantado em uma configuração de alta disponibilidade que oferece replicação entre zonas e failover automático.

Modernização de processos de desenvolvimento e cultura

O DevOps pode ser considerado um conjunto amplo de processos, cultura e ferramentas que promovem agilidade e reduzem o tempo de lançamento de aplicativos e funcionalidades ao isolar recursos de desenvolvimento, operações e equipes relacionadas. As técnicas do DevOps visam melhorar a qualidade e a confiabilidade do software.

Uma discussão detalhada sobre DevOps está além do escopo deste documento, mas alguns aspectos importantes relacionados à melhoria da confiabilidade e da resiliência do aplicativo são abordados nas seções a seguir. Para mais detalhes, consulte a página do DevOps do Google Cloud.

Design para capacidade de teste

O teste automatizado é um componente essencial das práticas modernas de entrega de software. A capacidade de executar um conjunto abrangente de testes de unidade, integração e sistema é essencial para verificar se o aplicativo se comporta conforme o esperado e se pode progredir para a próxima etapa do ciclo de implantação. A capacidade de realizar testes é um critério importante de design para o aplicativo.

Recomendamos que você use testes de unidade para a maior parte dos testes, porque eles são rápidos de executar e geralmente simples de manter. Também recomendamos que você automatize a integração de nível superior e os testes do sistema. Esses testes serão bastante simplificados se você adotar técnicas de infraestrutura como código, porque ambientes e recursos de teste dedicados podem ser criados sob demanda e eliminados assim que os testes forem concluídos.

À medida que a porcentagem da sua codebase coberta pelos testes aumenta, você reduz a incerteza e a possível diminuição da confiabilidade de cada alteração de código. Uma cobertura de teste adequada significa que é possível fazer mais mudanças antes que a confiabilidade fique abaixo de um nível aceitável.

O teste automatizado é um componente essencial da integração contínua. Executar um conjunto robusto de testes automatizados em cada confirmação de código fornece feedback rápido sobre as mudanças, melhorando a qualidade e a confiabilidade do software. Ferramentas nativas do Google Cloud, como o Cloud Build, e ferramentas de terceiros, como o Jenkins, ajudam você a implementar a integração contínua.

Automatize as implantações

A integração contínua e a automatização abrangente de teste dão a você confiança na estabilidade do software. Quando elas estiverem em uso, a próxima etapa será automatizar a implantação do aplicativo. O nível de automatização de implantação varia de acordo com a maturidade da sua organização.

Escolher uma estratégia de implantação adequada é essencial para diminuir os riscos associados à implantação de novos softwares. Com a estratégia certa, é possível aumentar gradualmente a exposição de novas versões para públicos maiores, verificando o comportamento durante o processo. Também é possível definir provisionamentos claros para a reversão, caso ocorram problemas.

Adoção de práticas de SRE para lidar com falhas

Quando se trata de aplicativos distribuídos que operam em escala, é comum haver algum grau de falha em um ou mais componentes. Se você adotar os padrões descritos neste documento, seu aplicativo poderá lidar melhor com interrupções causadas por uma versão defeituosa do software, por um encerramento inesperado de máquinas virtuais ou até mesmo por uma falha temporária de infraestrutura que afeta uma zona inteira.

No entanto, mesmo com um design de aplicativo cuidadoso, você vai encontrar eventos inesperados que exigem intervenção humana. Se você implementar processos estruturados para gerenciar esses eventos, poderá reduzir muito o impacto causado e resolvê-los mais rapidamente. Além disso, se você examinar as causas e as respostas ao evento, poderá ajudar a proteger o aplicativo contra eventos semelhantes no futuro.

Processos fortes para gerenciar incidentes (em inglês) e realizar análises sem apontar culpados (em inglês) são princípios fundamentais da SRE. Embora a implementação das práticas completas da SRE do Google possa não ser prática para sua organização, é possível melhorar a resiliência do aplicativo ao adotar um conjunto mínimo de diretrizes. Os anexos no livro sobre SRE (em inglês) contêm alguns modelos que podem ajudar a moldar os processos.

Validação e revisão da arquitetura

À medida que seu aplicativo evolui, o comportamento do usuário, os perfis de tráfego e até as prioridades de negócios podem mudar. De modo semelhante, outros serviços ou infraestrutura de que o aplicativo depende podem evoluir. Portanto, é importante testar e validar periodicamente a resiliência e a escalonabilidade dele.

Teste da resiliência

É fundamental testar se o aplicativo responde às falhas da maneira esperada. O princípio geral é que a melhor maneira de evitar falhas é introduzi-las e aprender com elas.

A simulação e a introdução de falhas são complexas. Além de verificar o comportamento do aplicativo ou serviço, é preciso garantir que as métricas adequadas e os alertas esperados sejam gerados. Recomendamos uma abordagem estruturada, em que você apresenta falhas simples e, em seguida, faz o encaminhamento.

Por exemplo, é possível proceder da maneira mencionada a seguir, validando e documentando o comportamento em cada etapa:

  • Introduzir falhas intermitentes
  • Bloquear o acesso às dependências do serviço
  • Bloquear toda a comunicação em rede
  • Encerrar os hosts

Para mais detalhes, assista ao vídeo Como quebrar seus sistemas para torná-los inquebráveis (em inglês) do Google Cloud Next 2019.

Se você estiver usando uma malha de serviço, como o Istio, para gerenciar os serviços do aplicativo, é possível injetar falhas (em inglês) na camada do aplicativo em vez de eliminar pods ou máquinas. Outra opção é injetar pacotes corrompidos na camada TCP. É possível introduzir atrasos para simular a latência da rede ou um sistema upstream sobrecarregado. Também é possível introduzir cancelamentos, que imitam as falhas em sistemas upstream.

Teste do comportamento de escalonamento

Recomendamos que você use testes automatizados não funcionais para verificar se o aplicativo faz o escalonamento conforme o esperado. Geralmente, essa verificação acontece com testes de carga ou desempenho. É possível usar ferramentas simples, como a hey (em inglês) para enviar carga para um aplicativo da Web. Para um exemplo mais detalhado que mostra como realizar testes de carga em um endpoint REST, consulte Teste de carga distribuída usando o Google Kubernetes Engine.

Uma abordagem comum é garantir que as métricas principais se mantenham dentro dos níveis esperados para cargas diferentes. Por exemplo, se você está testando a escalonabilidade do nível da Web, é possível medir as latências médias de solicitação para volumes altos de solicitações de usuários. De maneira semelhante, quando se trata de um recurso de processamento de back-end, será possível medir o tempo médio de processamento de tarefas quando o volume delas aumentar de uma hora para outra.

Além disso, é recomendável que os testes meçam se o número de recursos, criados para lidar com a carga de teste, está dentro do intervalo esperado. Por exemplo, os testes podem verificar se o número de VMs criadas para lidar com algumas tarefas de back-end não excede um determinado valor.

Também é importante testar casos extremos. Como seu aplicativo ou serviço se comporta quando o limite máximo de escalonamento é atingido? Qual é o comportamento se o serviço estiver sendo reduzido e a carga aumentar outra vez, repentinamente?

Arquitetura contínua

O universo tecnológico se move com rapidez, principalmente quando se trata da nuvem. Novos produtos e recursos são lançados rotineiramente, há o surgimento de novos padrões e as demandas de usuários e partes interessadas internas continuam a crescer.

Conforme definido na postagem de blog Princípios da arquitetura nativa da nuvem (em inglês), é interessante sempre procurar maneiras de refinar, simplificar e melhorar a arquitetura dos seus aplicativos. Sistemas de software funcionam como sistemas vivos e precisam se adaptar para refletir as mudanças em suas prioridades.

A seguir