DevOps

Introdução ao Kubernetes: Orquestrando Containers em Escala Já leu

13 min de leitura

Introdução ao Kubernetes: Orquestrando Containers em Escala
O Docker resolve o problema de empacotar e executar uma aplicação em um único container. O Docker Compose resolve o problema de orquestrar múltiplos containers em uma única máquina. Mas quando a aplicação cresce além de

O Docker resolve o problema de empacotar e executar uma aplicação em um único container. O Docker Compose resolve o problema de orquestrar múltiplos containers em uma única máquina. Mas quando a aplicação cresce além de uma máquina — quando são necessários dezenas de instâncias distribuídas em múltiplos servidores, com failover automático, atualizações sem downtime e escala dinâmica baseada em demanda — uma nova categoria de problema emerge: a orquestração de containers em escala.

O Kubernetes — frequentemente abreviado como K8s — é o sistema de orquestração de containers que se tornou o padrão da indústria para gerenciar cargas de trabalho containerizadas em ambientes de produção. Originado no Google, que orquestrou containers em escala por mais de uma década antes de abrir o projeto, o Kubernetes foi doado à Cloud Native Computing Foundation em 2014 e desde então se tornou o projeto de infraestrutura de código aberto mais ativo do mundo.

A proposta central do Kubernetes é declarativa: o engenheiro descreve o estado desejado do sistema — "quero cinco réplicas desta aplicação, cada uma com 512MB de memória, acessíveis pela porta 80" — e o Kubernetes trabalha continuamente para garantir que o estado real corresponde ao declarado. Se um container morre, o Kubernetes inicia um novo. Se um nó falha, as cargas de trabalho são reagendadas em nós saudáveis. Se o tráfego aumenta, novas réplicas são criadas automaticamente.


Arquitetura do Kubernetes

Um cluster Kubernetes é composto por dois tipos de máquinas com responsabilidades distintas.

O Plano de Controle

O plano de controle é o cérebro do cluster — o conjunto de componentes que gerencia o estado desejado e toma decisões sobre o cluster.

kube-apiserver — o único ponto de entrada para todas as operações no cluster. Toda comunicação — do kubectl, dos outros componentes do plano de controle, dos agentes nos nós — passa pela API. É stateless e pode ser escalado horizontalmente.

etcd — o banco de dados distribuído de chave-valor que armazena todo o estado do cluster. É o componente mais crítico — perder o etcd sem backup significa perder o cluster inteiro. Em produção, o etcd é executado em um cluster de três ou cinco instâncias para tolerância a falhas.

kube-scheduler — observa pods recém-criados que ainda não têm um nó atribuído e seleciona o nó mais adequado baseado em recursos disponíveis, afinidades, taints e tolerations.

kube-controller-manager — executa os controladores que implementam a lógica de reconciliação. O controlador de réplicas garante que o número correto de pods está em execução. O controlador de endpoints atualiza a lista de IPs nos Services. O controlador de jobs garante que jobs completam com sucesso.

Os Nós Worker

Os nós worker são as máquinas que executam as cargas de trabalho — os containers da aplicação.

kubelet — o agente que roda em cada nó. Recebe especificações de pods do kube-apiserver e garante que os containers descritos estão rodando e saudáveis. Reporta o estado dos containers de volta ao plano de controle.

kube-proxy — mantém as regras de rede em cada nó, implementando o conceito de Services — abstrações que expõem um conjunto de pods como um endpoint de rede estável.

Container Runtime — o software que executa os containers. O Kubernetes suporta qualquer runtime compatível com a CRI — Container Runtime Interface. O containerd é o runtime padrão na maioria das distribuições modernas.

┌─────────────────────────────────────────────────────────────┐
│                     PLANO DE CONTROLE                        │
│                                                             │
│  ┌──────────────┐  ┌──────┐  ┌────────────┐  ┌──────────┐ │
│  │ kube-apiserver│  │ etcd │  │ scheduler  │  │controller│ │
│  └──────────────┘  └──────┘  └────────────┘  └──────────┘ │
└─────────────────────────────────────────────────────────────┘
            │                          │
     ┌──────┴──────┐             ┌─────┴──────┐
     │    NÓ 1     │             │    NÓ 2    │
     │             │             │            │
     │  kubelet    │             │  kubelet   │
     │  kube-proxy │             │ kube-proxy │
     │             │             │            │
     │ ┌─────────┐ │             │ ┌────────┐ │
     │ │  Pod A  │ │             │ │ Pod B  │ │
     │ │ Pod C   │ │             │ │ Pod D  │ │
     │ └─────────┘ │             │ └────────┘ │
     └─────────────┘             └────────────┘

