Balanceamento de carga nativa do contentor através da entrada

Esta página explica como usar o equilíbrio de carga nativo de contentores no Google Kubernetes Engine (GKE). O balanceamento de carga nativa do contentor permite que os balanceadores de carga segmentem diretamente os pods do Kubernetes e distribuam o tráfego uniformemente para os pods.

Para mais informações sobre as vantagens, os requisitos e as limitações do balanceamento de carga nativa do contentor, consulte o artigo Balanceamento de carga nativa do contentor.

Antes de começar

Antes de começar, certifique-se de que realizou as seguintes tarefas:

  • Ative a API Google Kubernetes Engine.
  • Ative a API Google Kubernetes Engine
  • Se quiser usar a CLI gcloud para esta tarefa, instale-a e, em seguida, inicialize-a. Se instalou anteriormente a CLI gcloud, execute gcloud components update para obter a versão mais recente.

Use o balanceamento de carga nativa do contentor

As secções seguintes explicam uma configuração de balanceamento de carga nativa do contentor no GKE.

Crie um cluster nativo de VPC

Para usar o equilíbrio de carga nativo do contentor, o cluster do GKE tem de ter os IPs de alias ativados.

Por exemplo, o comando seguinte cria um cluster do GKE, neg-demo-cluster, com uma sub-rede aprovisionada automaticamente:

  • Para o modo de piloto automático, as moradas IP de alias estão ativadas por predefinição:

    gcloud container clusters create-auto neg-demo-cluster \
        --location=COMPUTE_LOCATION
    

    Substitua COMPUTE_LOCATION pela localização do Compute Engine para o cluster.

  • Para o modo Standard, ative os endereços IP de alias quando criar o cluster:

    gcloud container clusters create neg-demo-cluster \
        --enable-ip-alias \
        --create-subnetwork="" \
        --network=default \
        --location=us-central1-a
    

Crie uma implementação

A seguinte amostra de implementação, neg-demo-app, executa uma única instância de um servidor HTTP em contentor. Recomendamos que use cargas de trabalho que usem o feedback de prontidão do pod.

Usar o feedback de disponibilidade de pods

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: neg-demo-app # Label for the Deployment
  name: neg-demo-app # Name of Deployment
spec:
  selector:
    matchLabels:
      run: neg-demo-app
  template: # Pod template
    metadata:
      labels:
        run: neg-demo-app # Labels Pods from this Deployment
    spec: # Pod specification; each Pod created by this Deployment has this specification
      containers:
      - image: registry.k8s.io/serve_hostname:v1.4 # Application to run in Deployment's Pods
        name: hostname # Container name
        ports:
        - containerPort: 9376
          protocol: TCP
  

Usar um atraso codificado

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: neg-demo-app # Label for the Deployment
  name: neg-demo-app # Name of Deployment
spec:
  minReadySeconds: 60 # Number of seconds to wait after a Pod is created and its status is Ready
  selector:
    matchLabels:
      run: neg-demo-app
  template: # Pod template
    metadata:
      labels:
        run: neg-demo-app # Labels Pods from this Deployment
    spec: # Pod specification; each Pod created by this Deployment has this specification
      containers:
      - image: registry.k8s.io/serve_hostname:v1.4 # Application to run in Deployment's Pods
        name: hostname # Container name
      # Note: The following line is necessary only on clusters running GKE v1.11 and lower.
      # For details, see https://cloud.google.com/kubernetes-engine/docs/how-to/container-native-load-balancing#align_rollouts
        ports:
        - containerPort: 9376
          protocol: TCP
      terminationGracePeriodSeconds: 60 # Number of seconds to wait for connections to terminate before shutting down Pods
  

Nesta implementação, cada contentor executa um servidor HTTP. O servidor HTTP devolve o nome de anfitrião do servidor de aplicações (o nome do pod no qual o servidor é executado) como resposta.

Guarde este manifesto como neg-demo-app.yaml e, em seguida, crie a implementação:

kubectl apply -f neg-demo-app.yaml

Crie um serviço para um balanceador de carga nativa do contentor

