☸️ Kubernetes — K8s

Kubernetes —
Orquestração de Containers

Aprende a orquestrar, escalar e gerir aplicações em containers com Kubernetes — do cluster local em minikube ao deploy em produção, sempre com alta disponibilidade.

Pods & Nodes Deployments Services ConfigMaps & Secrets Ingress Helm kubectl YAML Manifests Escalamento Namespaces

01 O que é o Kubernetes?

Conceitos fundamentais antes de começar

O Kubernetes (abreviado K8s — o "8" representa as 8 letras entre o K e o s) é um sistema open-source de orquestração de containers criado pela Google em 2014 e hoje mantido pela CNCF (Cloud Native Computing Foundation). Automatiza o deploy, o escalamento e a gestão de aplicações em containers.

Se o Docker te permite empacotar uma aplicação num container, o Kubernetes gere centenas ou milhares desses containers em múltiplos servidores — garantindo que estão sempre a correr, que escalam quando necessário e que as actualizações se fazem sem downtime.

📦

Pod

A unidade mínima do Kubernetes. Contém um ou mais containers que partilham rede e armazenamento. É efémero — se morrer, o Kubernetes cria um novo.

unidade básica
🖥️

Node

Máquina física ou virtual que executa os Pods. Pode ser um servidor on-premises, uma VM ou um nó gerido na cloud (GKE, EKS, AKS).

máquina de trabalho
🏗️

Cluster

Conjunto de Nodes geridos pelo Kubernetes. Tem um Control Plane (o "cérebro") e vários Worker Nodes (onde as apps correm).

conjunto de nodes
🚀

Deployment

Descreve o estado desejado da aplicação: quantas réplicas, que imagem, que recursos. O Kubernetes garante que esse estado é sempre mantido.

estado desejado
🌐

Service

Ponto de acesso estável para um conjunto de Pods. Os Pods mudam de IP constantemente — o Service dá um IP e DNS fixos para aceder a eles.

rede estável
📁

Namespace

Partição virtual dentro do cluster. Isola recursos por equipa, ambiente (dev/prod) ou projecto. Diferentes namespaces podem ter os mesmos nomes de recursos.

isolamento lógico
🗂️

ConfigMap

Armazena configurações não sensíveis (variáveis de ambiente, ficheiros de config) separadas do código — facilita a portabilidade entre ambientes.

configuração
🔏

Secret

Armazena dados sensíveis (passwords, tokens, certificados) de forma codificada (base64). Injectados nos Pods como variáveis de ambiente ou volumes.

dados sensíveis
🔀

Ingress

Gere o tráfego HTTP/HTTPS externo para os serviços do cluster. Um Ingress Controller (nginx, traefik) implementa as regras de roteamento.

tráfego externo
💾

PersistentVolume

Armazenamento persistente para os Pods. Ao contrário dos dados em container, um PV sobrevive ao ciclo de vida dos Pods.

persistência
📈

HPA

Horizontal Pod Autoscaler — escala automaticamente o número de réplicas com base em métricas (CPU, memória, requests por segundo).

auto-escalamento
🎡

Helm Chart

Pacote reutilizável de manifests Kubernetes. O Helm é o "gestor de pacotes" do K8s — instala stacks complexas com um só comando.

gestor de pacotes

Arquitectura do Cluster Kubernetes

KUBERNETES CLUSTER
┌─────────────────────────────────────────────────────────────────────────┐
│                                                                         │
│  CONTROL PLANE (cérebro do cluster)                                    │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │  kube-apiserver   etcd           kube-scheduler               │  │
│  │  (porta de entrada) (base de dados  (decide onde correm         │  │
│  │                      do cluster)     os Pods)                   │  │
│  │                                                                  │  │
│  │  controller-manager  (reconcilia estado actual vs. desejado)      │  │
│  └──────────────────────────────────────────────────────────────────┘  │
│           │                   │                   │                     │
│  WORKER NODE 1        WORKER NODE 2        WORKER NODE 3           │
│  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐            │
│  │ kubelet       │    │ kubelet       │    │ kubelet       │            │
│  │ kube-proxy   │    │ kube-proxy   │    │ kube-proxy   │            │
│  │ ┌──────────┐ │    │ ┌──────────┐ │    │ ┌──────────┐ │            │
│  │ │ Pod A    │ │    │ │ Pod B    │ │    │ │ Pod C    │ │            │
│  │ │ nginx    │ │    │ │ nginx    │ │    │ │ postgres │ │            │
│  │ └──────────┘ │    │ └──────────┘ │    │ └──────────┘ │            │
│  │ ┌──────────┐ │    │ ┌──────────┐ │    │              │            │
│  │ │ Pod D    │ │    │ │ Pod E    │ │    │              │            │
│  │ │ api-app  │ │    │ │ api-app  │ │    │              │            │
│  │ └──────────┘ │    │ └──────────┘ │    │              │            │
│  └──────────────┘    └──────────────┘    └──────────────┘            │
│                                                                         │
│  kubectl (CLI do utilizador) ──► kube-apiserver                       │
└─────────────────────────────────────────────────────────────────────────┘
ℹ️
Kubernetes vs. Docker Docker e Kubernetes não são concorrentes — são complementares. O Docker (ou qualquer runtime de containers) cria e executa containers. O Kubernetes orquestra esses containers em escala, garantindo disponibilidade, escalamento automático e actualizações sem downtime. Pensa no Docker como o motor de um carro e no Kubernetes como o GPS e o piloto automático.