Instalando um Cluster Local com kind

Para aprendizado e desenvolvimento local, o kind (Kubernetes in Docker) cria um cluster Kubernetes completo usando containers Docker como nós — sem precisar de máquinas virtuais:

# Instala o kind
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.22.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind

# Instala o kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/kubectl

# Cria um cluster com configuração personalizada
cat > kind-config.yml << 'EOF'
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
    kubeadmConfigPatches:
      - |
        kind: InitConfiguration
        nodeRegistration:
          kubeletExtraArgs:
            node-labels: "ingress-ready=true"
    extraPortMappings:
      - containerPort: 80
        hostPort: 80
        protocol: TCP
      - containerPort: 443
        hostPort: 443
        protocol: TCP
  - role: worker
  - role: worker
EOF

kind create cluster --config kind-config.yml --name meu-cluster

# Verifica que o cluster está funcionando
kubectl cluster-info
kubectl get nodes

Os Objetos Fundamentais do Kubernetes

O Kubernetes é operado inteiramente através de objetos — recursos declarados em arquivos YAML que descrevem o estado desejado do sistema. Cada objeto tem quatro campos obrigatórios: apiVersion, kind, metadata e spec.

Pod: A Unidade Atômica

O Pod é o objeto mais básico do Kubernetes — um grupo de um ou mais containers que compartilham o mesmo namespace de rede e podem compartilhar volumes. Containers dentro de um pod se comunicam via localhost.

Na prática, pods raramente são criados diretamente. Eles são gerenciados por objetos de nível superior — Deployments, StatefulSets, Jobs — que garantem que o número correto de pods está sempre em execução.

# pod.yml — apenas para entender a estrutura; use Deployments em produção
apiVersion: v1
kind: Pod
metadata:
  name: minha-api
  namespace: default
  labels:
    app: minha-api
    versao: "1.5.0"
spec:
  containers:
    - name: api
      image: ghcr.io/minha-empresa/minha-api:1.5.0
      ports:
        - containerPort: 3000
          protocol: TCP
      env:
        - name: NODE_ENV
          value: production
        - name: PORT
          value: "3000"
      resources:
        requests:       # Mínimo garantido pelo scheduler
          memory: "128Mi"
          cpu: "100m"   # 100 millicores = 0.1 CPU
        limits:         # Máximo permitido
          memory: "512Mi"
          cpu: "500m"
      livenessProbe:
        httpGet:
          path: /health
          port: 3000
        initialDelaySeconds: 30
        periodSeconds: 10
        failureThreshold: 3
      readinessProbe:
        httpGet:
          path: /health
          port: 3000
        initialDelaySeconds: 10
        periodSeconds: 5
        failureThreshold: 3
  restartPolicy: Always

Deployment: Gerenciando Réplicas com Atualizações Controladas

O Deployment é o objeto mais usado para cargas de trabalho stateless. Ele gerencia um ReplicaSet — que por sua vez gerencia os pods — e orquestra atualizações com estratégias de rollout configuráveis:

# deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: minha-api
  namespace: producao
  labels:
    app: minha-api
  annotations:
    kubernetes.io/change-cause: "Deploy versão 1.5.0 — feature: checkout redesign"
spec:
  replicas: 3

  selector:
    matchLabels:
      app: minha-api

  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1        # Máximo de pods extras durante atualização
      maxUnavailable: 0  # Zero pods indisponíveis — zero downtime

  template:
    metadata:
      labels:
        app: minha-api
        versao: "1.5.0"
    spec:
      # Distribui pods entre nós diferentes para resiliência
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: kubernetes.io/hostname
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: minha-api

      # Graceful shutdown — aguarda conexões ativas terminarem
      terminationGracePeriodSeconds: 30

      containers:
        - name: api
          image: ghcr.io/minha-empresa/minha-api:1.5.0
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 3000

          env:
            - name: NODE_ENV
              value: production
            - name: PORT
              value: "3000"
            # Injeta variáveis de ambiente a partir de Secrets
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: minha-api-secrets
                  key: database-url
            - name: REDIS_URL
              valueFrom:
                secretKeyRef:
                  name: minha-api-secrets
                  key: redis-url

          resources:
            requests:
              memory: "256Mi"
              cpu: "200m"
            limits:
              memory: "512Mi"
              cpu: "1000m"

          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 30
            periodSeconds: 15
            timeoutSeconds: 5
            failureThreshold: 3

          readinessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 10
            periodSeconds: 5
            timeoutSeconds: 3
            failureThreshold: 3

          # Garante que o processo recebe SIGTERM ao terminar
          lifecycle:
            preStop:
              exec:
                command: ["/bin/sh", "-c", "sleep 5"]

          # Contexto de segurança — sem root
          securityContext:
            runAsNonRoot: true
            runAsUser: 1000
            readOnlyRootFilesystem: true
            allowPrivilegeEscalation: false
            capabilities:
              drop: ["ALL"]

          # Volume para arquivos temporários (filesystem read-only)
          volumeMounts:
            - name: tmp
              mountPath: /tmp

      volumes:
        - name: tmp
          emptyDir: {}

      # Pull de imagem privada
      imagePullSecrets:
        - name: ghcr-credentials

