Implantar aplicações no Kubernetes

KubernetesBeginner
Pratique Agora

Introdução

Na primeira seção do curso, você explorou um cluster Kubernetes sem modificá-lo. Aprendeu que o kubectl envia solicitações ao servidor de API e que os controladores do Kubernetes comparam continuamente o estado desejado com o estado atual.

Agora você fará suas primeiras solicitações para executar uma aplicação. Em vez de informar ao Kubernetes cada ação de baixo nível que deve ser realizada, você descreverá o resultado desejado em arquivos YAML chamados manifestos. O Kubernetes armazena as definições desses objetos e trabalha para concretizá-las.

Você começará com um único Pod, para que seja fácil visualizar a estrutura básica de um manifesto. Em seguida, definirá um Deployment que gerencia dois Pods. A comparação entre um Pod independente e Pods gerenciados por um Deployment mostrará por que os controladores de nível mais alto normalmente são preferíveis para aplicações.

Este laboratório se concentra deliberadamente na criação de cargas de trabalho. Mais adiante, você aprenderá a expor aplicações por meio de Services, depois que Pods, labels e Deployments já forem conceitos familiares.

Entender objetos declarativos do Kubernetes

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ê relacionará o conceito de estado desejado do laboratório anterior aos manifestos do Kubernetes e preparará um espaço de trabalho para suas primeiras definições de aplicações.

Dos comandos ao estado desejado

O Kubernetes oferece dois estilos gerais de gerenciamento:

  • Com um comando imperativo, você solicita diretamente uma ação, como “criar um Pod chamado first-nginx”.
  • Com um manifesto declarativo, você salva a configuração desejada do objeto em um arquivo e solicita ao Kubernetes que faça o cluster corresponder a ela.

Os arquivos declarativos são valiosos porque podem ser lidos antes de uma alteração, aplicados repetidamente, comparados para identificar diferenças e armazenados em um sistema de controle de versão. Este curso dará prioridade ao estilo declarativo.

Todo objeto do Kubernetes retornado pela API possui campos importantes no nível superior:

  • apiVersion seleciona o grupo e a versão da API do Kubernetes.
  • kind identifica o tipo de objeto, como Pod ou Deployment.
  • metadata informa a identidade do objeto, incluindo seu nome e seus labels.
  • spec descreve o estado desejado desse objeto.
  • status relata o estado observado e normalmente é preenchido pelo Kubernetes após a criação, não escrito no seu manifesto.

Isso cria um ciclo importante:

manifest spec -> API server stores desired state -> controllers act -> object status reports actual state

Preparar o diretório de manifestos

Acesse o diretório preparado para este laboratório:

cd /home/labex/project/k8s-manifests

Confirme sua localização com pwd, que significa print working directory:

pwd
/home/labex/project/k8s-manifests

Crie um pequeno arquivo de anotações registrando os quatro campos de manifesto que você usará. O comando printf escreve cada string entre aspas em uma linha separada, e > redireciona essa saída para um arquivo, substituindo-o caso já exista.

printf '%s\n' apiVersion kind metadata spec > manifest-fields.txt

Exiba o arquivo para verificá-lo:

cat manifest-fields.txt
apiVersion
kind
metadata
spec

Esse arquivo de anotações funciona como uma pequena verificação de aprendizagem: os quatro campos aparecerão nos dois manifestos que você criará a seguir.

Escrever e validar um manifesto de Pod

Nesta etapa, você escreverá um manifesto YAML para um Pod e validará sua estrutura antes de enviá-lo ao cluster.

Conhecer o Pod

Um Pod é o menor objeto implantável do Kubernetes. Um Pod oferece a um ou mais contêineres estreitamente relacionados uma identidade de rede e um contexto de armazenamento compartilhados. Na maioria dos exemplos introdutórios, cada Pod contém um único contêiner.

O Pod deste laboratório executará o NGINX, um servidor web leve. A imagem está fixada em nginx:1.27-alpine. Fixar uma versão torna o resultado mais reproduzível do que usar a tag variável latest. A imagem já foi armazenada em cache no cluster, portanto o trabalho não depende de um download da internet.

Criar o arquivo YAML

Verifique se você está no diretório de manifestos:

cd /home/labex/project/k8s-manifests

