Dia 29: Kubernetes Básico no Linux — Pods, Deployments e kubectl

Chegámos ao penúltimo dia do curso Linux 30 Dias. Depois de aprender Docker no Dia 22 e contentorização, damos o salto para a orquestração de contentores. O Kubernetes é hoje a plataforma de facto para gerir aplicações contentorizadas em escala — mas para um sysadmin que vem de systemd e shell, a curva pode parecer íngreme. Este artigo cobre os conceitos essenciais, a instalação do kubectl, Pods, Deployments, Services, ConfigMaps, Secrets e os erros mais comuns que vão aparecer no primeiro contacto.

Audiência: sysadmins Linux que dominam Docker e querem começar a orquestrar contentores. Vamos usar o K3s — uma distribuição Kubernetes leve e ideal para laboratórios — mas todos os conceitos aplicam-se a qualquer cluster.

1. Introdução

O Kubernetes (pronuncia-se koo-ber-NET-ees) é um orquestrador de contentores código aberto, originalmente desenvolvido pelo Google e hoje mantido pela CNCF. O nome vem do grego e significa timoneiro — aquele que governa o leme do navio. A metáfora é apropriada: o Kubernetes assume o leme de uma frota de contentores, decidindo onde cada um corre, reiniciando os que falham e escalando conforme a carga.

Para um sysadmin, o Kubernetes resolve problemas que o Docker Compose (que cobrimos no Dia 22) não resolve bem: escalonamento automático, recuperação de falhas de nós, actualizações rolling sem downtime e descoberta de serviços entre múltiplas máquinas. O Docker Compose é excelente para desenvolvimento e stacks simples num único hospedeiro; o Kubernetes brilha quando se precisa de vários nós, alta disponibilidade e ciclos de vida complexos.

✓ Boa prática: Se a sua aplicação cabe num único servidor e não precisa de alta disponibilidade, o Docker Compose é suficiente. O Kubernetes acrescenta complexidade real — só vale a pena quando se justifica pela escala ou pela resiliência. Não é uma medalha; é uma ferramenta.

A versão actual do Kubernetes segue um ciclo de lançamento de aproximadamente quatro meses. Cada versão tem suporte durante 14 meses. A versão mais recente pode ser consultada na página oficial de releases. Para este artigo usaremos comandos compatíveis com versões 1.28+.

2. Conceitos do Kubernetes

Antes de instalar qualquer coisa, convém dominar o vocabulário. O Kubernetes tem a sua própria linguagem — entender estes conceitos é metade do trabalho.

Cluster, Nós e Control Plane

Um cluster Kubernetes é um conjunto de máquinas (físicas ou virtuais) que executam contentores em conjunto. Divide-se em dois tipos de nós:

  • Control Plane (plano de controlo) — o cérebro do cluster. Corre componentes como o kube-apiserver, o etcd (base de dados do estado do cluster), o kube-scheduler e o kube-controller-manager. Nunca se correm cargas de trabalho aqui em produção.
  • Worker Nodes (nós de trabalho) — onde os contentores realmente executam. Cada Worker corre o kubelet (agente que fala com o Control Plane) e um container runtime (containerd, CRI-O).

Pods, ReplicaSets e Deployments

O Pod é a unidade mínima do Kubernetes — não o contentor. Um Pod contém um ou mais contentores que partilham rede e armazenamento. Pensa num Pod como uma máquina virtual muito pequena: tem o seu próprio IP, porta e volumes. Na prática, a maioria dos Pods contém apenas um contentor.

O ReplicaSet garante que um número desejado de réplicas de um Pod está sempre a correr. O Deployment é uma camada por cima do ReplicaSet que acrescenta actualizações rolling e rollbacks. Raramente se cria um ReplicaSet directamente — usa-se sempre o Deployment.

Services, Ingress, ConfigMaps e Secrets