Service: Expondo Pods com IP Estável

Pods têm IPs efêmeros — quando um pod morre e é recriado, recebe um IP diferente. O Service resolve esse problema fornecendo um IP e nome DNS estáveis para um conjunto de pods selecionados por labels:

# service.yml
apiVersion: v1
kind: Service
metadata:
  name: minha-api
  namespace: producao
spec:
  selector:
    app: minha-api  # Seleciona pods com este label

  ports:
    - name: http
      port: 80          # Porta do Service
      targetPort: 3000  # Porta do container
      protocol: TCP

  type: ClusterIP  # Acessível apenas dentro do cluster

Os tipos de Service disponíveis:

ClusterIP — padrão. Acessível apenas dentro do cluster. Usado para comunicação interna entre serviços.

NodePort — expõe o Service em uma porta estática em cada nó do cluster. Acessível externamente via IP_DO_NO:NODEPORT. Raramente usado em produção diretamente.

LoadBalancer — solicita ao cloud provider a criação de um load balancer externo. Na AWS, cria um Network Load Balancer ou Classic Load Balancer. É a forma mais comum de expor serviços em clusters gerenciados como EKS.

ConfigMap e Secret: Configuração Separada do Código

# configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: minha-api-config
  namespace: producao
data:
  NODE_ENV: "production"
  LOG_LEVEL: "info"
  PORT: "3000"
  # Arquivo de configuração completo como entrada no ConfigMap
  nginx.conf: |
    server {
      listen 80;
      location / {
        proxy_pass http://localhost:3000;
      }
    }
# secret.yml
apiVersion: v1
kind: Secret
metadata:
  name: minha-api-secrets
  namespace: producao
type: Opaque
# Valores em base64 — não é criptografia, apenas encoding
# Em produção, use External Secrets Operator ou Sealed Secrets
data:
  database-url: cG9zdGdyZXNxbDovL...  # base64 da string
  redis-url: cmVkaXM6Ly9...
  jwt-secret: c2VjcmV0b211aXRvc2VndXJv...

Ingress: Roteamento HTTP de Entrada

O Ingress define regras de roteamento HTTP/HTTPS para o tráfego de entrada no cluster. Requer um Ingress Controller — um pod que implementa as regras definidas pelo Ingress:

# ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minha-api
  namespace: producao
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "10m"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
    cert-manager.io/cluster-issuer: "letsencrypt-producao"
spec:
  ingressClassName: nginx

  tls:
    - hosts:
        - api.empresa.com
      secretName: api-empresa-tls

  rules:
    - host: api.empresa.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: minha-api
                port:
                  number: 80

Comandos Essenciais do kubectl

# ── Contextos e clusters ──────────────────────────
kubectl config get-contexts          # Lista clusters configurados
kubectl config use-context meu-cluster  # Muda de cluster

# ── Inspecionando recursos ────────────────────────
kubectl get pods -n producao         # Lista pods no namespace producao
kubectl get pods -A                  # Lista pods em todos os namespaces
kubectl get all -n producao          # Todos os recursos do namespace
kubectl describe pod minha-api-abc -n producao  # Detalhes de um pod
kubectl logs minha-api-abc -n producao          # Logs de um pod
kubectl logs minha-api-abc -n producao -f       # Logs em tempo real
kubectl logs minha-api-abc -n producao --previous  # Logs da instância anterior

# ── Aplicando configurações ───────────────────────
kubectl apply -f deployment.yml      # Cria ou atualiza recursos
kubectl apply -f ./manifests/        # Aplica todos os YAMLs de um diretório
kubectl delete -f deployment.yml     # Remove recursos descritos no arquivo

# ── Operações em pods ─────────────────────────────
kubectl exec -it minha-api-abc -n producao -- /bin/sh  # Shell interativo
kubectl port-forward pod/minha-api-abc 8080:3000        # Port forward local