Depois de criar uma implementação, tem de agrupar os respetivos pods num serviço.

O seguinte serviço de exemplo, neg-demo-svc, segmenta a implementação de exemplo que criou na secção anterior:

apiVersion: v1
kind: Service
metadata:
  name: neg-demo-svc # Name of Service
spec: # Service's specification
  type: ClusterIP
  selector:
    run: neg-demo-app # Selects Pods labelled run: neg-demo-app
  ports:
  - name: http
    port: 80 # Service's port
    protocol: TCP
    targetPort: 9376

O balanceador de carga não é criado até criar um Ingress para o serviço.

Guarde este manifesto como neg-demo-svc.yaml e, em seguida, crie o serviço:

kubectl apply -f neg-demo-svc.yaml

Crie um Ingress para o serviço

O seguinte exemplo de Ingress, neg-demo-ing, segmenta o serviço que criou:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: neg-demo-ing
spec:
  defaultBackend:
    service:
      name: neg-demo-svc # Name of the Service targeted by the Ingress
      port:
        number: 80 # Should match the port used by the Service

Guarde este manifesto como neg-demo-ing.yaml e, em seguida, crie a entrada:

kubectl apply -f neg-demo-ing.yaml

Após a criação do Ingress, é criado um Application Load Balancer no projeto e são criados grupos de pontos finais de rede(NEGs) em cada zona em que o cluster é executado. Os pontos finais no NEG e os pontos finais do serviço são mantidos sincronizados.

Valide a entrada

Depois de implementar uma carga de trabalho, agrupar os respetivos pods num serviço e criar um Ingress para o serviço, deve verificar se o balanceador de carga nativo de contentores foi aprovisionado com êxito.

Obtenha o estado do Ingress:

kubectl describe ingress neg-demo-ing

A saída inclui eventos ADD e CREATE:

Events:
Type     Reason   Age                From                     Message
----     ------   ----               ----                     -------
Normal   ADD      16m                loadbalancer-controller  default/neg-demo-ing
Normal   Service  4s                 loadbalancer-controller  default backend set to neg-demo-svc:32524
Normal   CREATE   2s                 loadbalancer-controller  ip: 192.0.2.0

Teste o balanceador de carga

As secções seguintes explicam como pode testar a funcionalidade de um balanceador de carga nativo de contentores.

Endereço IP de entrada de visitas

Aguarde vários minutos até que o Application Load Balancer seja configurado.

Pode verificar se o balanceador de carga nativo do contentor está a funcionar visitando o endereço IP do Ingress.

Para obter o endereço IP de entrada, execute o seguinte comando:

kubectl get ingress neg-demo-ing

Na saída do comando, o endereço IP do Ingress é apresentado na coluna ADDRESS. Visite o endereço IP num navegador de Internet.

Verifique o estado de funcionamento do serviço de back-end

Também pode obter o estado de funcionamento do serviço de back-end do balanceador de carga.

  1. Obtenha uma lista dos serviços de back-end em execução no seu projeto:

    gcloud compute backend-services list
    

    Registe o nome do serviço de back-end que inclui o nome do serviço, como neg-demo-svc.

  2. Obtenha o estado de saúde do serviço de back-end:

    gcloud compute backend-services get-health BACKEND_SERVICE_NAME --global
    

    Substitua BACKEND_SERVICE_NAME pelo nome do serviço de back-end.

Teste a entrada