02 Instalação e Primeiros Passos

Colocar um cluster Kubernetes a funcionar localmente

Para aprender Kubernetes localmente, existem várias ferramentas. O minikube é a mais recomendada para iniciantes — cria um cluster de nó único no teu PC em segundos.

Opções para cluster local

🚗

minikube

Cluster local de nó único. Suporta múltiplos drivers (Docker, VirtualBox, Hyper-V). O mais fácil para começar.

recomendado iniciantes
🐋

Docker Desktop

Inclui K8s integrado. Activa com um toggle nas preferências. Ideal se já tens o Docker Desktop instalado.

Windows / macOS

kind

Kubernetes IN Docker — cria clusters dentro de containers Docker. Multi-nó no mesmo PC. Rápido e leve.

CI/CD local
🎯

k3s

Kubernetes ultra-leve (apenas 50 MB). Ideal para edge computing, IoT e máquinas com poucos recursos.

lightweight

Instalar minikube e kubectl

Linux (Ubuntu/Debian) — instalar minikube + kubectl
# ── kubectl (cliente de linha de comandos) ──────────────────────────
curl -LO "https://dl.k8s.io/release/$(curl -sL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kubectl version --client

# ── minikube ────────────────────────────────────────────────────────
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

# ── iniciar cluster (usa Docker como driver por defeito) ─────────────
minikube start
😄  minikube v1.33.0 on Ubuntu 22.04
✨  Using the docker driver based on existing profile
🏄  Done! kubectl is now configured to use "minikube" cluster

# verificar estado do cluster
kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:49153
kubectl get nodes
NAME       STATUS   ROLES           AGE   VERSION
minikube   Ready    control-plane   2m    v1.30.0
macOS (Homebrew)
brew install kubectl minikube
minikube start --driver=docker
Windows (Winget / PowerShell como Administrador)
# instalar kubectl e minikube via winget
winget install Kubernetes.kubectl
winget install Kubernetes.minikube

# ou via Chocolatey
choco install kubernetes-cli minikube

# iniciar (requer Hyper-V ou Docker Desktop activo)
minikube start --driver=docker

Comandos essenciais do minikube

minikube — gestão do cluster local
minikube start           # iniciar o cluster
minikube stop            # parar (preserva o estado)
minikube delete          # eliminar completamente o cluster
minikube status          # ver estado do cluster
minikube dashboard       # abrir dashboard web no browser
minikube tunnel          # expor Services LoadBalancer localmente
minikube addons list     # listar extensões disponíveis
minikube addons enable ingress   # activar Ingress Controller nginx
minikube addons enable metrics-server  # activar métricas (para HPA)
💡
Dashboard visual: Executa minikube dashboard para abrir uma interface web completa onde podes ver todos os recursos do cluster, ver logs dos Pods e fazer scaling sem usar o terminal. Muito útil para aprender.

03 kubectl — A CLI do Kubernetes

Dominar a ferramenta de linha de comandos

O kubectl é a ferramenta de linha de comandos para interagir com qualquer cluster Kubernetes. Comunica com o kube-apiserver do Control Plane via REST. Cada comando segue a estrutura: kubectl [verbo] [recurso] [nome] [flags].

