Quando microsserviços sofrem sobrecarga ou falhas parciais, o tráfego não controlado pode se propagar pelo sistema e derrubar serviços saudáveis. O circuit breaking no nível de rede — por meio dos proxies sidecar do Service Mesh (ASM) — rejeita o excesso de tráfego antes que ele chegue ao backend, sem exigir alterações de código em cada serviço. Abordagens tradicionais, como Resilience4j, exigem incorporar a lógica de circuit breaking diretamente no código da aplicação.
Configure o campo connectionPool em uma DestinationRule para limitar conexões simultâneas e requisições pendentes para um serviço de destino. As seções abaixo detalham os parâmetros, demonstram o comportamento em quatro topologias de dimensionamento de pods e explicam como monitorar o circuit breaking em produção.
Pré-requisitos
Antes de começar, verifique se você tem:
Um cluster Container Service for Kubernetes (ACK) adicionado à sua instância do Service Mesh (ASM) (consulte Adicionar um cluster a uma instância do ASM)
Como funciona o circuit breaking do pool de conexões
Crie uma DestinationRule com configurações de connectionPool para ativar o circuit breaking em um serviço alvo. Para obter a referência completa dos campos, consulte Destination Rule.
Três parâmetros controlam o comportamento do pool de conexões:
|
Parâmetro |
Tipo |
Obrigatório |
Padrão |
Descrição |
|
|
int32 |
Não |
2^32-1 |
Número máximo de conexões HTTP/1.1 ou TCP para um host de destino. Os proxies sidecar aplicam esse limite tanto no lado do cliente quanto no do servidor. Um único pod cliente não pode abrir mais conexões do que esse valor, e um único pod servidor não pode aceitar mais. Capacidade efetiva do lado do servidor: min(pods clientes, pods servidores) x |
|
|
int32 |
Não |
1024 |
Quantidade máxima de requisições enfileiradas enquanto aguardam uma conexão disponível. Definir este valor como |
|
|
int32 |
Não |
1024 |
Limite de requisições ativas para um backend. |
O campoconnectionPoollimita conexões e requisições enfileiradas, mas não remove hosts não saudáveis do pool de balanceamento de carga. Para remover hosts com base nas taxas de erro, combineconnectionPoolcomoutlierDetectionna mesma DestinationRule. Consulte Destination Rule para ver os campos deoutlierDetection.
Com um único pod cliente e um único pod servidor, esses parâmetros apresentam comportamento previsível. Em produção, os serviços geralmente executam vários pods. Os cenários a seguir mostram como o circuit breaking se comporta em quatro topologias comuns:
Um pod cliente, um pod de serviço de destino
Um pod cliente, vários pods de serviço de destino
Vários pods clientes, um pod de serviço de destino
Vários pods clientes, vários pods de serviço de destino
Implante as aplicações de exemplo
A configuração de exemplo possui dois componentes:
Servidor: Uma aplicação Flask escutando na porta 9080 no endpoint
/hello. Cada requisição leva 5 segundos para processamento (simulando um backend lento).Cliente: Um script Python que envia 10 requisições paralelas por lote. Os lotes são disparados nos segundos 0, 20 e 40 de cada minuto, permitindo que múltiplos pods clientes enviem requisições simultaneamente.
-
Salve o YAML a seguir e execute
kubectl apply -f <file-name>.yamlpara implantar as aplicações de exemplo. -
Verifique se os pods estão em execução. Saída esperada:
kubectl get po | grep circuitcircuit-breaker-sample-client-d4f64d66d-fwrh4 2/2 Running 0 1m22s circuit-breaker-sample-server-6d6ddb4b-gcthv 2/2 Running 0 1m22s
Sem uma DestinationRule, o servidor processa todas as 10 requisições simultâneas e cada resposta retorna 200:
----------Info----------
Status: 200, Start: 02:39:20, End: 02:39:25, Elapsed Time: 5.016539812088013
Status: 200, Start: 02:39:20, End: 02:39:25, Elapsed Time: 5.012614488601685
Status: 200, Start: 02:39:20, End: 02:39:25, Elapsed Time: 5.015984535217285
Status: 200, Start: 02:39:20, End: 02:39:25, Elapsed Time: 5.015599012374878
Status: 200, Start: 02:39:20, End: 02:39:25, Elapsed Time: 5.012874364852905
Status: 200, Start: 02:39:20, End: 02:39:25, Elapsed Time: 5.018714904785156
Status: 200, Start: 02:39:20, End: 02:39:25, Elapsed Time: 5.010422468185425
Status: 200, Start: 02:39:20, End: 02:39:25, Elapsed Time: 5.012431621551514
Status: 200, Start: 02:39:20, End: 02:39:25, Elapsed Time: 5.011001348495483
Status: 200, Start: 02:39:20, End: 02:39:25, Elapsed Time: 5.01432466506958
Crie uma DestinationRule para circuit breaking
Defina uma DestinationRule para o serviço de destino a fim de ativar o circuit breaking. Para mais informações, consulte Gerenciar regras de destino.
A regra a seguir limita as conexões TCP a 5:
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: circuit-breaker-sample-server
spec:
host: circuit-breaker-sample-server
trafficPolicy:
connectionPool:
tcp:
maxConnections: 5
Cenário 1: Um pod cliente, um pod de serviço de destino
-
Reinicie o pod cliente e verifique seus logs. Todas as 10 requisições têm sucesso, mas apenas 5 terminam em aproximadamente 5 segundos. As demais aguardam mais de 10 segundos porque ficam enfileiradas até que uma conexão seja liberada. Com apenas
tcp.maxConnectionsdefinido, as requisições excedentes são enfileiradas em vez de falhar — a profundidade padrão da fila é 2^32 - 1.----------Info---------- Status: 200, Start: 02:49:40, End: 02:49:45, Elapsed Time: 5.0167787075042725 Status: 200, Start: 02:49:40, End: 02:49:45, Elapsed Time: 5.011920690536499 Status: 200, Start: 02:49:40, End: 02:49:45, Elapsed Time: 5.017078161239624 Status: 200, Start: 02:49:40, End: 02:49:45, Elapsed Time: 5.018405437469482 Status: 200, Start: 02:49:40, End: 02:49:45, Elapsed Time: 5.018689393997192 Status: 200, Start: 02:49:40, End: 02:49:50, Elapsed Time: 10.018936395645142 Status: 200, Start: 02:49:40, End: 02:49:50, Elapsed Time: 10.016417503356934 Status: 200, Start: 02:49:40, End: 02:49:50, Elapsed Time: 10.019930601119995 Status: 200, Start: 02:49:40, End: 02:49:50, Elapsed Time: 10.022735834121704 Status: 200, Start: 02:49:40, End: 02:49:55, Elapsed Time: 15.02303147315979 -
Para um circuit breaking com falha rápida, limite também
http.http1MaxPendingRequests. Atualize a DestinationRule. Para mais informações, consulte Gerenciar regras de destino.apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: circuit-breaker-sample-server spec: host: circuit-breaker-sample-server trafficPolicy: connectionPool: tcp: maxConnections: 5 http: http1MaxPendingRequests: 1 -
Reinicie o pod cliente e verifique seus logs. Quatro requisições são rejeitadas imediatamente (503), cinco chegam ao destino e uma fica enfileirada (concluindo em aproximadamente 10 segundos após aguardar uma conexão livre).
----------Info---------- Status: 503, Start: 02:56:40, End: 02:56:40, Elapsed Time: 0.005339622497558594 Status: 503, Start: 02:56:40, End: 02:56:40, Elapsed Time: 0.007254838943481445 Status: 503, Start: 02:56:40, End: 02:56:40, Elapsed Time: 0.0044133663177490234 Status: 503, Start: 02:56:40, End: 02:56:40, Elapsed Time: 0.008964776992797852 Status: 200, Start: 02:56:40, End: 02:56:45, Elapsed Time: 5.018309116363525 Status: 200, Start: 02:56:40, End: 02:56:45, Elapsed Time: 5.017424821853638 Status: 200, Start: 02:56:40, End: 02:56:45, Elapsed Time: 5.019804954528809 Status: 200, Start: 02:56:40, End: 02:56:45, Elapsed Time: 5.01643180847168 Status: 200, Start: 02:56:40, End: 02:56:45, Elapsed Time: 5.025975227355957 Status: 200, Start: 02:56:40, End: 02:56:50, Elapsed Time: 10.01716136932373 -
Verifique a contagem de conexões ativas no proxy sidecar do cliente. Saída esperada: Cinco conexões ativas do proxy cliente para o pod de destino, correspondendo ao limite de
maxConnections.kubectl exec $(kubectl get pod --selector app=circuit-breaker-sample-client --output jsonpath='{.items[0].metadata.name}') -c istio-proxy -- curl -X POST http://localhost:15000/clusters | grep circuit-breaker-sample-server | grep cx_activeoutbound|9080||circuit-breaker-sample-server.default.svc.cluster.local::172.20.192.124:9080::cx_active::5
Cenário 2: Um pod cliente, vários pods de serviço de destino
Este cenário testa se o limite de conexão se aplica por pod ou por serviço. Com um cliente e três pods de destino:
Limite por pod: Cada pod permite 5 conexões, totalizando 15. Todas as 10 requisições devem ter sucesso em aproximadamente 5 segundos.
Limite por serviço: Apenas 5 conexões no total, independentemente da quantidade de pods. O comportamento de limitação corresponde ao Cenário 1.
-
Dimensione o serviço de destino para três réplicas.
kubectl scale deployment/circuit-breaker-sample-server --replicas=3 -
Reinicie o pod cliente e verifique seus logs. O padrão de limitação é idêntico ao do Cenário 1. Adicionar mais pods de destino não aumenta o limite de conexão do cliente. O limite de conexão aplica-se por serviço, não por pod.
----------Info---------- Status: 503, Start: 03:06:20, End: 03:06:20, Elapsed Time: 0.011791706085205078 Status: 503, Start: 03:06:20, End: 03:06:20, Elapsed Time: 0.0032286643981933594 Status: 503, Start: 03:06:20, End: 03:06:20, Elapsed Time: 0.012153387069702148 Status: 503, Start: 03:06:20, End: 03:06:20, Elapsed Time: 0.011871814727783203 Status: 200, Start: 03:06:20, End: 03:06:25, Elapsed Time: 5.012892484664917 Status: 200, Start: 03:06:20, End: 03:06:25, Elapsed Time: 5.013102769851685 Status: 200, Start: 03:06:20, End: 03:06:25, Elapsed Time: 5.016939163208008 Status: 200, Start: 03:06:20, End: 03:06:25, Elapsed Time: 5.014261484146118 Status: 200, Start: 03:06:20, End: 03:06:25, Elapsed Time: 5.01246190071106 Status: 200, Start: 03:06:20, End: 03:06:30, Elapsed Time: 10.021712064743042 -
Verifique a distribuição de conexões ativas. Saída esperada: O proxy distribui as conexões entre os pods — duas por pod, seis no total em vez de cinco. Conforme mencionado na documentação do Envoy e do Istio, um proxy permite certa margem de variação na quantidade de conexões.
kubectl exec $(kubectl get pod --selector app=circuit-breaker-sample-client --output jsonpath='{.items[0].metadata.name}') -c istio-proxy -- curl -X POST http://localhost:15000/clusters | grep circuit-breaker-sample-server | grep cx_activeoutbound|9080||circuit-breaker-sample-server.default.svc.cluster.local::172.20.192.124:9080::cx_active::2 outbound|9080||circuit-breaker-sample-server.default.svc.cluster.local::172.20.192.158:9080::cx_active::2 outbound|9080||circuit-breaker-sample-server.default.svc.cluster.local::172.20.192.26:9080::cx_active::2
Cenário 3: Vários pods clientes, um pod de serviço de destino
-
Ajuste as réplicas: dimensione o servidor para 1 e o cliente para 3.
kubectl scale deployment/circuit-breaker-sample-server --replicas=1 kubectl scale deployment/circuit-breaker-sample-client --replicas=3 -
Reinicie os pods clientes e verifique seus logs.
A taxa de erros 503 aumenta em cada cliente. Cada proxy cliente aplica seu próprio limite de 5 conexões de forma independente, mas o único proxy do serviço de destino também impõe um limite de 5 conexões. Apenas 5 requisições de todos os clientes combinados podem ter sucesso simultaneamente.
-
Verifique os logs do proxy cliente para identificar as flags de resposta.
Requisições limitadas retornam um erro 503 com uma de duas flags de resposta:
Flag
Significado
Onde ocorre
UOOverflow upstream (circuit breaking)
O proxy cliente limita a requisição localmente
URXLimite de nova tentativa/conexão upstream excedido
O proxy do serviço de destino rejeita a requisição
Distinga as duas examinando
DURATION,UPSTREAM_HOSTeUPSTREAM_CLUSTERno log de acesso. Requisições comUOnão possuem host upstream (limitadas antes do envio), enquanto requisições comURXchegaram ao proxy de destino e foram rejeitadas lá. -
Confirme verificando os logs do proxy do serviço de destino.
O proxy do serviço de destino também retorna 503 com a flag
UO. Isso confirma que as entradasURXnos logs do proxy cliente originam-se da rejeição de conexões excedentes pelo proxy do serviço de destino.
Resumo do fluxo de requisições:
Cada proxy cliente aplica um limite de 5 conexões independentemente. Com 3 clientes, até 15 requisições podem sair dos proxies clientes em paralelo. No entanto, o único proxy do serviço de destino também impõe um limite de 5 conexões, aceitando apenas 5 e rejeitando o restante. As requisições rejeitadas aparecem como URX nos logs do proxy cliente.
Cenário 4: Vários pods clientes, vários pods de serviço de destino
Dimensionar o serviço de destino aumenta a taxa geral de sucesso porque o proxy de cada pod de destino permite independentemente 5 conexões.
-
Defina o servidor para 2 réplicas e o cliente para 3. Com 2 pods de destino (cada um aceitando 5), 10 das 30 requisições totais (de 3 clientes) têm sucesso por lote.
kubectl scale deployment/circuit-breaker-sample-server --replicas=2 kubectl scale deployment/circuit-breaker-sample-client --replicas=3 -
Dimensione o servidor para 3 réplicas. 15 requisições têm sucesso por lote.
kubectl scale deployment/circuit-breaker-sample-server --replicas=3 -
Dimensione o servidor para 4 réplicas. Ainda assim, apenas 15 requisições têm sucesso. O limite do proxy cliente restringe a 5 por cliente, independentemente de quantos pods de destino estejam disponíveis. Com 3 clientes, o máximo é 3 x 5 = 15 requisições simultâneas bem-sucedidas.
kubectl scale deployment/circuit-breaker-sample-server --replicas=4
Resumo das restrições de cliente e servidor
|
Função |
Como o limite se aplica |
|
Cliente |
Cada proxy cliente aplica o limite independentemente. Se |
|
Serviço de destino |
O proxy de cada pod de destino aplica o limite independentemente. Com 50 pods ativos e |
Monitore métricas de circuit breaking
Quando o circuit breaking é ativado, o Envoy gera métricas para detectar e diagnosticar limitações.
|
Métrica |
Tipo |
Descrição |
|
|
Gauge |
|
|
|
Gauge |
|
Ative métricas de circuit breaking
Configure
proxyStatsMatcherpara o proxy sidecar. Selecione Regular Expression Match e defina o valor como.*circuit_breaker.*. Para mais informações, consulte proxyStatsMatcher.Reimplante os Deployments
circuit-breaker-sample-serverecircuit-breaker-sample-client. Para mais informações, consulte Reimplantar cargas de trabalho.Execute novamente o teste de circuit breaking dos cenários anteriores.
-
Consulte as métricas do proxy cliente. Saída esperada:
kubectl exec -it deploy/circuit-breaker-sample-client -c istio-proxy -- curl localhost:15090/stats/prometheus | grep circuit_breaker | grep circuit-breaker-sample-serverenvoy_cluster_circuit_breakers_default_cx_open{cluster_name="outbound|9080||circuit-breaker-sample-server.default.svc.cluster.local"} 1 envoy_cluster_circuit_breakers_default_cx_pool_open{cluster_name="outbound|9080||circuit-breaker-sample-server.default.svc.cluster.local"} 0 envoy_cluster_circuit_breakers_default_remaining_cx{cluster_name="outbound|9080||circuit-breaker-sample-server.default.svc.cluster.local"} 0 envoy_cluster_circuit_breakers_default_remaining_cx_pools{cluster_name="outbound|9080||circuit-breaker-sample-server.default.svc.cluster.local"} 18446744073709551613 envoy_cluster_circuit_breakers_default_remaining_pending{cluster_name="outbound|9080||circuit-breaker-sample-server.default.svc.cluster.local"} 1 envoy_cluster_circuit_breakers_default_remaining_retries{cluster_name="outbound|9080||circuit-breaker-sample-server.default.svc.cluster.local"} 4294967295 envoy_cluster_circuit_breakers_default_remaining_rq{cluster_name="outbound|9080||circuit-breaker-sample-server.default.svc.cluster.local"} 4294967295 envoy_cluster_circuit_breakers_default_rq_open{cluster_name="outbound|9080||circuit-breaker-sample-server.default.svc.cluster.local"} 0 envoy_cluster_circuit_breakers_default_rq_pending_open{cluster_name="outbound|9080||circuit-breaker-sample-server.default.svc.cluster.local"} 0 envoy_cluster_circuit_breakers_default_rq_retry_open{cluster_name="outbound|9080||circuit-breaker-sample-server.default.svc.cluster.local"} 0 envoy_cluster_circuit_breakers_high_cx_open{cluster_name="outbound|9080||circuit-breaker-sample-server.default.svc.cluster.local"} 0 envoy_cluster_circuit_breakers_high_cx_pool_open{cluster_name="outbound|9080||circuit-breaker-sample-server.default.svc.cluster.local"} 0 envoy_cluster_circuit_breakers_high_rq_open{cluster_name="outbound|9080||circuit-breaker-sample-server.default.svc.cluster.local"} 0 envoy_cluster_circuit_breakers_high_rq_pending_open{cluster_name="outbound|9080||circuit-breaker-sample-server.default.svc.cluster.local"} 0 envoy_cluster_circuit_breakers_high_rq_retry_open{cluster_name="outbound|9080||circuit-breaker-sample-server.default.svc.cluster.local"} 0
Configure alertas para circuit breaking
Use o Managed Service for Prometheus para coletar métricas de circuit breaking e configurar regras de alerta. Para detalhes sobre integração de componentes, consulte Gerenciar componentes.
Se você já utiliza uma instância autogerenciada do Prometheus para coletar métricas do ASM (consulte Monitorar instâncias do ASM usando uma instância autogerenciada do Prometheus ), pule a etapa 1.
No Managed Service for Prometheus, conecte o cluster do plano de dados ao componente Alibaba Cloud ASM ou atualize-o para a versão mais recente.
-
Crie uma regra de alerta com uma instrução PromQL personalizada. Para mais informações, consulte Usar uma instrução PromQL personalizada para criar uma regra de alerta. Utilize os seguintes parâmetros como referência:
Parâmetro
Exemplo
Descrição
Instruções PromQL personalizadas
(sum by(cluster_name, pod_name, namespace) (envoy_cluster_circuit_breakers_default_cx_open)) != 0Verifica se o circuit breaking está ativo em algum pool de conexões. Agrupa por nome do serviço upstream, pod e namespace para que você possa identificar exatamente onde ocorre a limitação.
Mensagem de alerta
Circuit breaking is active. The TCP connection limit has been reached. Namespace: {{$labels.namespace}}, Pod: {{$labels.pod_name}}, Upstream service: {{$labels.cluster_name}}
Identifica o pod afetado, seu namespace e o serviço upstream.