Docker & Kubernetes

O que Todo Dev Deve Saber sobre Kustomize em Kubernetes: Overlays, Patches e Bases Reutilizáveis

14 min de leitura

O que Todo Dev Deve Saber sobre Kustomize em Kubernetes: Overlays, Patches e Bases Reutilizáveis

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 é

<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 &quot;padrão&quot; 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: &quot;256Mi&quot;

cpu: &quot;100m&quot;

limits:

memory: &quot;512Mi&quot;

cpu: &quot;500m&quot;</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: &quot;info&quot;

DATABASE_TIMEOUT: &quot;30s&quot;</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: &quot;1.0&quot;

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: &quot;512Mi&quot;

cpu: &quot;250m&quot;

limits:

memory: &quot;1Gi&quot;

cpu: &quot;1000m&quot;

env:

  • name: LOG_LEVEL

value: &quot;warn&quot;</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: &quot;true&quot;

namePrefix: myapp-

commonAnnotations:

kustomize.config.k8s.io/version: &quot;4.5.7&quot;</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: &quot;postgres.default.svc.cluster.local&quot;

cache_ttl: &quot;300&quot;

log_level: &quot;info&quot;</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: &quot;true&quot;</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: &quot;prod-postgres.production.svc.cluster.local&quot;</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: &quot;nlb&quot;

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>&lt;!-- FIM --&gt;</p>

Comentários

Mais em Docker & Kubernetes

Redes em Docker: bridge, host, overlay e macvlan na Prática na Prática
Redes em Docker: bridge, host, overlay e macvlan na Prática na Prática

Entendendo as Redes no Docker: Uma Perspectiva Prática Quando você começa a t...

Dominando Segurança em Docker: Rootless Containers, Seccomp e AppArmor em Projetos Reais
Dominando Segurança em Docker: Rootless Containers, Seccomp e AppArmor em Projetos Reais

Introdução: O Cenário Atual de Segurança em Containers Docker revolucionou a...

Como Usar Kubernetes: Arquitetura do Cluster, Control Plane e Worker Nodes em Produção
Como Usar Kubernetes: Arquitetura do Cluster, Control Plane e Worker Nodes em Produção

Introdução ao Kubernetes e Sua Arquitetura Kubernetes, frequentemente abrevia...