Outra forma de testar se o balanceador de carga funciona como esperado é dimensionar a implementação de amostra, enviar pedidos de teste para o Ingress e verificar se o número correto de réplicas responde.

  1. Dimensione a implementação neg-demo-app de uma instância para duas instâncias:

    kubectl scale deployment neg-demo-app --replicas 2
    

    Este comando pode demorar alguns minutos a ser concluído.

  2. Verifique se a implementação está concluída:

    kubectl get deployment neg-demo-app
    

    A saída deve incluir duas réplicas disponíveis:

    NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    neg-demo-app   2         2         2            2           26m
    
  3. Obtenha o endereço IP de entrada:

    kubectl describe ingress neg-demo-ing
    

    Se este comando devolver um erro 404, aguarde alguns minutos para que o balanceador de carga seja iniciado e, em seguida, tente novamente.

  4. Conte o número de respostas distintas do equilibrador de carga:

    for i in `seq 1 100`; do \
      curl --connect-timeout 1 -s IP_ADDRESS && echo; \
    done  | sort | uniq -c
    

    Substitua IP_ADDRESS pelo endereço IP de entrada.

    O resultado é semelhante ao seguinte:

    44 neg-demo-app-7f7dfd7bc6-dcn95
    56 neg-demo-app-7f7dfd7bc6-jrmzf
    

    Neste resultado, o número de respostas distintas é igual ao número de réplicas, o que indica que todos os pods de back-end estão a publicar tráfego.

Limpar

Depois de concluir as tarefas nesta página, siga estes passos para remover os recursos e evitar a incorrência de encargos indesejados na sua conta:

Elimine o cluster

gcloud

gcloud container clusters delete neg-demo-cluster

Consola

  1. Aceda à página do Google Kubernetes Engine na Trusted Cloud consola.

    Aceda ao Google Kubernetes Engine

  2. Selecione neg-demo-cluster e clique em Eliminar.

  3. Quando lhe for pedido que confirme, clique em Eliminar.

Resolução de problemas

Use as técnicas abaixo para validar a configuração de rede. As secções seguintes explicam como resolver problemas específicos relacionados com o balanceamento de carga nativo do contentor.

  • Consulte a documentação sobre o equilíbrio de carga para saber como listar os grupos de pontos finais de rede.

  • Pode encontrar o nome e as zonas do NEG que correspondem a um serviço na anotação neg-status do serviço. Obtenha a especificação do serviço com:

    kubectl get svc SVC_NAME -o yaml
    

    A anotação metadata:annotations:cloud.google.com/neg-status indica o nome do NEG correspondente do serviço e as zonas do NEG.

  • Pode verificar o estado do serviço de back-end que corresponde a um NEG com o seguinte comando:

    gcloud compute backend-services --project PROJECT_NAME \
        get-health BACKEND_SERVICE_NAME --global
    

    O serviço de back-end tem o mesmo nome que o respetivo NEG.

  • Para imprimir os registos de eventos de um serviço:

    kubectl describe svc SERVICE_NAME
    

    A string do nome do serviço inclui o nome e o espaço de nomes do serviço do GKE correspondente.

Não é possível criar um cluster com IPs de alias

Sintomas

Quando tenta criar um cluster com IPs de alias, pode encontrar o seguinte erro:

ResponseError: code=400, message=IP aliases cannot be used with a legacy network.
Potenciais causas

Encontra este erro se tentar criar um cluster com IPs de alias que também use uma rede antiga.

Resolução

Certifique-se de que não cria um cluster com IPs de alias e uma rede antiga ativada em simultâneo. Para mais informações sobre a utilização de IPs de alias, consulte o artigo Crie um cluster nativo da VPC.

O tráfego não chega aos pontos finais

Sintomas
Erros 502/503 ou ligações rejeitadas.
Potenciais causas

Geralmente, os novos pontos finais ficam acessíveis depois de os associar ao equilibrador de carga, desde que respondam às verificações de estado. Pode encontrar erros 502 ou ligações rejeitadas se o tráfego não conseguir alcançar os pontos finais.

Os erros 502 e as ligações rejeitadas também podem ser causados por um contentor que não processa SIGTERM. Se um contentor não processar explicitamente SIGTERM, termina imediatamente e deixa de processar pedidos. O balanceador de carga continua a enviar tráfego de entrada para o contentor terminado, o que gera erros.

O balanceador de carga nativa do contentor só tem um ponto final de back-end. Durante uma atualização gradual, o ponto final antigo é desprogramado antes de o novo ponto final ser programado.

Os pods de back-end são implementados numa nova zona pela primeira vez após o aprovisionamento de um equilibrador de carga nativo do contentor. A infraestrutura do balanceador de carga é programada numa zona quando existe, pelo menos, um ponto final na zona. Quando um novo ponto final é adicionado a uma zona, a infraestrutura do balanceador de carga é programada e causa interrupções do serviço.

