<h2>Introdução ao Kustomize: Por que Precisamos Dele</h2>
<p>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 <strong>Kustomize</strong> entra em cena.</p>
<p>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 <strong>bases</strong> (manifests padrão) e <strong>overlays</strong> (variações para ambientes específicos), usando <strong>patches</strong> para aplicar mudanças cirúrgicas. Isso mantém seu código declarativo, versionável e fácil de debugar.</p>
<h2>Arquitetura Fundamental: Bases, Overlays e Patches</h2>
<h3>O Conceito de Bases</h3>
<p>Uma <strong>base</strong> é 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.</p>
<p>Considere uma aplicação web simples. Sua base ficaria assim:</p>
<pre><code>base/
├── kustomization.yaml
├── deployment.yaml
├── service.yaml
└── configmap.yaml</code></pre>
<p>Arquivo <code>base/deployment.yaml</code>:</p>
<pre><code class="language-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"</code></pre>
<p>Arquivo <code>base/service.yaml</code>:</p>
<pre><code class="language-yaml">apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
selector:
app: webapp
ports:
- port: 80
targetPort: 8080
type: ClusterIP</code></pre>
<p>Arquivo <code>base/configmap.yaml</code>:</p>
<pre><code class="language-yaml">apiVersion: v1
kind: ConfigMap
metadata:
name: webapp-config
data:
LOG_LEVEL: "info"
DATABASE_TIMEOUT: "30s"</code></pre>
<p>Arquivo <code>base/kustomization.yaml</code>:</p>
<pre><code class="language-yaml">apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- configmap.yaml
commonLabels:
version: "1.0"
managed-by: kustomize</code></pre>
<p>O arquivo <code>kustomization.yaml</code> é 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.</p>
<h3>O Conceito de Overlays</h3>
<p>Um <strong>overlay</strong> é 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 <code>kustomization.yaml</code> e referencia a base.</p>
<p>A estrutura fica assim:</p>
<pre><code>.
├── 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</code></pre>
<p>Um overlay <strong>nunca</strong> duplica a base. Ele referencia a base e aplica mudanças através de patches. Arquivo <code>overlays/prod/kustomization.yaml</code>:</p>
<pre><code class="language-yaml">apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
patchesStrategicMerge:
- patches/deployment.yaml
replicas:
- name: webapp
count: 5
commonLabels:
environment: prod</code></pre>
<h3>Patches: Aplicando Mudanças Cirúrgicas</h3>
<p>Um <strong>patch</strong> é 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: <strong>strategic merge patches</strong>, <strong>JSON patches</strong> e <strong>patches com comentários</strong>.</p>
<p>Arquivo <code>overlays/prod/patches/deployment.yaml</code> (strategic merge patch):</p>
<pre><code class="language-yaml">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"</code></pre>
<p>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.</p>
<h2>Exemplos Práticos: Do Conceito à Aplicação</h2>
<h3>Exemplo Completo: Aplicação Multi-Ambiente</h3>
<p>Vamos construir uma aplicação real com três ambientes. Começamos criando a estrutura:</p>
<pre><code class="language-bash">mkdir -p kustomize-demo/base kustomize-demo/overlays/{dev,staging,prod}
cd kustomize-demo</code></pre>
<p>Arquivo <code>base/kustomization.yaml</code>:</p>
<pre><code class="language-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"</code></pre>
<p>Arquivo <code>base/deployment.yaml</code>:</p>
<pre><code class="language-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</code></pre>
<p>Arquivo <code>base/service.yaml</code>:</p>
<pre><code class="language-yaml">apiVersion: v1
kind: Service
metadata:
name: api
spec:
type: ClusterIP
ports:
- name: http
port: 80
targetPort: http
selector:
tier: backend</code></pre>
<p>Arquivo <code>base/configmap.yaml</code>:</p>
<pre><code class="language-yaml">apiVersion: v1
kind: ConfigMap
metadata:
name: config
data:
db_host: "postgres.default.svc.cluster.local"
cache_ttl: "300"
log_level: "info"</code></pre>
<p>Agora os overlays. Arquivo <code>overlays/dev/kustomization.yaml</code>:</p>
<pre><code class="language-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-</code></pre>
<p>Arquivo <code>overlays/dev/patches/deployment.yaml</code>:</p>
<pre><code class="language-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"</code></pre>
<p>Arquivo <code>overlays/prod/kustomization.yaml</code>:</p>
<pre><code class="language-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-</code></pre>
<p>Arquivo <code>overlays/prod/patches/deployment.yaml</code>:</p>
<pre><code class="language-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"</code></pre>
<p>Arquivo <code>overlays/prod/patches/service.yaml</code>:</p>
<pre><code class="language-yaml">apiVersion: v1
kind: Service
metadata:
name: api
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
spec:
type: LoadBalancer</code></pre>
<p>Para ver o resultado final sem aplicar, use:</p>
<pre><code class="language-bash"># 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</code></pre>
<h3>Técnicas Avançadas: Vars e ConfigMap Generators</h3>
<p>Além de patches simples, Kustomize oferece funcionalidades avançadas. Use <strong>vars</strong> para substituir valores referenciados e <strong>generators</strong> para criar ConfigMaps e Secrets dinamicamente.</p>
<p>Arquivo <code>base/kustomization.yaml</code> (versão expandida):</p>
<pre><code class="language-yaml">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</code></pre>
<p>Quando você usa <code>configMapGenerator</code>, Kustomize adiciona um hash ao final do nome (ex: <code>app-config-4h67f2b9</code>) automaticamente, garantindo que mudanças triggerem um rollout de pods. Isso é muito mais eficiente do que patches manuais.</p>
<h2>Fluxo de Trabalho Profissional: Organização e Manutenção</h2>
<h3>Estrutura de Projeto Escalável</h3>
<p>Em projetos grandes, você terá múltiplas aplicações. Organize assim:</p>
<pre><code>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</code></pre>
<p>Arquivo <code>clusters/prod-cluster/kustomization.yaml</code>:</p>
<pre><code class="language-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</code></pre>
<p>Isso permite que uma CI/CD simples execute <code>kubectl apply -k clusters/prod-cluster/</code> e toda a produção seja atualizada de forma consistente.</p>
<h3>Validação e Testes</h3>
<p>Antes de aplicar manifests, valide-os:</p>
<pre><code class="language-bash"># 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 -</code></pre>
<p>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.</p>
<h3>Versionamento e Mudanças Gradativas</h3>
<p>Ao atualizar imagens ou configurações, Kustomize se integra perfeitamente com git. Faça mudanças em um branch, valide com <code>kubectl kustomize</code>, 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.</p>
<p>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.</p>
<h2>Conclusão</h2>
<p>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: <strong>(1) Bases</strong> como fonte única de verdade com manifests completos e autossuficientes, <strong>(2) Overlays</strong> como variações leves para ambientes específicos que referenciam a base sem duplicação, e <strong>(3) Patches</strong> 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.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://kustomize.io/" target="_blank" rel="noopener noreferrer">Dokumentação Oficial do Kustomize</a></li>
<li><a href="https://kubernetes.io/docs/tasks/manage-kubernetes-objects/declarative-config/#kustomize" target="_blank" rel="noopener noreferrer">Kubernetes Kustomize na Documentação Oficial</a></li>
<li><a href="https://kubectl.docs.kubernetes.io/guides/introduction/kustomize/" target="_blank" rel="noopener noreferrer">Guia Prático: Kustomize Patterns</a></li>
<li><a href="https://itnext.io/kustomize-template-free-kubernetes-deployment-cd247f45f856" target="_blank" rel="noopener noreferrer">Real World Kubernetes Configuration Management with Kustomize</a></li>
<li><a href="https://www.manning.com/books/kubernetes-in-action-second-edition" target="_blank" rel="noopener noreferrer">Livro: Kubernetes in Action (Marko Lukšsa) - Capítulo sobre Kustomize</a></li>
</ul>
<p><!-- FIM --></p>