Introdução ao Kustomize: Por que Precisamos Dele
Kubernetes é poderoso, mas gerenciar manifests YAML em múltiplos ambientes é um pesadelo. Quando você tem um deployment que funciona em desenvolvimento, produção e testes, manter três arquivos YAML separados leva a inconsistências, duplicação de código e erros humanos difíceis de rastrear. É aqui que Kustomize entra em cena.
Kustomize é uma ferramenta nativa do kubectl (integrada desde a versão 1.14) que permite organizar, customizar e reutilizar manifestos Kubernetes sem usar templates ou linguagens especiais. Em vez de substituir valores com Helm ou processar templates, Kustomize trabalha com bases (manifests padrão) e overlays (variações para ambientes específicos), usando patches para aplicar mudanças cirúrgicas. Isso mantém seu código declarativo, versionável e fácil de debugar.
Arquitetura Fundamental: Bases, Overlays e Patches
O Conceito de Bases
Uma base é um conjunto completo e autossuficiente de manifestos Kubernetes que define sua aplicação. Ela contém tudo que a aplicação precisa: Deployments, Services, ConfigMaps, Secrets, etc. A base é pensada para ser o estado "padrão" da sua aplicação, sem customizações específicas de ambiente.
Considere uma aplicação web simples. Sua base ficaria assim:
base/
├── kustomization.yaml
├── deployment.yaml
├── service.yaml
└── configmap.yaml
Arquivo base/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
labels:
app: webapp
spec:
replicas: 2
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
containers:
- name: webapp
image: myapp:latest
ports:
- containerPort: 8080
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
Arquivo base/service.yaml:
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
selector:
app: webapp
ports:
- port: 80
targetPort: 8080
type: ClusterIP
Arquivo base/configmap.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: webapp-config
data:
LOG_LEVEL: "info"
DATABASE_TIMEOUT: "30s"
Arquivo base/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- configmap.yaml
commonLabels:
version: "1.0"
managed-by: kustomize
O arquivo kustomization.yaml é o coração da base. Ele lista todos os recursos que fazem parte da aplicação e define configurações comuns como labels que serão aplicados a todos os recursos automaticamente.
O Conceito de Overlays
Um overlay é uma variação da base para um ambiente específico. Enquanto a base define o comportamento padrão, overlays definem ajustes: mais réplicas para produção, diferentes limites de recursos, URLs diferentes, etc. Um overlay é uma pasta que sempre contém um kustomization.yaml e referencia a base.
A estrutura fica assim:
.
├── base/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ └── configmap.yaml
├── overlays/
│ ├── dev/
│ │ ├── kustomization.yaml
│ │ └── patches/
│ │ └── deployment.yaml
│ ├── staging/
│ │ ├── kustomization.yaml
│ │ └── patches/
│ │ └── deployment.yaml
│ └── prod/
│ ├── kustomization.yaml
│ └── patches/
│ └── deployment.yaml
Um overlay nunca duplica a base. Ele referencia a base e aplica mudanças através de patches. Arquivo overlays/prod/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
patchesStrategicMerge:
- patches/deployment.yaml
replicas:
- name: webapp
count: 5
commonLabels:
environment: prod
Patches: Aplicando Mudanças Cirúrgicas
Um patch é uma mudança incremental em um recurso. Em vez de reescrever o manifesto inteiro, você especifica exatamente o que muda. Kustomize suporta diferentes tipos de patches: strategic merge patches, JSON patches e patches com comentários.
Arquivo overlays/prod/patches/deployment.yaml (strategic merge patch):
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
replicas: 5
template:
spec:
containers:
- name: webapp
image: myapp:v2.1.0
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "1000m"
env:
- name: LOG_LEVEL
value: "warn"
Quando você aplica este overlay, Kustomize faz um merge inteligente: mantém tudo da base (Service, ConfigMap, labels comuns) e sobrescreve apenas os campos do Deployment que você especificou. O resultado é uma aplicação de produção com 5 réplicas, imagem versão 2.1.0 e recursos aumentados, sem duplicar nenhum código.
Exemplos Práticos: Do Conceito à Aplicação
Exemplo Completo: Aplicação Multi-Ambiente
Vamos construir uma aplicação real com três ambientes. Começamos criando a estrutura:
mkdir -p kustomize-demo/base kustomize-demo/overlays/{dev,staging,prod}
cd kustomize-demo
Arquivo base/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: default
resources:
- deployment.yaml
- service.yaml
- configmap.yaml
commonLabels:
app: myapp
managed: "true"
namePrefix: myapp-
commonAnnotations:
kustomize.config.k8s.io/version: "4.5.7"
Arquivo base/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 2
selector:
matchLabels:
tier: backend
template:
metadata:
labels:
tier: backend
spec:
containers:
- name: api
image: myapp-api:1.0.0
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
env:
- name: DATABASE_HOST
valueFrom:
configMapKeyRef:
name: config
key: db_host
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 10
periodSeconds: 5
Arquivo base/service.yaml:
apiVersion: v1
kind: Service
metadata:
name: api
spec:
type: ClusterIP
ports:
- name: http
port: 80
targetPort: http
selector:
tier: backend
Arquivo base/configmap.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: config
data:
db_host: "postgres.default.svc.cluster.local"
cache_ttl: "300"
log_level: "info"
Agora os overlays. Arquivo overlays/dev/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: dev
bases:
- ../../base
replicas:
- name: api
count: 1
patchesStrategicMerge:
- patches/deployment.yaml
commonLabels:
environment: development
namePrefix: dev-
Arquivo overlays/dev/patches/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
template:
spec:
containers:
- name: api
image: myapp-api:latest
imagePullPolicy: Always
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 256Mi
env:
- name: DEBUG
value: "true"
Arquivo overlays/prod/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: production
bases:
- ../../base
replicas:
- name: api
count: 5
patchesStrategicMerge:
- patches/deployment.yaml
- patches/service.yaml
commonLabels:
environment: production
namePrefix: prod-
Arquivo overlays/prod/patches/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
template:
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- myapp
topologyKey: kubernetes.io/hostname
containers:
- name: api
image: myapp-api:2.0.0
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 2
memory: 2Gi
env:
- name: DATABASE_HOST
value: "prod-postgres.production.svc.cluster.local"
Arquivo overlays/prod/patches/service.yaml:
apiVersion: v1
kind: Service
metadata:
name: api
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
spec:
type: LoadBalancer
Para ver o resultado final sem aplicar, use:
# Visualizar manifests de desenvolvimento
kubectl kustomize overlays/dev
# Visualizar manifests de produção
kubectl kustomize overlays/prod
# Aplicar em desenvolvimento
kubectl apply -k overlays/dev
# Aplicar em produção
kubectl apply -k overlays/prod
Técnicas Avançadas: Vars e ConfigMap Generators
Além de patches simples, Kustomize oferece funcionalidades avançadas. Use vars para substituir valores referenciados e generators para criar ConfigMaps e Secrets dinamicamente.
Arquivo base/kustomization.yaml (versão expandida):
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: default
resources:
- deployment.yaml
- service.yaml
configMapGenerator:
- name: app-config
literals:
- app.name=myapp
- app.version=1.0.0
files:
- config.properties
secretGenerator:
- name: app-secrets
envs:
- secrets.env
vars:
- name: IMAGE_TAG
objref:
kind: ConfigMap
name: app-config
apiVersion: v1
fieldref:
fieldpath: data.image_tag
commonLabels:
app: myapp
Quando você usa configMapGenerator, Kustomize adiciona um hash ao final do nome (ex: app-config-4h67f2b9) automaticamente, garantindo que mudanças triggerem um rollout de pods. Isso é muito mais eficiente do que patches manuais.
Fluxo de Trabalho Profissional: Organização e Manutenção
Estrutura de Projeto Escalável
Em projetos grandes, você terá múltiplas aplicações. Organize assim:
infra/
├── apps/
│ ├── webapp/
│ │ ├── base/
│ │ └── overlays/
│ │ ├── dev/
│ │ ├── staging/
│ │ └── prod/
│ ├── api/
│ │ ├── base/
│ │ └── overlays/
│ │ ├── dev/
│ │ ├── staging/
│ │ └── prod/
│ └── worker/
│ ├── base/
│ └── overlays/
│ ├── dev/
│ ├── staging/
│ └── prod/
└── clusters/
├── dev-cluster/
│ └── kustomization.yaml
├── staging-cluster/
│ └── kustomization.yaml
└── prod-cluster/
└── kustomization.yaml
Arquivo clusters/prod-cluster/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../apps/webapp/overlays/prod
- ../../apps/api/overlays/prod
- ../../apps/worker/overlays/prod
namespace: production
commonLabels:
cluster: prod-cluster
managed-by: kustomize
Isso permite que uma CI/CD simples execute kubectl apply -k clusters/prod-cluster/ e toda a produção seja atualizada de forma consistente.
Validação e Testes
Antes de aplicar manifests, valide-os:
# Validar sintaxe YAML
kubectl kustomize overlays/prod | kubectl apply --dry-run=client -f -
# Validar contra um cluster específico
kubectl apply -k overlays/prod --dry-run=server -f -
# Usar ferramentas como kubeval
kubectl kustomize overlays/prod | kubeval --strict
# Usar kube-score para boas práticas
kubectl kustomize overlays/prod | kube-score score -
Em um pipeline CI/CD, você rodaria esses comandos antes de fazer merge para garantir que nenhuma configuração inválida entre na base de código.
Versionamento e Mudanças Gradativas
Ao atualizar imagens ou configurações, Kustomize se integra perfeitamente com git. Faça mudanças em um branch, valide com kubectl kustomize, e após merge em main, seu CD pipeline aplica automaticamente. Como todos os manifests finais são gerados, você tem histórico completo de mudanças.
Para mudanças gradativas (canary deployments), combine Kustomize com Flagger ou Argo Rollouts. Kustomize cuida da configuração base, e essas ferramentas gerenciam o rollout inteligente.
Conclusão
Kustomize resolve um problema fundamental em operações Kubernetes: como manter múltiplos ambientes sem duplicar código ou usar templates complexos. Os três conceitos-chave que você deve dominar são: (1) Bases como fonte única de verdade com manifests completos e autossuficientes, (2) Overlays como variações leves para ambientes específicos que referenciam a base sem duplicação, e (3) Patches como modificações cirúrgicas que aplicam mudanças apenas nos campos necessários. Ao estruturar seus projetos dessa forma, você ganha legibilidade, reutilização e controle de versão natural através do git, tudo sem deixar o mundo declarativo do Kubernetes.