Resolução

Configure os contentores para processarem SIGTERM e continuarem a responder a pedidos durante o período de tolerância de encerramento (30 segundos por predefinição). Configure os pods para começarem a falhar nas verificações de funcionamento quando receberem SIGTERM. Isto indica ao balanceador de carga que pare de enviar tráfego para o pod enquanto a desprogramação do ponto final está em curso.

Se a sua aplicação não for encerrada corretamente e deixar de responder a pedidos quando receber um SIGTERM, pode usar o preStop hook para processar o SIGTERM e continuar a publicar tráfego enquanto a desprogramação do ponto final está em curso.

lifecycle:
  preStop:
    exec:
      # if SIGTERM triggers a quick exit; keep serving traffic instead
      command: ["sleep","60"]

Consulte a documentação sobre a terminação de pods.

Se o back-end do equilibrador de carga tiver apenas uma instância, configure a estratégia de implementação para evitar a desativação da única instância antes de a nova instância estar totalmente programada. Para pods de aplicações geridos pela carga de trabalho Deployment, isto pode ser conseguido configurando a estratégia de implementação com o parâmetro maxUnavailable igual a 0.

strategy:
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

Para resolver problemas relacionados com o tráfego que não chega aos pontos finais, verifique se as regras da firewall permitem o tráfego TCP de entrada para os seus pontos finais nos intervalos 130.211.0.0/22 e 35.191.0.0/16. Para saber mais, consulte o artigo Adicionar verificações de funcionamento na documentação do Cloud Load Balancing.

Veja os serviços de back-end no seu projeto. A string de nome do serviço de back-end relevante inclui o nome e o espaço de nomes do serviço do GKE correspondente:

gcloud compute backend-services list

Obtenha o estado de funcionamento do back-end do serviço de back-end:

gcloud compute backend-services get-health BACKEND_SERVICE_NAME

Se todos os back-ends estiverem em mau estado, a sua firewall, Ingress ou serviço podem estar incorretamente configurados.

Se alguns back-ends estiverem em mau estado durante um curto período, a latência de programação de rede pode ser a causa.

Se alguns back-ends não aparecerem na lista de serviços de back-end, a latência de programação pode ser a causa. Pode verificar isto executando o seguinte comando, em que NEG_NAME é o nome do serviço de back-end. (Os NEGs e os serviços de back-end partilham o mesmo nome):

gcloud compute network-endpoint-groups list-network-endpoints NEG_NAME

Verifique se todos os pontos finais esperados estão no NEG.

Se tiver um pequeno número de back-ends (por exemplo, 1 pod) selecionados por um balanceador de carga nativo de contentores, pondere aumentar o número de réplicas e distribuir os pods de back-end por todas as zonas abrangidas pelo cluster do GKE. Isto garante que a infraestrutura do balanceador de carga subjacente está totalmente programada. Caso contrário, considere restringir os pods de back-end a uma única zona.

Se configurar uma política de rede para o ponto final, certifique-se de que a entrada da sub-rede só de proxy é permitida.

Implementação interrompida

Sintomas
A implementação de uma atualização é interrompida e o número de réplicas atualizadas não corresponde ao número desejado de réplicas.
Potenciais causas

As verificações de funcionamento da implementação estão a falhar. A imagem do contentor pode estar danificada ou a verificação de estado pode estar configurada incorretamente. A substituição progressiva de Pods aguarda até que o Pod iniciado recentemente passe o respetivo indicador de disponibilidade do Pod. Isto só ocorre se o pod estiver a responder às verificações de funcionamento do balanceador de carga. Se o pod não responder ou se a verificação do estado estiver configurada incorretamente, as condições do gate de prontidão não podem ser cumpridas e a implementação não pode continuar.

Se estiver a usar a versão kubectl 1.13 ou superior, pode verificar o estado das readiness gates de um pod com o seguinte comando:

kubectl get pod POD_NAME -o wide

Verifique a coluna READINESS GATES.

