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

DevSecOps: Integrando Segurança em Todo o Pipeline
DevSecOps: Integrando Segurança em Todo o Pipeline

Durante décadas, a segurança foi tratada como uma fase — algo que acontecia d...

Azure Kubernetes Service (AKS)
Azure Kubernetes Service (AKS)

O Kubernetes em si é a mesma plataforma, seja no EKS da AWS ou no AKS do Azur...

Artigo 29 — Ansible: Configurando Servidores de Forma Declarativa
Artigo 29 — Ansible: Configurando Servidores de Forma Declarativa

Artigo 29 — Ansible: Configurando Servidores de Forma Declarativa O Que o Ter...