ComandoO que faz
🔍 Ver e listar recursos
kubectl get podsLista todos os Pods do namespace actual. Adiciona -A para todos os namespaces.
kubectl get pods -o wideLista Pods com informação adicional: IP, Node onde corre, imagem.
kubectl get allLista todos os recursos principais: Pods, Services, Deployments, ReplicaSets.
kubectl describe pod <nome>Informação detalhada de um Pod — eventos, condições, variáveis de ambiente, volumes.
kubectl get nodesLista todos os Nodes do cluster com estado e versão.
kubectl get namespacesLista todos os namespaces existentes.
📝 Criar e aplicar recursos
kubectl apply -f ficheiro.yamlAplica a configuração definida num manifest YAML. Cria ou actualiza o recurso. Preferir sempre este método.
kubectl create -f ficheiro.yamlCria um recurso. Falha se já existir (usa apply em alternativa).
kubectl run nginx --image=nginxCria um Pod simples a partir de uma imagem. Útil para testes rápidos.
kubectl apply -f pasta/Aplica todos os ficheiros YAML numa directoria recursivamente.
🗑️ Eliminar recursos
kubectl delete -f ficheiro.yamlElimina os recursos definidos no manifest. Método preferido.
kubectl delete pod <nome>Elimina um Pod (se for gerido por Deployment, será recriado automaticamente).
kubectl delete deployment <nome>Elimina o Deployment e todos os Pods associados.
🔧 Depuração e acesso
kubectl logs <pod>Ver logs de um Pod. Adiciona -f para seguir em tempo real. -c container para multi-container.
kubectl exec -it <pod> -- bashAbrir terminal interactivo dentro de um Pod em execução. Usa sh se bash não existir.
kubectl port-forward pod/<nome> 8080:80Redirecionar porta do Pod para o localhost. Permite testar serviços sem expor externamente.
kubectl top podsVer consumo de CPU e memória dos Pods (requer metrics-server activo).
kubectl events --sort-by=.lastTimestampVer todos os eventos do cluster ordenados por data — essencial para diagnóstico.
⚙️ Configuração e contextos
kubectl config get-contextsListar todos os clusters configurados (minikube, GKE, AKS, etc.).
kubectl config use-context <nome>Mudar para outro cluster. Fundamental para trabalhar com múltiplos clusters.
kubectl -n <namespace> get podsEspecificar namespace. Equivalente a --namespace. -n kube-system para recursos do sistema.
kubectl explain deploymentDocumentação inline de qualquer recurso K8s. Inclui todos os campos disponíveis.
kubectl — sessão de diagnóstico típica
# Ver o estado geral de tudo
kubectl get all -A
NAMESPACE   NAME                          READY   STATUS    RESTARTS   AGE
default     pod/nginx-7f456874d9-xk2m4    1/1     Running   0          5m
default     pod/api-6b9f4c8d7f-p9n3r     1/1     Running   0          3m

# Pod com erro? Descreve para ver o motivo
kubectl describe pod api-6b9f4c8d7f-p9n3r
Events:
  Warning  BackOff   2m   kubelet  Back-off restarting failed container

# Ver os logs do pod problemático
kubectl logs api-6b9f4c8d7f-p9n3r --previous
Error: Cannot connect to database at host 'db': connection refused

# Entrar no container para diagnóstico manual
kubectl exec -it api-6b9f4c8d7f-p9n3r -- sh
/ # env | grep DB_
DB_HOST=db
DB_PORT=5432
/ # ping db
ping: bad address 'db'

04 Pods — A Unidade Básica

Criar, gerir e compreender os Pods do Kubernetes

Um Pod é a unidade de deployment mais pequena do Kubernetes. Contém um ou mais containers que partilham o mesmo namespace de rede (mesmo IP e portas) e podem partilhar volumes de armazenamento. Na maioria dos casos, um Pod tem exactamente um container.

⚠️
Pods são efémeros: Um Pod pode ser eliminado e recriado a qualquer momento — por falha do Node, actualização de imagem, ou rebalanceamento de recursos. Nunca armazenes estado importante directamente num Pod. Usa PersistentVolumes para dados persistentes e Deployments em vez de Pods nus.

Manifest YAML de um Pod

pod-nginx.yaml
apiVersion: v1
kind: Pod
metadata:
  name: meu-nginx
  labels:
    app: nginx
    versao: v1
spec:
  containers:
    - name: nginx
      image: nginx:1.25-alpine
      ports:
        - containerPort: 80
      resources:              # SEMPRE definir limites
        requests:
          memory: "64Mi"
          cpu: "100m"       # 100 milliCPU = 0.1 CPU
        limits:
          memory: "128Mi"
          cpu: "200m"
      livenessProbe:         # reinicia se ficar "stuck"
        httpGet:
          path: /
          port: 80
        initialDelaySeconds: 5
        periodSeconds: 10
      readinessProbe:        # só entra em serviço quando pronto
        httpGet:
          path: /
          port: 80
        initialDelaySeconds: 3
        periodSeconds: 5
