Compartilhar via


Design de aplicativo de cargas de trabalho de missão crítica no Azure

Quando você projeta um aplicativo, os requisitos de aplicativos funcionais e não funcionais são críticos. Esta área de design descreve padrões de arquitetura e estratégias de dimensionamento que podem ajudar a tornar seu aplicativo resiliente a falhas.

Importante

Este artigo faz parte da série de cargas de trabalho de missão crítica do Azure Well-Architected Framework. Se você não estiver familiarizado com esta série, recomendamos que você comece com O que é uma carga de trabalho de missão crítica?.

Arquitetura de unidade de escala

Todos os aspectos funcionais de uma solução devem ser capazes de ser dimensionados para atender às mudanças na demanda. Recomendamos que você use uma arquitetura de unidade de escala para otimizar a escalabilidade de ponta a ponta por meio da compartimentação e também para padronizar o processo de adição e remoção de capacidade. Uma unidade de escala é uma unidade lógica ou função que pode ser dimensionada independentemente. Uma unidade pode ser composta por componentes de código, plataformas de hospedagem de aplicativos, os carimbos de implantação que abrangem os componentes relacionados e até mesmo assinaturas para oferecer suporte a requisitos de multilocatário.

Recomendamos essa abordagem porque ela aborda os limites de escala de recursos individuais e todo o aplicativo. Ele ajuda com cenários complexos de implantação e atualização porque uma unidade de escala pode ser implantada como uma unidade. Além disso, você pode testar e validar versões específicas de componentes em uma unidade antes de direcionar o tráfego do usuário para ela.

Suponha que seu aplicativo de missão crítica seja um catálogo de produtos on-line. Ele tem um fluxo de usuário para processar comentários e classificações de produtos. O fluxo usa APIs para recuperar e postar comentários e classificações e componentes de suporte como um ponto de extremidade OAuth, armazenamento de dados e filas de mensagens. Os pontos de extremidade de API sem monitoração de estado representam unidades funcionais granulares que devem se adaptar às alterações sob demanda. A plataforma de aplicativo subjacente também deve ser capaz de dimensionar de acordo. Para evitar gargalos de desempenho, os componentes e dependências a jusante também devem ser dimensionados em um grau apropriado. Eles podem escalar independentemente, como unidades de escala separadas, ou juntos, como parte de uma única unidade lógica.

Exemplo de unidades de escala

A imagem a seguir mostra os escopos possíveis para unidades de escala. Os escopos variam de pods de microsserviço a nós de cluster e carimbos de implantação regional.

Diagrama que mostra vários escopos para unidades de escala.

Considerações sobre o design

  • Escopo. O escopo de uma unidade de escala, a relação entre as unidades de escala e seus componentes devem ser definidos de acordo com um modelo de capacidade. Leve em consideração os requisitos não funcionais de desempenho.

  • Limites de escala. Os limites e cotas de escala de assinatura do Azure podem ter influência no design do aplicativo, nas opções de tecnologia e na definição de unidades de escala. As unidades de escala podem ajudá-lo a ignorar os limites de escala de um serviço. Por exemplo, se um cluster AKS em uma unidade pode ter apenas 1.000 nós, você pode usar duas unidades para aumentar esse limite para 2.000 nós.

  • Carga esperada. Use o número de solicitações para cada fluxo de usuário, a taxa de solicitação de pico esperada (solicitações por segundo) e os padrões de tráfego diário/semanal/sazonal para informar os requisitos de escala principal. Considere também os padrões de crescimento esperados para o tráfego e o volume de dados.

  • Desempenho degradado aceitável. Determine se um serviço degradado com altos tempos de resposta é aceitável sob carga. Quando você está modelando a capacidade necessária, o desempenho necessário da solução sob carga é um fator crítico.

  • Requisitos não funcionais. Os cenários técnicos e de negócios têm considerações distintas sobre resiliência, disponibilidade, latência, capacidade e observabilidade. Analise esses requisitos no contexto dos principais fluxos de usuário de ponta a ponta. Você terá relativa flexibilidade nas opções de design, tomada de decisões e tecnologia em um nível de fluxo de usuário.