Esta coluna não existe no kubectl 1.12 e inferior. Um pod marcado como estando no estado READY pode ter um portão de prontidão com falhas. Para verificar esta situação, use o seguinte comando:

kubectl get pod POD_NAME -o yaml

As portas de prontidão e o respetivo estado são apresentados no resultado.

Resolução

Verifique se a imagem do contentor na especificação do pod da sua implementação está a funcionar corretamente e se consegue responder às verificações de estado. Verifique se as verificações de estado estão configuradas corretamente.

Erros do modo degradado

Sintomas

A partir da versão 1.29.2-gke.1643000 do GKE, pode receber os seguintes avisos no seu serviço no Explorador de registos quando os NEGs são atualizados:

Entering degraded mode for NEG <service-namespace>/<service-name>-<neg-name>... due to sync err: endpoint has missing nodeName field
Potenciais causas

Estes avisos indicam que o GKE detetou configurações incorretas de pontos finais durante uma atualização do NEG com base em objetos EndpointSlice, o que aciona um processo de cálculo mais detalhado denominado modo degradado. O GKE continua a atualizar os NEGs com base no melhor esforço, corrigindo a configuração incorreta ou excluindo os pontos finais inválidos das atualizações do NEG.

Seguem-se alguns erros comuns:

  • endpoint has missing pod/nodeName field
  • endpoint corresponds to an non-existing pod/node
  • endpoint information for attach/detach operation is incorrect
Resolução

Normalmente, os estados transitórios causam estes eventos e são corrigidos automaticamente. No entanto, os eventos causados por configurações incorretas em objetos EndpointSlice personalizados permanecem não resolvidos. Para compreender a configuração incorreta, examine os objetos EndpointSlice correspondentes ao serviço:

kubectl get endpointslice -l kubernetes.io/service-name=<service-name>

Valide cada ponto final com base no erro no evento.

Para resolver o problema, tem de modificar manualmente os objetos EndpointSlice. A atualização aciona novamente a atualização dos NEGs. Assim que o erro de configuração deixar de existir, o resultado é semelhante ao seguinte:

NEG <service-namespace>/<service-name>-<neg-name>... is no longer in degraded mode

Problemas conhecidos

O balanceamento de carga nativa do contentor no GKE tem os seguintes problemas conhecidos:

Condição de corrida do portão de prontidão do NEG

Em determinadas condições, os gates de prontidão podem devolver um "falso positivo" de estado pronto antes de a verificação de estado de entrada comunicar um estado saudável, gerando eventos de erro como os seguintes no objeto de entrada:

NEG is not attached to any BackendService with health checking. Marking condition "cloud.google.com/load-balancer-neg-ready" to True.

Sintomas

Este problema faz com que o balanceador de carga comunique um erro 503 (failed_to_pick_backend) ao tráfego enquanto o GKE está a realizar a atualização contínua na carga de trabalho de implementação.

Causas

Embora o controlador NEG do GKE se baseie nas informações de verificação de funcionamento do NEG do Compute Engine para comunicar se o ponto final está em bom estado, considere a seguinte sequência de eventos:

  1. É criado um novo pod a partir de uma atualização contínua.
  2. O controlador NEG do GKE adiciona este novo endereço IP do pod ao grupo de pontos finais da rede.
  3. O controlador NEG do GKE pede o estado de funcionamento no ponto final adicionado recentemente.
  4. O serviço NEG do Compute Engine ainda não tem informações de estado de funcionamento e devolve um estado vazio.
  5. O controlador NEG do GKE assume que um estado vazio significa que não está configurada nenhuma verificação de estado e marca o pod como pronto.
  6. O GKE remove o pod antigo, pensando que o novo está pronto para servir tráfego.
  7. Se o novo pod for o único back-end restante para o balanceador de carga, o balanceador de carga devolve uma resposta 503 Service Unavailable.
  8. Assim que o pod começar a passar na respetiva verificação de estado, o equilibrador de carga começa a devolver respostas 200 OK conforme esperado.

