DevOps

Kubernetes em Produção: Segurança, GitOps e Deploys Avançados Já leu

14 min de leitura

Kubernetes em Produção: Segurança, GitOps e Deploys Avançados
Criar um cluster EKS e fazer uma aplicação rodar nele é relativamente simples. Operar esse cluster com segurança, confiabilidade e rastreabilidade ao longo do tempo é um desafio de ordem diferente. A distância entre "fun

Criar um cluster EKS e fazer uma aplicação rodar nele é relativamente simples. Operar esse cluster com segurança, confiabilidade e rastreabilidade ao longo do tempo é um desafio de ordem diferente. A distância entre "funciona" e "está pronto para produção" em Kubernetes é medida em três dimensões: quanto o cluster resiste a ataques e erros de configuração, quanto o estado desejado é rastreável e reproduzível, e quão sofisticadas são as estratégias de deploy para minimizar o risco de cada mudança.

Este artigo cobre essas três dimensões: segurança com Pod Security Standards e Network Policies, GitOps com ArgoCD para deploys declarativos e auditáveis, e estratégias avançadas de deploy — Blue-Green e Canary — que reduzem o risco de cada nova versão.


Pod Security Standards: Limitando o Que os Pods Podem Fazer

Por padrão, um pod no Kubernetes pode rodar como root, montar o sistema de arquivos do host, escalar privilégios e acessar namespaces do sistema operacional do nó. Em produção, nenhum desses comportamentos deve ser permitido a menos que explicitamente necessário.

O Pod Security Standards é o mecanismo nativo do Kubernetes — introduzido na versão 1.25 como substituto ao Pod Security Policy — que define três perfis de segurança aplicados no nível de namespace:

Privileged — sem restrições. Permite todos os comportamentos. Usado apenas para workloads de infraestrutura como o próprio CNI e o CSI Driver.

Baseline — previne as configurações mais conhecidamente perigosas. Bloqueia containers privilegiados, HostPID, HostIPC e HostNetwork, mas ainda permite rodar como root.

Restricted — segurança máxima. Segue as melhores práticas de hardening. Exige que os containers rodem como não-root, que o filesystem raiz seja somente leitura e que capabilities sejam explicitamente removidas.

# Aplica o perfil restricted ao namespace producao
# Pods que violarem as políticas serão bloqueados (enforce)
# Pods que violarem serão logados mas não bloqueados (audit/warn)
apiVersion: v1
kind: Namespace
metadata:
  name: producao
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: latest
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: latest

Para que os pods funcionem no perfil restricted, o Deployment precisa incluir as configurações de segurança correspondentes:

# Deployment compatível com o perfil restricted
spec:
  template:
    spec:
      # SecurityContext no nível do pod
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        runAsGroup: 1000
        fsGroup: 1000
        seccompProfile:
          type: RuntimeDefault

      containers:
        - name: api
          image: ghcr.io/empresa/minha-api:1.5.0

          # SecurityContext no nível do container
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: ["ALL"]
            # Adiciona apenas capabilities específicas se necessário
            # add: ["NET_BIND_SERVICE"]

          volumeMounts:
            # Diretórios que precisam de escrita montados como emptyDir
            - name: tmp
              mountPath: /tmp
            - name: cache
              mountPath: /app/.cache

      volumes:
        - name: tmp
          emptyDir: {}
        - name: cache
          emptyDir: {}

Network Policies: Microsegmentação de Rede

Por padrão, todos os pods em um cluster Kubernetes podem se comunicar livremente entre si — independentemente do namespace. As Network Policies definem regras de firewall no nível de pod, restringindo quais conexões são permitidas.

Uma estratégia eficaz começa com uma política de default deny que bloqueia todo o tráfego, seguida de políticas específicas que abrem apenas o que é necessário:

# Política de default deny — bloqueia todo tráfego não explicitamente permitido
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: producao
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
---
# Permite que a API receba tráfego do Ingress Controller
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: permitir-ingress-para-api
  namespace: producao
spec:
  podSelector:
    matchLabels:
      app: minha-api
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx
          podSelector:
            matchLabels:
              app.kubernetes.io/name: ingress-nginx
      ports:
        - protocol: TCP
          port: 3000