Recomendações sobre design

  • Defina o escopo de uma unidade de escala e os limites que acionarão a unidade para escala.

  • Certifique-se de que todos os componentes do aplicativo possam ser dimensionados de forma independente ou como parte de uma unidade de escala que inclua outros componentes relacionados.

  • Definir a relação entre unidades de escala, com base em um modelo de capacidade e requisitos não funcionais.

  • Defina um carimbo de implantação regional para unificar o provisionamento, o gerenciamento e a operação de recursos de aplicativos regionais em uma unidade de escala heterogênea, mas interdependente. À medida que a carga aumenta, carimbos extras podem ser implantados, na mesma região do Azure ou em regiões diferentes, para dimensionar horizontalmente a solução.

  • Use uma assinatura do Azure como a unidade de escala para que os limites de escala em uma única assinatura não restrinjam a escalabilidade. Essa abordagem se aplica a cenários de aplicativos de alta escala que têm um volume de tráfego significativo.

  • Modele a capacidade necessária em torno de padrões de tráfego identificados para garantir que a capacidade suficiente seja provisionada nos horários de pico para evitar a degradação do serviço. Como alternativa, otimize a capacidade fora do horário de pico.

  • Meça o tempo necessário para fazer operações de scale-out e scale-in para garantir que as variações naturais no tráfego não criem um nível inaceitável de degradação do serviço. Acompanhe as durações de operação de escala como uma métrica operacional.

Observação

Ao implantar em uma zona de aterrissagem do Azure, verifique se a assinatura da zona de aterrissagem é dedicada ao aplicativo para fornecer um limite de gerenciamento claro e evitar o antipadrão Vizinho barulhento.

Distribuição global

É impossível evitar falhas em qualquer ambiente altamente distribuído. Esta seção fornece estratégias para mitigar muitos cenários de falha. O aplicativo deve ser capaz de resistir a falhas regionais e zonais. Ele deve ser implantado em um modelo ativo/ativo para que a carga seja distribuída entre todas as regiões.

Assista a este vídeo para obter uma visão geral de como planejar falhas em aplicativos de missão crítica e maximizar a resiliência:

Considerações sobre o design

  • Redundância. Seu aplicativo deve ser implantado em várias regiões. Além disso, em uma região, é altamente recomendável que você use zonas de disponibilidade para permitir tolerância a falhas no nível do datacenter. As zonas de disponibilidade têm um perímetro de latência inferior a 2 milissegundos entre as zonas de disponibilidade. Para cargas de trabalho que são "tagarelas" entre zonas, essa latência pode introduzir uma penalidade de desempenho para a transferência de dados entre zonas.

  • Modelo ativo/ativo. Uma estratégia de implantação ativa/ativa é recomendada porque maximiza a disponibilidade e fornece um SLA (contrato de nível de serviço) composto mais alto. No entanto, ele pode apresentar desafios em torno da sincronização de dados e consistência para muitos cenários de aplicativos. Enfrente os desafios em um nível de plataforma de dados, considerando as compensações do aumento do custo e do esforço de engenharia.

    Uma implantação ativa/ativa em vários provedores de nuvem é uma maneira de potencialmente mitigar a dependência de recursos globais em um único provedor de nuvem. No entanto, uma estratégia de implantação ativa/ativa multicloud introduz uma quantidade significativa de complexidade em torno do CI/CD. Além disso, dadas as diferenças nas especificações de recursos e recursos entre os provedores de nuvem, você precisaria de carimbos de implantação especializados para cada nuvem.

  • Distribuição geográfica. A carga de trabalho pode ter requisitos de conformidade para residência de dados geográficos, proteção de dados e retenção de dados. Considere se há regiões específicas onde os dados devem residir ou onde os recursos precisam ser implantados.

  • Origem do pedido. A proximidade geográfica e a densidade de usuários ou sistemas dependentes devem informar as decisões de projeto sobre a distribuição global.

  • Conectividade. A forma como a carga de trabalho é acessada por usuários ou sistemas externos influenciará seu design. Considere se o aplicativo está disponível na Internet pública ou em redes privadas que usam circuitos VPN ou Azure ExpressRoute.

Para obter recomendações de design e opções de configuração no nível da plataforma, consulte Plataforma de aplicativos: distribuição global.

Arquitetura orientada a eventos fracamente acoplada

O acoplamento permite a comunicação entre serviços através de interfaces bem definidas. Um acoplamento solto permite que um componente de aplicação opere de forma independente. Um estilo de arquitetura de microsserviços é consistente com os requisitos de missão crítica. Ele facilita a alta disponibilidade, evitando falhas em cascata.

Para acoplamento flexível, recomendamos que você incorpore o design orientado a eventos. O processamento assíncrono de mensagens por meio de um intermediário pode criar resiliência.

Diagrama que ilustra a comunicação assíncrona orientada a eventos.

Em alguns cenários, os aplicativos podem combinar acoplamento flexível e rígido, dependendo dos objetivos de negócios.