O controlador NEG do GKE não consegue distinguir entre dois estados de verificação de funcionamento diferentes: missing-health-check-because-not-attached-to-backend e missing-health-check-because-health-check-is-not-yet-programmed.

Uma vez que o controlador do NEG não consegue fazer esta distinção, o controlador do NEG do GKE tem de presumir que, se nenhum dos pontos finais do NEG tiver informações de verificação de estado, este NEG não pertence a nenhum BackendService.

Embora este cenário seja improvável, ter um número relativamente pequeno de pods (por exemplo, 2) em comparação com o número de NEGs aumenta o risco desta condição de corrida. Lembre-se de que os GNEs são criados para cada região, com um GNE por zona, o que normalmente resulta em três GNEs.

O corolário é que, se existir um número relativamente maior de agrupamentos, de modo que cada NEG tenha sempre vários agrupamentos antes do início da atualização contínua, é muito improvável que esta condição de concorrência seja acionada.

Resoluções:

A melhor forma de evitar esta condição de corrida (e, em última análise, evitar respostas 503 Service Unavailable durante as atualizações contínuas) é ter mais back-ends no grupo de pontos finais de rede.

Certifique-se de que a estratégia de atualização contínua está configurada para garantir que, pelo menos, 1 Pod está sempre em execução.

Por exemplo, se apenas 2 pods forem executados normalmente, uma configuração de exemplo pode ter o seguinte aspeto:

strategy:
   type: RollingUpdate
   rollingUpdate:
     maxUnavailable: 0
     maxSurge: 1

O exemplo anterior é uma sugestão. Tem de atualizar a estratégia com base em vários fatores, como o número de réplicas.

Recolha de lixo incompleta

O GKE recolhe o lixo dos balanceadores de carga nativos do contentor a cada dois minutos. Se um cluster for eliminado antes de os equilibradores de carga serem totalmente removidos, tem de eliminar manualmente os NEGs do equilibrador de carga.

Veja os NEGs no seu projeto executando o seguinte comando:

gcloud compute network-endpoint-groups list

No resultado do comando, procure os NEGs relevantes.

Para eliminar um NEG, execute o seguinte comando, substituindo NEG_NAME pelo nome do NEG:

gcloud compute network-endpoint-groups delete NEG_NAME

Alinhe as implementações de cargas de trabalho com a propagação de pontos finais

Quando implementa uma carga de trabalho no cluster ou quando atualiza uma carga de trabalho existente, o balanceador de carga nativo de contentores pode demorar mais tempo a propagar novos pontos finais do que a concluir a implementação da carga de trabalho. A implementação de exemplo que implementar neste guia usa dois campos para alinhar a respetiva implementação com a propagação de pontos finais: terminationGracePeriodSeconds e minReadySeconds.

terminationGracePeriodSeconds permite que o pod seja desligado corretamente, aguardando que as ligações terminem depois de um pod ser agendado para eliminação.

minReadySeconds adiciona um período de latência após a criação de um Pod. Especifica um número mínimo de segundos durante os quais um novo Pod deve estar no estado Ready, sem que nenhum dos respetivos contentores falhe, para que o Pod seja considerado disponível.

Deve configurar os valores minReadySeconds e terminationGracePeriodSeconds das suas cargas de trabalho para 60 segundos ou mais para garantir que o serviço não é interrompido devido a implementações de cargas de trabalho.

terminationGracePeriodSeconds está disponível em todas as especificações de agrupamentos e minReadySeconds está disponível para implementações e DaemonSets.

Para saber mais acerca do ajuste preciso das implementações, consulte a RollingUpdateStrategy.

initialDelaySeconds no grupo de anúncios readinessProbe não respeitado

Pode esperar que a configuração initialDelaySeconds no readinessProbe do pod seja respeitada pelo balanceador de carga nativo do contentor. No entanto, o readinessProbe é implementado pelo kubelet, e a configuração initialDelaySeconds controla a verificação de estado do kubelet e não o balanceador de carga nativo do contentor. O balanceamento de carga nativa do contentor tem a sua própria verificação do estado de funcionamento do balanceamento de carga.

O que se segue?