guia de estudo · kubernetes básico em 8 etapas + bônus
Kubernetes explicado
pela lente da F1.
Este guia ensina Kubernetes do zero, em oito etapas e um bônus, com todos os exercícios rodando em um cluster Minikube na sua máquina. Você começa entendendo por que um orquestrador existe e como conversar com o cluster pelo kubectl; depois estuda a anatomia do cluster e os Pods, Services e ConfigMaps, ReplicaSets e Deployments, volumes persistentes, probes e o HPA. Na etapa bônus, junta essas peças em um deployment de uma imagem real do Docker Hub e decide quais delas o app precisa e quais ficam de fora. Antes das etapas, a seção seguinte apresenta a lente de F1 do guia: uma comparação geral que mostra o mesmo sistema funcionando dentro de uma equipe de Fórmula 1.
Cada etapa só depende do que foi explicado antes dela e traz um exercício pequeno que prova que você entendeu. Kubernetes se aprende executando e errando de propósito, então não pule os blocos "Mão na massa".
Como usar este guia: mantenha um terminal aberto ao lado do texto. Todos os comandos foram pensados para o Minikube, que você instala na Etapa 01. Quando um exemplo mostra a saída esperada, compare com a sua. Nomes de pods, IPs e tempos vão variar; a estrutura da saída é o que importa. Quando a saída aparece abreviada, ela está marcada com (...).
Organize os arquivos por etapa. Crie uma pasta para cada etapa (etapa03/, etapa04/, etapa05/ e assim por diante) e salve os YAMLs dentro dela. Isso é necessário porque o nome deployment.yaml se repete nas Etapas 05, 08 e 09, e service.yaml e hpa.yaml se repetem nas Etapas 08 e 09, e trabalhar numa pasta só faria um arquivo sobrescrever o anterior. Rode os comandos de dentro da pasta da etapa. Ao fim de cada etapa existe um passo de limpeza; execute-o antes de seguir, para que objetos antigos não interfiram nos exercícios seguintes.
A lente de F1 deste guia
Uma equipe de F1 num fim de semana de GP
Uma equipe de Fórmula 1 chega a um fim de semana de Grande Prêmio com dois carros, uma garagem no pit lane e algumas dezenas de pessoas com funções bem separadas. Na mureta (o pit wall, a bancada voltada para a pista) ficam o estrategista e os engenheiros que decidem: o que cada carro deve fazer, quando parar, que alvo perseguir. Na garagem ficam os mecânicos, organizados em uma equipe por carro, cada uma com um chefe de mecânicos. Eles não decidem estratégia: montam, ajustam e consertam os carros conforme o que a mureta definiu. Os carros, na pista, são as unidades que de fato fazem o trabalho. O regulamento limita cada equipe a dois carros por corrida, e esse limite vai aparecer algumas vezes como o ponto em que a comparação deixa de valer.
Ninguém na mureta encosta no carro. A informação circula por dois canais: o rádio e o intercom da equipe, por onde passam ordens e perguntas, e a telemetria, centenas de sensores que transmitem continuamente o estado de cada carro. Para entrar nesse canal é preciso estar na frequência da equipe e ter credencial. Tudo o que a equipe sabe e decide fica registrado nos servidores de dados: o plano do fim de semana, as versões de cada peça, o histórico de cada carro. Quem quer saber como um carro está consulta esse registro pelo canal oficial, não vai até a garagem perguntar.
O carro em si é descrito por uma folha de especificação: chassi, unidade de potência (o motor do carro, que junta o motor a combustão e a parte elétrica que recupera e devolve energia), pacote aerodinâmico, cada componente com sua versão. Dois carros montados a partir da mesma folha são equivalentes, e um chassi destruído numa batida é substituído por outro montado a partir da mesma folha. O chassi é descartável; a especificação é o que permanece. Separada dela existe a folha de parâmetros (mapas de motor, modos de energia, alvos), que muda o comportamento do carro sem trocar nenhuma peça. Quem é chamado pelo rádio e pela cronometragem é o número de corrida do carro, nunca o número de série do chassi que está embaixo dele.
É a mesma divisão de trabalho que o Kubernetes faz: o control plane decide como a mureta, os nós executam como as garagens, os Pods rodam como os carros, cada Pod sai de um manifesto como o carro sai da folha de especificação, e tudo o que se sabe e se ordena passa por um canal único, como o rádio e a telemetria. Neste guia, você ocupa o lugar de quem escreve o plano e conversa com a mureta pelo rádio.
Uma regra fixa vale para o guia inteiro: cada chassi montado é um Pod. Trocar o chassi é criar um Pod novo, com outro número de série (outro IP), enquanto o número de corrida (o Service) continua o mesmo. Já religar um sistema que desligou dentro do carro, sem trocar o chassi, é reiniciar um container dentro do mesmo Pod.
Os termos em palavras simples
43
O quadro abaixo traduz cada termo técnico do guia para uma ideia do dia a dia, sem F1 e sem jargão. Não precisa decorar; volte a ele quando um termo aparecer no texto.
| Termo técnico | Em palavras simples |
|---|---|
| Cluster | Um grupo de computadores que trabalham juntos como se fossem um só. |
| Control plane (master node) | A gerência: decide o que roda em cada computador e acompanha se tudo está como foi pedido, mas não faz o trabalho em si. |
| Worker node | Um dos computadores que faz o trabalho de fato. |
| API Server | O balcão de atendimento: todo pedido e toda consulta passam por ele. |
| kubectl | O aplicativo que você usa para falar com o balcão. |
| kubeconfig | O endereço do balcão e o seu crachá de acesso, guardados num arquivo. |
| Imperativo e declarativo | Dar uma ordem na hora ("faça isto agora") ou deixar por escrito como as coisas devem ficar. |
| Events | O histórico, com horário, do que aconteceu com cada item. |
| etcd | O caderno oficial onde fica anotado tudo o que foi pedido e como cada coisa está agora. |
| kubelet | O encarregado de cada computador, que cuida do que roda ali. |
| kube-proxy | A lista de contatos de cada computador, que sabe para quem repassar cada chamada. |
| Máquina virtual | Um computador inteiro simulado dentro de outro, com sistema operacional próprio: completo, mas pesado. |
| Kernel do hospedeiro | A base do sistema do computador, dividida por todos os programas que rodam nele. |
| Imagem de container | Uma receita fechada e com versão: seguida em qualquer lugar, dá sempre o mesmo resultado. |
| Container | O programa preparado a partir da receita, rodando separado dos outros; pode ser reiniciado sem mexer no resto. |
| Pod | A menor caixa que o sistema gerencia: um ou mais programas que andam sempre juntos. Se a caixa é trocada, é outra caixa, com outro endereço. |
| Segundo container no Pod | Um programa ajudante dentro da mesma caixa, como um gravador que registra o que o principal faz. |
| IP do Pod | O endereço da caixa, que muda quando ela é substituída. |
| Namespace | Pastas separadas dentro do mesmo sistema, cada uma com a sua lista de itens. |
| Label | Uma etiqueta colada no item, usada para achar de uma vez todos os itens do mesmo tipo. |
| Annotation | Um bilhete preso ao item, escrito para pessoas lerem; não serve para buscar. |
| Manifesto YAML | O formulário preenchido que descreve o que você quer, guardado para ser reutilizado. |
| Estado desejado | O pedido permanente, como "quero sempre 3 funcionando", que o sistema trabalha sozinho para manter. |
| Minikube | Uma versão de bancada do sistema, que roda no seu computador para estudar. |
| Service | Um número fixo de atendimento que repassa a ligação para quem estiver disponível, mesmo que as pessoas mudem. |
| ClusterIP, NodePort, LoadBalancer | Um ramal só interno; o mesmo ramal aberto em cada computador para quem liga de fora; um número público divulgado. |
| ConfigMap | A folha de configurações separada do programa, lida quando ele começa a rodar. |
| Secret | A mesma folha, para senhas e chaves, guardada com mais cuidado. |
| ReplicaSet | A regra "sempre N cópias funcionando": se uma some, outra é criada. |
| Deployment | Quem cuida das versões: troca a versão com calma e guarda o histórico das anteriores. |
| Rolling update | Trocar a versão uma cópia de cada vez, sem fechar o atendimento. |
| Rollback | Voltar para a versão anterior, que funcionava. |
| emptyDir | Um rascunho temporário, jogado fora junto com a caixa. |
| hostPath | Uma gaveta de um computador específico: só serve para quem está naquele computador. |
| PVC | O pedido de espaço para guardar dados ("preciso de 1 GB"). |
| PV | O espaço de armazenamento real que atende esse pedido. |
| StorageClass | O cardápio dos tipos de armazenamento que podem ser criados quando alguém pede. |
| restartPolicy | A regra de religar sozinho um programa que caiu, na mesma caixa. |
| Probes | Perguntas de rotina feitas a cada poucos segundos para saber se o programa está bem. |
| Liveness, readiness, startup | "Está vivo?" (se não, reinicia); "pode atender?" (se não, fica fora do atendimento); "já terminou de ligar?" (enquanto não, as outras perguntas esperam). |
| HPA | O ajuste automático do número de cópias conforme o movimento, sempre entre um mínimo e um máximo. |
| metrics-server | O medidor de consumo que alimenta esse ajuste; sem ele, não há números para decidir. |
| resources.requests | A reserva declarada de recursos; as porcentagens de uso são calculadas em cima dela. |
Onde a comparação para
9
A comparação ajuda a fixar a divisão de papéis, mas não é uma equivalência perfeita. Estes são os pontos em que a F1 deixa de valer para cada etapa:
- Etapa 01: uma equipe repõe um carro em horas, entre sessões, e o regulamento fixa dois carros; o Kubernetes cria um Pod novo em segundos, a qualquer momento, e o limite de quantidade é só o que o cluster aguenta.
- Etapa 02: o rádio da F1 é voz, interpretada por pessoas; a API do Kubernetes é um protocolo HTTP rígido, em que cada pedido tem método, caminho e código de resposta, sem nenhuma margem de interpretação.
- Etapa 03: os sistemas de um carro são fisicamente integrados e projetados juntos, enquanto os containers de um Pod são processos independentes que podem ser trocados um a um na especificação sem mexer nos outros.
- Etapa 04: um número de corrida aponta para um carro só, enquanto o Service distribui requisições entre várias réplicas ao mesmo tempo; o balanceamento de carga não tem equivalente na F1.
- Etapa 05: na F1, um carro é reposto entre sessões, em horas, e o regulamento limita a equipe a dois carros; o ReplicaSet repõe em segundos, a qualquer momento, e mantém o número de réplicas que for declarado.
- Etapa 06: na F1, os dados do carro são transmitidos ou baixados para os servidores, ou seja, existe uma cópia; o PVC não copia nada, o Pod escreve direto no volume externo, e se o PVC for apagado com política
Deleteos dados vão junto. - Etapa 07: na F1, religar um sistema em pista envolve o piloto e quase sempre custa a sessão; no Kubernetes, o kubelet reinicia o container em segundos, sem intervenção de ninguém, e o Pod continua sendo o mesmo Pod, com o contador de RESTARTS aumentando.
- Etapa 08: num GP a equipe corre com exatamente dois carros, por regulamento; o HPA, que coloca mais ou menos réplicas conforme a carga, só tem paralelo numa operação imaginária sem limite de carros, e esta é a etapa em que a comparação mais se afasta da F1 real.
- Etapa 09: num carro de F1 o piloto troca mapas e modos pelo volante em plena volta, e o valor novo vale na hora; no Kubernetes, uma variável de ambiente lida de um ConfigMap só muda quando o Pod é recriado.
Etapa 01 motivação + setup
Por que o Kubernetes existe
O problema
Imagine o e-commerce de uma empresa média. Durante onze meses ele recebe um volume previsível de acessos. Na semana da Black Friday, o tráfego multiplica várias vezes. Se você dimensiona os servidores para o dia a dia, o site cai no pico. Se dimensiona para o pico, paga por máquinas ociosas o ano inteiro. A infraestrutura precisa crescer e encolher conforme a demanda.
Existe um segundo problema, menos visível. O desenvolvedor roda a aplicação na própria máquina, o QA testa em outra e a produção é uma terceira. Qualquer diferença de versão de biblioteca, de sistema operacional ou de configuração produz o clássico "na minha máquina funciona". Manter dev, testes e produção idênticos, na mão, é uma fonte constante de bugs.
Todo projeto de software esbarra nesses dois problemas: manter ambientes iguais e escalar quando a demanda muda. O Kubernetes automatiza a solução dos dois.
O conceito
A peça comum às duas soluções é o container.
Uma máquina virtual (VM) carrega hardware virtualizado e um sistema operacional completo: ocupa gigabytes e leva minutos para subir. Um container compartilha o kernel do sistema operacional da máquina hospedeira e leva apenas os arquivos, binários e bibliotecas de que o app precisa: ocupa megabytes e sobe em segundos. Uma imagem de container empacota esse ambiente inteiro e pode ser executada igual em qualquer lugar, o que resolve o "na minha máquina funciona".
Containers resolvem o problema do ambiente e criam outro. Com dezenas ou centenas deles, alguém precisa criá-los, destruí-los, vigiar a saúde de cada um e recriar o que falhar. Fazer isso manualmente não escala. O Kubernetes (K8s) é o orquestrador que assume essas tarefas.
A ideia central do Kubernetes é o estado desejado. Você não executa uma sequência de ordens ("suba este container, depois aquele"). Você declara o resultado que quer ("quero 3 cópias desta aplicação rodando") e o Kubernetes compara continuamente o que existe com o que foi declarado, corrigindo qualquer diferença. Se uma das cópias some às 3h da manhã (o Pod é apagado ou o nó onde ele rodava cai), o cluster percebe que existem 2 cópias em vez de 3 e cria a que falta, sem ninguém acordar. Quando o que falha é só o processo dentro do container, quem reage é outro mecanismo, que você vai separar com clareza na Etapa 07.
Os conceitos-chave
Container
processo isolado que compartilha o kernel do hospedeiro e leva só o que o app precisa. Muito mais leve que uma VM.
Imagem
pacote imutável com o ambiente do app. O container é uma imagem em execução.
Escala horizontal
mais cópias da aplicação dividindo a carga (é o que o K8s automatiza). Escala vertical é dar mais CPU e RAM à mesma máquina.
Cluster e nó (node)
o K8s organiza máquinas (nós) em um cluster, que é tratado como uma única plataforma.
Pod
o grupo de containers que o K8s gerencia como unidade. É a menor unidade gerenciada (detalhes na Etapa 03).
Namespace
separação lógica de recursos dentro do cluster. Quando você não indica nenhum, o namespace usado é o default.
kubectl
ferramenta de linha de comando que conversa com a API REST do cluster (detalhes na Etapa 02).
Minikube
cluster K8s de 1 nó rodando na sua máquina. Feito para estudo, é o laboratório deste guia.
Estado desejado
você declara o que quer, e o K8s faz acontecer e mantém. Se algo cai, ele recria.
Montando o laboratório
# 1. Instale o Docker Desktop (docker.com). No Linux, use o Docker# Engine (docs.docker.com/engine/install). Confirme:$ docker -vDocker version 27.x# 2. Instale o kubectl (kubernetes.io/docs/tasks/tools) e confirme.# Use --client: ainda não existe cluster, então só a versão do# cliente pode ser consultada (sem --client, o kubectl tentaria falar# com um servidor inexistente e imprimiria um erro de conexão)$ kubectl version --clientClient Version: v1.30.xKustomize Version: v5.x# 3. Instale o Minikube (minikube.sigs.k8s.io/docs/start) e suba o cluster:$ minikube start😄 minikube v1.33 on Windows 11✨ Using the docker driver🏄 Done! kubectl is now configured to use "minikube"# 4. Confirme que o cluster está de pé:$ kubectl get nodesNAME STATUS ROLES AGE VERSIONminikube Ready control-plane 1m v1.30.x# 5. Cluster vazio = sucesso:$ kubectl get podsNo resources found in default namespace.# 6. Agora que o cluster existe, a versão completa mostra cliente e servidor:$ kubectl version --output=yamlclientVersion:(...)serverVersion:(...)
A primeira execução do minikube start baixa a imagem do cluster e demora alguns minutos. É normal.
Comandos essenciais desta etapa
Mão na massa
O menor exercício possível: provar que o laboratório está vivo.
- Rode
minikube starte aguarde o "Done!". - Rode
kubectl get nodes. Observe o nóminikubecomSTATUS Ready. Se aparecerNotReady, aguarde 1 minuto e repita. - Rode
kubectl get pods. ObserveNo resources found in default namespace.Um cluster vazio e pronto é o ponto de partida da próxima etapa. - Bônus: rode
minikube stope depoiskubectl get nodes. Observe a mensagem de erro de conexão (o cluster não responde). Suba de novo comminikube start. Agora você sabe como "cluster fora do ar" aparece no terminal.
Dica: Se o minikube start falhar, a causa mais comum é o Docker Desktop não estar aberto. No Linux, o equivalente é o serviço parado: sudo systemctl start docker. Suba o Docker primeiro e tente de novo.
Anota aí: Container é um ambiente leve e replicável; Kubernetes mantém o estado desejado desses containers: você declara o que quer, ele garante.
Etapa 02 linha de comando + API
kubectl e a API, como você comanda o cluster
O problema
O cluster está de pé (Etapa 01), mas você ainda não sabe como falar com ele. Imagine que o serviço de checkout parou de responder às 2h da manhã e você está de plantão. Você precisa descobrir quais pods existem, em que estado estão, o que o app está imprimindo e, se necessário, entrar no container para investigar. Nada disso pode depender de acesso físico à máquina onde o container roda.
O conceito
Tudo no Kubernetes passa pela API REST do nó master (o *control plane*). O kubectl é o cliente dessa API: cada comando que você digita vira uma requisição HTTP (GET, POST, PUT ou DELETE) contra o cluster.
O caminho de um comando é este:
- Você digita
kubectl get pods. - O kubectl lê o arquivo kubeconfig, que guarda o endereço do API Server, as credenciais e o contexto do cluster atual. O Minikube preenche esse arquivo sozinho no
minikube start. - O kubectl envia um
GETao API Server (para o namespacedefault, o caminho é/api/v1/namespaces/default/pods). - A resposta volta e o kubectl a formata no terminal.
A API é dividida em grupos. O grupo "core" tem os recursos fundamentais (pods, services, replicasets); outros grupos cuidam de segurança, armazenamento e autoscaling. Como é REST puro, qualquer linguagem (Python, Go, Java, C#) consegue chamá-la, o que abre espaço para automação.
Existem dois jeitos de trabalhar com o kubectl:
- Imperativo: você dá a ordem direta, como em
kubectl run nginx --image=nginx. É rápido e serve para testes. - Declarativo: você escreve um arquivo YAML que descreve o estado desejado e roda
kubectl apply -f arquivo.yaml. É o modo dominante em projetos reais, porque o arquivo pode ser versionado no Git, revisado e reaplicado.
Neste guia começamos pelo imperativo, que é mais rápido para aprender, e passamos ao declarativo na Etapa 03.
-v=6 mostra a requisição feita.Diagrama: feito para este guiaOs conceitos-chave
API Server
a porta de entrada do cluster; recebe toda requisição, valida e executa.
kubeconfig
arquivo com endereço do cluster, credenciais e contexto atual.
Imperativo vs. declarativo
dar ordens passo a passo versus descrever o estado final.
Events
histórico de eventos de um objeto (imagem baixada, container criado, falha). Aparece no final do describe.
Verbos do dia a dia
- get: consulta recursos e o status de cada um (get pods, get svc, get nodes). - create / run / apply: criam recursos. run cria um pod imperativo; apply -f aplica um YAML. - delete: remove. Cuidado, pode afetar quem depende do recurso. - describe: mostra configuração e a seção Events. É a primeira parada quando algo trava. - logs: mostra o que o container está imprimindo. É a principal fonte de informação no debug. - exec: executa um comando dentro do container, por exemplo um shell.
Ciclo de vida de um Pod
# Cria um pod chamado "nginx" a partir da imagem nginx:1.14.2 do Docker Hub$ kubectl run nginx --image=nginx:1.14.2 --port=80pod/nginx created# Acompanhe até o status ficar Running (o primeiro estado é ContainerCreating)$ kubectl get podsNAME READY STATUS RESTARTS AGEnginx 1/1 Running 0 30s# Detalhes + eventos (veja a linha "Pulling image" na seção Events)$ kubectl describe pod nginxName: nginx(...)Events:Type Reason Age From MessageNormal Scheduled 35s default-scheduler Successfully assigned default/nginx to minikubeNormal Pulling 34s kubelet Pulling image "nginx:1.14.2"Normal Pulled 20s kubelet Successfully pulled image "nginx:1.14.2"Normal Created 20s kubelet Created container nginxNormal Started 20s kubelet Started container nginx# O que o container está imprimindo (o nginx escreve o log de acesso na saída# padrão, então ele só mostra linhas depois que chegam requisições)$ kubectl logs nginx# Abre um shell dentro do container (digite "exit" para sair).# Atenção: depois deste comando você está DENTRO do container e o prompt# passa a ser "# ". As linhas abaixo que começam com "# " (ls e exit) não# são comentários: são comandos digitados no shell do container.$ kubectl exec -it nginx -- /bin/sh# ls /usr/share/nginx/html50x.html index.html# exit# De volta ao seu terminal (prompt "$"). Limpeza:$ kubectl delete pod nginxpod "nginx" deleted
Se o status ficar em ImagePullBackOff, o cluster não conseguiu baixar a imagem. Confira o nome e a tag da imagem e a sua conexão com a internet. Para ver a requisição HTTP por trás de um comando, aumente o nível de log do kubectl. O formato exato muda de versão para versão, mas você deve encontrar uma linha com o método, o caminho da API e o status:
Comandos essenciais desta etapa
Mão na massa
Execute o ciclo completo e provoque o primeiro erro de propósito.
- Rode a sequência do exemplo (
run,get,describe,logs,delete). Observe a seçãoEventsdodescribe: as linhasPulling image,Created containereStarted containercontam a história do pod em ordem. - Crie um pod quebrado de propósito:
kubectl run quebrado --image=nginx:versao-que-nao-existe. - Rode
kubectl get pods. Observe o statusErrImagePull, que logo viraImagePullBackOff. - Rode
kubectl describe pod quebrado. Observe nos Events a mensagem dizendo que a imagem não foi encontrada. Você acabou de praticar o fluxo de debug real. - Rode
kubectl get pods -v=6e observe a linha comGET .../api/v1/namespaces/default/pods. Esse é o comando traduzido em HTTP. - Limpe:
kubectl delete pod quebrado.
Dica: Grave o fluxo de debug: get pods (qual o status?), depois describe pod (o que dizem os Events?), depois logs (o que o app diz?). Essa sequência resolve a maioria dos problemas.
Anota aí: Tudo no K8s é uma chamada à API REST, o kubectl é o cliente dessa API, e quando algo trava o caminho é sempre get, describe, logs.
Etapa 03 arquitetura + labels
A anatomia do cluster e a menor unidade dele, o Pod
O problema
Na Etapa 02 você criou um pod com um comando imperativo. Funciona, mas há dois furos. Primeiro, você não sabe o que exatamente foi criado nem onde dentro do cluster. Segundo, se amanhã precisar recriar aquele pod idêntico em outro ambiente, só terá a memória do comando que digitou. Além disso, imagine um time com 40 pods no mesmo cluster (checkout, catálogo, pagamento, busca). Como listar apenas os do checkout? Precisamos entender as peças do cluster, descrever os objetos em arquivos versionáveis e agrupá-los por critérios que o próprio Kubernetes consiga usar.
O conceito
Um cluster é um grupo de máquinas (nodes) trabalhando juntas, com papéis bem definidos:
- O master node (control plane) gerencia o cluster e decide onde executar os Pods.
- Os worker nodes executam os Pods e os demais recursos.
- O etcd é o banco de dados distribuído que guarda a configuração e o estado de tudo no cluster.
- O kubelet é o agente que roda em cada node e gerencia os Pods daquele node.
- O kube-proxy encaminha o tráfego de rede até o Pod certo.
- O API Server é a porta de entrada, por onde passa tudo que o kubectl faz (Etapa 02).
O Pod é a menor unidade que o Kubernetes gerencia: um envelope com um ou mais containers que dividem rede e armazenamento. Quatro fatos sobre Pods:
- Um Pod representa um processo em execução e pode ter mais de um container, todos dividindo o mesmo IP e os mesmos volumes.
- Pods são efêmeros. Nascem e morrem o tempo todo, e você nunca deve contar com um Pod específico existindo amanhã. Esta frase é a raiz das Etapas 04, 05 e 06.
- Cada Pod recebe um IP interno do cluster.
- Pods são descritos em YAML.
Para organizar muitos objetos existem labels (rótulos) e annotations (anotações). Labels são pares chave-valor usados para identificar e selecionar objetos: é por label que um Service encontra seus Pods (Etapa 04) e que um Deployment sabe quais Pods são seus (Etapa 05). Annotations são metadados livres (autor, link de documentação, dados de auditoria) que não participam de seleção; servem para pessoas e ferramentas lerem. Essa diferença é uma pergunta clássica de prova.
Os conceitos-chave
Master node
gerencia o cluster e decide onde executar os Pods.
Worker node
executa os Pods e demais recursos.
etcd
banco distribuído com a configuração e o estado do cluster.
kubelet
agente de cada node que gerencia os Pods locais.
kube-proxy
encaminha o tráfego de rede para os Pods certos.
API Server
porta de entrada de tudo que o kubectl faz.
Pod
um ou mais containers com rede e volumes compartilhados; efêmero.
Label
chave-valor para identificar e selecionar objetos.
Annotation
metadado livre para documentação, sem papel em seleção.
Manifesto
arquivo YAML que descreve um objeto. Todo manifesto tem apiVersion, kind, metadata e spec.
O primeiro YAML
Crie a pasta etapa03/ e salve o manifesto abaixo nela como meu-pod.yaml. Este é o esqueleto que você precisa saber de cabeça.
apiVersion: v1 # versão da API do K8s p/ este objetokind: Pod # o TIPO de objeto criadometadata: # dados SOBRE o objetoname: meu-podlabels: # rótulos: chave-valor p/ SELECIONARapp: myappambiente: estudoannotations: # anotações: metadados livres (docs)autor: "Davi Gomes"spec: # a ESPECIFICAÇÃO: o que roda dentrocontainers:- name: meu-containerimage: nginx:1.14.2ports:- containerPort: 80
Todo objeto K8s segue esse esqueleto: apiVersion + kind + metadata + spec. O que muda de um objeto para outro é o valor de kind e o conteúdo de spec; o resto é sempre igual. Quem domina esse esqueleto consegue ler qualquer manifesto.
Comandos essenciais desta etapa
Mão na massa
- Salve o YAML acima como
etapa03/meu-pod.yamle aplique comkubectl apply -f meu-pod.yaml. - Rode
kubectl get pods --show-labels. Observe os dois labels na última coluna. - Rode
kubectl get pods -l app=myappe depoiskubectl get pods -l app=outra-coisa. Observe: o primeiro encontra o pod, o segundo respondeNo resources found in default namespace.Esse é o mecanismo de seleção em ação. - Adicione um label em tempo real com
kubectl label pod meu-pod time=devopse confirme com--show-labels. - Não delete o pod agora: ele será o alvo do Service na próxima etapa, e a limpeza dele está no final da Etapa 04. Se você deletou, aplique o YAML de novo. Essa é a vantagem do declarativo: o arquivo recria o objeto idêntico.
Dica: Labels são seleção operacional (o K8s usa). Annotations são documentação (pessoas usam). Para registrar o autor de um Pod, use uma annotation; para agrupar os Pods de uma aplicação, use um label.
Anota aí: Todo objeto K8s é apiVersion + kind + metadata + spec, e labels são o mecanismo pelo qual tudo no cluster encontra tudo.
Etapa 04 rede + configuração
Services e ConfigMaps, endereço fixo e configuração externa
O problema
Pela Etapa 03, Pods são efêmeros e cada Pod novo nasce com um IP diferente. Agora imagine o frontend de um e-commerce chamando a API de pedidos. Hoje a API está no IP 10.244.0.5. Amanhã o Pod morre, é recriado e passa a ser 10.244.0.9. Se o frontend tem o IP antigo escrito no código, ele quebra a cada recriação. Como alguém fala com Pods de forma confiável se o endereço muda o tempo todo?
Há um segundo problema. Todo serviço precisa de configuração (portas, URLs de outros serviços, flags). Se esses valores ficam dentro da imagem do container, cada mudança obriga a rebuildar e republicar a imagem. Trocar uma URL não deveria exigir um novo build.
O conceito
Problema 1: o Service. O Service é um ponto de entrada estável que encontra os Pods por label e balanceia o tráfego entre eles. O fluxo é:
- Uma requisição chega ao Service, que tem IP e nome fixos.
- O Service seleciona os Pods pelo label selector (o mecanismo da Etapa 03).
- O tráfego é distribuído de forma balanceada entre as réplicas saudáveis.
O nome do Service vira um hostname interno do cluster. Se o Service se chama auth-service, qualquer Pod do cluster consegue acessar http://auth-service, mesmo que os Pods por trás dele troquem de IP repetidamente.
Existem três tipos de Service:
- ClusterIP (o padrão): IP interno, acessível só de dentro do cluster. Serve para a comunicação entre serviços (por exemplo, API para banco).
- NodePort: abre uma porta fixa (de 30000 a 32767) em cada nó. O acesso de fora é feito por
IP-do-nó:porta. É o jeito de expor algo no Minikube. - LoadBalancer: provisiona um balanceador externo com IP público. É o tipo usado em produção na nuvem (AWS, GCP, Azure).
Problema 2: o ConfigMap. O ConfigMap guarda a configuração em um objeto separado do container. Você o injeta no Pod de três formas:
- variáveis de ambiente (a mais comum, via
envFrom); - arquivos montados (ótimo para configurações longas);
- argumentos de linha de comando.
Quando a configuração muda, você atualiza o ConfigMap e a imagem continua a mesma, sem rebuild. Mas atenção a um detalhe: variáveis de ambiente são lidas quando o container inicia. Atualizar o ConfigMap não altera as variáveis de um Pod que já está rodando; o Pod precisa ser recriado para enxergar os valores novos (você vai ver isso acontecer na prática na Etapa 09). Já os ConfigMaps montados como arquivos costumam ser atualizados dentro do Pod depois de algum tempo, sem recriá-lo, mas o app precisa reler o arquivo para usar o valor novo.
app=A, não pelo IP. O Pod com app: B não recebe tráfego desse Service. Deployment e ReplicaSet aparecem na Etapa 05.Diagrama: kubernetes.io, The Kubernetes Authors, CC BY 4.0Os conceitos-chave
Service
endereço estável (IP e nome) que seleciona Pods por label e balanceia o tráfego.
selector
o par chave-valor que liga o Service aos Pods; precisa casar com o label dos Pods.
port / targetPort / nodePort
port é onde o Service escuta; targetPort é a porta do container que recebe; nodePort é a porta exposta no nó (só no tipo NodePort).
ClusterIP
acesso interno apenas. NodePort: acesso externo por porta do nó. LoadBalancer: acesso externo por IP público na nuvem.
Endpoints
a lista de IPs de Pods que o Service está de fato usando; vazia quando nenhum Pod casa com o selector.
ConfigMap
objeto com pares chave-valor de configuração, separado da imagem.
envFrom
injeta todas as chaves de um ConfigMap como variáveis de ambiente (lidas só na criação do container).
Secret
irmão do ConfigMap, destinado a senhas e chaves.
Repare no selector
Crie a pasta etapa04/ e salve os manifestos desta etapa nela.
# app.yaml: salve o Service neste arquivoapiVersion: v1kind: Servicemetadata:name: meu-service # vira o hostname internospec:type: NodePort # troque p/ ClusterIP (interno) ou LoadBalancer (nuvem)selector: # ← O ELO: procura Pods com este labelapp: myappports:- protocol: TCPport: 80 # porta em que o Service escutatargetPort: 80 # porta do container que recebenodePort: 30080 # porta exposta no nó (só NodePort)
O selector do Service casa com o label do Pod (app: myapp), exatamente o meu-pod da Etapa 03. É assim que o Service sabe para quem mandar o tráfego.
Comandos essenciais desta etapa
Mão na massa
- Com o
meu-podda Etapa 03 rodando (labelapp: myapp), salve o YAML do Service comoetapa04/app.yamle aplique. - Rode
minikube service meu-service --urle abra a URL no navegador. Observe a página de boas-vindas do nginx. Você acessou um Pod efêmero por um endereço estável. - Teste o elo: delete o pod (
kubectl delete pod meu-pod) e recarregue o navegador. Observe que a página deixa de carregar (o navegador mostra conexão recusada ou tempo esgotado, dependendo do sistema e do driver), já que não existe mais nenhum Pod com o label. Confirme a causa comkubectl get endpoints meu-service: a lista de endpoints está vazia.Recrie o pod com$ kubectl get endpoints meu-serviceNAME ENDPOINTS AGEmeu-service <none> 5mkubectl apply -f ../etapa03/meu-pod.yamle recarregue. O acesso voltou sem você tocar no Service, ekubectl get endpoints meu-servicevolta a mostrar um IP (algo como10.244.0.7:80). - Aplique o ConfigMap e o pod (
kubectl apply -f configmap-pod.yaml) e rodekubectl exec pod-config -- env. ObserveMENSAGEMeMODOna lista de variáveis. - Limpe os objetos desta etapa e da anterior:(A ordem das linhas pode variar.) Feche também o terminal do$ kubectl delete -f app.yaml -f configmap-pod.yaml -f ../etapa03/meu-pod.yamlservice "meu-service" deletedconfigmap "minha-config" deletedpod "pod-config" deletedpod "meu-pod" deleted
minikube service, com Ctrl+C.
Dica: Regra de ouro: exponha o mínimo. Um banco de dados deve ser ClusterIP, invisível de fora. Apenas a porta de entrada do sistema vira NodePort ou LoadBalancer. Para senhas e chaves, o irmão seguro do ConfigMap é o Secret.
Anota aí: Service dá endereço fixo a Pods que mudam (achando-os por label); ConfigMap tira a configuração de dentro da imagem, mas variáveis de ambiente só mudam quando o Pod é recriado.
Etapa 05 auto-cura + escala
Auto-cura e escala com ReplicaSets e Deployments
O problema
Até aqui você criou Pods manualmente, o que serve para aprender. Em produção, três situações quebram esse modelo.
O conceito
Duas peças resolvem isso, uma embrulhando a outra.
ReplicaSet: garante o número de réplicas. Você declara replicas: 3 e ele monitora os Pods por label selector. Se um Pod some, o ReplicaSet percebe que a contagem caiu e cria outro em segundos, sem ninguém pedir. Essa é a auto-cura, o estado desejado da Etapa 01 mantido na prática. O ReplicaSet cuida de Pods que desaparecem. Se o que falha é só o processo dentro do container (um crash), o Pod continua existindo e quem reinicia o container é o kubelet, dentro do mesmo Pod; essa separação é detalhada na Etapa 07.
Deployment: gerencia versões. Ele cria e gerencia o ReplicaSet por baixo dos panos e adiciona:
- Rolling Update: ao trocar a imagem, o Deployment sobe réplicas novas e desliga as antigas gradualmente, e o serviço nunca fica fora do ar.
- Rollback: se a versão nova quebrar,
kubectl rollout undovolta para a anterior em segundos. - Escala manual:
kubectl scale --replicas=5muda o número de réplicas com um comando.
Cada versão do template tem o seu próprio ReplicaSet. Na troca de versão, o Deployment cria um ReplicaSet novo e vai reduzindo o antigo até zero. O nome de um Pod revela a quem ele pertence: em nginx-fiap-7d4b9c-aaa, o trecho 7d4b9c identifica o ReplicaSet (a versão), e o sufixo final identifica o Pod. Quando o sufixo do meio muda no meio de um rolling update, você está vendo Pods de um ReplicaSet novo.
Os YAMLs de ReplicaSet e Deployment são quase idênticos, mas no dia a dia usamos quase sempre o Deployment, justamente pelo controle de versão.
1. Antes da atualização: todos os Pods rodam a versão antiga atrás do Service.
2. Um Pod novo sobe com outro IP; um antigo começa a sair.
3. A troca continua um Pod por vez, e o Service segue atendendo com os Pods prontos.
4. Fim: todos os Pods estão na versão nova, e o endereço do Service não mudou.
Os conceitos-chave
ReplicaSet
mantém N réplicas rodando sempre; se uma cai, nasce outra. Seleciona Pods por label selector.
Deployment
gerencia ReplicaSets e o histórico de versões; é o objeto que você usa na prática.
template
o molde de Pod embutido no Deployment (é o esqueleto de Pod da Etapa 03).
Rolling Update
troca gradual de versão, sem downtime.
Rollback
retorno a uma revisão anterior com rollout undo.
Revisão
cada alteração do template do Deployment gera uma revisão no histórico, e cada revisão corresponde a um ReplicaSet.
O Deployment completo e o ciclo de vida
Crie a pasta etapa05/ e salve o manifesto abaixo nela como deployment.yaml.
apiVersion: apps/v1kind: Deploymentmetadata:name: nginx-fiapspec:replicas: 3 # quero 3 cópias, sempreselector:matchLabels:app: nginx-app # gerencio os Pods com este label...template: # ...e este é o "molde" de cada Podmetadata:labels:app: nginx-app # tem que casar com o selector ↑spec:containers:- name: nginximage: nginx:1.14.2 # trocar esta versão dispara o rolling updateports:- containerPort: 80
O template é literalmente o esqueleto de Pod da Etapa 03 dentro do Deployment. Nada aqui é novo: o Deployment compõe peças que você já conhece.
Comandos essenciais desta etapa
Mão na massa
Este é o exercício central do guia. Vá com calma e observe cada estado.
- Aplique o
deployment.yamle confirme os 3 podsRunning. Rode tambémkubectl get rse observe o ReplicaSetnginx-fiap-<hash>com 3 réplicas: o prefixo dos nomes dos Pods é o nome dele. - Delete um pod na mão (copie um nome real do
kubectl get pods) e rodekubectl get podsde novo, rápido. Observe o substituto emContainerCreating. Você tentou violar o estado desejado e o cluster corrigiu. - Rode o rolling update para
nginx:1.16.1e acompanhe comkubectl rollout status. Observe a mensagem de sucesso. Rodekubectl get rse observe que há agora dois ReplicaSets: o novo com 3 réplicas e o antigo com 0. É por isso que o sufixo do nome dos Pods mudou. - Quebre de propósito com a imagem
nginx:nao-existe. Observe nokubectl get podsos pods antigosRunningsegurando o serviço e os novos emImagePullBackOff, e nokubectl get rsum terceiro ReplicaSet comREADY 0. - Execute
kubectl rollout undo deployment/nginx-fiape confirme comkubectl rollout history deployment/nginx-fiap. Observe que a lista vai 1, 3, 4: a revisão 2 virou a 4. Confirme a imagem atual comkubectl describe deployment nginx-fiap | grep Image, que deve mostrarnginx:1.16.1. Você fez um rollback em um comando. - Limpe:
kubectl delete -f deployment.yaml(saída:deployment.apps "nginx-fiap" deleted). Os ReplicaSets e Pods do Deployment são removidos junto.
Dica: Faça o teste da auto-cura pelo menos uma vez: aplicar o deployment, deletar um pod na mão e ver o substituto nascer. É nesse momento que o modelo de "estado desejado" deixa de ser teoria.
Anota aí: Ninguém cria Pod avulso em produção: o Deployment garante N réplicas (auto-cura), troca versões sem downtime e volta atrás em um comando, usando um ReplicaSet por versão.
Etapa 06 persistência de dados
Volumes, dados que sobrevivem ao Pod
O problema
Pods são efêmeros, e a Etapa 05 mostrou como isso é frequente: pods são recriados a cada rolling update. Para uma API sem estado isso é ótimo. Para um banco de dados é um desastre. Imagine o Pod do PostgreSQL do e-commerce sendo recriado durante um update, e todos os pedidos da Black Friday desaparecendo junto, porque estavam gravados no sistema de arquivos do container. Dados de verdade não podem viver dentro do container.
O conceito
O Kubernetes resolve isso com uma cadeia de quatro conceitos: Volume, PersistentVolume (PV), PersistentVolumeClaim (PVC) e StorageClass (SC).
- O PVC é o *pedido* de armazenamento: "quero 1Gi, leitura e escrita por um nó".
- O PV é o *disco real* que atende o pedido (NFS, EBS da AWS, hostPath e outros).
- A StorageClass descreve *tipos* de disco (padrão, SSD rápido) e permite que o cluster crie PVs automaticamente quando um PVC os solicita.
O Pod referencia apenas o PVC e não precisa saber onde o disco fica fisicamente. Essa separação permite que o mesmo YAML de Pod rode no Minikube e na AWS, mudando só o que atende o pedido. No Minikube, a StorageClass standard já provisiona PVs automaticamente: você cria o PVC e o PV aparece.
Os tipos de volume mais comuns:
- emptyDir: nasce vazio junto com o Pod e morre com o Pod. Serve para arquivos temporários e para compartilhar dados entre containers do mesmo Pod.
- hostPath: monta um diretório do nó dentro do container. É útil em desenvolvimento e perigoso em produção, porque amarra o Pod àquele nó.
- persistentVolumeClaim: o jeito correto para dados de verdade, que sobrevivem a reinício e recriação do Pod.
Os modos de acesso do PVC definem quem pode montar o volume:
- ReadWriteOnce (RWO): leitura e escrita por um único nó. É o caso típico de banco de dados.
- ReadOnlyMany (ROX): somente leitura, por vários nós. Serve para assets estáticos.
- ReadWriteMany (RWX): leitura e escrita por vários nós. Exige NFS ou um backend equivalente.
standard do Minikube. Com emptyDir, o dado vive só enquanto o Pod existir.Diagrama: feito para este guiaOs conceitos-chave
emptyDir
volume temporário que vive e morre com o Pod.
hostPath
diretório do nó montado no container; amarra o Pod ao nó.
PVC (PersistentVolumeClaim)
pedido de armazenamento feito pelo usuário.
PV (PersistentVolume)
o disco que atende o pedido.
StorageClass
perfil de armazenamento que provisiona PVs automaticamente.
Bound
status que indica que o PVC foi vinculado a um PV.
volumeMounts / volumes
no Pod, volumes declara a origem e volumeMounts define onde ela aparece dentro do container.
Escreva, destrua, leia de volta
Crie a pasta etapa06/ e salve os manifestos desta etapa nela.
# pvc.yamlapiVersion: v1kind: PersistentVolumeClaimmetadata:name: meu-pvcspec:accessModes:- ReadWriteOnce # um nó lê e escreve (típico de banco)resources:requests:storage: 1Gi # quanto disco eu quero# no Minikube, a StorageClass "standard" provisiona o PV sozinha
Comandos essenciais desta etapa
Bound indica sucesso)standard do Minikube, o PV correspondente costuma ser apagado junto, com os dados (a política de reclaim padrão é Delete; confira a do seu cluster com kubectl get storageclass)Mão na massa
- Aplique o PVC e rode
kubectl get pvc. Observe oSTATUS Bound: o pedido foi atendido por um PV criado automaticamente. Confirme comkubectl get pv. - Execute a sequência completa do exemplo: escrever o arquivo, deletar o pod, recriar o pod, ler o arquivo. Observe o
sobrevivi!voltar. Esse é o contrato do PVC. - Experimento de contraste: salve o
pod-emptydir.yamle repita a sequência (escrever, deletar, recriar, ler). Observe oNo such file or directoryno final: o arquivo sumiu. Essa é a diferença entre efêmero e persistente, vista na prática. - Limpe, nesta ordem:
kubectl delete pod pod-persistente pod-efemeroe depoiskubectl delete pvc meu-pvc. Com o PVC apagado,kubectl get pvdeve ficar vazio (o PV foi removido junto, como descrito acima).
Dica: Dado importante sempre fica atrás de um PVC. Se os dados sumiram após um restart ou uma recriação, quase certamente estavam em emptyDir ou no sistema de arquivos do próprio container.
Anota aí: PVC é o pedido, PV é o disco, StorageClass descreve os tipos de disco, e dado que importa mora sempre atrás de um PVC.
Etapa 07 saúde do app
Probes, como o cluster sabe que seu app está vivo
O problema
Às 3h da manhã, o seu app entra em deadlock. O processo continua de pé, então para o Kubernetes está tudo Running, mas nenhuma requisição é respondida. Os mecanismos de recuperação que você viu até aqui agem quando um processo termina ou quando um Pod some; nenhum deles enxerga um processo vivo porém travado. Existe ainda um caso parecido: o app acabou de subir, mas ainda está carregando dados e não deveria receber tráfego, e o Service já está mandando requisições para ele. Sem informação extra, o cluster não distingue "rodando" de "funcionando".
O conceito
Antes das probes, convém separar três situações que no dia a dia todo mundo chama de "o app caiu", porque cada uma é tratada por um mecanismo diferente:
- O processo dentro do container termina (um crash, falta de memória, uma exceção não tratada). O Pod continua existindo e quem reage é o kubelet do nó: ele reinicia o container dentro do mesmo Pod, conforme a
restartPolicydo Pod (o padrão éAlways). O nome e o IP do Pod não mudam, e a colunaRESTARTSsobe. Isso acontece sem nenhuma probe configurada. - O Pod some (alguém o apagou, o nó caiu). O Pod inteiro deixa de existir e quem reage é o ReplicaSet (Etapa 05), que cria um Pod novo, com nome e IP novos. Um Pod avulso, sem ReplicaSet ou Deployment, não volta.
- O processo continua vivo, mas travado (deadlock, loop infinito, dependência esgotada). Nada terminou e nenhum Pod sumiu, então os dois mecanismos anteriores não percebem. Só uma probe de liveness detecta, e quando ela falha o kubelet reinicia o container, como no caso 1.
As probes são verificações de saúde declaradas no manifesto do Pod. O kubelet as executa periodicamente e age conforme o resultado.
São três probes, e o ponto mais importante é o que acontece quando cada uma falha:
- Liveness pergunta "está vivo?". Se falha, o kubelet reinicia o container (dentro do mesmo Pod). É para apps que travam e só voltam com restart.
- Readiness pergunta "está pronto para receber requisições?". Se falha, o Pod sai do balanceador (o Service para de mandar tráfego) sem ser reiniciado. É para apps que carregam dados antes de atender ou que ficam temporariamente indisponíveis.
- Startup pergunta "terminou de inicializar?". Enquanto roda, segura as outras probes, evitando que a liveness mate um app lento antes da hora.
Cada probe pode verificar de três formas:
- httpGet: o kubelet faz um GET em um endpoint (por exemplo
/health) e espera um status de sucesso (200 OK). É o mais comum. Um status de erro, como 404 ou 500, conta como falha. - tcpSocket: testa se a porta aceita conexão. Serve para serviços que não falam HTTP.
- exec: executa um comando dentro do container; código de saída 0 significa saudável.
Boas práticas da aula: use as três probes juntas em produção; use endpoints diferentes para cada probe; comece pelas configurações padrão e ajuste intervalos só quando o app pedir; monitore as falhas de probe (por exemplo, com Prometheus), porque elas são o primeiro sinal de problema.
Os conceitos-chave
restartPolicy
política do Pod para containers que terminam (padrão Always); é o que reinicia um container que caiu, com ou sem probes.
Liveness probe
falhou, o kubelet reinicia o container.
Readiness probe
falhou, o Pod sai do balanceador sem reiniciar.
Startup probe
enquanto não passa, as outras ficam suspensas.
httpGet / tcpSocket / exec
os três mecanismos de verificação.
periodSeconds
de quantos em quantos segundos a probe roda.
failureThreshold
quantas falhas seguidas são toleradas antes de agir (o padrão é 3).
initialDelaySeconds
quanto esperar antes da primeira verificação.
Exemplos comentados
apiVersion: v1kind: Podmetadata:name: exemplo-probesspec:containers:- name: appimage: exemplo:latestports:- containerPort: 80livenessProbe: # falhou → REINICIA o containerhttpGet:path: /healthport: 80periodSeconds: 10 # verifica a cada 10stimeoutSeconds: 5failureThreshold: 3 # 3 falhas seguidas → reinicia (3 é o padrão)readinessProbe: # falhou → SAI do balanceadorhttpGet:path: /ready # boa prática: endpoint separadoport: 80periodSeconds: 5startupProbe: # segura as outras até o app subirhttpGet:path: /startupport: 80initialDelaySeconds: 120 # app lento: espera 2minperiodSeconds: 30
Este YAML é ilustrativo: a imagem exemplo:latest é fictícia. O experimento executável vem a seguir, e na Etapa 09 você verá as probes funcionando em uma imagem real. Uma ressalva sobre a startupProbe do exemplo: o initialDelaySeconds: 120 é válido, mas a forma mais comum de dar tempo a um app lento é multiplicar failureThreshold por periodSeconds (por exemplo, 30 tentativas a cada 10 segundos dão até 300 segundos). Assim a startupProbe termina assim que o app responde, em vez de esperar sempre um atraso fixo.
Comandos essenciais desta etapa
Last State mostra por que o container anterior terminouMão na massa
- Aplique o
liveness-exec.yamle rodekubectl get pods -w. Observe a colunaRESTARTSsubir sozinha: a primeira reinicialização aparece cerca de 45 segundos após o início (30s saudável mais 3 falhas espaçadas de 5s). Cada incremento é o kubelet reiniciando um container que parou de passar na verificação. - Em outro terminal, rode
kubectl describe pod liveness-exec. Observe nos Events o parWarning Unhealthyseguido deNormal Killing. Aprenda a reconhecer essa dupla: em produção, ela é o diagnóstico de probe falhando. - Rode o experimento do crash (
kubectl run crash ...) e compare. Observe que oRESTARTStambém sobe, mas odescribemostraLast State: TerminatedcomExit Code: 1e nenhum eventoLiveness probe failed. Este é o caso 1, tratado pelo kubelet sem probe; o do passo 2 é o caso 3, que só a liveness resolve. - Limpe:
kubectl delete pod liveness-exec crash.
Dica: Running não significa saudável. Um app pode estar rodando e travado em um deadlock. É exatamente por isso que as probes existem.
Anota aí: Processo que cai o kubelet reinicia, Pod que some o ReplicaSet recria, e processo vivo porém travado só a liveness detecta: liveness falhou reinicia o container, readiness falhou tira o Pod do balanceador sem reiniciar, e startup segura as duas até o app terminar de subir.
Etapa 08 escala automática
HPA, escala automática baseada em métricas
O problema
Volte ao e-commerce da Etapa 01: chegou a Black Friday. Você já sabe escalar manualmente com kubectl scale --replicas=... (Etapa 05). Ficar de plantão no terminal ajustando réplicas a cada pico de acesso, inclusive às 2h da manhã, não é viável. O cluster deve medir a carga e ajustar o número de réplicas sozinho.
O conceito
O Horizontal Pod Autoscaler (HPA) monitora métricas dos Pods (CPU, memória e outras) e ajusta o número de réplicas entre um mínimo e um máximo definidos por você.
O funcionamento: o HPA compara a utilização atual dos Pods com o alvo configurado (por exemplo, manter a média de CPU em 70%). Se a utilização passa do alvo, ele cria réplicas, até o máximo. Se cai, ele remove réplicas, até o mínimo, economizando recursos. O scale down é propositalmente lento (cerca de 5 minutos de estabilidade) para evitar oscilações constantes diante de variações rápidas de carga.
Quando um HPA passa a controlar um Deployment, é ele quem decide o número de réplicas dentro da faixa minReplicas a maxReplicas. O valor replicas escrito no manifesto do Deployment vale só até o HPA agir: se ele for maior que o piso e não houver carga, o HPA o reduz depois de alguns minutos (você vai ver isso na Etapa 09).
Quais métricas o HPA consegue usar. CPU e memória são lidas nativamente, por meio do metrics-server. As demais exigem componentes adicionais instalados no cluster: métricas personalizadas do seu app (requisições por segundo, tamanho de fila), métricas externas (por exemplo, vindas do Prometheus) e E/S de disco precisam de um adaptador de métricas customizadas ou externas. Sem esse adaptador, só CPU e memória estão disponíveis. A métrica certa depende do perfil do app: um app pesado em CPU escala por CPU; um que consome muita RAM escala por memória.
Dois pré-requisitos costumam ser esquecidos:
- O cluster precisa do metrics-server para ler CPU e memória. No Minikube:
minikube addons enable metrics-server. - O container precisa declarar `resources.requests.cpu`, porque "70% de CPU" significa 70% do que o Pod pediu. Sem requests, o HPA mostra
<unknown>e não escala.
Os conceitos-chave
HPA
controlador que ajusta o número de réplicas de um Deployment conforme métricas.
metrics-server
componente que coleta CPU e memória dos Pods; pré-requisito do HPA.
resources.requests
quantidade de recurso que o container declara precisar; base do cálculo de percentual.
resources.limits
teto de recurso que o container pode consumir.
milicore (m)
unidade de CPU; 100m equivale a 0,1 CPU.
minReplicas / maxReplicas
piso e teto de réplicas; o HPA trabalha dentro dessa faixa.
averageUtilization
percentual médio de uso, relativo ao requests, que o HPA tenta manter.
Adaptador de métricas
componente extra necessário para métricas que não sejam CPU ou memória.
Deployment, HPA, Service e teste de carga
Crie a pasta etapa08/ e salve os três manifestos nela (os nomes deployment.yaml e service.yaml também aparecem na Etapa 09, por isso a separação por pasta).
# deployment.yamlapiVersion: apps/v1kind: Deploymentmetadata:name: nginx-hpaspec:replicas: 1 # começa com 1; o HPA assume daquiselector:matchLabels:app: nginx-hpatemplate:metadata:labels:app: nginx-hpaspec:containers:- name: nginximage: nginx:1.14.2resources: # SEM requests o HPA não calcula %requests:cpu: "100m" # 100 milicores = 0,1 CPUlimits:cpu: "200m"
Comandos essenciais desta etapa
Mão na massa
O experimento final desta parte, que junta Deployment, Service e HPA.
- Ative o metrics-server e aplique os três YAMLs (Deployment, HPA, Service). Rode
kubectl get hpae observe os targets. Se aparecer<unknown>, aguarde cerca de 1 minuto: o metrics-server precisa coletar a primeira leitura. - Suba o gerador de carga em um terminal e rode
kubectl get hpa -wem outro. Observe o percentual de CPU passar de 70% e, em seguida, a colunaREPLICASsubir. Se o percentual estacionar abaixo do alvo, use o plano B. Confirme comkubectl get pods: pods novos nasceram sem nenhum comando seu. - Pare a carga (Ctrl+C) e continue assistindo. Observe que as réplicas levam cerca de 5 minutos para encolher. Essa lentidão é proposital.
- Rode
kubectl top podsdurante o teste para ver a métrica crua que alimenta o HPA. - Limpe:
kubectl delete -f deployment.yaml -f hpa.yaml -f service.yaml. O podgeradorfoi criado com--rme já se apagou quando você interrompeu a carga; confirme comkubectl get pods. Mantenha o metrics-server ativo, porque a Etapa 09 também usa o HPA.
Dica: Se o kubectl get hpa mostra <unknown> nos targets, há duas causas possíveis: o metrics-server não está ativo ou o container não declarou resources.requests.
Anota aí: HPA é o estado desejado aplicado à escala: você define alvo, piso e teto, mas sem metrics-server e sem resources.requests ele não enxerga nada, e só CPU e memória vêm prontos (o resto exige um adaptador).
Etapa 09 bônus · imagem real do docker hub
Bônus, aplicação profissional com uma imagem real do Docker Hub
O problema
As oito etapas anteriores usaram o nginx de propósito: uma imagem simples, sem ruído, para você aprender a mecânica de cada peça. Mas um deployment real não é feito de peças isoladas. Três perguntas só aparecem quando você junta tudo: como as peças se encaixam em um app de verdade, como testar probes em uma aplicação com endpoints de saúde reais (em vez do YAML ilustrativo da Etapa 07) e quais peças usar, ou deixar de fora, para esse app específico.
O conceito
Esta etapa monta um deployment completo com a imagem stefanprodan/podinfo do Docker Hub (docker.io/stefanprodan/podinfo). O podinfo é um projeto open source pensado para ser usado em demonstrações. Ele expõe /healthz e /readyz de verdade, aceita configuração por variáveis de ambiente (PODINFO_UI_MESSAGE, PODINFO_UI_COLOR) e tem um endpoint /panic que encerra o processo de propósito.
Esse último ponto merece cuidado, porque /panic e liveness testam coisas diferentes. Quando o processo do podinfo termina, quem reinicia o container é o kubelet, pela `restartPolicy` do Pod (caso 1 da Etapa 07). Isso aconteceria mesmo que o manifesto não tivesse nenhuma livenessProbe. A liveness só entra em ação no caso 3, processo vivo que não responde de forma saudável. Por isso este exercício usa o /panic para demonstrar o reinício de um container que caiu, e testa a liveness de verdade apontando a probe para um caminho que não existe, de modo que o endpoint passe a devolver 404 e a probe falhe.
Os quatro manifestos se encaixam pelo label app: podinfo, o mesmo mecanismo que costurou o guia inteiro (Etapa 03):
- O ConfigMap injeta as variáveis via
envFrom(Etapa 04). - O Deployment declara 3 réplicas (Etapa 05),
resources.requests(obrigatório para o HPA, Etapa 08) e duas probes: liveness em/healthze readiness em/readyz(Etapa 07). - O Service é NodePort porque aqui o podinfo é o ponto de entrada do sistema, aplicando a regra de ouro da Etapa 04.
- O HPA lê a CPU declarada no Deployment e escala entre 2 e 8 réplicas (Etapa 08).
Há uma interação entre o Deployment e o HPA que vale conhecer: o Deployment declara replicas: 3, mas o HPA tem minReplicas: 2. Enquanto há alguma carga, ou nos primeiros minutos, você verá 3 réplicas. Sem carga, depois de cerca de 5 minutos (a janela de estabilidade da Etapa 08), o HPA reduz o Deployment para 2. A partir daí, quem manda no número de réplicas é o HPA, e o replicas: 3 do manifesto deixa de valer. Em produção, é comum omitir replicas do Deployment quando existe HPA, justamente para evitar essa disputa.
Também importa o que não entrou. Nem toda peça aprendida cabe em todo app.
- A startupProbe (Etapa 07) ficou de fora porque o podinfo sobe em frações de segundo. Usar uma peça só porque ela existe no manual adiciona configuração sem benefício.
- O PVC (Etapa 06) também ficou fora do Deployment, por um motivo técnico. O problema: o PVC da Etapa 06 usa
ReadWriteOnce, e esse modo não pode ser dividido entre réplicas em nós diferentes. No Minikube (um nó só) colar ovolumes.persistentVolumeClaimem um Deployment com várias réplicas pode até subir sem reclamar, mas num cluster com vários nós as réplicas extras ficam presas, disputando o mesmo disco (o evento aparece comoFailedAttachVolume). As saídas, se o app precisasse de estado: um cache compartilhado de verdade (o podinfo tem a flag--cache-serverpara falar com um Redis) ou um disco por réplica, com um StatefulSet evolumeClaimTemplates, assunto do módulo avançado. A Etapa 06 continua válida: ela é a resposta certa para o cenário dela, um Pod único com dados, como um banco.
app: podinfo, e o HPA passa a mandar no número de réplicas do Deployment.Diagrama: feito para este guiaOs conceitos-chave
ConfigMap com envFrom
configuração externa injetada como variáveis (Etapa 04).
Deployment com 3 réplicas
auto-cura e preparo para rolling update (Etapa 05).
Probes reais
liveness em /healthz, readiness em /readyz (Etapa 07).
restartPolicy
o que reinicia um container cujo processo terminou (/panic), sem relação com a liveness.
Service NodePort
o app é o ponto de entrada, então é exposto (Etapa 04).
HPA entre 2 e 8 réplicas
escala pela CPU declarada em requests e, sem carga, reduz as 3 réplicas iniciais para 2 (Etapa 08).
Tag de versão fixa
usar podinfo:6.14.1 em vez de latest, para que o deploy seja reprodutível.
rollout restart
comando que recria os Pods de um Deployment, necessário para que eles leiam um ConfigMap alterado.
Endpoints
lista de Pods que estão de fato recebendo tráfego de um Service; Pods com readiness falhando saem dela.
Os quatro manifestos e o teste completo
Crie a pasta etapa09/ e salve os quatro manifestos nela.
# configmap.yamlapiVersion: v1kind: ConfigMapmetadata:name: podinfo-configdata:PODINFO_UI_MESSAGE: "olá do pitstop.dev!" # aparece na tela do appPODINFO_UI_COLOR: "#34d399" # cor do banner na UI
É o mesmo mecanismo da Etapa 04 (envFrom), mas aqui as chaves são variáveis que o podinfo realmente lê para montar a própria interface.
Comandos essenciais desta etapa
Mão na massa
Neste exercício você monta o deployment completo, provoca falhas controladas e confirma cada comportamento. Os blocos de comandos de cada passo estão no exemplo acima.
- Salve os quatro YAMLs em
etapa09/(configmap.yaml,deployment.yaml,service.yaml,hpa.yaml) e aplique com um únicokubectl applyapontando para os quatro arquivos. - Rode
kubectl get pods,hpaaté ver os podsRunning(3 no começo) e o HPA com um valor de CPU real (não<unknown>). Se travar, confirme que o metrics-server da Etapa 08 continua ativo. Lembre que, sem carga, o HPA reduzirá as 3 réplicas para 2 em cerca de 5 minutos; isso é esperado, e os passos seguintes funcionam com qualquer um dos dois números. - Rode
minikube service podinfo --url, deixe esse terminal aberto e, em outro, execute o laçofor i in 1 2 3 4 5; do curl -s <URL> | grep hostname; done. Observe mais de um hostname na saída. Se preferir o navegador, saiba que ele costuma reaproveitar a conexão e pode mostrar sempre o mesmo hostname. - Reproduza a pegadinha do ConfigMap: edite a mensagem no
configmap.yaml, aplique comkubectl apply -f configmap.yaml, confirme quekubectl exec ... envainda mostra o valor antigo e só depois rodekubectl rollout restart deployment/podinfo. Confirme a mudança de novo. (Você também pode usarkubectl edit configmap podinfo-config, que abre o editor padrão do sistema, geralmente o vi; editar o arquivo e reaplicar é mais simples e mantém o YAML versionado como fonte da verdade.) - Provoque um crash com
curl .../panice acompanhekubectl get pods -waté ver oRESTARTSsubir sozinho, sem você reiniciar nada. Observe comkubectl describeque o container anterior está emTerminatedcom código de saída diferente de zero e que não há evento de liveness: quem reiniciou foi o kubelet, pelarestartPolicy. - Teste a liveness de verdade: troque o caminho da probe para
/nao-existecomkubectl patch, espere cerca de 30 segundos e observe oRESTARTSsubir (o status pode alternar comCrashLoopBackOffentre uma reinicialização e outra, o que é esperado) e, nodescribe, os eventosLiveness probe failed: HTTP probe failed with statuscode: 404eKilling. Restaure comkubectl apply -f deployment.yamle rodekubectl rollout status deployment/podinfo, esperando a mensagemsuccessfully rolled outantes do passo 7, para que ohead -n 1não escolha um Pod antigo que ainda está terminando. Saiba que esseapplydevolvereplicaspara 3; o HPA reduz de novo para 2 se não houver carga. - Desligue a readiness de um Pod com
curl -X POST .../readyz/disablee, depois de cerca de 15 segundos, confirme comkubectl get pods(READY 0/1,RESTARTSinalterado) ekubectl get endpoints podinfoque aquele Pod saiu da lista, sem ser reiniciado. Com 2 réplicas sobra 1 IP; com 3, sobram 2. - Feche gerando carga como na Etapa 08 (o mesmo gerador busybox contra
http://podinfo:9898) e observe o HPA escalar este Deployment sozinho. - Limpe:
kubectl delete -f configmap.yaml -f deployment.yaml -f service.yaml -f hpa.yaml. Feche o terminal dominikube servicecom Ctrl+C. Se quiser desligar o laboratório,minikube stop.
Dica: A pegadinha do ConfigMap é um erro comum: editar o ConfigMap não reinicia nada sozinho (a menos que exista algo como um Reloader cuidando disso). Em produção, o comando depois de aplicar uma configuração nova quase sempre é kubectl rollout restart deployment/<nome>.
Anota aí: Um manifesto profissional não é a soma de tudo que você aprendeu: é a escolha do que o app precisa (ConfigMap, réplicas, probes reais, Service, HPA) e a recusa consciente do que ele não precisa (startupProbe num app rápido, PVC num Deployment com várias réplicas); e crash de processo é tratado pelo kubelet, enquanto só uma probe que falha prova a liveness.
Etapa 10 labs online
Pratique de graça em clusters reais
Estas plataformas dão um terminal com Kubernetes real direto no navegador, sem instalar nada. Comece pelas 100% gratuitas e passe para as pagas só quando quiser desafios maiores.
Killercoda
100% grátisCenários interativos de Kubernetes direto no navegador: você recebe um terminal com cluster real e um roteiro guiado. Sucessor do Katacoda.
Bom para: Praticar cada etapa deste guia sem instalar nada. Comece por aqui.
killercoda.com/kubernetesPlay with Kubernetes
100% grátisCluster Kubernetes real e temporário (sessões de 4 horas) no navegador, mantido pela Docker. Você monta o cluster na mão com kubeadm.
Bom para: Testar comandos kubectl livremente e entender a montagem de um cluster.
labs.play-with-k8s.comKube by Example
100% grátisTutoriais e exemplos curtos mantidos pela Red Hat: um conceito por página (Pods, Services, Deployments...), com exemplos direto ao ponto.
Bom para: Revisar um conceito específico rapidamente, como uma segunda explicação.
kubebyexample.comIntroduction to Kubernetes (LFS158)
100% grátisCurso oficial gratuito da Linux Foundation (quem mantém o K8s), disponível também no edX. Base teórica sólida com certificado de participação.
Bom para: Consolidar a teoria com o material oficial depois de fechar este guia.
training.linuxfoundation.orgKubernetes the Hard Way
100% grátisO lendário roteiro do Kelsey Hightower: montar um cluster peça por peça, na unha, sem instaladores. É o "monte o carro parafuso por parafuso": quem faz, nunca mais olha a garagem do mesmo jeito.
Bom para: Nível avançado, quando quiser entender o que o Minikube esconde de você.
github.com/kelseyhightowerKodeKloud
Free tier + pagoLabs guiados estilo desafio: "esse Pod não sobe, descubra por quê". Alguns labs são gratuitos; o catálogo completo (e os simulados CKA/CKAD) é pago.
Bom para: Troubleshooting realista, o mais próximo de uma entrevista técnica.
kodekloud.comiximiuz Labs
Free tier + pagoPlaygrounds de containers e Kubernetes no navegador com desafios práticos. Vários playgrounds e desafios liberados no plano gratuito.
Bom para: Experimentos mais profundos: rede, imagens, internals de containers.
labs.iximiuz.comRoteiro sugerido: a cada etapa deste guia, abra o Killercoda e repita os comandos no cenário equivalente (Pods, Deployments, Volumes). Depois das 9 etapas, use o KodeKloud para desafios de troubleshooting.
Etapa 11 · revisão
Checklist de autoavaliação
Etapa 01: Motivação e setup
- Sei explicar a diferença entre container e VM e por que o container é mais leve.
- Subi o Minikube e vi o nó
Readynokubectl get nodes. - Sei o que significa "estado desejado" e consigo dar um exemplo.
Etapa 02: kubectl e API
- Criei um pod imperativo com
kubectl rune o vi chegar aRunning. - Provoquei um
ImagePullBackOffde propósito e encontrei a causa nos Events dodescribe. - Sei recitar o fluxo de debug: get, describe, logs.
- Sei explicar o caminho de um comando: kubeconfig, API Server, resposta.
Etapa 03: Cluster, Pods e labels
- Sei citar o papel de master, worker, etcd, kubelet, kube-proxy e API Server.
- Escrevi um YAML de Pod do zero seguindo apiVersion + kind + metadata + spec.
- Filtrei pods com
-le sei explicar quando usar label e quando usar annotation.
Etapa 04: Services e ConfigMap
- Expus um pod via NodePort e abri a página no navegador com
minikube service --url. - Apaguei o pod, vi o Service ficar sem endpoints (
kubectl get endpoints) e vi o acesso voltar ao recriar o pod. - Sei dizer qual tipo de Service usar para banco interno, teste no Minikube e produção na nuvem.
- Injetei um ConfigMap como variáveis de ambiente, confirmei com
kubectl exec -- enve sei explicar por que mudar o ConfigMap não muda as variáveis de um Pod em execução.
Etapa 05: ReplicaSets e Deployments
- Deletei um pod de um Deployment e vi o substituto nascer sozinho (auto-cura).
- Fiz um rolling update, quebrei um deploy de propósito e voltei com
rollout undo. - Usei
kubectl get rspara ver um ReplicaSet por versão e sei explicar por que o sufixo do nome dos Pods muda numa troca de versão. - Sei explicar por que os pods antigos seguram o serviço quando a imagem nova falha.
Etapa 06: Volumes
- Sei explicar a cadeia PVC (pedido), PV (disco), StorageClass (tipos de disco).
- Escrevi um arquivo num PVC, destruí o pod, recriei e li o arquivo de volta.
- Repeti o teste com
emptyDir(em um Pod de outro nome) e vi o arquivo sumir. - Sei dizer quando usar emptyDir, hostPath e persistentVolumeClaim.
Etapa 07: Probes
- Sei dizer o que acontece quando cada probe falha (liveness, readiness, startup).
- Sei separar os três casos: crash de processo (kubelet reinicia o container), Pod que some (ReplicaSet cria Pod novo) e processo vivo porém travado (só a liveness detecta).
- Rodei o experimento liveness-exec e vi a coluna RESTARTS subir sozinha, com o par
UnhealthyeKillingnos Events. - Sei explicar por que
Runningnão significa saudável.
Etapa 08: HPA
- Sei citar os dois pré-requisitos do HPA (metrics-server e resources.requests).
- Sei dizer quais métricas vêm prontas (CPU e memória) e quais exigem um adaptador.
- Gerei carga e vi o HPA aumentar as réplicas sem nenhum comando meu.
- Sei explicar por que o scale down demora cerca de 5 minutos.
Etapa 09: Aplicação profissional
- Apliquei os quatro manifestos do podinfo e vi as réplicas
Runningcom o HPA lendo a CPU. - Reproduzi a pegadinha do ConfigMap e resolvi com
rollout restart. - Provoquei um crash com
/panice sei explicar que o reinício foi feito pelo kubelet (restartPolicy), não pela liveness. - Provoquei uma falha real de liveness (probe em um caminho inexistente, 404) e vi os eventos
Liveness probe failedeKilling. - Restaurei a probe com
kubectl applye esperei orollout statusterminar antes de seguir. - Tirei um Pod do Service com
/readyz/disablee confirmei comkubectl get endpointseREADY 0/1, sem reinício. - Sei explicar por que o HPA reduz as 3 réplicas para 2 e por que a startupProbe e o PVC ficaram de fora desse Deployment.
Próximos passos: com todos os itens marcados, repita os exercícios em uma das plataformas listadas na seção anterior, desta vez sem consultar o guia. O tema seguinte é o módulo avançado, onde aparece o StatefulSet citado na Etapa 09.
Notas do professor: precisões técnicas
- Este bloco reúne detalhes que ajudam a ler os exemplos com segurança. Nada aqui muda o caminho dos exercícios; são esclarecimentos sobre comportamentos que costumam gerar dúvida.
- Sobre o que você vai ver no terminal
- Saídas de exemplo. As saídas mostradas são formatos típicos. Nomes de pods, hashes de ReplicaSet, IPs, idades e colunas variam conforme a versão do Kubernetes e do ambiente. Compare a estrutura, não o texto exato. O histórico de
rollout historye as saídas dekubectl topsão aproximados, e as saídas deget pods -wsão abreviadas: no ambiente real, as linhas aparecem com intervalos maiores do que o exemplo sugere. - Caminho da API (Etapa 02).
kubectl get podsno namespacedefaultfazGET /api/v1/namespaces/default/pods. O caminho/api/v1/podslista pods de todos os namespaces. O-v=6mostra a requisição real, e o formato da linha de log varia entre versões do kubectl. - Versão do kubectl antes do cluster (Etapa 01).
kubectl versionsem--clienttenta consultar o servidor. Antes dominikube startisso termina em erro de conexão, o que é esperado e não indica instalação defeituosa. - `kubectl get endpoints` (Etapas 04 e 09). Em versões recentes do Kubernetes, o comando pode exibir um aviso de depreciação em favor de EndpointSlices. Ele continua útil para os exercícios; a alternativa é
kubectl get endpointslices. - Sobre comportamentos que surpreendem
- Rollback (Etapa 05).
rollout undovolta para a revisão imediatamente anterior, não para a original. No exemplo, restauranginx:1.16.1, nãonginx:1.14.2. A revisão restaurada recebe um número novo no histórico e reaproveita o ReplicaSet dela. - `kubectl delete pvc` (Etapa 06). Com a StorageClass
standarddo Minikube, a política de reclaim costuma serDelete: apagar o PVC apaga o PV e os dados. Confira a política do seu cluster comkubectl get storageclass. Em ambientes de produção, essa política é uma decisão importante. - Alterar um Pod existente (Etapa 06). O
specde um Pod não pode ser modificado depois de criado (com poucas exceções, como a imagem). Para trocar o tipo de volume, crie um Pod com outro nome ou apague e recrie o Pod. - Tempo do restart (Etapa 07). No experimento do liveness-exec, a primeira reinicialização ocorre por volta de 45 segundos (30s saudável mais 3 falhas espaçadas de 5s). Os ciclos seguintes podem demorar mais, porque o kubelet aguarda o container encerrar (até 30s por padrão, o
terminationGracePeriodSeconds) antes de recriá-lo. - Quem reinicia o quê (Etapas 07 e 09). O kubelet reinicia containers cujo processo terminou, por causa da
restartPolicy, com ou sem probe. A liveness probe existe para o caso em que o processo continua vivo mas não responde. Um endpoint que derruba o processo (como o/panicdo podinfo) demonstra o primeiro caso, não o segundo. - Métricas do HPA (Etapa 08). O HPA lê nativamente CPU e memória, via metrics-server. Requisições por segundo, métricas do Prometheus, E/S de disco e qualquer outra métrica exigem um adaptador de métricas customizadas ou externas instalado no cluster.
- HPA e `replicas` (Etapas 08 e 09). Com um HPA ativo, o número de réplicas é decidido por ele dentro de
minReplicasemaxReplicas. Oreplicasdo Deployment funciona como ponto de partida e é ajustado pelo HPA, o que explica as 3 réplicas virarem 2 na Etapa 09. Reaplicar o manifesto (kubectl apply) devolvereplicasao valor escrito nele, e o HPA volta a ajustar depois da janela de estabilidade. - Balanceamento do Service (Etapa 09). O balanceamento é por conexão e a escolha da réplica é aleatória. Clientes que reaproveitam a conexão (como um navegador) tendem a falar sempre com a mesma réplica.
- startupProbe (Etapa 07). A forma mais comum de dar tempo a um app lento é
failureThresholdmultiplicado porperiodSeconds, em vez de uminitialDelaySecondsfixo. O exemplo cominitialDelaySeconds: 120é válido e foi mantido por fidelidade ao material de origem. - Terminologia e pontos a verificar
- Master node e control plane. O material das aulas usa "Master node". A documentação atual do Kubernetes prefere control plane, que é o que aparece na coluna ROLES do
kubectl get nodesna Etapa 01. Os dois nomes se referem à mesma coisa. - Tag da imagem (Etapa 09). A tag
stefanprodan/podinfo:6.14.1vem do material do guia. Confirme no Docker Hub que ela existe antes de executar; se não existir, use uma tag publicada mais recente. A tag fixa, em vez delatest, é o ponto didático e permanece. - Manifestos de apoio. Os arquivos
liveness-exec.yaml(Etapa 07),service.yamldo HPA (Etapa 08) epod-emptydir.yaml(Etapa 06) são complementos de apoio aos exercícios, no padrão da documentação oficial do Kubernetes.