Considerações sobre o design

  • Dependências de tempo de execução. Os serviços fracamente acoplados não devem ser restringidos a usar a mesma plataforma de computação, linguagem de programação, tempo de execução ou sistema operacional.

  • Dimensionamento. Os serviços devem poder ser dimensionados de forma independente. Otimize o uso da infraestrutura e dos recursos da plataforma.

  • Tolerância a falhas. As falhas devem ser tratadas separadamente e não devem afetar as transações do cliente.

  • Integridade transacional. Considere o efeito da criação e persistência de dados que acontece em serviços separados.

  • Rastreamento distribuído. O rastreamento de ponta a ponta pode exigir orquestração complexa.

Recomendações sobre design

  • Alinhe os limites de microsserviços com fluxos de usuários críticos.

  • Use a comunicação assíncrona orientada a eventos sempre que possível para oferecer suporte à escala sustentável e ao desempenho ideal.

  • Use padrões como Caixa de Saída e Sessão Transacional para garantir a consistência para que cada mensagem seja processada corretamente.

Exemplo: abordagem orientada a eventos

A implementação de referência on-line de missão crítica usa microsserviços para processar uma única transação comercial. Ele aplica operações de gravação de forma assíncrona com um agente de mensagens e um trabalhador. As operações de leitura são síncronas, com o resultado retornado diretamente ao chamador.

Diagrama que mostra a comunicação orientada a eventos.

Padrões de resiliência e tratamento de erros no código do aplicativo

Um aplicativo de missão crítica deve ser projetado para ser resiliente para que ele resolva o maior número possível de cenários de falha. Essa resiliência maximiza a disponibilidade e a confiabilidade do serviço. O aplicativo deve ter recursos de autorrecuperação, que você pode implementar usando padrões de design como Tentativas com Backoff e Disjuntor.

Para falhas não transitórias que não podem ser totalmente atenuadas na lógica do aplicativo, o modelo de integridade e os wrappers operacionais precisam tomar medidas corretivas. O código do aplicativo deve incorporar instrumentação e log adequados para informar o modelo de integridade e facilitar a solução de problemas subsequente ou a análise de causa raiz, conforme necessário. Você precisa implementar o rastreamento distribuído para fornecer ao chamador uma mensagem de erro abrangente que inclua uma ID de correlação quando ocorrer uma falha.

Ferramentas como o Application Insights podem ajudá-lo a consultar, correlacionar e visualizar rastreamentos de aplicativos.

Considerações sobre o design

  • Configurações adequadas. Não é incomum que problemas transitórios causem falhas em cascata. Por exemplo, repetir sem recuo apropriado agrava o problema quando um serviço está sendo limitado. Você pode espaçar atrasos de repetição linearmente ou aumentá-los exponencialmente para recuar através de atrasos crescentes.

  • Pontos de extremidade de integridade. Você pode expor verificações funcionais no código do aplicativo usando pontos de extremidade de integridade que as soluções externas podem sondar para recuperar o status de integridade do componente do aplicativo.

Recomendações sobre design

Aqui estão alguns padrões comuns de engenharia de software para aplicativos resilientes:

Padrão Resumo
Nivelamento de Carga Baseado em Fila Introduz um buffer entre os consumidores e os recursos solicitados para garantir níveis de carga consistentes. À medida que as solicitações do consumidor são enfileiradas, um processo de trabalho as manipula em relação ao recurso solicitado em um ritmo definido pelo trabalhador e pela capacidade do recurso solicitado de processar as solicitações. Se os consumidores esperam respostas às suas solicitações, você precisa implementar um mecanismo de resposta separado. Aplique uma ordem de priorização para que as atividades mais importantes sejam executadas primeiro.
Interruptor de Circuito Fornece estabilidade aguardando recuperação ou rejeitando solicitações rapidamente em vez de bloquear enquanto aguarda um serviço ou recurso remoto indisponível. Esse padrão também lida com falhas que podem levar um tempo variável para serem recuperadas quando uma conexão é feita com um serviço ou recurso remoto.
Bulkhead Tentativas de particionar instâncias de serviço em grupos com base nos requisitos de carga e disponibilidade, isolando falhas para sustentar a funcionalidade do serviço.
Saga Gerencia a consistência de dados em microsserviços que possuem armazenamentos de dados independentes, garantindo que os serviços se atualizem uns aos outros por meio de canais de eventos ou mensagens definidos. Cada serviço executa transações locais para atualizar seu próprio estado e publica um evento para disparar a próxima transação local da saga. Se uma atualização de serviço falhar, a saga executará transações de compensação para neutralizar as etapas de atualização de serviço anteriores. As etapas de atualização de serviço individuais podem implementar padrões de resiliência, como nova tentativa.
Monitoramento do Ponto de Extremidade de Integridade Implementa verificações funcionais em um aplicativo que ferramentas externas podem acessar por meio de pontos de extremidade expostos em intervalos regulares. Você pode interpretar respostas dos pontos de extremidade usando as principais métricas operacionais para informar a integridade do aplicativo e disparar respostas operacionais, como gerar um alerta ou executar uma implantação de reversão compensatória.
Repetir Lida com falhas transitórias de forma elegante e transparente.
- Cancelar se a falha não for passageira e for improvável que tenha sucesso se a operação for tentada novamente.
- Tente novamente se a falha for incomum ou rara e a operação provavelmente terá sucesso se for tentada novamente imediatamente.
- Tente novamente após um atraso se a falha for causada por uma condição que pode precisar de um curto período de tempo para se recuperar, como conectividade de rede ou falhas de alta carga. Aplique uma estratégia de recuo adequada à medida que os atrasos de repetição aumentam.
Limitação Controla o consumo de recursos usados pelos componentes do aplicativo, protegendo-os de ficarem sobrecarregados. Quando um recurso atinge um limite de carga, ele adia operações de prioridade mais baixa e degrada a funcionalidade não essencial para que a funcionalidade essencial possa continuar até que recursos suficientes estejam disponíveis para retornar à operação normal.