Um Service dá um IP estável e um nome DNS a um conjunto de Pods, mesmo que esses Pods sejam destruídos e recriados. O Ingress expõe serviços HTTP/HTTPS ao mundo exterior, tipicamente com terminação TLS. Os ConfigMaps guardam configuração não sensível; os Secrets guardam dados sensíveis (palavras-passe, tokens) codificados em base64.

Conceito Função Equivalente Docker
Pod Unidade mínima com 1+ contentores container
Deployment Gestão de réplicas e rolling updates docker compose (parcial)
Service IP estável e DNS para Pods docker network + expose
Ingress HTTP(S) routing externo nginx proxy / Traefik
ConfigMap Configuração não sensível .env file / env_file
Secret Dados sensíveis (base64) docker secrets (Swarm)

A documentação completa de todos os conceitos está no site oficial: Kubernetes Concepts.

3. Instalar o kubectl

O kubectl é a ferramenta de linha de comando para falar com o cluster Kubernetes. Instala-se na máquina de onde se administra — não necessariamente no próprio cluster.

Instalação directa (recomendada)

# Descarregar o binário mais recente
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"

# Tornar executável e instalar em /usr/local/bin
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

# Verificar a versão
kubectl version --client

Instalação via gestor de pacotes

# Debian/Ubuntu
sudo apt-get update && sudo apt-get install -y kubectl

# Fedora/RHEL
sudo dnf install -y kubectl

# Arch Linux
sudo pacman -S kubectl

Configurar o kubeconfig

O kubectl lê a configuração de acesso ao cluster a partir de ~/.kube/config. Este ficheiro contém os endpoints, certificados e tokens de autenticação. Quando se instala um cluster, o ficheiro é gerado automaticamente; para clusters remotos, copia-se do Control Plane:

# No Control Plane, copiar o kubeconfig para a máquina de administração
scp root@control-plane:/etc/kubernetes/admin.conf ~/.kube/config

# Proteger o ficheiro (contém credenciais)
chmod 600 ~/.kube/config

# Testar a ligação
kubectl cluster-info
kubectl get nodes

⚠ Atenção: O ficheiro ~/.kube/config dá acesso total ao cluster. Trata-o como uma palavra-passe de root — nunca o commits para git nem o partilhas por e-mail.

Atalhos úteis

Para escrever menos, activa a auto-compleção no bash:

# Auto-compleção bash
echo 'source <(kubectl completion bash)' >> ~/.bashrc

# Aliase k = kubectl
echo 'alias k=kubectl' >> ~/.bashrc
source ~/.bashrc

# Agora: k get pods

Guia completo de instalação em Install Tools e referência de comandos em kubectl Commands.

4. Pods e Deployments

É altura de meter as mãos no cluster. Vamos criar um cluster K3s (leve, single-binary) e lançar a primeira aplicação.

Instalar um cluster K3s (1 comando)

# Instalar K3s — Kubernetes leve num único binário
curl -sfL https://get.k3s.io | sh -

# O K3s cria automaticamente o kubeconfig
# Copiar para o local padrão para o kubectl encontrar
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config
chmod 600 ~/.kube/config

# Verificar que o cluster está a funcionar
kubectl get nodes

O K3s é a forma mais rápida de ter um cluster Kubernetes real. Em produção usa-se em edge computing, sites remotos e laboratórios. Documentação completa em docs.k3s.io e código no GitHub.

Criar um Pod imperativamente

A forma mais rápida de lançar um Pod — ideal para testes rápidos:

# Lançar um Pod nginx
kubectl run nginx --image=nginx:latest

# Ver os Pods em execução
kubectl get pods

# Obter detalhes de um Pod
kubectl describe pod nginx

# Abrir uma shell dentro do Pod
kubectl exec -it nginx -- /bin/bash

# Ver logs
kubectl logs nginx

# Eliminar o Pod
kubectl delete pod nginx

⚠ Atenção: Pods criados com kubectl run não são geridos por um Deployment. Se o Pod morrer, ninguém o reinicia. Em produção usa-se sempre um Deployment.

Criar um Deployment declarativamente