Você usará um here-document para criar o arquivo. O shell redireciona todas as linhas entre <<'EOF' e o EOF de fechamento para first-pod.yaml. Colocar o primeiro EOF entre aspas impede que o shell expanda caracteres especiais dentro do YAML.

cat <<'EOF' > first-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: first-nginx
  namespace: default
  labels:
    app: first-nginx
spec:
  containers:
    - name: nginx
      image: nginx:1.27-alpine
      imagePullPolicy: IfNotPresent
      ports:
        - name: http
          containerPort: 80
          protocol: TCP
EOF

O YAML representa a hierarquia por meio da indentação. Use espaços de forma consistente; tabulações podem tornar o YAML inválido. Um hífen, como em - name: nginx, inicia um item de uma lista.

Leia o objeto de cima para baixo:

  • apiVersion: v1 seleciona a API principal usada pelos Pods.
  • kind: Pod declara o tipo do recurso.
  • metadata.name atribui ao Pod o nome estável first-nginx.
  • metadata.namespace: default coloca o Pod no namespace comum de aplicações do curso, em vez de um namespace do sistema.
  • metadata.labels associa app=first-nginx, que poderá ser usado posteriormente para selecionar este Pod.
  • spec.containers é uma lista dos contêineres que o Pod deve executar.
  • imagePullPolicy: IfNotPresent usa a imagem armazenada em cache quando ela estiver disponível.
  • A porta nomeada http usa containerPort: 80 e protocol: TCP. Ela documenta onde o NGINX escuta dentro do contêiner; não expõe o Pod para fora do cluster.

Validar antes de criar

Use uma execução simulada no lado do cliente para analisar o arquivo sem criar o Pod. A opção -f significa file, e --dry-run=client mantém a solicitação local:

kubectl apply --dry-run=client -f first-pod.yaml
pod/first-nginx created (dry run)

As palavras dry run são essenciais: a sintaxe é válida, mas o cluster ainda não foi alterado.

Peça ao kubectl que exiba o objeto normalizado em YAML:

A opção de saída -o significa output format. Ao fornecer yaml, você solicita que o kubectl renderize o objeto analisado como YAML, em vez de imprimir apenas uma linha de resultado:

kubectl apply --dry-run=client -f first-pod.yaml -o yaml

Você verá seus campos, além dos valores padrão adicionados pelo cliente. Isso é útil para detectar problemas de indentação, nomes de campos e tipos antes de realizar uma aplicação de verdade.

Criar e inspecionar seu primeiro Pod

Nesta etapa, você aplicará o manifesto validado, observará o Kubernetes conduzir o Pod em direção ao estado desejado e inspecionará o objeto resultante.

Aplicar o manifesto

Acesse o diretório de manifestos, se necessário:

cd /home/labex/project/k8s-manifests

Aplique o arquivo sem a opção de execução simulada:

kubectl apply -f first-pod.yaml
pod/first-nginx created

O kubectl apply envia o objeto ao servidor de API. O servidor de API armazena a especificação desejada do Pod, enquanto o agendador e o kubelet trabalham juntos para executá-lo no nó.

Aguardar a disponibilidade

A criação de um Pod é assíncrona: o kubectl apply pode retornar antes que o contêiner esteja pronto. Use kubectl wait para aguardar a condição Ready do Pod. O comando termina com sucesso quando a condição se torna verdadeira ou falha após 60 segundos:

kubectl wait --for=condition=Ready pod/first-nginx --timeout=60s
pod/first-nginx condition met

Agora liste o Pod. Este laboratório repete -o wide porque cada laboratório deve poder ser usado de forma independente: -o seleciona um formato de saída, e wide acrescenta campos como o IP do Pod e o nome do nó:

kubectl get pod first-nginx -o wide
NAME          READY   STATUS    RESTARTS   AGE   IP           NODE
first-nginx   1/1     Running   ...        ...   ...          labex-v135

READY=1/1 significa que o único contêiner está pronto, enquanto STATUS=Running representa a fase do Pod. A visualização ampla também mostra o IP do Pod e o nó ao qual ele foi atribuído. Os IPs e as idades dos Pods são valores gerados, portanto podem variar.

Inspecionar labels e propriedade

Exiba os labels do Pod:

kubectl get pod first-nginx --show-labels