ciclo de vida de um Pod
# Criar o Pod a partir do manifest
kubectl apply -f pod-nginx.yaml
pod/meu-nginx created

# Ver o estado do Pod (aguarda ficar Running)
kubectl get pod meu-nginx -w
NAME        READY   STATUS              RESTARTS   AGE
meu-nginx   0/1     ContainerCreating   0          3s
meu-nginx   1/1     Running             0          8s

# Redirecionar porta para testar localmente
kubectl port-forward pod/meu-nginx 8080:80
Forwarding from 127.0.0.1:8080 -> 80
# Abre http://localhost:8080 — deves ver a página Nginx

# Ver logs em tempo real
kubectl logs -f meu-nginx

# Eliminar o Pod
kubectl delete pod meu-nginx
pod "meu-nginx" deleted

Probes — Monitorização de Saúde

💓

livenessProbe

Verifica se o container está vivo. Se falhar, o container é reiniciado. Evita Pods "stuck" que não respondem.

reinicia se falhar

readinessProbe

Verifica se o container está pronto para receber tráfego. Se falhar, é removido do Service (mas não reiniciado).

tráfego pausado
🚀

startupProbe

Para apps lentas a arrancar. Desactiva as outras probes até o container estar pronto — evita reinícios prematuros.

arranque lento

05 Deployments — Gerir Réplicas

Manter o estado desejado da aplicação

Na prática nunca criamos Pods directamente — usamos Deployments. Um Deployment descreve o estado desejado (quantas réplicas, que imagem) e o ReplicaSet subjacente garante que esse estado é sempre mantido. Se um Pod morrer, o Deployment cria um novo automaticamente.

deployment-api.yaml — Deployment completo com boas práticas
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
  namespace: producao
  labels:
    app: api
spec:
  replicas: 3                 # 3 instâncias em paralelo
  selector:
    matchLabels:
      app: api             # gere os Pods com este label
  strategy:
    type: RollingUpdate      # actualização sem downtime
    rollingUpdate:
      maxUnavailable: 0    # nunca reduz a capacidade
      maxSurge: 1          # cria 1 Pod novo antes de remover o antigo
  template:                   # template de cada Pod
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: meuregistry/api:1.5.0   # tag específica, nunca latest
          ports:
            - containerPort: 3000
          envFrom:
            - configMapRef:
                name: api-config   # vars de ambiente de ConfigMap
            - secretRef:
                name: api-secrets   # vars sensíveis de Secret
          resources:
            requests:
              memory: "256Mi"
              cpu: "200m"
            limits:
              memory: "512Mi"
              cpu: "500m"
          readinessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 10
      terminationGracePeriodSeconds: 30  # tempo para fechar conexões
gerir Deployments
# Aplicar o Deployment
kubectl apply -f deployment-api.yaml
deployment.apps/api-deployment created

# Ver estado do rollout em tempo real
kubectl rollout status deployment/api-deployment
Waiting for deployment "api-deployment" rollout to finish: 1 of 3 updated...
Waiting for deployment "api-deployment" rollout to finish: 2 of 3 updated...
deployment "api-deployment" successfully rolled out

# Actualizar a imagem (rolling update sem downtime)
kubectl set image deployment/api-deployment api=meuregistry/api:1.6.0
# Ou editar directamente o manifest e aplicar de novo

# Rollback para a versão anterior se algo correr mal
kubectl rollout undo deployment/api-deployment
deployment.apps/api-deployment rolled back

# Ver histórico de revisões
kubectl rollout history deployment/api-deployment
REVISION  CHANGE-CAUSE
1         image: api:1.5.0
2         image: api:1.6.0

# Escalar manualmente o número de réplicas
kubectl scale deployment/api-deployment --replicas=5
deployment.apps/api-deployment scaled
💡
RollingUpdate vs. Recreate: A estratégia RollingUpdate actualiza os Pods gradualmente — zero downtime. A estratégia Recreate elimina todos os Pods antes de criar os novos — gera downtime mas é útil quando a nova versão é incompatível com a anterior (ex: migrações de BD que não permitem duas versões em simultâneo).

06 Services — Rede e Descoberta

Expor e ligar aplicações de forma estável