A forma correcta — escrever um ficheiro YAML e aplicar. Guarda-se em deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-deployment
  labels:
    app: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:1.25
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
          limits:
            memory: "128Mi"
            cpu: "500m"

Aplicar o ficheiro:

# Aplicar o Deployment
kubectl apply -f deployment.yaml

# Ver o Deployment
kubectl get deployments
kubectl get pods -l app=web

# Ver o estado do rollout
kubectl rollout status deployment/web-deployment

# Escalar para 5 réplicas
kubectl scale deployment web-deployment --replicas=5

# Actualizar a imagem (rolling update)
kubectl set image deployment/web-deployment nginx=nginx:1.26

# Fazer rollback se algo correr mal
kubectl rollout undo deployment/web-deployment

# Ver o histórico de revisões
kubectl rollout history deployment/web-deployment

A diferença entre abordagem imperativa (kubectl run) e declarativa (kubectl apply -f) é fundamental: a declarativa é reproduzível, versionável em git e idempotente — conceitos que já explorámos no Dia 24 (Git para Sysadmins). Documentação de Pods em kubernetes.io e de Deployments aqui.

5. Services e Ingress

Os Pods têm IPs efémeros — mudam a cada reinício. O Service resolve este problema, dando um IP estável e um nome DNS interno. O Ingress vai um passo além e encaminha tráfego HTTP exterior para serviços internos.

Tipos de Service

Tipo Acesso Casos de uso
ClusterIP Apenas dentro do cluster Comunicação interna entre serviços
NodePort Porta alta em todos os nós (30000-32767) Testes rápidos, acesso externo simples
LoadBalancer IP externo via cloud provider Produção em cloud (AWS, GCP, Azure)
ExternalName Alias DNS para serviço externo Aceder a BD externa via DNS interno

Expor um Deployment como Service

# Expor como NodePort (acessível fora do cluster)
kubectl expose deployment web-deployment \
  --type=NodePort \
  --port=80 \
  --name=web-service

# Ver o Service e a porta atribuída
kubectl get svc web-service

# Testar o acesso (substituir a porta)
curl http://localhost:$(kubectl get svc web-service -o jsonpath='{.spec.ports[0].nodePort}')

Service declarativo

apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  type: ClusterIP
  selector:
    app: web
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP

Ingress

O Ingress é a forma correcta de expor serviços HTTP/HTTPS ao exterior. Precisa de um Ingress Controller (nginx-ingress, Traefik — que o K3s já traz instalado). Um Ingress simple:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: web.exemplo.pt
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80
# Aplicar e testar
kubectl apply -f ingress.yaml
kubectl get ingress

# Resolver o host localmente para testar
echo "127.0.0.1 web.exemplo.pt" | sudo tee -a /etc/hosts
curl http://web.exemplo.pt

Documentação de Services em kubernetes.io e de Ingress aqui.

6. ConfigMaps e Secrets

Configuração e segredos são a última peça do puzzle. O Kubernetes separa configuração não sensível (ConfigMaps) de dados sensíveis (Secrets), permitindo que a mesma imagem de contentor corra em diferentes ambientes apenas alterando os valores injectados.

ConfigMaps — configuração não sensível

# Criar um ConfigMap a partir de literais
kubectl create configmap app-config \
  --from-literal=LOG_LEVEL=info \
  --from-literal=MAX_CONNECTIONS=100 \
  --from-literal=ENV=production

# Ver o ConfigMap
kubectl get configmap app-config -o yaml

# Criar a partir de um ficheiro
kubectl create configmap nginx-config --from-file=nginx.conf

De forma declarativa:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  LOG_LEVEL: "info"
  MAX_CONNECTIONS: "100"
  ENV: "production"
  app.properties: |
    debug=false
    timeout=30
    cache_size=256

Para consumir num Pod — como variáveis de ambiente:

spec:
  containers:
  - name: app
    image: minha-app:latest
    envFrom:
    - configMapRef:
        name: app-config

Secrets — dados sensíveis