Procure por app=first-nginx. Os labels são armazenados junto com o objeto e se tornarão importantes quando Deployments e Services selecionarem Pods.

Pergunte ao Kubernetes se outro objeto controla este Pod. -o jsonpath='...' extrai campos específicos em vez de imprimir o objeto inteiro. A expressão percorre metadata.ownerReferences; {"\n"} adiciona uma nova linha final para que o prompt do shell apareça na linha seguinte:

kubectl get pod first-nginx -o jsonpath='Owner: {.metadata.ownerReferences[0].kind}{"\n"}'
Owner:

O proprietário está vazio porque você criou diretamente este Pod independente. Se ele for excluído, nenhum controlador de nível superior saberá que deve substituí-lo. Você fará essa comparação com Pods gerenciados nas próximas etapas.

Verificação da etapa

Você transformou um arquivo local de estado desejado em um objeto Kubernetes em execução. A API aceitou o Pod, o agendador o atribuiu a um nó e o kubelet deixou o contêiner pronto. O Pod existe, mas nenhum controlador gerencia seu ciclo de vida.

Definir um Deployment

Nesta etapa, você definirá um Deployment que solicitará ao Kubernetes a manutenção de duas cópias de um Pod NGINX.

Por que usar um Deployment?

Um Pod independente é útil para aprendizagem, mas as aplicações geralmente precisam de um controlador. Um Deployment fornece uma quantidade desejada de réplicas e um modelo de Pod. Ele cria um ReplicaSet, que mantém a quantidade solicitada de Pods.

A cadeia de propriedade é:

Deployment -> ReplicaSet -> Pods -> containers

Se um Pod gerenciado desaparecer, o ReplicaSet perceberá que a quantidade atual de réplicas está abaixo da quantidade desejada e criará uma substituição. Em laboratórios posteriores, você usará Deployments para escalabilidade e atualizações graduais.

Criar o manifesto do Deployment

Retorne ao diretório de manifestos:

cd /home/labex/project/k8s-manifests

Crie course-web-deployment.yaml com um here-document:

cat <<'EOF' > course-web-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: course-web
  labels:
    app: course-web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: course-web
  template:
    metadata:
      labels:
        app: course-web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27-alpine
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
EOF

O Deployment usa apps/v1, a API estável para Deployments. Seu spec apresenta três campos importantes:

  • replicas: 2 é a quantidade desejada de Pods.
  • selector.matchLabels identifica os Pods gerenciados pelo Deployment.
  • template é o modelo usado para criar cada Pod.

O seletor e template.metadata.labels usam ambos app: course-web. Eles precisam corresponder; caso contrário, o Deployment não conseguiria identificar os Pods criados a partir de seu próprio modelo.

Validar o Deployment

Analise o manifesto sem alterar o cluster:

kubectl apply --dry-run=client -f course-web-deployment.yaml
deployment.apps/course-web created (dry run)

Use kubectl diff para comparar o manifesto com o estado atual. Um código de saída diferente de zero simplesmente significa que o objeto seria alterado; || true impede que o shell trate essa diferença esperada como uma falha:

kubectl diff -f course-web-deployment.yaml || true

Como course-web ainda não existe, a saída mostra o objeto completo como uma adição, com linhas iniciadas por +. Diferentemente de apply, diff não altera o cluster.

Implantar e inspecionar a aplicação gerenciada

Nesta etapa, você aplicará o Deployment, aguardará suas duas réplicas e inspecionará os recursos relacionados selecionados pelo label compartilhado.

Aplicar e aguardar o Deployment

Primeiro, acesse o diretório que contém o manifesto. Em seguida, aplique o estado desejado salvo; -f informa ao kubectl que ele deve ler a partir desse arquivo:

cd /home/labex/project/k8s-manifests
kubectl apply -f course-web-deployment.yaml
deployment.apps/course-web created

Aguarde a conclusão do rollout do Deployment. Um rollout é o processo de levar os Pods do Deployment ao modelo e à quantidade de réplicas desejados:

kubectl rollout status deployment/course-web --timeout=60s
deployment "course-web" successfully rolled out

Liste o Deployment:

kubectl get deployment course-web
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
course-web   2/2     2            2           ...

READY=2/2 significa que as duas réplicas desejadas estão prontas. UP-TO-DATE=2 significa que ambas usam o modelo de Pod atual, e AVAILABLE=2 significa que as duas estão disponíveis.