Os Pods têm IPs dinâmicos que mudam constantemente. Um Service é uma abstracção que fornece um IP e DNS estáveis para um conjunto de Pods seleccionados por labels. O Service também faz balanceamento de carga entre os Pods.

Tipos de Services

🔒

ClusterIP

Expõe o serviço apenas dentro do cluster. É o tipo por defeito. Acesso por outros Pods via nome-servico:porta.

interno apenas
🚪

NodePort

Expõe o serviço numa porta estática em todos os Nodes (30000–32767). Acesso externo via IP-do-Node:NodePort.

dev / testing
⚖️

LoadBalancer

Provisiona um balanceador de carga externo no fornecedor cloud (GCP, AWS, Azure). Em minikube, usa minikube tunnel.

cloud / produção
🔗

ExternalName

Mapeia o serviço para um nome DNS externo. Útil para integrar serviços externos sem mudar a configuração das apps.

DNS externo
service-api.yaml — Service ClusterIP + Service LoadBalancer
# ── Service ClusterIP — acesso interno (outros Pods) ────────────────
apiVersion: v1
kind: Service
metadata:
  name: api-service
spec:
  type: ClusterIP                  # padrão — não precisa ser explicitado
  selector:
    app: api                        # liga aos Pods com label app=api
  ports:
    - port: 80                      # porta do Service (vista de fora)
      targetPort: 3000              # porta do container
      protocol: TCP
---
# ── Service LoadBalancer — acesso externo (Internet) ─────────────────
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: LoadBalancer
  selector:
    app: frontend
  ports:
    - port: 80
      targetPort: 8080
DNS interno — como os Pods comunicam
# Dentro de qualquer Pod, podes chamar outro serviço pelo nome:
# http://api-service          (mesmo namespace)
# http://api-service:80       (com porta explícita)
# http://api-service.producao.svc.cluster.local  (FQDN completo)

# Testar DNS de dentro de um Pod de debugging
kubectl run debug --image=curlimages/curl --rm -it -- sh
/ # curl http://api-service/health
{"status":"ok","version":"1.5.0"}
/ # nslookup api-service
Server:    10.96.0.10
Address:   10.96.0.10#53
Name:      api-service.default.svc.cluster.local
Address:   10.100.42.137

07 ConfigMaps e Secrets

Separar configuração e segredos do código

Uma das melhores práticas cloud-native é separar a configuração do código. O Kubernetes fornece dois recursos para isso: ConfigMaps para configurações não sensíveis e Secrets para dados sensíveis.

configmap-e-secret.yaml
# ── ConfigMap — configurações não sensíveis ──────────────────────────
apiVersion: v1
kind: ConfigMap
metadata:
  name: api-config
data:
  NODE_ENV: "production"
  PORT: "3000"
  LOG_LEVEL: "info"
  DB_HOST: "postgres-service"
  DB_PORT: "5432"
  app.properties: |           # ficheiro de configuração completo
    max.connections=100
    timeout=30s
    retry.attempts=3
---
# ── Secret — dados sensíveis (codificados em base64) ─────────────────
apiVersion: v1
kind: Secret
metadata:
  name: api-secrets
type: Opaque
stringData:              # escrever em texto simples (K8s codifica)
  DB_PASSWORD: "supersegredo123"
  JWT_SECRET: "chave-jwt-muito-secreta"
  API_KEY: "sk-live-xxxxxxxxxxx"
criar ConfigMaps e Secrets via CLI
# Criar ConfigMap a partir de ficheiro .env
kubectl create configmap api-config --from-env-file=.env

# Criar ConfigMap a partir de valores directos
kubectl create configmap app-config \
  --from-literal=NODE_ENV=production \
  --from-literal=PORT=3000

# Criar Secret (K8s codifica automaticamente em base64)
kubectl create secret generic api-secrets \
  --from-literal=DB_PASSWORD=supersegredo123 \
  --from-literal=JWT_SECRET=chave-secreta

# Verificar (os valores aparecem codificados)
kubectl get secret api-secrets -o yaml
data:
  DB_PASSWORD: c3VwZXJzZWdyZWRvMTIz  # base64

# Descodificar para verificar (em base64)
kubectl get secret api-secrets -o jsonpath='{.data.DB_PASSWORD}' | base64 -d
supersegredo123
Secrets NÃO são encriptados por defeito: Os Secrets do Kubernetes são apenas codificados em base64 — qualquer pessoa com acesso ao cluster ou ao etcd pode lê-los. Para produção real, usa Sealed Secrets, External Secrets Operator com HashiCorp Vault ou Azure Key Vault, e activa a encryption at rest do etcd.

