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.