Inspecionar os recursos relacionados

Use o seletor de labels -l app=course-web para listar os recursos relacionados:

kubectl get deployment,replicaset,pods -l app=course-web

A saída contém um Deployment, um ReplicaSet e dois Pods. Os sufixos gerados do ReplicaSet e dos Pods variarão:

NAME                         READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/course-web   2/2     2            2           ...

NAME                                    DESIRED   CURRENT   READY   AGE
replicaset.apps/course-web-...          2         2         2       ...

NAME                              READY   STATUS    RESTARTS   AGE
pod/course-web-...-...            1/1     Running   ...        ...
pod/course-web-...-...            1/1     Running   ...        ...

Essa visualização confirma que as duas réplicas desejadas do Deployment se transformaram em dois Pods prontos. Na próxima etapa, você rastreará as relações de propriedade entre esses recursos.

Verificação da etapa

Você aplicou um manifesto de Deployment e aguardou que seu estado desejado fosse alcançado. O Kubernetes criou um ReplicaSet e dois Pods, e o label compartilhado app=course-web permitiu listá-los como um único grupo de aplicação.

Rastrear a propriedade dos controladores

Nesta etapa, você seguirá as referências de proprietário do Kubernetes de um Pod gerenciado até seu ReplicaSet e, depois, até o Deployment. Você também reaplicará o manifesto para observar a idempotência declarativa.

Inspecionar o proprietário de um Pod

Armazene o nome de um Pod gerado em uma variável do shell. A sintaxe NAME=$(command) é uma substituição de comando: o shell executa o comando e salva sua saída em NAME. Aqui, -l app=course-web seleciona os Pods correspondentes, e o JSONPath extrai o nome gerado do primeiro Pod:

POD_NAME=$(kubectl get pods -l app=course-web -o jsonpath='{.items[0].metadata.name}')

Imprima o nome para saber qual Pod foi selecionado:

echo "$POD_NAME"

Agora inspecione seu proprietário direto. Colocar "$POD_NAME" entre aspas passa o nome armazenado como um único argumento seguro para o comando:

kubectl get pod "$POD_NAME" -o jsonpath='Owner: {.metadata.ownerReferences[0].kind}/{.metadata.ownerReferences[0].name}{"\n"}'
Owner: ReplicaSet/course-web-...

Ao contrário do Pod independente first-nginx, um Pod gerenciado por um Deployment tem um ReplicaSet como proprietário. O próprio ReplicaSet pertence ao Deployment.

Exiba o proprietário do ReplicaSet. O primeiro comando repete o mesmo padrão de substituição de comando, desta vez armazenando um nome de ReplicaSet em RS_NAME:

RS_NAME=$(kubectl get replicaset -l app=course-web -o jsonpath='{.items[0].metadata.name}')
kubectl get replicaset "$RS_NAME" -o jsonpath='Owner: {.metadata.ownerReferences[0].kind}/{.metadata.ownerReferences[0].name}{"\n"}'
Owner: Deployment/course-web

Reaplicar o estado desejado

Aplique o mesmo manifesto novamente:

kubectl apply -f course-web-deployment.yaml
deployment.apps/course-web unchanged

unchanged demonstra uma propriedade declarativa importante: aplicar repetidamente o mesmo estado desejado é seguro. O Kubernetes só precisa agir quando a configuração desejada e a configuração atual são diferentes.

Verificação da etapa

Agora você tem um Pod independente e uma aplicação gerenciada por um Deployment. Ambos executam contêineres, mas o Deployment acrescenta uma hierarquia de controladores que mantém duas réplicas e serve de base para futuras operações de escalabilidade e atualizações graduais.

Resumo

Você passou da exploração somente leitura do cluster ao gerenciamento declarativo de aplicações. Aprendeu as funções de apiVersion, kind, metadata e spec; validou manifestos com execuções simuladas no lado do cliente; criou e inspecionou um Pod independente; e implantou duas réplicas gerenciadas por um Deployment.

Mais importante, você observou a cadeia de propriedade dos controladores, do Deployment ao ReplicaSet e deste aos Pods. Essa base de estado desejado prepara você para diagnosticar aplicações, expô-las por meio de Services, dimensionar réplicas e realizar atualizações graduais nas próximas seções do curso.