08 Volumes e Persistência

Guardar dados que sobrevivem ao ciclo de vida dos Pods

Os dados dentro de um container são perdidos quando o Pod é destruído. Para persistência, o Kubernetes usa PersistentVolumes (PV) — recursos de armazenamento do cluster — e PersistentVolumeClaims (PVC) — pedidos de armazenamento pelos Pods.

FLUXO DE ARMAZENAMENTO PERSISTENTE

  Administrador cria:                  Programador define:
  ┌─────────────────────┐              ┌─────────────────────┐
  │  PersistentVolume   │◄─────────────│  PersistentVolume   │
  │  (PV)               │   vincula    │  Claim (PVC)        │
  │                     │             │                     │
  │  capacity: 10Gi     │             │  storage: 5Gi       │
  │  accessMode: RWO    │             │  accessMode: RWO    │
  │  storageClass: ssd  │             │  storageClass: ssd  │
  └─────────────────────┘             └──────────┬──────────┘
           │                                     │ montado em
           │ armazenamento real                  ▼
           │ (NFS, cloud disk,       ┌─────────────────────┐
           │  hostPath...)           │       Pod           │
           └────────────────────────►│  volumeMounts:      │
                                     │   /data → PVC       │
                                     └─────────────────────┘
postgres-com-pvc.yaml — Base de dados com volume persistente
# ── PersistentVolumeClaim — pedido de armazenamento ─────────────────
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-pvc
spec:
  accessModes:
    - ReadWriteOnce         # montado em leitura/escrita por 1 Node
  storageClassName: standard  # classe de storage do cluster
  resources:
    requests:
      storage: 5Gi
---
# ── Deployment do PostgreSQL usando o PVC ────────────────────────────
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
spec:
  replicas: 1                   # BD = apenas 1 réplica (sem estado partilhado)
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:16-alpine
          env:
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: api-secrets
                  key: DB_PASSWORD
          volumeMounts:
            - name: pgdata
              mountPath: /var/lib/postgresql/data
      volumes:
        - name: pgdata
          persistentVolumeClaim:
            claimName: postgres-pvc   # referência ao PVC criado acima
ℹ️
StatefulSets para bases de dados: Para workloads com estado como bases de dados (PostgreSQL, MySQL, Redis Cluster), a melhor prática é usar StatefulSets em vez de Deployments. Os StatefulSets garantem identidade de rede estável, ordem de deployment e PVCs persistentes por Pod — fundamentais para clusters de BD com réplicas.

09 Ingress — Tráfego HTTP Externo

Expor múltiplos serviços com um único ponto de entrada

Em produção, não vamos ter um LoadBalancer por serviço (seria muito caro e complexo). O Ingress é um recurso que define regras de roteamento HTTP/HTTPS — um único IP externo distribui o tráfego por múltiplos serviços com base no host ou no caminho da URL.

INGRESS — ROTEAMENTO HTTP

  Internet
      │
      ▼
  ┌──────────────────────────────────────────────────────┐
  │  Ingress Controller (nginx)                          │
  │  IP externo: 20.40.50.60                             │
  │                                                      │
  │  app.empresa.pt     → frontend-service:80            │
  │  api.empresa.pt     → api-service:80                 │
  │  app.empresa.pt/admin → admin-service:80             │
  └──────────────────────────────────────────────────────┘
         │               │               │
         ▼               ▼               ▼
  ┌──────────┐    ┌──────────┐    ┌──────────┐
  │ frontend │    │   api    │    │  admin   │
  │  Pods    │    │  Pods    │    │  Pods    │
  └──────────┘    └──────────┘    └──────────┘
ingress.yaml — roteamento por host e por caminho
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-principal
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    cert-manager.io/cluster-issuer: "letsencrypt-prod"  # SSL automático
spec:
  ingressClassName: nginx
  tls:                               # HTTPS com certificado automático
    - hosts:
        - app.empresa.pt
        - api.empresa.pt
      secretName: empresa-tls
  rules:
    - host: app.empresa.pt            # roteamento por host
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: frontend-service
                port:
                  number: 80
    - host: api.empresa.pt            # roteamento por host diferente
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 80
activar Ingress no minikube e testar
# Activar o addon nginx-ingress-controller
minikube addons enable ingress
💡  ingress is an addon maintained by Kubernetes.
🌟  The 'ingress' addon is enabled