# ── Rollout e atualizações ────────────────────────
kubectl rollout status deployment/minha-api -n producao
kubectl rollout history deployment/minha-api -n producao
kubectl rollout undo deployment/minha-api -n producao   # Rollback
kubectl set image deployment/minha-api api=ghcr.io/empresa/api:1.6.0 -n producao

# ── Escalando ─────────────────────────────────────
kubectl scale deployment/minha-api --replicas=5 -n producao

# ── Diagnóstico ───────────────────────────────────
kubectl top pods -n producao         # Uso de CPU e memória dos pods
kubectl top nodes                    # Uso de CPU e memória dos nós
kubectl get events -n producao --sort-by='.lastTimestamp'

Horizontal Pod Autoscaler

O HPA escala automaticamente o número de réplicas baseado em métricas — CPU, memória ou métricas customizadas via Prometheus Adapter:

# hpa.yml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: minha-api
  namespace: producao
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: minha-api

  minReplicas: 3
  maxReplicas: 20

  metrics:
    # Escala por CPU
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70

    # Escala por memória
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80

    # Escala por métrica customizada do Prometheus
    - type: Pods
      pods:
        metric:
          name: http_requests_per_second
        target:
          type: AverageValue
          averageValue: "100"

  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60   # Aguarda 60s antes de escalar para cima
      policies:
        - type: Pods
          value: 4
          periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300  # Aguarda 5min antes de escalar para baixo
      policies:
        - type: Pods
          value: 1
          periodSeconds: 60

Namespaces: Isolamento Lógico no Cluster

Os Namespaces dividem um cluster em ambientes virtuais isolados. É uma forma eficiente de separar equipes, ambientes ou projetos sem precisar de clusters separados:

# Cria os namespaces para diferentes ambientes
kubectl create namespace producao
kubectl create namespace staging
kubectl create namespace monitoring

# Define o namespace padrão para o contexto atual
kubectl config set-context --current --namespace=producao
# network-policy.yml — isola o namespace de produção
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: isolamento-producao
  namespace: producao
spec:
  podSelector: {}  # Aplica a todos os pods do namespace

  policyTypes:
    - Ingress
    - Egress

  ingress:
    # Permite tráfego apenas do Ingress Controller
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx

  egress:
    # Permite saída para DNS
    - ports:
        - protocol: UDP
          port: 53
    # Permite saída para o namespace de banco de dados
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: databases

O Que Vem a Seguir

O próximo artigo aprofunda o Kubernetes em produção com o Amazon EKS — o serviço gerenciado de Kubernetes da AWS. Serão cobertos o provisionamento do cluster via Terraform, a gestão de node groups com Karpenter para auto scaling eficiente e as práticas de segurança essenciais para clusters EKS em produção.


Referências para Aprofundamento

Documentação oficial - Kubernetes Documentation — kubernetes.io — Documentação oficial completa do Kubernetes, incluindo conceitos, tutoriais e referência da API. O ponto de partida obrigatório para qualquer estudo sério de K8s. - kubectl Cheat Sheet — kubernetes.io — Referência rápida dos comandos kubectl mais usados, organizada por categoria de operação.

Aprendizado prático - kind Documentation — kind.sigs.k8s.io — Documentação do kind para criação de clusters locais, incluindo configurações avançadas de multi-node e networking. - Killercoda Kubernetes Playgrounds — killercoda.com — Ambiente interativo online para praticar Kubernetes sem instalação local, com cenários guiados e cluster real no navegador.

Livros e cursos de referência - Kubernetes: Up and Running — O'Reilly — Livro de referência escrito pelos criadores do Kubernetes, cobrindo desde os fundamentos até operações avançadas em produção. - CNCF Landscape — landscape.cncf.io — Mapa interativo do ecossistema Cloud Native, mostrando todas as ferramentas e projetos organizados por categoria. Essencial para entender o ecossistema ao redor do Kubernetes.

Comentários

Mais em DevOps

Azure para Quem Já Conhece AWS
Azure para Quem Já Conhece AWS

Ao longo dos doze meses do currículo principal desta série, a AWS funcionou c...

Variáveis de Ambiente e Secrets em Pipelines
Variáveis de Ambiente e Secrets em Pipelines

Em abril de 2022, a empresa de segurança GitGuardian publicou um relatório re...

Deploy Automático para Servidores com GitHub Actions
Deploy Automático para Servidores com GitHub Actions

Todo o trabalho construído nos artigos anteriores — testes, build de imagens,...