Helm Fundamentos: Charts, Values, Templates e Releases
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).
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.
1. Charts: Estrutura e Anatomia
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.
Estrutura de um Chart
Quando você cria um Chart com helm create minha-aplicacao, você obtém a seguinte estrutura:
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)
O arquivo Chart.yaml é o coração do Chart. Ele define metadados essenciais:
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
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: helm install release-name ./minha-aplicacao.
2. Values: Parametrizando Comportamentos
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 values.yaml como variáveis que podem ser sobrescritas.
Estrutura e Hierarquia de Values
O arquivo values.yaml é 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:
# 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
A beleza dos Values está na sobrescrita em camadas. Você pode fornecer valores em múltiplos níveis:
- Padrão:
values.yamldo Chart - CLI: via flag
-f arquivo.yamlou--set chave=valor - Ambiente: usando arquivos como
values-prod.yaml,values-dev.yaml
Exemplo de uso prático:
# 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
A hierarquia é importante: valores fornecidos via --set sobrescrevem tudo; valores de -f sobrescrevem padrões; e padrões são usados como base.
3. Templates: Gerando YAML Dinamicamente
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.
Sintaxe Básica de Templates
Um template Helm usa a sintaxe {{ }} para inserir variáveis e lógica. Aqui está um exemplo de um deployment.yaml típico:
# 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 }}
Vamos decompor os elementos principais:
1. Variáveis simples:
replicas: {{ .Values.replicaCount }} # Substitui pelo valor de replicaCount
image: {{ .Values.image.repository }} # Acessa valores aninhados
2. Funções e pipes:
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
3. Lógica condicional:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
# Só inclui a linha se autoscaling NÃO estiver ativado
4. Loops:
env:
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
# Itera sobre um mapa de variáveis de ambiente
Templates Auxiliares (_helpers.tpl)
O arquivo _helpers.tpl contém macros reutilizáveis. Evita duplicação e garante consistência:
{{/*
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 }}
Essas macros garantem que nomes, labels e metadados sejam consistentes em todos os manifestos.
Renderização em Tempo Real
Para validar seus templates antes de instalar, use:
# 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
Isso é crucial para debugging — você vê exatamente qual YAML será aplicado.
4. Releases: Instalação e Gerenciamento
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.
Ciclo de Vida de uma Release
# 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
Exemplo Prático: Fluxo Completo
Vamos simular um fluxo real:
# 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
Namespaces e Isolamento
Releases podem ser instaladas em diferentes namespaces, permitindo isolamento perfeito:
# 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
Estratégia de Versionamento
Para manter Releases estáveis, mantenha uma estratégia clara:
# 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
Integrando Tudo: Um Exemplo Real
Vamos colocar todos os conceitos em prática com uma aplicação real simples (uma API Python):
Chart.yaml:
apiVersion: v2
name: api-python
description: Uma API Python com FastAPI
type: application
version: 1.0.0
appVersion: "0.1.0"
values.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"
templates/deployment.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 }}
Instalação e uso:
# 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
Conclusão
Helm não é apenas um gerenciador de pacotes — é um framework para abstrair a complexidade do Kubernetes mantendo flexibilidade. Você aprendeu que:
-
Charts são pacotes reutilizáveis 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.
-
Values são o mecanismo de parametrização 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.
-
Templates geram YAML dinamicamente 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.
-
Releases são instâncias de Charts instaladas 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.