# Aplicar o manifest do Ingress
kubectl apply -f ingress.yaml

# Ver IP do Ingress
kubectl get ingress
NAME                CLASS   HOSTS                        ADDRESS        PORTS     AGE
ingress-principal   nginx   app.empresa.pt,api.emp...   192.168.49.2   80, 443   30s

# Testar localmente (adicionar ao /etc/hosts):
# 192.168.49.2  app.empresa.pt api.empresa.pt
curl -H "Host: api.empresa.pt" http://192.168.49.2/health
{"status":"ok"}

10 Escalamento Automático

HPA, VPA e Cluster Autoscaler

Uma das maiores vantagens do Kubernetes é o escalamento automático. Existem três tipos complementares: HPA (mais Pods), VPA (Pods maiores) e Cluster Autoscaler (mais Nodes).

📈

HPA — Horizontal Pod Autoscaler

Aumenta/diminui o número de réplicas (Pods) com base em métricas. A forma mais comum de escalamento.

mais/menos Pods
⬆️

VPA — Vertical Pod Autoscaler

Ajusta automaticamente os requests/limits de CPU e memória dos Pods com base no uso real histórico.

Pods maiores/menores
🏗️

Cluster Autoscaler

Adiciona/remove Nodes do cluster quando os Pods não conseguem ser agendados por falta de recursos.

mais/menos Nodes
📅

KEDA

Kubernetes Event-Driven Autoscaling. Escala com base em eventos externos (fila de mensagens, cron, métricas customizadas).

event-driven

Horizontal Pod Autoscaler (HPA)

hpa.yaml — escala automaticamente de 2 a 10 réplicas
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api-deployment
  minReplicas: 2         # mínimo sempre garantido
  maxReplicas: 10        # máximo de réplicas
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70   # escala se CPU > 70%
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80   # escala se memória > 80%
testar o HPA com carga simulada
# Activar metrics-server (necessário para HPA)
minikube addons enable metrics-server

# Aplicar o HPA
kubectl apply -f hpa.yaml

# Ver estado do HPA
kubectl get hpa
NAME      REFERENCE               TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
api-hpa   Deployment/api-deploy   23%/70%, 0%/80%   2       10        2          1m

# Simular carga (em outro terminal)
kubectl run carga --image=busybox --rm -it -- sh
/ # while true; do wget -q -O- http://api-service/; done

# Observar o HPA escalar em tempo real
kubectl get hpa -w
api-hpa   Deployment/api   85%/70%    2   10   2    2m
api-hpa   Deployment/api   92%/70%    2   10   4    3m  # escalou para 4!
api-hpa   Deployment/api   67%/70%    2   10   4    5m
api-hpa   Deployment/api   12%/70%    2   10   2    8m  # voltou a 2

11 Helm — Gestor de Pacotes

Instalar e gerir aplicações complexas com Charts

O Helm é o gestor de pacotes do Kubernetes — como o apt do Debian ou o npm do Node.js. Um Chart é um pacote Helm que contém todos os manifests Kubernetes necessários para deployar uma aplicação, parametrizados com templates.

instalar e usar Helm
# ── instalar Helm ───────────────────────────────────────────────────
# Linux / macOS
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# macOS via Homebrew
brew install helm
# Windows via Winget
winget install Helm.Helm

# ── repositórios de Charts ──────────────────────────────────────────
helm repo add stable https://charts.helm.sh/stable
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update                     # actualizar lista de charts

# ── exemplos de instalação ──────────────────────────────────────────
# Instalar nginx-ingress-controller
helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx --create-namespace

# Instalar PostgreSQL com configuração customizada
helm install meu-postgres bitnami/postgresql \
  --set auth.postgresPassword=segredo \
  --set primary.persistence.size=10Gi \
  --namespace databases --create-namespace

# Instalar Prometheus + Grafana (stack de monitorização)
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install kube-prometheus prometheus-community/kube-prometheus-stack \
  --namespace monitoring --create-namespace
Comando HelmO que faz
📦 Gerir releases
helm install nome chart/Instala um Chart como release. Cada instalação tem um nome único.
helm upgrade nome chart/Actualiza uma release existente. Usa --install para instalar se não existir.
helm rollback nome 1Volta a uma revisão anterior. O número é o número da revisão histórica.
helm uninstall nomeRemove completamente a release e todos os recursos criados.
helm listLista todas as releases instaladas no namespace actual.
helm status nomeEstado detalhado de uma release — recursos criados, notas de instalação.
🔍 Explorar Charts
helm search hub nginxPesquisa charts no Artifact Hub (repositório público central).
helm show values bitnami/nginxMostra todos os valores configuráveis de um chart.
helm template nome chart/Renderiza os manifests YAML sem instalar — útil para ver o que seria criado.
🔨 Criar charts próprios
helm create meu-chartCria a estrutura de pastas de um chart novo com templates de exemplo.
helm lint meu-chart/Valida a sintaxe e a estrutura do chart.
helm package meu-chart/Empacota o chart num ficheiro .tgz para distribuição.

