<h2>Helm Fundamentos: Charts, Values, Templates e Releases</h2>
<p>Helm é o gerenciador de pacotes do Kubernetes, frequentemente comparado ao apt, yum ou npm, mas para orquestrações containerizadas. Se você está aqui, provavelmente já conhece a dor de escrever centenas de linhas de YAML para cada deployment no Kubernetes — Helm resolve isso de forma elegante e reutilizável. Neste artigo, vamos explorar os quatro pilares do Helm: Charts (como os pacotes são organizados), Values (como parametrizamos comportamentos), Templates (como geramos YAMLs dinamicamente) e Releases (como instalamos e atualizamos na prática).</p>
<p>A proposta do Helm é simples: evitar duplicação, aumentar consistência entre ambientes e permitir que você compartilhe aplicações Kubernetes de forma encapsulada. Você verá que esses quatro conceitos não são isolados — eles trabalham juntos em um ecossistema coeso e bem pensado.</p>
<h2>1. Charts: Estrutura e Anatomia</h2>
<p>Um Chart no Helm é um pacote que contém tudo o que você precisa para rodar uma aplicação no Kubernetes. Pense nele como um diretório estruturado que segue convenções específicas, similar a um repositório npm ou um pacote Python, mas orientado ao Kubernetes.</p>
<h3>Estrutura de um Chart</h3>
<p>Quando você cria um Chart com <code>helm create minha-aplicacao</code>, você obtém a seguinte estrutura:</p>
<pre><code>minha-aplicacao/
├── Chart.yaml # Metadados do Chart (nome, versão, descrição)
├── values.yaml # Valores padrão (configurações)
├── charts/ # Dependências (outros Charts)
├── templates/ # Arquivos de template (YAML + Helm)
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── configmap.yaml
│ ├── _helpers.tpl # Templates auxiliares reutilizáveis
│ └── NOTES.txt # Instruções pós-instalação
├── README.md # Documentação do Chart
├── .helmignore # Arquivos ignorados (como .gitignore)
└── values-*.yaml # Valores específicos de ambientes (opcional)</code></pre>
<p>O arquivo <code>Chart.yaml</code> é o coração do Chart. Ele define metadados essenciais:</p>
<pre><code class="language-yaml">apiVersion: v2 # Versão da API do Helm (v2 é moderna)
name: minha-aplicacao # Nome único do Chart
description: Minha aplicação # Descrição breve
type: application # application ou library
version: 1.0.0 # Versão do Chart
appVersion: "2.4.1" # Versão da aplicação dentro do Chart
keywords:
- web
- api
home: https://github.com/user/repo
sources:
- https://github.com/user/repo
maintainers:
- name: João Silva
email: joao@example.com</code></pre>
<p>Um Chart bem estruturado é autossuficiente. Ele não apenas encapsula a aplicação, mas também documenta como usá-la, permitindo que qualquer pessoa instale sua aplicação com um único comando: <code>helm install release-name ./minha-aplicacao</code>.</p>
<h2>2. Values: Parametrizando Comportamentos</h2>
<p>Values (valores) são o mecanismo pelo qual você torna um Chart flexível. Ao invés de codificar imagens Docker, réplicas ou limites de recursos diretamente nos templates YAML, você os coloca no arquivo <code>values.yaml</code> como variáveis que podem ser sobrescritas.</p>
<h3>Estrutura e Hierarquia de Values</h3>
<p>O arquivo <code>values.yaml</code> é um arquivo YAML contendo valores padrão para sua aplicação. Quando você instala um Chart, o Helm mescla esse arquivo com valores fornecidos via CLI ou arquivos adicionais:</p>
<pre><code class="language-yaml"># values.yaml - Arquivo padrão do Chart
replicaCount: 3
image:
repository: minha-aplicacao
pullPolicy: IfNotPresent
tag: "2.4.1"
service:
type: ClusterIP
port: 80
targetPort: 8080
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
ingress:
enabled: true
className: nginx
hosts:
- host: app.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: app-tls
hosts:
- app.example.com
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 10
targetCPUUtilizationPercentage: 80</code></pre>
<p>A beleza dos Values está na <strong>sobrescrita em camadas</strong>. Você pode fornecer valores em múltiplos níveis:</p>
<ol>
<li><strong>Padrão</strong>: <code>values.yaml</code> do Chart</li>
<li><strong>CLI</strong>: via flag <code>-f arquivo.yaml</code> ou <code>--set chave=valor</code></li>
<li><strong>Ambiente</strong>: usando arquivos como <code>values-prod.yaml</code>, <code>values-dev.yaml</code></li>
</ol>
<p>Exemplo de uso prático:</p>
<pre><code class="language-bash"># Usar valores padrão
helm install minha-release ./minha-aplicacao
Sobrescrever via CLI
helm install minha-release ./minha-aplicacao \
--set replicaCount=5 \
--set image.tag=3.0.0
Usar arquivo de valores customizado
helm install minha-release ./minha-aplicacao \
-f values-producao.yaml
Combinar múltiplos arquivos (último sobrescreve anterior)
helm install minha-release ./minha-aplicacao \
-f values.yaml \
-f values-prod.yaml \
--set replicaCount=10</code></pre>
<p>A hierarquia é importante: valores fornecidos via <code>--set</code> sobrescrevem tudo; valores de <code>-f</code> sobrescrevem padrões; e padrões são usados como base.</p>
<h2>3. Templates: Gerando YAML Dinamicamente</h2>
<p>Templates são onde a mágica acontece. Eles são arquivos YAML enriquecidos com lógica de template (usando Go templating, o mesmo do Docker Compose). O Helm processa esses templates, substituindo placeholders por valores reais, e gera YAML válido para o Kubernetes.</p>
<h3>Sintaxe Básica de Templates</h3>
<p>Um template Helm usa a sintaxe <code>{{ }}</code> para inserir variáveis e lógica. Aqui está um exemplo de um <code>deployment.yaml</code> típico:</p>
<pre><code class="language-yaml"># templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "minha-aplicacao.fullname" . }}
labels:
{{- include "minha-aplicacao.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "minha-aplicacao.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "minha-aplicacao.selectorLabels" . | nindent 8 }}
spec:
serviceAccountName: {{ include "minha-aplicacao.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
protocol: TCP
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 5
periodSeconds: 5
resources:
{{- toYaml .Values.resources | nindent 12 }}
env:
- name: ENVIRONMENT
value: {{ .Values.environment | quote }}
- name: LOG_LEVEL
value: {{ .Values.logLevel | quote }}</code></pre>
<p>Vamos decompor os elementos principais:</p>
<p><strong>1. Variáveis simples:</strong></p>
<pre><code class="language-yaml">replicas: {{ .Values.replicaCount }} # Substitui pelo valor de replicaCount
image: {{ .Values.image.repository }} # Acessa valores aninhados</code></pre>
<p><strong>2. Funções e pipes:</strong></p>
<pre><code class="language-yaml">image: "{{ .Values.image.tag | default .Chart.AppVersion }}"
Usa .Values.image.tag, ou .Chart.AppVersion se vazio (função default)
labels: {{- include "minha-aplicacao.labels" . | nindent 4 }}
Inclui um template auxiliar (_helpers.tpl) e indenta o resultado</code></pre>
<p><strong>3. Lógica condicional:</strong></p>
<pre><code class="language-yaml">{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
Só inclui a linha se autoscaling NÃO estiver ativado</code></pre>
<p><strong>4. Loops:</strong></p>
<pre><code class="language-yaml">env:
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
Itera sobre um mapa de variáveis de ambiente</code></pre>
<h3>Templates Auxiliares (_helpers.tpl)</h3>
<p>O arquivo <code>_helpers.tpl</code> contém macros reutilizáveis. Evita duplicação e garante consistência:</p>
<pre><code class="language-tpl">{{/*
Expandir o nome do Chart.
*/}}
{{- define "minha-aplicacao.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Criar um nome totalmente qualificado para a aplicação.
*/}}
{{- define "minha-aplicacao.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Labels comuns
*/}}
{{- define "minha-aplicacao.labels" -}}
helm.sh/chart: {{ include "minha-aplicacao.chart" . }}
{{ include "minha-aplicacao.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Seletor de labels
*/}}
{{- define "minha-aplicacao.selectorLabels" -}}
app.kubernetes.io/name: {{ include "minha-aplicacao.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}</code></pre>
<p>Essas macros garantem que nomes, labels e metadados sejam consistentes em todos os manifestos.</p>
<h3>Renderização em Tempo Real</h3>
<p>Para validar seus templates <strong>antes</strong> de instalar, use:</p>
<pre><code class="language-bash"># Ver o YAML gerado sem instalar
helm template minha-release ./minha-aplicacao
Com valores customizados
helm template minha-release ./minha-aplicacao \
-f values-prod.yaml \
--set replicaCount=5
Salvar em arquivo para revisão
helm template minha-release ./minha-aplicacao > manifesto.yaml
cat manifesto.yaml</code></pre>
<p>Isso é crucial para debugging — você vê exatamente qual YAML será aplicado.</p>
<h2>4. Releases: Instalação e Gerenciamento</h2>
<p>Um Release é uma instância de um Chart rodando no seu cluster Kubernetes. Se um Chart é como um pacote (similar a um .deb no apt), uma Release é como um programa instalado no seu computador. Você pode ter múltiplas Releases do mesmo Chart com configurações diferentes.</p>
<h3>Ciclo de Vida de uma Release</h3>
<pre><code class="language-bash"># 1. Instalar uma Release
helm install minha-release ./minha-aplicacao
Resultado: Release "minha-release" instalada no namespace default
2. Listar Releases
helm list # Todas as Releases em todos namespaces
helm list -n producao # Apenas no namespace 'producao'
helm list --all-namespaces # Mais explícito
3. Ver status e detalhes
helm status minha-release # Status atual
helm get values minha-release # Valores usados nesta Release
helm get manifest minha-release # YAML gerado e instalado
helm get notes minha-release # Notas pós-instalação (NOTES.txt)
4. Atualizar uma Release
helm upgrade minha-release ./minha-aplicacao \
--set replicaCount=5
Resultará em um novo revision
5. Histórico de mudanças
helm history minha-release # Todas as revisões
helm history minha-release -o yaml # Em formato YAML
6. Reverter para versão anterior
helm rollback minha-release 1 # Volta à revisão 1
helm rollback minha-release # Volta à revisão anterior
7. Desinstalar
helm uninstall minha-release # Remove toda a Release</code></pre>
<h3>Exemplo Prático: Fluxo Completo</h3>
<p>Vamos simular um fluxo real:</p>
<pre><code class="language-bash"># 1. Criar um Chart básico
helm create meu-site
2. Customizar values.yaml para nossa aplicação
cat > meu-site/values.yaml <<EOF
replicaCount: 2
image:
repository: nginx
tag: "1.21.0"
pullPolicy: IfNotPresent
service:
type: LoadBalancer
port: 80
targetPort: 80
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
EOF
3. Validar o Chart
helm lint meu-site
Output: ==> Linting meu-site
1 chart(s) linted, 0 chart(s) failed
4. Ver o YAML antes de instalar
helm template meu-site ./meu-site --set replicaCount=3
5. Instalar no cluster
helm install website meu-site \
--namespace producao \
--create-namespace \
-f meu-site/values.yaml
6. Verificar a instalação
helm list -n producao
helm status website -n producao
kubectl get pods -n producao
7. Fazer upgrade para nova versão
helm upgrade website meu-site \
--namespace producao \
--set image.tag=1.22.0
8. Ver histórico
helm history website -n producao
9. Se algo der errado, reverter
helm rollback website -n producao
helm status website -n producao
10. Desinstalar quando não precisar mais
helm uninstall website -n producao</code></pre>
<h3>Namespaces e Isolamento</h3>
<p>Releases podem ser instaladas em diferentes namespaces, permitindo isolamento perfeito:</p>
<pre><code class="language-bash"># Instalar a mesma aplicação em múltiplos ambientes
helm install app-dev meu-site -n desenvolvimento
helm install app-prod meu-site -n producao --set replicaCount=5
Cada uma é independente
helm list -n desenvolvimento
helm list -n producao
Upgrades isolados por namespace
helm upgrade app-dev meu-site -n desenvolvimento --set image.tag=2.0.0
helm upgrade app-prod meu-site -n producao --set image.tag=2.0.0
Ambas continuam rodando independentemente</code></pre>
<h3>Estratégia de Versionamento</h3>
<p>Para manter Releases estáveis, mantenha uma estratégia clara:</p>
<pre><code class="language-bash"># Use tags semânticas
helm install app-v1.0.0 meu-site --set version=1.0.0
Para upgrades menores, use upgrade
helm upgrade app-v1.0.0 meu-site --set version=1.1.0
Para mudanças maiores, considere release novo
helm install app-v2.0.0 meu-site --set version=2.0.0
Manter ambas rodando em paralelo é possível
helm list # Mostra app-v1.0.0 e app-v2.0.0</code></pre>
<h2>Integrando Tudo: Um Exemplo Real</h2>
<p>Vamos colocar todos os conceitos em prática com uma aplicação real simples (uma API Python):</p>
<p><strong>Chart.yaml:</strong></p>
<pre><code class="language-yaml">apiVersion: v2
name: api-python
description: Uma API Python com FastAPI
type: application
version: 1.0.0
appVersion: "0.1.0"</code></pre>
<p><strong>values.yaml:</strong></p>
<pre><code class="language-yaml">replicaCount: 3
image:
repository: meu-registo.azurecr.io/api-python
pullPolicy: IfNotPresent
tag: "0.1.0"
service:
type: ClusterIP
port: 80
targetPort: 8000
ingress:
enabled: true
className: nginx
hosts:
- host: api.exemplo.com
paths:
- path: /
pathType: Prefix
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
env:
DEBUG: "false"
LOG_LEVEL: "info"
DATABASE_URL: "postgresql://user:pass@postgres:5432/db"</code></pre>
<p><strong>templates/deployment.yaml:</strong></p>
<pre><code class="language-yaml">apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "api-python.fullname" . }}
labels:
{{- include "api-python.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "api-python.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "api-python.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 5
periodSeconds: 5
resources:
{{- toYaml .Values.resources | nindent 12 }}
env:
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}</code></pre>
<p><strong>Instalação e uso:</strong></p>
<pre><code class="language-bash"># Desenvolvimento (poucos recursos, debug ativado)
helm install api-dev ./api-python \
--namespace dev \
--create-namespace \
--set replicaCount=1 \
--set env.DEBUG=true \
--set env.LOG_LEVEL=debug \
--set resources.requests.cpu=100m
Produção (muitos recursos, debug desativado)
helm install api-prod ./api-python \
--namespace prod \
--create-namespace \
--set replicaCount=5 \
--set ingress.hosts[0].host=api.producao.com \
--set resources.limits.cpu=1000m \
--set resources.limits.memory=1Gi
Upgrade em produção
helm upgrade api-prod ./api-python \
--namespace prod \
--set image.tag=0.2.0 \
--set replicaCount=10</code></pre>
<h2>Conclusão</h2>
<p>Helm não é apenas um gerenciador de pacotes — é um framework para abstrair a complexidade do Kubernetes mantendo flexibilidade. Você aprendeu que:</p>
<ol>
<li><strong>Charts são pacotes reutilizáveis</strong> que encapsulam toda a configuração de uma aplicação em Kubernetes, seguindo uma estrutura padrão (Chart.yaml, values.yaml, templates/). Eles permitem compartilhar aplicações de forma versionada e documentada.</li>
</ol>
<ol>
<li><strong>Values são o mecanismo de parametrização</strong> que torna Charts adaptáveis a diferentes ambientes sem modificar templates. A combinação de arquivo padrão, sobrescrita via arquivo e CLI oferece flexibilidade máxima para seus deployments.</li>
</ol>
<ol>
<li><strong>Templates geram YAML dinamicamente</strong> usando Go templating, transformando valores em manifestos Kubernetes válidos. Funcionalidades como condicionais, loops, funções e templates auxiliares evitam duplicação e garantem consistência.</li>
</ol>
<ol>
<li><strong>Releases são instâncias de Charts instaladas</strong> no seu cluster, com histórico completo de versões e capacidade de rollback. Você pode rodar múltiplas releases do mesmo Chart com diferentes configurações, oferecendo isolamento perfeito entre ambientes.</li>
</ol>
<h2>Referências</h2>
<ul>
<li><a href="https://helm.sh/docs/" target="_blank" rel="noopener noreferrer">Documentação Oficial do Helm</a></li>
<li><a href="https://helm.sh/docs/chart_best_practices/" target="_blank" rel="noopener noreferrer">Helm Chart Best Practices</a></li>
<li><a href="https://pkg.go.dev/text/template" target="_blank" rel="noopener noreferrer">Go Template Language Reference</a></li>
<li><a href="https://kubernetes.io/docs/" target="_blank" rel="noopener noreferrer">Kubernetes Official Documentation</a></li>
<li><a href="https://artifacthub.io/" target="_blank" rel="noopener noreferrer">Helm Hub - Repository de Charts Públicos</a></li>
</ul>
<p><!-- FIM --></p>