Introdução
Agora você já sabe implantar, expor e dimensionar uma aplicação. A próxima questão operacional é: como substituir a versão da aplicação sem deixar todo o serviço indisponível?
Um Deployment do Kubernetes resolve isso com uma atualização contínua. Quando o modelo de Pod é alterado, o Deployment cria um novo ReplicaSet, inicia gradualmente novos Pods e remove os antigos apenas quando os substitutos ficam disponíveis. Durante todo o processo, o Service mantém o mesmo endereço estável.
Neste laboratório, você moverá uma aplicação NGINX de uma imagem fixada para outra, relacionará as revisões do Deployment aos ReplicaSets e Pods, provocará deliberadamente uma publicação com erro e fará a reversão para a última revisão saudável. Você também manterá o manifesto salvo consistente com o estado ativo recuperado — uma prática importante para evitar que um kubectl apply posterior reintroduza a falha.
Ler o ambiente inicial
Inicialização do ambiente: Este laboratório inicia um cluster Kubernetes completo para você. A configuração do plano de controle, do nó e dos componentes de rede normalmente leva 2–3 minutos. Aguarde pacientemente até que o ambiente termine de carregar antes de começar.
Nesta etapa, você confirmará a conexão com o cluster e preparará um cliente HTTP dentro do cluster.
O ambiente deste curso já contém um cluster Kubernetes em execução, portanto não é necessário criar um. Primeiro, verifique para onde o kubectl enviará os comandos. config current-context identifica a conexão ativa, get node consulta a saúde do nó e version informa as versões do cliente local e do servidor de API acessível:
kubectl config current-context
kubectl get node
kubectl version
O contexto e o nó são chamados labex-v135, o nó está Ready e o servidor informa o Kubernetes v1.35.x. Um contexto é a seleção no kubeconfig que conecta o kubectl a um determinado cluster, usuário e namespace padrão.
Crie agora um pequeno Pod cliente. Você o usará mais tarde para acessar a aplicação por meio do Service. kubectl run cria um Pod; --image escolhe o BusyBox, --restart=Never mantém o recurso como um Pod independente e o separador -- introduz o comando de longa duração sleep 3600. Em seguida, kubectl wait aguarda por no máximo 30 segundos pela condição Ready:
kubectl run release-client --image=busybox:1.36 --image-pull-policy=IfNotPresent \
--restart=Never -- sleep 3600
kubectl wait --for=condition=Ready pod/release-client --timeout=30s
Assim, o cliente fica separado dos Pods web, reproduzindo melhor a forma como uma carga de trabalho acessa outra dentro de um cluster.
Implantar a base estável
Nesta etapa, você estabelecerá uma revisão conhecida e saudável da aplicação antes de fazer qualquer alteração.
Antes de praticar uma atualização, estabeleça uma revisão confiável. Crie um manifesto contendo um Deployment com três réplicas e um Service ClusterIP estável.
cd entra no diretório de trabalho preparado. A sintaxe de documento incorporado cat <<'EOF' > release-web.yaml grava no arquivo tudo o que aparece até o EOF de fechamento. A linha --- separa dois objetos do Kubernetes no mesmo arquivo YAML:
cd /home/labex/project/update-lab
cat <<'EOF' > release-web.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: release-web
annotations:
kubernetes.io/change-cause: "Initial release: nginx 1.26"
spec:
replicas: 3
strategy:
type: RollingUpdate
selector:
matchLabels:
app: release-web
template:
metadata:
labels:
app: release-web
spec:
containers:
- name: nginx
image: nginx:1.26-alpine
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: release-web
spec:
selector:
app: release-web
ports:
- name: http
port: 80
targetPort: http
EOF
kubectl apply -f release-web.yaml
kubectl rollout status deployment/release-web --timeout=60s
kubectl get deployment,service,pods -l app=release-web
O Deployment gerencia os Pods, enquanto o Service os seleciona de forma independente por meio do rótulo. Atualizar o Deployment não alterará o endereço do Service.
Confirme a base a partir do cliente. Em kubectl exec POD -- COMMAND, -- separa os argumentos do kubectl do comando executado dentro do contêiner. wget -qO- faz a requisição silenciosamente e envia a resposta para a saída padrão; o pipe encaminha essa resposta para head, que exibe apenas o início:
kubectl exec release-client -- wget -qO- http://release-web | head
Publicar uma nova imagem de forma declarativa
Nesta etapa, você alterará o modelo de Pod salvo e permitirá que o Deployment execute uma atualização contínua.
Um Deployment inicia uma atualização quando seu modelo de Pod é alterado. Como a imagem está dentro desse modelo, modificá-la cria uma nova revisão e um novo ReplicaSet.
Atualize a imagem e também a descrição legível da alteração no manifesto salvo. Cada substituição sed -i 's/old/new/' file edita o arquivo diretamente. Em seguida, grep -nE exibe os números das linhas que contêm qualquer um dos termos da expressão regular estendida (change-cause ou image:), permitindo conferir as duas alterações antes de aplicá-las:
cd /home/labex/project/update-lab
sed -i 's/Initial release: nginx 1.26/Release nginx 1.27/' release-web.yaml
sed -i 's/nginx:1.26-alpine/nginx:1.27-alpine/' release-web.yaml
grep -nE 'change-cause|image:' release-web.yaml
kubectl apply -f release-web.yaml
kubectl rollout status deployment/release-web --timeout=60s
kubectl apply altera o estado desejado. Em seguida, o controlador do Deployment trabalha de forma assíncrona até que as três réplicas atualizadas estejam disponíveis. Para obter uma tabela concentrada dos Pods, -l seleciona o rótulo da aplicação e -o custom-columns associa cabeçalhos a caminhos de campos dos objetos:
kubectl get deployment release-web
kubectl get pods -l app=release-web \
-o custom-columns='NAME:.metadata.name,IMAGE:.spec.containers[0].image,READY:.status.containerStatuses[0].ready'
As três réplicas atuais usam a nova imagem. Após a conclusão da atualização, talvez você ainda veja brevemente um Pod com a imagem antiga no estado Terminating. Esse Pod já não é uma réplica desejada; o Kubernetes está apenas concluindo seu encerramento gracioso em segundo plano.
Relacionar revisões, ReplicaSets e Pods
Nesta etapa, você inspecionará os objetos por trás da atualização bem-sucedida e confirmará que o Service continuou disponível.
A atualização terminou, mas entender o que mudou é mais útil do que ver apenas “sucesso”. kubectl rollout history consulta as revisões armazenadas do Deployment. O comando get replicasets a seguir usa -l para manter apenas os objetos relacionados e custom-columns para comparar suas contagens de réplicas e imagens:
kubectl rollout history deployment/release-web
kubectl get replicasets -l app=release-web \
-o custom-columns='NAME:.metadata.name,DESIRED:.spec.replicas,CURRENT:.status.replicas,READY:.status.readyReplicas,IMAGE:.spec.template.spec.containers[0].image'
Você deverá ver dois ReplicaSets. O novo possui três Pods; o antigo permanece com zero réplicas para que seu modelo de Pod fique disponível para uma reversão. O nome de um ReplicaSet inclui um hash derivado do modelo de Pod, por isso os nomes dos Pods mudaram quando a imagem foi alterada.
Verifique a imagem ativa e a quantidade de backends do Service. As duas expressões JSONPath usam range para repetir um modelo de saída sobre uma lista. Os espaços literais e {"\n"} produzem uma linha legível para cada Pod ou endpoint:
kubectl get pods -l app=release-web \
-o jsonpath='{range .items[*]}{.metadata.name}{" "}{.spec.containers[0].image}{"\n"}{end}'
kubectl get endpointslices -l kubernetes.io/service-name=release-web \
-o jsonpath='{range .items[*].endpoints[*]}{.addresses[0]}{" ready="}{.conditions.ready}{"\n"}{end}'
kubectl exec release-client -- wget -qO- http://release-web | head
A revisão mudou, mas o Service continua com três backends prontos e o mesmo nome estável.
Diagnosticar uma publicação com erro
Nesta etapa, você introduzirá um erro controlado na imagem e usará os eventos do Pod para identificar por que a atualização não consegue ser concluída.
Agora simule um erro comum de publicação: uma tag de imagem de contêiner que não existe. Mantenha essa falha no mesmo Deployment para que o mecanismo de atualização demonstre uma importante propriedade de segurança. Os dois comandos sed -i editam diretamente o texto da anotação e da imagem. Normalmente, o tempo limite esperado da atualização retornaria um status de falha; || true permite deliberadamente que a lição continue:
cd /home/labex/project/update-lab
sed -i 's/Release nginx 1.27/Broken release: missing image/' release-web.yaml
sed -i 's/nginx:1.27-alpine/nginx:does-not-exist-course/' release-web.yaml
kubectl apply -f release-web.yaml
kubectl rollout status deployment/release-web --timeout=20s || true
kubectl get deployment release-web
kubectl get pods -l app=release-web
A atualização não termina porque um novo Pod não consegue obter sua imagem. O || true permite que a lição continue após o tempo limite esperado.
Encontre o Pod com falha e inspecione seus eventos. $(...) armazena a saída do comando em BAD_POD. O primeiro pipe envia o JSON do Kubernetes para o jq; select(...) mantém o Pod que usa a imagem com erro e -r retorna seu nome como texto simples. Um segundo pipe para head -n1 mantém apenas um nome. Por fim, sed -n '/Events:/,$p' exibe a saída de describe desde Events: até o final:
BAD_POD=$(kubectl get pods -l app=release-web \
-o json | jq -r '.items[] | select(.spec.containers[0].image == "nginx:does-not-exist-course") | .metadata.name' | head -n1)
echo "$BAD_POD"
kubectl describe pod "$BAD_POD" | sed -n '/Events:/,$p'
kubectl get replicasets -l app=release-web
Procure ErrImagePull ou ImagePullBackOff. Observe que o ReplicaSet saudável anterior ainda possui Pods disponíveis, portanto o Service pode continuar respondendo:
kubectl exec release-client -- wget -qO- http://release-web | head
Isso demonstra disponibilidade durante uma atualização malsucedida — não prova que a nova publicação funciona.
Reverter e reconciliar o manifesto
Nesta etapa, você restaurará a revisão saudável anterior e corrigirá o manifesto salvo.
O Deployment ativo mantém um histórico de revisões, permitindo que o Kubernetes restaure o modelo de Pod anterior. rollout undo solicita ao controlador do Deployment que reutilize a revisão anterior; rollout status aguarda essa recuperação, e custom-columns exibe a imagem recuperada e a quantidade de réplicas prontas:
kubectl rollout history deployment/release-web
kubectl rollout undo deployment/release-web
kubectl rollout status deployment/release-web --timeout=60s
kubectl get deployment release-web \
-o custom-columns='NAME:.metadata.name,IMAGE:.spec.template.spec.containers[0].image,READY:.status.readyReplicas'
A imagem voltou a estar saudável e as três réplicas estão prontas. rollout undo cria uma nova revisão a partir de um modelo de Pod antigo; ele não retrocede o contador de revisões.
Ainda falta uma tarefa. O arquivo YAML continua contendo a imagem com erro, portanto um kubectl apply futuro quebraria o Deployment novamente. Reconcilie o estado desejado salvo com o estado ativo recuperado. As substituições corrigem o arquivo, apply o sincroniza e o comando final de histórico confirma a sequência resultante de revisões:
cd /home/labex/project/update-lab
sed -i 's/Broken release: missing image/Rollback to nginx 1.27/' release-web.yaml
sed -i 's/nginx:does-not-exist-course/nginx:1.27-alpine/' release-web.yaml
kubectl apply -f release-web.yaml
kubectl rollout status deployment/release-web --timeout=60s
grep -nE 'change-cause|image:' release-web.yaml
kubectl rollout history deployment/release-web
Agora o cluster e o arquivo descrevem a mesma publicação saudável.
Escolher um limite de atualização mais seguro
Nesta etapa, você tornará explícitos os limites de capacidade e disponibilidade da atualização do Deployment.
As configurações da estratégia do Deployment controlam a capacidade temporária permitida durante futuras atualizações:
maxUnavailableindica quantas réplicas desejadas podem ficar indisponíveis durante uma atualização.maxSurgeindica quantos Pods extras podem existir temporariamente acima da quantidade desejada de réplicas.
Para esta aplicação pequena, com três réplicas, exija que as três réplicas desejadas permaneçam disponíveis e permita um Pod extra.
O comando sed usa um endereço, /type: RollingUpdate/, para localizar a linha da estratégia. A ação a\ acrescenta o YAML das linhas seguintes, com a indentação necessária. Depois da aplicação, o JSONPath extrai os dois valores da estratégia para que você possa verificar a alteração sem examinar o objeto inteiro:
cd /home/labex/project/update-lab
sed -i '/type: RollingUpdate/a\ rollingUpdate:\n maxUnavailable: 0\n maxSurge: 1' release-web.yaml
kubectl apply -f release-web.yaml
kubectl get deployment release-web \
-o jsonpath='maxUnavailable={.spec.strategy.rollingUpdate.maxUnavailable}{"\n"}maxSurge={.spec.strategy.rollingUpdate.maxSurge}{"\n"}'
kubectl get deployment release-web
Alterar apenas os campos da estratégia não substitui os Pods, pois o modelo de Pod não foi modificado. Em uma futura atualização da imagem, o Kubernetes poderá criar um Pod extra e não deverá reduzir intencionalmente o número de réplicas disponíveis para menos de três.
Essa configuração prioriza a disponibilidade, mas exige capacidade livre no cluster. Não existe um valor universalmente ideal: aplicações maiores costumam usar porcentagens, e as escolhas reais dependem da capacidade disponível, do tempo de inicialização e da interrupção aceitável.
Resumo
Você acompanhou uma publicação de aplicação durante todo o seu ciclo operacional:
- estabeleceu uma base saudável com um Deployment e um Service estável;
- alterou uma imagem fixada de forma declarativa e aguardou a atualização;
- relacionou as revisões do Deployment aos ReplicaSets antigo e novo;
- diagnosticou uma falha ao obter a imagem enquanto a publicação anterior continuava atendendo às requisições;
- reverteu para o último modelo de Pod saudável;
- reconciliou o manifesto para que uma aplicação posterior não restaure a imagem inválida; e
- configurou limites explícitos de disponibilidade e capacidade adicional para futuras atualizações.
A ideia central é que um Deployment gerencia o estado desejado ao longo do tempo. Uma atualização não é simplesmente editar uma imagem: é uma transição controlada entre ReplicaSets, que deve ser observada, verificada e, se necessário, revertida.