Criar um Chart personalizado

estrutura de um Helm Chart
helm create minha-api
Creating minha-api

# Estrutura criada:
minha-api/
├── Chart.yaml          # metadados do chart (nome, versão, descrição)
├── values.yaml         # valores por defeito (configuráveis)
├── templates/
│   ├── deployment.yaml # template do Deployment (usa {{ .Values.* }})
│   ├── service.yaml    # template do Service
│   ├── ingress.yaml    # template do Ingress
│   ├── hpa.yaml        # template do HPA
│   └── _helpers.tpl    # funções auxiliares reutilizáveis
└── charts/             # dependências (sub-charts)

# Instalar o chart em modo de desenvolvimento
helm install minha-api ./minha-api \
  --set image.tag=1.5.0 \
  --set replicaCount=3

# Actualizar com novos valores
helm upgrade minha-api ./minha-api \
  --set image.tag=1.6.0

12 Boas Práticas

Produção-ready: o que fazer e o que evitar

Segurança

Nunca usar imagens :latest

Em produção usa sempre tags específicas (ex: nginx:1.25.3). :latest pode introduzir mudanças inesperadas a qualquer momento.

Segurança

Correr como utilizador não-root

Define securityContext.runAsNonRoot: true e readOnlyRootFilesystem: true no Pod spec para reduzir a superfície de ataque.

Performance

Sempre definir requests e limits

Sem resources.requests o scheduler não consegue tomar boas decisões. Sem limits, um Pod pode afogar todos os outros no Node.

Performance

Configurar Probes sempre

readinessProbe evita enviar tráfego para Pods não prontos. livenessProbe reinicia Pods que ficaram presos sem conseguir responder.

Fiabilidade

Usar Namespaces para isolamento

Separa ambientes (dev, staging, prod) e equipas em namespaces diferentes. Aplica ResourceQuotas por namespace para evitar abusos.

Fiabilidade

Pod Disruption Budgets

Define quantos Pods podem estar indisponíveis durante manutenções (maxUnavailable: 1). Evita que actualizações de Nodes deixem a app sem réplicas suficientes.

GitOps

Infraestrutura como Código

Guarda todos os manifests YAML num repositório Git. Usa ArgoCD ou Flux para sincronizar automaticamente o cluster com o repositório.

GitOps

Nunca fazer kubectl apply manual

Em produção, alterações ao cluster devem passar sempre por PR no Git e ser aplicadas pelo pipeline CI/CD — nunca directamente da máquina de um developer.

Observabilidade

Logs estruturados (JSON)

As aplicações devem escrever logs em JSON para facilitar a indexação e pesquisa em ferramentas como Loki, Elasticsearch ou Datadog.

Observabilidade

Expor métricas Prometheus

Todas as aplicações devem expor um endpoint /metrics no formato Prometheus. Usa client libraries (promclient para Node, Prometheus para Python...).

Segurança

Network Policies

Por defeito, todos os Pods comunicam entre si. Define NetworkPolicy para implementar microsegmentação — cada Pod só comunica com quem precisa.

Performance

Multi-réplica + Anti-affinity

Com podAntiAffinity, garantes que as réplicas ficam em Nodes diferentes — se um Node cair, a app continua a servir tráfego.

Fluxo de trabalho típico em equipa

💻
Código
Developer commita no Git
🔨
Build
CI constrói imagem Docker
🧪
Testes
Testes automáticos
☁️
Push
Push para registry
📝
PR Manifest
Atualiza tag no YAML
🤖
GitOps
ArgoCD aplica no cluster
📊
Monitor
Alertas & métricas
🎯
Por onde começar em produção? Plataformas geridas como GKE (Google), EKS (AWS) e AKS (Azure) gerem o Control Plane por ti — só pagas pelos Worker Nodes. Para começar sem custos, usa minikube localmente ou k3s numa VPS barata. A CNCF Landscape em cncf.io/landscape é o mapa completo do ecossistema cloud-native.