---
# Permite que a API acesse o banco de dados e o Redis
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: permitir-saida-api
  namespace: producao
spec:
  podSelector:
    matchLabels:
      app: minha-api
  policyTypes:
    - Egress
  egress:
    # DNS — necessário para qualquer comunicação
    - ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
    # Banco de dados PostgreSQL via RDS Proxy
    - to:
        - ipBlock:
            cidr: 10.0.20.0/24  # Subnet do RDS Proxy
      ports:
        - protocol: TCP
          port: 5432
    # Redis via ElastiCache
    - to:
        - ipBlock:
            cidr: 10.0.20.0/24
      ports:
        - protocol: TCP
          port: 6379
    # API externa de pagamento (HTTPS)
    - to:
        - ipBlock:
            cidr: 0.0.0.0/0
            except:
              - 10.0.0.0/8
              - 172.16.0.0/12
              - 192.168.0.0/16
      ports:
        - protocol: TCP
          port: 443

GitOps com ArgoCD

O GitOps é um modelo operacional em que o estado desejado de toda a infraestrutura e das aplicações é declarado em repositórios Git. O Git torna-se a única fonte de verdade — qualquer mudança no cluster passa por um commit, uma revisão e um merge, criando um histórico auditável de tudo que foi modificado, por quem e quando.

O ArgoCD é o controlador de GitOps mais adotado no ecossistema Kubernetes. Ele monitora um repositório Git e garante continuamente que o estado do cluster corresponde ao declarado no repositório — detectando e revertendo automaticamente desvios causados por mudanças manuais.

Instalando o ArgoCD

# Instala o ArgoCD no cluster
kubectl create namespace argocd
kubectl apply -n argocd \
  -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# Aguarda todos os componentes ficarem prontos
kubectl wait --for=condition=available \
  --timeout=300s deployment -l app.kubernetes.io/part-of=argocd \
  -n argocd

# Obtém a senha inicial do admin
kubectl get secret argocd-initial-admin-secret \
  -n argocd \
  -o jsonpath="{.data.password}" | base64 -d

# Acessa a interface via port-forward
kubectl port-forward svc/argocd-server -n argocd 8080:443

Via Terraform e Helm para ambientes de produção:

# argocd.tf
resource "helm_release" "argocd" {
  name             = "argocd"
  repository       = "https://argoproj.github.io/argo-helm"
  chart            = "argo-cd"
  namespace        = "argocd"
  create_namespace = true
  version          = "6.4.0"

  values = [yamlencode({
    global = {
      domain = "argocd.${var.domain_name}"
    }

    server = {
      ingress = {
        enabled          = true
        ingressClassName = "alb"
        annotations = {
          "alb.ingress.kubernetes.io/scheme"      = "internet-facing"
          "alb.ingress.kubernetes.io/target-type" = "ip"
          "alb.ingress.kubernetes.io/backend-protocol" = "HTTPS"
        }
        tls = true
      }
    }

    configs = {
      params = {
        # Força HTTPS — o ArgoCD roda atrás do ALB
        "server.insecure" = "true"
      }

      cm = {
        # SSO com GitHub
        "dex.config" = yamlencode({
          connectors = [{
            type = "github"
            id   = "github"
            name = "GitHub"
            config = {
              clientID     = var.github_client_id
              clientSecret = var.github_client_secret
              orgs = [{
                name = var.github_org
              }]
            }
          }]
        })
      }

      rbac = {
        # Time de DevOps tem acesso admin
        # Outros times têm acesso read-only por padrão
        "policy.default" = "role:readonly"
        "policy.csv"     = <<-EOT
          p, role:admin, applications, *, */*, allow
          p, role:admin, clusters, get, *, allow
          p, role:admin, repositories, *, *, allow
          g, ${var.github_org}:devops, role:admin
        EOT
      }
    }

    repoServer = {
      resources = {
        requests = { cpu = "100m", memory = "256Mi" }
        limits   = { memory = "512Mi" }
      }
    }
  })]
}

Estrutura do Repositório GitOps

A estrutura do repositório Git que o ArgoCD monitora segue convenções bem estabelecidas:

gitops-repo/
├── apps/                          # Definições das Applications do ArgoCD
│   ├── minha-api-staging.yml
│   ├── minha-api-producao.yml
│   └── monitoring.yml
│
├── clusters/
│   ├── staging/
│   │   ├── namespaces.yml
│   │   ├── network-policies.yml
│   │   └── resource-quotas.yml
│   └── producao/
│       ├── namespaces.yml
│       ├── network-policies.yml
│       └── resource-quotas.yml
│
└── workloads/
    ├── minha-api/
    │   ├── base/                  # Configuração base (Kustomize)
    │   │   ├── deployment.yml
    │   │   ├── service.yml
    │   │   ├── hpa.yml
    │   │   └── kustomization.yml
    │   ├── staging/               # Overlay de staging
    │   │   ├── kustomization.yml
    │   │   └── patches/
    │   │       └── replicas.yml
    │   └── producao/              # Overlay de produção
    │       ├── kustomization.yml
    │       └── patches/
    │           └── replicas.yml
    └── monitoring/
        └── ...

Application do ArgoCD

Cada Application do ArgoCD define qual repositório Git e qual caminho monitorar, e em qual cluster e namespace aplicar:

# apps/minha-api-producao.yml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: minha-api-producao
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: producao

  source:
    repoURL: https://github.com/empresa/gitops-repo
    targetRevision: main
    path: workloads/minha-api/producao

  destination:
    server: https://kubernetes.default.svc
    namespace: producao

  syncPolicy:
    automated:
      prune: true      # Remove recursos deletados do Git
      selfHeal: true   # Reverte mudanças manuais no cluster
      allowEmpty: false

    syncOptions:
      - CreateNamespace=true
      - PrunePropagationPolicy=foreground
      - PruneLast=true

    retry:
      limit: 3
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

  # Ignora campos gerenciados pelo cluster (não pelo Git)
  ignoreDifferences:
    - group: apps
      kind: Deployment
      jsonPointers:
        - /spec/replicas  # HPA gerencia réplicas — ignora diffs

Kustomize: Gerenciando Variações por Ambiente

O Kustomize é integrado ao kubectl e ao ArgoCD, permitindo customizar manifestos base para diferentes ambientes sem duplicação:

# workloads/minha-api/base/kustomization.yml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - deployment.yml
  - service.yml
  - hpa.yml
  - service-account.yml

commonLabels:
  app: minha-api
  managed-by: argocd

images:
  - name: ghcr.io/empresa/minha-api
    newTag: latest  # Substituído pelo overlay de cada ambiente
# workloads/minha-api/producao/kustomization.yml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: producao

resources:
  - ../base
  - external-secret.yml
  - ingress.yml

images:
  - name: ghcr.io/empresa/minha-api
    newTag: "abc1234"  # SHA do commit — atualizado pelo pipeline de CI

patches:
  - path: patches/deployment-producao.yml
    target:
      kind: Deployment
      name: minha-api
# workloads/minha-api/producao/patches/deployment-producao.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: minha-api
spec:
  replicas: 5
  template:
    spec:
      containers:
        - name: api
          resources:
            requests:
              memory: "512Mi"
              cpu: "500m"
            limits:
              memory: "1Gi"
              cpu: "2000m"

O pipeline de CI atualiza a tag da imagem no repositório GitOps após o build:

# No workflow de CI — etapa de atualização do GitOps
- name: Atualiza imagem no repositório GitOps
  run: |
    git clone https://x-access-token:${{ secrets.GITOPS_TOKEN }}@github.com/empresa/gitops-repo.git
    cd gitops-repo

    # Atualiza a tag da imagem com o SHA do commit
    cd workloads/minha-api/producao
    kustomize edit set image ghcr.io/empresa/minha-api:${{ github.sha }}

    git config user.email "pipeline@empresa.com"
    git config user.name "Pipeline CI"
    git add .
    git commit -m "chore: atualiza minha-api para ${{ github.sha }}

    Deploy automático pelo pipeline de CI
    Repositório: ${{ github.repository }}
    Run: ${{ github.run_id }}"
    git push

Estratégias Avançadas de Deploy

Blue-Green Deployment

O deploy Blue-Green mantém duas versões do ambiente em paralelo — Blue (versão atual) e Green (nova versão). O tráfego é transferido instantaneamente de Blue para Green após verificação da saúde do Green. O rollback é igualmente instantâneo — basta redirecionar o tráfego de volta para Blue.

# blue-green/deployment-blue.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: minha-api-blue
  namespace: producao
  labels:
    app: minha-api
    slot: blue
spec:
  replicas: 3
  selector:
    matchLabels:
      app: minha-api
      slot: blue
  template:
    metadata:
      labels:
        app: minha-api
        slot: blue
        versao: "1.4.0"
    spec:
      containers:
        - name: api
          image: ghcr.io/empresa/minha-api:1.4.0
---
# blue-green/deployment-green.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: minha-api-green
  namespace: producao
  labels:
    app: minha-api
    slot: green
spec:
  replicas: 3
  selector:
    matchLabels:
      app: minha-api
      slot: green
  template:
    metadata:
      labels:
        app: minha-api
        slot: green
        versao: "1.5.0"
    spec:
      containers:
        - name: api
          image: ghcr.io/empresa/minha-api:1.5.0
---
# O Service aponta para blue ou green — alterado no momento da troca
apiVersion: v1
kind: Service
metadata:
  name: minha-api
  namespace: producao
spec:
  selector:
    app: minha-api
    slot: blue  # Muda para "green" no momento da promoção
  ports:
    - port: 80
      targetPort: 3000

Script de troca Blue-Green:

#!/bin/bash
# scripts/blue-green-switch.sh

NAMESPACE="producao"
NOVO_SLOT="${1:?Uso: $0 <blue|green>}"
VERSAO="${2:?Uso: $0 <blue|green> <versao>}"

echo "Verificando saúde do slot $NOVO_SLOT antes da troca..."

# Verifica que o deployment do novo slot está saudável
kubectl rollout status deployment/minha-api-$NOVO_SLOT \
  -n $NAMESPACE --timeout=120s

READY=$(kubectl get deployment minha-api-$NOVO_SLOT \
  -n $NAMESPACE -o jsonpath='{.status.readyReplicas}')
DESIRED=$(kubectl get deployment minha-api-$NOVO_SLOT \
  -n $NAMESPACE -o jsonpath='{.spec.replicas}')

if [ "$READY" != "$DESIRED" ]; then
  echo "ERRO: $READY/$DESIRED pods prontos no slot $NOVO_SLOT"
  exit 1
fi

echo "Slot $NOVO_SLOT saudável ($READY/$DESIRED pods). Transferindo tráfego..."

# Transfere o tráfego para o novo slot
kubectl patch service minha-api -n $NAMESPACE \
  -p "{\"spec\":{\"selector\":{\"app\":\"minha-api\",\"slot\":\"$NOVO_SLOT\"}}}"

echo "Tráfego transferido para $NOVO_SLOT (versão $VERSAO)"
echo "Para rollback: kubectl patch service minha-api -n $NAMESPACE -p '{...slot anterior...}'"

Canary Deployment com Argo Rollouts

O Argo Rollouts estende o Kubernetes com estratégias de deploy avançadas — Canary e Blue-Green com análise automática de métricas:

# Instala o Argo Rollouts
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts \
  -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
# rollout.yml — substitui o Deployment para uso com Argo Rollouts
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: minha-api
  namespace: producao
spec:
  replicas: 10
  selector:
    matchLabels:
      app: minha-api
  template:
    metadata:
      labels:
        app: minha-api
    spec:
      containers:
        - name: api
          image: ghcr.io/empresa/minha-api:1.5.0
          # ... resto da spec do container

  strategy:
    canary:
      # Services separados para stable e canary
      stableService: minha-api-stable
      canaryService: minha-api-canary

      # Análise automática de métricas a cada step
      analysis:
        templates:
          - templateName: analise-taxa-erros
        startingStep: 2
        args:
          - name: service-name
            value: minha-api-canary

      steps:
        # Step 1: 10% do tráfego para o canary
        - setWeight: 10
        # Step 2: Pausa e analisa métricas por 5 minutos
        - pause: { duration: 5m }
        # Step 3: 30% do tráfego
        - setWeight: 30
        - pause: { duration: 5m }
        # Step 4: 50% do tráfego
        - setWeight: 50
        - pause: { duration: 10m }
        # Step 5: 100% — promoção completa
        - setWeight: 100
---
# Template de análise de métricas
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: analise-taxa-erros
  namespace: producao
spec:
  args:
    - name: service-name

  metrics:
    - name: taxa-de-sucesso
      interval: 1m
      successCondition: result[0] >= 0.99
      failureLimit: 3

      provider:
        prometheus:
          address: http://kube-prometheus-stack-prometheus.monitoring:9090
          query: |
            sum(rate(http_requests_total{
              service="{{args.service-name}}",
              status!~"5.."
            }[5m]))
            /
            sum(rate(http_requests_total{
              service="{{args.service-name}}"
            }[5m]))

    - name: latencia-p99
      interval: 1m
      successCondition: result[0] <= 0.5
      failureLimit: 3

      provider:
        prometheus:
          address: http://kube-prometheus-stack-prometheus.monitoring:9090
          query: |
            histogram_quantile(0.99,
              sum(rate(http_request_duration_seconds_bucket{
                service="{{args.service-name}}"
              }[5m])) by (le)
            )

Gerenciando o rollout via CLI do Argo:

# Instala o plugin kubectl para Argo Rollouts
kubectl argo rollouts version

# Visualiza o status do rollout em tempo real
kubectl argo rollouts get rollout minha-api -n producao --watch

# Promove o canary manualmente (ignora pauses)
kubectl argo rollouts promote minha-api -n producao

# Aborta o rollout e faz rollback para a versão estável
kubectl argo rollouts abort minha-api -n producao

# Retoma um rollout pausado
kubectl argo rollouts resume minha-api -n producao

Encerrando o Módulo 8

Com este artigo encerra-se o tema Kubernetes. Foram cobertos os fundamentos do Kubernetes, o provisionamento de clusters EKS com Terraform, IRSA para permissões seguras, Karpenter para auto scaling de nós, e neste artigo as práticas que separam um cluster funcional de um cluster pronto para produção: segurança com Pod Security Standards, GitOps com ArgoCD e estratégias avançadas de deploy com Argo Rollouts.

Os próximos temas entram no território de Segurança e Compliance em DevOps: como integrar segurança em cada etapa do pipeline, como garantir conformidade com regulações como LGPD e SOC2, e como construir uma cultura DevSecOps que trata segurança como responsabilidade de todos.


Referências para Aprofundamento

Documentação oficial - ArgoCD Documentation — argo-cd.readthedocs.io — Documentação completa do ArgoCD, cobrindo instalação, configuração de Applications, SSO e boas práticas de GitOps. - Argo Rollouts Documentation — argo-rollouts.readthedocs.io — Referência completa do Argo Rollouts com guias de estratégias Canary e Blue-Green, análise de métricas e integração com service meshes. - Kustomize Documentation — kustomize.io — Documentação oficial do Kustomize, incluindo conceitos de base e overlay, patches estratégicos e geração de ConfigMaps e Secrets.

Segurança - Kubernetes Pod Security Standards — kubernetes.io — Documentação oficial dos Pod Security Standards, definindo os perfis Privileged, Baseline e Restricted com exemplos de configuração. - NSA Kubernetes Hardening Guide — media.defense.gov — Guia de hardening do Kubernetes publicado pela NSA e CISA, cobrindo autenticação, autorização, rede e auditoria com recomendações práticas.

GitOps - OpenGitOps — opengitops.dev — Definição formal dos princípios do GitOps mantida pela CNCF, com recursos educacionais e referências de implementação.

Comentários

Mais em DevOps

Processos, Serviços e o Comando `systemctl`
Processos, Serviços e o Comando `systemctl`

Cada programa em execu&ccedil;&atilde;o no Linux &mdash; seja um servidor web...

Bitbucket e o Ecossistema Atlassian
Bitbucket e o Ecossistema Atlassian

Há uma situação muito específica que justifica o uso do Bitbucket: a organiza...

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...