Neste tutorial, mostramos como lidar com preempções enquanto você executa VMs preemptivas (PVMs) no Google Kubernetes Engine (GKE) que está sendo exibido. um aplicativo da Web. As PVMs são instâncias de computação acessíveis e de curta duração adequadas para cargas de trabalho tolerantes a falhas. Eles oferecem os mesmos tipos e opções de máquina que as instâncias de computação comuns e duram até 24 horas.
Este tutorial é destinado a desenvolvedores de aplicativos, arquitetos de sistemas e engenheiros de DevOps que definem, implementam e implantam aplicativos voltados para a Web e querem usar PVMs em implantações de produção. Neste tutorial, você precisa entender os conceitos fundamentais do Kubernetes e vários componentes no balanceamento de carga HTTP(S).
Contexto
Um PVM é limitado a um tempo de execução de 24 horas e recebe um aviso de 30 segundos de encerramento quando a instância está prestes a ser interrompida. Inicialmente, o PVM envia um aviso de preempção para a instância na forma de um sinal ACPI G2 Soft Off (SIGTERM
). Depois de 30 segundos, um sinal ACPI G3 Mechanical Off (SIGKILL
) é enviado para o sistema operacional da instância. Em seguida, a VM passa a instância para um estado TERMINATED
.
As PVMs são uma boa opção para cargas de trabalho distribuídas e tolerantes a falhas que não exigem disponibilidade contínua de uma única instância. Exemplos desse tipo de carga de trabalho incluem codificação de vídeo, renderização de efeitos visuais, análise de dados, simulação e genômica. No entanto, devido a limitações de disponibilidade e possíveis interrupções frequentes resultantes de preempçãos, as PVMs geralmente não são recomendadas para aplicativos da Web e voltados para o usuário.
Neste tutorial, descrevemos como configurar uma implantação que usa uma combinação de PVMs e VMs padrão no GKE para ajudar a disponibilizar tráfego de aplicativos da Web de maneira confiável sem interrupções.
Desafios no uso de PVMs
O maior desafio de usar PVMs na exibição de tráfego voltado ao usuário é garantir que as solicitações dos usuários não sejam interrompidas. Na preempção, você precisa abordar o seguinte:
- Como você garante a disponibilidade de um aplicativo quando ele estiver sendo executado em PVMs? As PVMs não têm disponibilidade garantida e são explicitamente excluídas dos contratos de nível de serviço do Compute Engine.
- Como você lida com o encerramento normal do aplicativo de maneira que as afirmações a seguir sejam verdadeiras:
- O balanceador de carga interrompe solicitações de encaminhamento para os pods em execução em uma instância que está sendo interrompida.
- As solicitações em andamento são processadas de maneira adequada, elas sejam concluídas ou encerradas.
- As conexões com os bancos de dados e o aplicativo são fechadas ou esvaziadas antes da instância ser encerrada.
- Como você lida com solicitações, como transações críticas para os negócios, que podem exigir garantias de tempo de atividade ou que não são tolerantes a falhas?
Considere os desafios na desativação de contêineres que são executados em PVMs. Do ponto de vista da implementação, a maneira mais fácil de escrever a lógica de limpeza quando uma instância é encerrada é por meio de um script de desligamento. No entanto, esses scripts não serão compatíveis se você estiver executando cargas de trabalho em contêineres no GKE.
Como alternativa, você pode usar um gerenciador SIGTERM
no seu aplicativo para escrever a lógica de limpeza. Os contêineres também fornecem ganchos de ciclo de vida, como preStop
, que é acionado imediatamente antes do contêiner ser encerrado. No Kubernetes, o Kubelet é responsável por executar eventos de ciclo de vida do contêiner. Em um cluster do Kubernetes, o Kubelet é executado na VM e observa as especificações do pod por meio do servidor da API Kubernetes (em inglês).
Ao remover um pod usando uma linha de comando ou uma API, o Kubelet percebe que o pod foi marcado como encerrado e inicia o processo de desligamento. A duração do processo é limitada pelo "período de carência", que é definido como um número definido de segundos após o qual o Kubelet envia um sinal SIGKILL
para os contêineres. Como parte do encerramento normal, se algum contêiner em execução no pod tiver definido um hook preStop
, o Kubelet executará o gancho dentro do contêiner. Em seguida, o Kubelet aciona um sinal SIGTERM
para process-ID 1
dentro de cada contêiner. Se o aplicativo estiver executando um gerenciador SIGTERM
, o gerenciador será executado. Quando o gerenciador é concluído, o Kubelet envia um sinal SIGKILL
para todos os processos ainda em execução no pod.
Imagine que um nó do Kubernetes está passando por preempção. É preciso ter um mecanismo para identificar o aviso de preempção e iniciar o processo de remoção do pod. Suponha que você esteja executando um programa que detecta um aviso de preempção e remove os pods em execução ao receber um evento. Após a remoção, a sequência de encerramento do pod descrita anteriormente é acionada. No entanto, nesse caso, o nó também está passando por uma desativação, que é processada pelo sistema operacional (SO) do nó. Esse encerramento pode interferir no processamento do ciclo de vida do contêiner do Kubelet, o que significa que o contêiner pode ser encerrado abruptamente, mesmo que ele esteja no meio da execução de um gancho preStop
.
Além disso, no ponto de disponibilidade de disponibilidade e gerenciamento de tráfego, a execução do aplicativo voltado para a Web exclusivamente em PVMs também pode causar vários desafios. Antes de usar as PVMs, considere as seguintes perguntas:
- O que acontece se a maioria das PVMs for interrompida de uma só vez? Como os aplicativos atendem a milhares de solicitações por segundo, como são as failovers sem interrupção?
- O que acontecerá se a capacidade do PVM não estiver disponível? Como fazer o escalonamento horizontal ou manter um estado estável de implantação do aplicativo nesse caso?
Arquitetura
Para resolver os desafios do uso de PVMs, faça o seguinte:
- Execute um cluster do GKE com dois pools de nós: um em PVMs de execução e o outro com VMs padrão em execução. Isso permite que você divida o tráfego e tenha um failover ativo para lidar com solicitações novas e em andamento em caso de preempção. Essa abordagem também permite dividir o tráfego entre VMs padrão e PVMs com base nos requisitos de garantias de tempo de atividade e tolerância a falhas. Para mais informações sobre como decidir o tamanho dos pools de nós, consulte o que considerar.
- Detecte avisos de preempção nas PVMs e remova os pods em execução no nó.
- Use um gancho
preStop
ou um gerenciadorSIGTERM
para executar a lógica de limpeza. - Certifique-se de que o Kubelet tenha permissão para lidar com o ciclo de vida do encerramento do pod e não esteja encerrado abruptamente.
- Liga o nó para que nenhum novo pod seja programado nele enquanto estiver sendo interrompido.
No diagrama a seguir, veja uma visualização de alto nível da arquitetura que você implantará neste tutorial.
Como o diagrama mostra, crie dois pools de nós: default-pool
é executado em VMs padrão e pvm-pool
é executado em PVMs. O programador padrão do GKE tenta distribuir os pods de maneira uniforme por todas as instâncias nos pools de nós. Por exemplo, se você estiver implantando quatro réplicas e tiver dois nós em execução em cada pool de nós, o programador provisiona um pod em todos os quatro nós. No entanto, ao usar PVMs, convém direcionar mais tráfego para pvm-pool
a fim de conseguir uma utilização de PVM maior e, portanto, uma economia. Por exemplo, talvez você queira provisionar três pods em pvm-pool
e um pod em default-pool
para failover, reduzindo o número de VMs padrão no cluster.
Para alcançar esse tipo de controle sobre a programação, você pode escrever seu próprio programador ou dividir o aplicativo em duas implantações. Cada implantação será fixada a um pool de nós com base nas regras de afinidade de nó.
Neste exemplo, você cria duas implantações, web-std
e web-pvm
, em que web-std
é fixado em default-pool
e web-pvm
é fixado em pvm-pool
.
Para nós pvm-pool
, é preciso detectar um aviso de preempção e, ao recebê-lo, começar a remover pods. É possível gravar sua própria lógica para ouvir o aviso de preempção e remover os pods em execução no nó.
Como alternativa, neste tutorial, é possível criar um agente usando o manipulador de eventos k8s-node-termination-handler
.
k8s-node-termination-handler
usa um daemonset do Kubernetes para criar um agente em cada instância em pvm-pool
. O agente monitora um evento de encerramento de nó usando APIs de metadados do Compute Engine.
Sempre que um evento de encerramento é observado, o agente inicia o processo de remoção do pod. Neste exemplo, 20 segundos são alocados como um período de carência para pods regulares e 10 segundos para pods do sistema. Isso significa que, se houver um gancho preStop
ou um gerenciador SIGTERM
configurado para o pod, ele poderá ser executado até 20 segundos antes de o pod ser encerrado por SIGKILL
. O período de carência é um parâmetro configurável em k8s-node-termination-handler
. O período de carência total para pods normais e do sistema não pode exceder a notificação de preempção: 30 segundos.
O agente também mantém o nó para impedir que novos pods sejam programados.
Quando você remove pods, o gancho preStop
é acionado. Neste exemplo, preStop
é configurado para fazer duas tarefas:
- Falha na verificação de integridade do aplicativo. Isso é feito para sinalizar ao balanceador de carga que ele precisa remover o pod do caminho de exibição da solicitação.
Suspender pela duração do período de carência (20 segundos) alocado por
k8s-node-termination-handler
. O pod permanece ativo por 20 segundos para processar as solicitações em andamento.
Enquanto o gancho preStop
estiver em execução, para garantir que o Kubelet não seja encerrado repentinamente pelo SO do nó, você criará um serviço systemd que bloqueia o encerramento.
Essa abordagem ajuda a garantir que o Kubelet possa gerenciar o ciclo de vida dos pods durante o encerramento sem a interferência do SO do nó. Use um daemonset do Kubernetes para criar o serviço. Esse daemonset será executado em todas as instâncias em pvm-pool
.
Neste exemplo, usamos o Traffic Director para gerenciar o tráfego. É possível usar qualquer solução de proxy, como OSS Envoy, Istio, Nginx ou HAProxy, para gerenciar o tráfego, desde que você siga as diretrizes do subsistema usadas neste exemplo, em que o Traffic Director está configurado. para fazer o seguinte:
Ativa o roteamento ponderado das solicitações. Neste exemplo, você cria três réplicas de aplicativos em
pvm-pool
e uma emdefault-pool
. O Traffic Director está configurado para dividir o tráfego de 75 a 25% entrepvm-pool
edefault-pool
. No caso de preempção, todas as solicitações são automaticamente encaminhadas paradefault-pool
.Neste tutorial, apresentamos um exemplo simples de divisão de tráfego. Também é possível definir condições de correspondência em portas de tráfego, campos de cabeçalho, URIs e muito mais para rotear a solicitação para um pool de nós específico. Para mais informações, consulte técnicas avançadas de roteamento de tráfego usando o Traffic Director.
No caso de preempção, se a solicitação resultar em um código de status
5xx
(por exemplo, devido a um gateway não estar disponível ou por uma conexão upstream expirar), o Traffic Director fará novas tentativas de até três solicitações. vezes.Use um disjuntor para limitar o número máximo de novas tentativas que podem ocorrer em um determinado momento.
Use a detecção de outliers para eliminar endpoints não íntegros do caminho de exibição do balanceador de carga.
O Traffic Director usa o modelo de proxy sidecar. Neste modelo, ocorrem os seguintes eventos:
- Os clientes enviam solicitações para um balanceador de carga gerenciado pelo Google Cloud.
- O balanceador de carga envia o tráfego para um proxy de borda configurado pelo Traffic Director.
- O proxy de borda aplica políticas predefinidas (como a distribuição de solicitações entre endpoints diferentes) e políticas de nova tentativa e disjuntores, além de balancear a carga das solicitações para serviços no cluster do GKE.
- O proxy secundário intercepta o tráfego e o encaminha para o aplicativo.
Para mais informações, veja como a interceptação e o encaminhamento de tráfego funcionam com o Traffic Director.
Objetivos
- Implante um cluster do GKE com dois pools de nós usando VMs padrão e PVMs.
- Implante um aplicativo de amostra.
- Configure os recursos do cluster para processar a preempção.
- Configurar o Traffic Director para controlar o tráfego para os serviços do GKE.
- Simula a preempção de uma PVM.
- Verifique se os pods estão sendo encerrados corretamente.
- Verifique se as novas solicitações não são encaminhadas para pods que estão passando por preempção.
- Verifique se as solicitações em andamento e novas são veiculadas sem interrupções.
Custos
Neste tutorial, usamos os seguintes componentes faturáveis do Google Cloud:
Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços. Novos usuários do Google Cloud podem ser qualificados para uma avaliação gratuita.
Ao concluir este tutorial, exclua os recursos criados para evitar o faturamento contínuo. Para mais informações, consulte Como fazer a limpeza.
Antes de começar
-
No Console do Google Cloud, acesse a página do seletor de projetos.
-
Selecione ou crie um projeto do Google Cloud.
-
No Console do Cloud, ative o Cloud Shell.
Na parte inferior do Console do Cloud, uma sessão do Cloud Shell é iniciada e exibe um prompt de linha de comando. O Cloud Shell é um ambiente com o SDK do Cloud pré-instalado com a ferramenta de linha de comando
gcloud
e os valores já definidos para seu projeto atual. A inicialização da sessão pode levar alguns segundos. - Ative a API Compute Engine, GKE, Container Analysis, Cloud Build, Container Registry, and Traffic Director.
- Encontre o ID do projeto e configure-o no Cloud Shell. Substitua
YOUR_PROJECT_ID
pelo ID do projeto:gcloud config set project YOUR_PROJECT_ID
- Exporte as seguintes variáveis de ambiente:
export PROJECT=$(gcloud config get-value project) export CLUSTER=$PROJECT-gke export REGION="us-central1" export ZONE="us-central1-c" export TERMINATION_HANDLER="https://github.com/GoogleCloudPlatform/k8s-node-termination-handler"
Verifique se o faturamento está ativado para seu projeto na nuvem. Saiba como confirmar se o faturamento está ativado para o projeto.
crie um cluster do GKE;
No Cloud Shell, crie um cluster do GKE que tenha um pool de nós padrão,
default-pool
, com instâncias padrão e um pool de nós personalizado,pvm-pool
, com PVMs:gcloud beta container clusters create $CLUSTER \ --zone=$ZONE \ --num-nodes="1" \ --enable-ip-alias \ --machine-type="n1-standard-4" \ --scopes=https://www.googleapis.com/auth/cloud-platform gcloud beta container node-pools create "pvm-pool" \ --cluster=$CLUSTER \ --zone=$ZONE \ --preemptible \ --machine-type="n1-standard-4" \ --scopes=https://www.googleapis.com/auth/cloud-platform \ --num-nodes="1"
Clone o repositório de código
solutions-gke-pvm-preemption-handler
usado neste tutorial:git clone https://github.com/GoogleCloudPlatform/solutions-gke-pvm-preemption-handler && \ cd solutions-gke-pvm-preemption-handler
Crie um daemonset que seja executado em instâncias PVM no cluster do GKE e crie um serviço systemd que bloqueie o encerramento do processo do Kubelet:
kubectl apply -f daemonset.yaml
Consiga o nome da instância do PVM implantada como parte do pool de nós
pvm-pool
:PVM=$(kubectl get no \ -o=jsonpath='{range .items[*]} \ {.metadata.name}{"\n"}{end}' | grep pvm)
Verifique se o serviço está implantado corretamente:
Crie uma regra de firewall a fim de usar o SSH para se conectar ao PVM por meio do encaminhamento do IAP:
gcloud compute firewall-rules create allow-ssh-ingress-from-iap \ --direction=INGRESS \ --action=allow \ --rules=tcp:22 \ --source-ranges=35.235.240.0/20
Use SSH para se conectar à PVM:
gcloud compute ssh $PVM --tunnel-through-iap --zone=$ZONE
No terminal PVM, verifique o status do serviço implantado:
systemctl status delay.service
Você vê o estado do serviço como
Active (exited).
.... delay.service - Delay GKE shutdown Loaded: loaded (/etc/systemd/system/delay.service; enabled; vendor preset: disabled) Active: active (exited) since Tue 2020-07-21 04:48:33 UTC; 1h 17min ago ...
Saia do terminal PVM digitando
exit
.
Deploy
k8s-node-termination-handler
:Clone o repositório:
git clone $TERMINATION_HANDLER
Em um editor de texto, abra o arquivo
k8s-node-termination-handler/deploy/k8s.yaml
e procure a seguinte linha:args: ["--logtostderr", "--exclude-pods=$(POD_NAME):$(POD_NAMESPACE)", "-v=10", "--taint=cloud.google.com/impending-node-termination::NoSchedule"]
Substitua a linha anterior pela linha a seguir, que aloca um período de carência de 10 segundos para encerrar os pods do sistema. Os 20 segundos restantes no período de carência são alocados automaticamente para pods regulares.
args: ["--logtostderr", "--exclude-pods=$(POD_NAME):$(POD_NAMESPACE)", "-v=10", "--taint=cloud.google.com/impending-node-termination::NoSchedule", "--system-pod-grace-period=10s"]
Implante o gerenciador:
kubectl apply \ -f k8s-node-termination-handler/deploy/k8s.yaml \ -f k8s-node-termination-handler/deploy/rbac.yaml
Verifique se o gerenciador de encerramento do nó foi implantado corretamente:
kubectl get ds node-termination-handler -n kube-system
A resposta será semelhante a:
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE node-termination-handler 1 1 1 1 1 <none> 101s
Como implantar o aplicativo
No Cloud Shell, configure as regras de firewall para verificações de integridade:
gcloud compute firewall-rules create fw-allow-health-checks \ --action=ALLOW \ --direction=INGRESS \ --source-ranges=35.191.0.0/16,130.211.0.0/22 \ --rules tcp
Implante o aplicativo:
kubectl apply -f deploy.yaml
Verifique se há duas implantações diferentes em execução em
default-pool
epvm-pool
:Para
default-pool
, execute o seguinte:kubectl get po -l app=web-std \ -o=custom-columns=NAME:.metadata.name,Node:.spec.nodeName
A resposta será semelhante a:
NAME Node web-std-695b5fb6c4-55gcc gke-vital-octagon-109612-default-pool-dcdb8fe5-2tc7
Para
pvm-pool
, execute o seguinte:kubectl get po -l app=web-pvm \ -o=custom-columns=NAME:.metadata.name,Node:.spec.nodeName
A resposta será semelhante a esta:
NAME Node web-pvm-6f867bfc54-nm6fb gke-vital-octagon-109612-gke-pvm-pool-664ec4ff-2cgc
Para cada serviço, um NEG autônomo é criado. Ele contém endpoints que são as portas e os endereços IP do pod. Para mais informações e exemplos, consulte Grupos de endpoints de rede autônomos.
Confirme se o NEG independente foi criado:
gcloud beta compute network-endpoint-groups list
A resposta será semelhante a esta:
NAME LOCATION ENDPOINT_TYPE SIZE k8s1-be35f81e-default-web-pvm-80-7c99357f us-central1-c GCE_VM_IP_PORT 1 k8s1-be35f81e-default-web-std-80-f16dfcec us-central1-c GCE_VM_IP_PORT 1
Para gerenciar o aplicativo usando o Traffic Director, implante as implantações como grupos de endpoints da rede (NEGs, na sigla em inglês). Conforme discutido na seção "Arquitetura", você usa um proxy secundário para criar as implantações.
Verifique se as implantações foram criadas com um proxy sidecar:
kubectl get pods -l app=web-std \ -o jsonpath={.items[*].spec.containers[*].name}
A resposta será semelhante a esta:
hello-app istio-proxy
Para ver resultados semelhantes para pods em execução em
pvm-pool
, execute o seguinte:kubectl get pods -l app=web-pvm \ -o jsonpath={.items[*].spec.containers[*].name}
Como criar o serviço Traffic Director
O Traffic Director usa uma configuração semelhante à de outros produtos do Cloud Load Balancing. Em outras palavras, é necessário configurar os seguintes componentes para o Traffic Director:
- Uma verificação de integridade Para mais informações sobre verificações de integridade, consulte Conceitos de verificação de integridade.
- Um serviço de back-end Para mais informações sobre serviços de back-end, consulte esta página.
- uma regra de rota Crie uma regra de encaminhamento e um mapa de URL. Para mais informações, consulte Como usar regras de encaminhamento e Como usar mapas de URL.
No Cloud Shell, encontre os NEGs que você criou anteriormente e armazene os nomes deles em uma variável:
Para o serviço
default-pool
, use o seguinte:NEG_NAME_STD=$(gcloud beta compute network-endpoint-groups list \ | grep web-std | awk '{print $1}')
Para o serviço
pvm-pool
, use o seguinte:NEG_NAME_PVM=$(gcloud beta compute network-endpoint-groups list \ | grep web-pvm | awk '{print $1}')
Crie a verificação de integridade:
gcloud compute health-checks create http td-gke-health-check \ --request-path=/health \ --use-serving-port \ --healthy-threshold=1 \ --unhealthy-threshold=2 \ --check-interval=2s \ --timeout=2s
Substitua os marcadores de posição nos arquivos de manifesto:
sed -i -e "s/\[PROJECT_ID\]/$PROJECT/g" td-gke-service-config.yaml sed -i -e "s/\[ZONE\]/$ZONE/g" td-gke-service-config.yaml sed -i -e "s/\[NEG_NAME_STD\]/$NEG_NAME_STD/g" td-gke-service-config.yaml sed -i -e "s/\[NEG_NAME_PVM\]/$NEG_NAME_PVM/g" td-gke-service-config.yaml sed -i -e "s/\[PROJECT_ID\]/$PROJECT/g" td-urlmap.yaml
Crie um serviço do Traffic Director:
gcloud compute backend-services import td-gke-service \ --source=td-gke-service-config.yaml --global
O serviço é configurado para dividir o tráfego usando o escalonador de capacidade entre os dois NEGs criados anteriormente. O serviço também é configurado com a detecção de outliers:
- Para detecção de outliers, defina Erros consecutivos (ou falhas de gateway antes de um host ser removido do serviço) como
2
. - Para quebras de circuito, defina Máx. de novas tentativas como
3
.
- Para detecção de outliers, defina Erros consecutivos (ou falhas de gateway antes de um host ser removido do serviço) como
Verifique se o serviço Traffic Director está implantado corretamente:
gcloud compute backend-services get-health td-gke-service --global
A resposta será semelhante a:
‐‐‐ backend: default-pool-service-NEG status: healthStatus: ‐ healthState: HEALTHY ... ‐‐‐ backend: pvm-pool-service-NEG status: healthStatus: ‐ healthState: HEALTHY ...
Talvez seja necessário aguardar alguns minutos e executar o comando várias vezes, antes que o back-end seja mostrado como
HEALTHY
.Crie um mapa de URLs que use o serviço criado:
gcloud compute url-maps import web-service-urlmap \ --source=td-urlmap.yaml
O mapa de URLs configura o roteamento de tráfego. Todas as solicitações para o caminho "
/*
" são redirecionadas para o serviço do Traffic Director que você criou. Além disso, o mapa também configura uma política para repetir solicitações (máximo de três vezes) que resultaram em um código de status5xx
.Crie o proxy HTTP de destino.
gcloud compute target-http-proxies create td-gke-proxy \ --url-map=web-service-urlmap
Crie a regra de encaminhamento que usa o endereço IP virtual (VIP) 0.0.0.0:
gcloud compute forwarding-rules create td-gke-forwarding-rule \ --global \ --load-balancing-scheme=INTERNAL_SELF_MANAGED \ --address=0.0.0.0 \ --target-http-proxy=td-gke-proxy \ --ports=80
Neste ponto, os serviços do GKE em
default-pool
epvm-pool
estão acessíveis no VIP do serviço com balanceamento de carga do Traffic Director.
Criar o balanceador de carga
Nesta seção, você configura um balanceador de carga e um proxy de borda para o tráfego de usuários. O balanceador de carga atua como um gateway para a configuração que você acabou de criar.
No Cloud Shell, crie um proxy de borda gerenciado pelo Traffic Director:
kubectl apply -f edge-proxy.yaml
Verifique se o balanceador de carga está íntegro e pronto para veicular o tráfego:
kubectl describe ingress gateway-proxy-ingress
A resposta será semelhante a:
... Host Path Backends ‐‐‐‐ ‐‐‐‐ ‐‐‐‐‐‐‐‐ * * gateway-proxy-svc:80 (10.20.0.14:80) Annotations: ingress.kubernetes.io/backends: {"k8s1-da0dd12b-default-gateway-proxy-svc-80-b3b7b808":"HEALTHY"} ...
O back-end precisa estar no estado
HEALTHY
. Pode levar vários minutos e várias tentativas do comando para que o balanceador de carga esteja pronto para aceitar o tráfego.Registre o endereço IP para uso posterior:
IPAddress=$(kubectl get ingress gateway-proxy-ingress \ -o jsonpath="{.status.loadBalancer.ingress[*].ip}")
Como gerar tráfego
Agora que a configuração está completa, é hora de testá-la.
No Cloud Shell, clique em Abrir uma nova guia para iniciar uma nova sessão do Cloud Shell.
No novo shell, defina o ID do projeto:
gcloud config set project YOUR_PROJECT_ID
Instale o Kubetail para observar vários registros de pods de aplicativos juntos:
sudo apt-get update sudo apt-get install kubetail kubetail web
No shell original, simule o tráfego:
seq 1 100 | xargs -I{} -n 1 -P 10 curl -I http://$IPAddress
Esse comando gera 100 solicitações, 10 solicitações paralelas por vez.
No novo shell, você verá registros semelhantes ao seguinte:
--- [web-pvm-6f867bfc54-nm6fb hello-app] Received request at: 2020-07-20 20:26:23.393 [web-pvm-6f867bfc54-nm6fb hello-app] Received request at: 2020-07-20 20:26:23.399 [web-std-6f867bfc54-55gcc hello-app] Received request at: 2020-07-20 20:26:24.001 ...
Verifique se as solicitações são distribuídas entre os pools de nós com base na divisão de 75% a 25% que você configurou anteriormente.
De volta ao shell original, gere tráfego usando
httperf
:sudo apt-get install httperf && \ httperf --server=$IPAddress --port=80 --uri=/ \ --num-conns=5000 --rate=20 --num-calls=1 \ --print-reply=header
Esse comando instala
httperf
e gera 5.000 solicitações no aplicativo de exemplo com a taxa de 20 solicitações por segundo. Esse teste é executado por aproximadamente 250 segundos.
Como simular a preempção
- No novo shell, saia do comando Kubetail pressionando Ctrl+C.
Consiga o nome da instância do PVM implantada como parte de
pvm-pool
:PVM=$(kubectl get no \ -o=jsonpath='{range .items[*]} \ {.metadata.name}{"\n"}{end}' | grep pvm)
Enquanto o teste
httperf
estiver em andamento, acione um evento de manutenção na instância do PVM para simular a preempção:gcloud compute instances simulate-maintenance-event $PVM \ --zone=us-central1-c
Observe os registros do aplicativo no novo shell:
kubectl logs -f deploy/web-pvm -c hello-app
Quando o pod recebe um sinal
SIGTERM
, o status de verificação de integridade do aplicativo informa explicitamentefail
:... Health status fail at: 2020-07-21 04:45:43.742 ...
O pod continua a receber solicitações até que seja removido do caminho de exibição da solicitação devido a falhas nas verificações de integridade. Essa remoção pode levar alguns segundos para ser propagada.
... Received request at: 2020-07-21 04:45:45.735 Received request at: 2020-07-21 04:45:45.743 Health status fail at: 2020-07-21 04:45:45.766 ...
Após alguns segundos, o pod deixa de receber novas solicitações. O pod continua ativo por 20 segundos. O manipulador
preStop
é feito para dormir por 20 segundos para permitir atividades de limpeza, incluindo solicitações em andamento. Em seguida, o pod é encerrado.... Health status fail at: 2020-07-21 04:46:01.796 2020-07-21 04:46:02.303 INFO 1 --- [ Thread-3] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@27ddd392: startup date [Tue Jul 21 04:39:44 UTC 2020]; root of context hierarchy Exiting PreStop hook ...
Se quiser, execute o comando
kubetail web
para filtrar registros de todos os pods do aplicativo em execução nos dois pools de nós. A saída mostra as solicitações sendo roteadas paradefault-pool
conforme a PVM está em preempção:... [web-pvm-6f867bfc54-nm6fb] Received request at: 2020-07-20 20:45:45.743 [web-pvm-6f867bfc54-nm6fb] Health status fail at: 2020-07-21 04:45:45.766 [web-std-6f867bfc54-55gcc] Received request at: 2020-07-20 04:45:45.780 [web-std-6f867bfc54-55gcc] Received request at: 2020-07-20 04:45:45.782 ...
Validações pós-preempção
No shell original, aguarde a conclusão do teste
Httperf
. Após a conclusão, o resultado será semelhante ao seguinte.... Reply status: 1xx=0 2xx=5000 3xx=0 4xx=0 5xx=0 ... Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0 ...
A resposta indica que a preempção é atendida por
default-pool
epvm-pool
foi encerrado normalmente.Verifique se o PVM está ativo novamente e se o estado original do cluster foi restaurado:
kubectl get po -l app=web-pvm \ -o=custom-columns=NAME:.metadata.name,Node:.spec.nodeName
Duas réplicas são provisionadas em
pvm-pool
:NAME Node web-pvm-6f867bfc54-9z2cp gke-vital-octagon-109612-gke-pvm-pool-664ec4ff-49lx
Considerações
Antes de executar esta solução na produção, considere estas qualificações:
- Neste tutorial, não usamos políticas de escalonamento automático de pods ou clusters. Em ambientes de produção, verifique se você tem o escalonamento automático correto para lidar com os picos de tráfego.
- Considere cuidadosamente a divisão entre VMs padrão e PVMs no cluster. Por exemplo, suponha que você esteja executando 100 PVMs e apenas uma VM padrão e 50% das PVMs passam por uma preempção. Nesse caso, o pool de VMs padrão levará algum tempo para ser redimensionado para recursos preemptivos. Enquanto isso, o tráfego dos usuários é afetado. Para atenuar grandes preempçãos, use aplicativos de terceiros, como Spot e
estafette-gke-preemptible-killer
(ambos em inglês). para distribuir as preempçãos e evitar que várias instâncias sejam desativadas juntas. - Com base no seu caso de uso, teste cuidadosamente o período de carência alocado para pods regulares e do sistema usando
k8s-node-termination-handler
. Devido à crítica do aplicativo, talvez seja necessário alocar mais de 20 segundos para os pods regulares. Uma desvantagem possível dessa abordagem é que talvez não haja tempo suficiente para que os pods do sistema sejam encerrados de maneira limpa. Isso pode resultar em possível perda de registros e métricas de monitoramento que são gerenciadas por pods do sistema.
Limpeza
Para evitar cobranças na sua conta do Google Cloud pelos recursos usados neste tutorial, exclua o projeto do Cloud criado para este tutorial ou exclua os recursos associados a ele.
Excluir o projeto do Cloud
A maneira mais fácil de eliminar o faturamento é excluir o projeto criado para o tutorial.
- No Console do Cloud, acesse a página Gerenciar recursos:
- Na lista de projetos, selecione o projeto que você quer excluir e clique em Excluir .
- Na caixa de diálogo, digite o ID do projeto e clique em Encerrar para excluí-lo.
Excluir os recursos
Se você quiser manter o projeto usado neste tutorial, exclua os recursos individuais.
No Cloud Shell, exclua o cluster do GKE:
gcloud container clusters delete $CLUSTER --zone=$ZONE --async
Excluir as regras de firewall
gcloud compute firewall-rules delete allow-ssh-ingress-from-iap && \ gcloud compute firewall-rules delete fw-allow-health-checks
Exclua o serviço Traffic Director:
gcloud compute forwarding-rules delete td-gke-forwarding-rule \ --global && \ gcloud compute target-http-proxies delete td-gke-proxy \ --global && \ gcloud compute url-maps delete web-service-urlmap \ --global && \ gcloud compute backend-services delete td-gke-service \ --global && \ gcloud compute health-checks delete td-gke-health-check \ --global
Excluir todos os NEGs:
NEG_NAME_EDGE=$(gcloud beta compute network-endpoint-groups list \ | grep gateway-proxy | awk '{print $1}') && \ gcloud beta compute network-endpoint-groups delete $NEG_NAME_EDGE \ --zone=$ZONE && \ gcloud beta compute network-endpoint-groups delete $NEG_NAME_STD \ --zone=$ZONE && \ gcloud beta compute network-endpoint-groups delete $NEG_NAME_PVM \ --zone=$ZONE
Exclua o código baixado, os artefatos e outras dependências:
cd .. && rm -rf solutions-gke-pvm-preemption-handler
A seguir
- Saiba mais sobre as práticas recomendadas para executar aplicativos do Kubernetes otimizados para custo no GKE.
- Saiba mais sobre como otimizar recursos em clusters de vários locatários do GKE com provisionamento automático.
- Saiba mais sobre otimizações de custo no Google Cloud para desenvolvedores e operadores.