Aqui estão algumas recomendações adicionais:

  • Use SDKs fornecidos pelo fornecedor, como os SDKs do Azure, para se conectar a serviços dependentes. Use recursos internos de resiliência em vez de implementar a funcionalidade personalizada.

  • Aplique uma estratégia de recuo adequada ao tentar novamente chamadas de dependência com falha para evitar um cenário de DDoS autoinfligido.

  • Defina critérios de engenharia comuns para todas as equipes de microsserviços de aplicativos para aumentar a consistência e a velocidade no uso de padrões de resiliência em nível de aplicativo.

  • Implemente padrões de resiliência usando pacotes padronizados comprovados, como Polly para C# ou Sentinel para Java.

  • Use IDs de correlação para todos os eventos de rastreamento e mensagens de log para vinculá-los a uma determinada solicitação. Retorne IDs de correlação ao chamador para todas as chamadas, não apenas para as solicitações com falha.

  • Use o log estruturado para todas as mensagens de log. Selecione um coletor de dados operacionais unificado para rastreamentos, métricas e logs de aplicativos para permitir que os operadores depurem problemas facilmente. Para obter mais informações, consulte Coletar, agregar e armazenar dados de monitoramento para aplicativos em nuvem.

  • Garantir que os dados operacionais sejam usados em conjunto com os requisitos de negócios para informar um modelo de integridade do aplicativo.

Seleção da linguagem de programação

É importante selecionar as linguagens e estruturas de programação certas. Essas decisões geralmente são conduzidas pelos conjuntos de habilidades ou tecnologias padronizadas na organização. No entanto, é essencial avaliar o desempenho, a resiliência e as capacidades gerais de várias linguagens e estruturas.

Considerações sobre o design

  • Recursos do kit de desenvolvimento. Há diferenças nos recursos oferecidos pelos SDKs de serviço do Azure em vários idiomas. Essas diferenças podem influenciar sua escolha de um serviço ou linguagem de programação do Azure. Por exemplo, se o Azure Cosmos DB for uma opção viável, Go pode não ser uma linguagem de desenvolvimento apropriada porque não há SDK primário.

  • Atualizações de recursos. Considere a frequência com que o SDK é atualizado com novos recursos para o idioma selecionado. SDKs comumente usados, como bibliotecas .NET e Java, são atualizados com frequência. Outros SDKs ou SDKs para outros idiomas podem ser atualizados com menos frequência.

  • Várias linguagens de programação ou frameworks. Você pode usar várias tecnologias para oferecer suporte a várias cargas de trabalho compostas. No entanto, você deve evitar a expansão porque ela introduz complexidade de gerenciamento e desafios operacionais.

  • Opção de computação. O software herdado ou proprietário pode não ser executado em serviços de PaaS. Além disso, talvez você não consiga incluir software herdado ou proprietário em contêineres.

Recomendações sobre design

  • Avalie todos os SDKs relevantes do Azure para obter os recursos necessários e as linguagens de programação escolhidas. Verifique o alinhamento com requisitos não funcionais.

  • Otimize a seleção de linguagens de programação e frameworks no nível de microsserviços. Use várias tecnologias, conforme apropriado.

  • Priorize o SDK do .NET para otimizar a confiabilidade e o desempenho. Os SDKs do .NET Azure normalmente fornecem mais recursos e são atualizados com frequência.

Próxima etapa

Revise as considerações para a plataforma do aplicativo.