Os Secrets guardam palavras-passe, tokens e chaves. São codificados em base64 — não são encriptados por defeito. Para segurança real em produção, usa-se SOPS + age ou External Secrets Operator.

# Criar um Secret (valores em base64)
kubectl create secret generic db-secret \
  --from-literal=username=admin \
  --from-literal=password=super-secreta

# Ver o Secret (mostra base64, não texto)
kubectl get secret db-secret -o yaml

# Descodificar um valor específico
kubectl get secret db-secret -o jsonpath='{.data.password}' | base64 -d

De forma declarativa — nota: os valores têm de estar em base64:

# Gerar base64 (não é encriptação!)
echo -n 'admin' | base64
echo -n 'super-secreta' | base64

apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
data:
  username: YWRtaW4=
  password: c3VwZXItc2NyZXRh

⚠ Atenção: base64 é codificação, não encriptação. Qualquer pessoa com acesso ao cluster consegue descodificar o Secret. Em produção, activa a encriptação de Secrets em descanso no etcd e usa ferramentas externas como SOPS, Vault ou Sealed Secrets.

Documentação de ConfigMaps em kubernetes.io e de Secrets aqui.

7. Erros Comuns e Checklist

Depois de dezenas de horas com Kubernetes, estes são os erros que aparecem repetidamente. Cada um tem uma causa directa e uma solução rápida.

Causa Problema Solução
ImagePullBackOff Imagem não existe ou sem permissão Verificar nome/tag; criar imagePullSecret para registos privados
CrashLoopBackOff Contentor arranca e morre repetidamente Ver logs com kubectl logs; validar config e variáveis
Pending Sem recursos ou sem node com taints compatíveis Ver kubectl describe pod; ajustar requests/limits
Connection refused Service a apontar para porta errada Confirmar targetPort no Service vs containerPort no Pod
kubectl: command not found kubectl não instalado ou não no PATH Instalar via binário ou gestor de pacotes; verificar echo $PATH
The connection to the server was refused kubeconfig em falta ou inválido Verificar ~/.kube/config; copiar do Control Plane

Comandos de diagnóstico essenciais

# Estado geral do cluster
kubectl get nodes
kubectl get pods --all-namespaces
kubectl get events --sort-by='.lastTimestamp'

# Diagnosticar um Pod problemático
kubectl describe pod NOME_DO_POD
kubectl logs NOME_DO_POD
kubectl logs NOME_DO_POD --previous

# Entrar num Pod em dificuldade
kubectl exec -it NOME_DO_POD -- /bin/sh

# Ver recursos consumidos
kubectl top nodes
kubectl top pods

# Ver todos os recursos de uma vez
kubectl get all

Checklist antes de production

✓ Checklist:

  • Definir resources.requests e limits em todos os containers
  • Configurar readinessProbe e livenessProbe em todos os Pods
  • Usar Deployments (nunca Pods isolados) para cargas de produção
  • Versionar todos os YAMLs em git — Infrastructure as Code
  • Activar encriptação de Secrets no etcd
  • Configurar backups do etcd (é o estado do cluster)
  • Definir PodDisruptionBudgets para garantir disponibilidade durante manutenções
  • Monitorizar com Prometheus + Grafana (ver Dia 25)
  • Centralizar registos com Loki ou ELK (ver Dia 26)

O Kubernetes tem uma curva de aprendizagem real, mas os conceitos base — Pods, Deployments, Services, ConfigMaps — são estáveis e não mudam entre versões. Depois de dominar estes, o resto é profundidade: StatefulSets, DaemonSets, Jobs, CronJobs, Helm, Operators. O importante é começar pequeno, num cluster de laboratório como o K3s, e iterar.

ℹ Próximo passo: O Dia 30 (último do curso) fecha com um projecto completo — juntar Docker, monitorização, registos e Kubernetes num stack ponta-a-ponta. Se quiseres aprofundar Kubernetes agora, a referência oficial do kubectl é o melhor ponto de partida.

Artigos relacionados: