Hooks no Helm: Automatizando Eventos do Ciclo de Vida
Hooks são pontos de execução que permitem você rodar scripts ou ações em momentos específicos do ciclo de vida de um release Helm. Diferentemente de um chart tradicional que simplesmente instala recursos, hooks permitem executar tarefas críticas como validações de banco de dados, migrations, backups ou limpeza de recursos órfãos antes ou depois de uma instalação, upgrade ou deletion.
Os hooks disponíveis no Helm são: pre-install, post-install, pre-upgrade, post-upgrade, pre-delete, post-delete, pre-rollback e post-rollback. Cada um representa um momento estratégico. Por exemplo, você pode usar um hook pre-install para criar um namespace customizado ou um post-install para esperar que todos os pods estejam prontos. A chave para entender hooks é reconhecer que eles são manifestos Kubernetes normais anotados com metadados especiais — não são lógica mágica, são recursos que rodam em sequência controlada.
Estrutura e Anotação de Hooks
Todo hook no Helm é um recurso Kubernetes convencional (Pod, Job, ConfigMap, etc.) que recebe a anotação helm.sh/hook com o tipo de evento. Você também pode especificar helm.sh/hook-weight para controlar a ordem de execução entre múltiplos hooks do mesmo tipo e helm.sh/hook-delete-policy para definir o que fazer com o recurso após execução.
Veja um exemplo prático de um hook pre-install que valida a conectividade com um banco de dados antes de instalar a aplicação:
apiVersion: v1
kind: Pod
metadata:
name: {{ include "minha-app.fullname" . }}-db-check
namespace: {{ .Release.Namespace }}
annotations:
helm.sh/hook: pre-install
helm.sh/hook-weight: "0"
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
spec:
serviceAccountName: {{ include "minha-app.serviceAccountName" . }}
restartPolicy: Never
containers:
- name: db-check
image: postgres:15-alpine
command:
- sh
- -c
- |
echo "Verificando conectividade com banco de dados..."
pg_isready -h {{ .Values.database.host }} -p {{ .Values.database.port }} -U {{ .Values.database.user }}
if [ $? -eq 0 ]; then
echo "Banco de dados está acessível!"
exit 0
else
echo "Falha ao conectar ao banco de dados!"
exit 1
fi
Neste exemplo, o pod executa antes de qualquer instalação (pre-install), com peso 0 (executa primeiro se houver múltiplos hooks), e é deletado automaticamente após sucesso ou falha (hook-succeeded ou hook-failed). Se o comando pg_isready falhar, todo o release falha — é uma validação crítica.
Hooks com Jobs para Operações Complexas
Em cenários mais robustos, use Kubernetes Jobs em vez de Pods simples. Jobs oferecem retry automático e melhor controle de execução. Um caso clássico é executar database migrations antes de um upgrade:
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "minha-app.fullname" . }}-db-migrate-{{ .Release.Revision }}
namespace: {{ .Release.Namespace }}
annotations:
helm.sh/hook: pre-upgrade
helm.sh/hook-weight: "-5"
helm.sh/hook-delete-policy: before-hook-creation
spec:
backoffLimit: 3
template:
metadata:
name: db-migrate
spec:
serviceAccountName: {{ include "minha-app.serviceAccountName" . }}
restartPolicy: Never
containers:
- name: migrate
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
command: ["/bin/sh", "-c"]
args:
- |
echo "Iniciando migrations..."
./migrations.sh
echo "Migrations concluídas com sucesso!"
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: {{ include "minha-app.fullname" . }}-db-secret
key: url
volumeMounts:
- name: migrations
mountPath: /migrations
volumes:
- name: migrations
configMap:
name: {{ include "minha-app.fullname" . }}-migrations
defaultMode: 0755
Este Job roda antes de qualquer upgrade (pre-upgrade), com peso -5 para executar antes de outros hooks com peso neutro. A política before-hook-creation garante que se o Job falhar, um novo será criado na próxima tentativa sem conflito de nome. O backoffLimit: 3 permite até 3 tentativas automáticas.
Library Charts: Reutilização de Código em Charts Helm
Library Charts são charts especiais projetados apenas para fornecer templates, helpers e funções reutilizáveis. Diferentemente de um chart normal que instala recursos, um library chart não instala nada por si só — ele serve como dependência que você importa em outros charts. Isso elimina duplicação massiva quando você tem múltiplos microsserviços com padrões similares: mesmo deployment template, mesmos ConfigMaps, mesmas policies.
A beleza de um library chart é que você define uma vez o padrão correto (labels, annotations, resource limits, security contexts) e todos os seus charts herdam isso automaticamente. Se precisar ajustar a policy de segurança globalmente, faz uma mudança em um local. Sem library charts, você teria que atualizar 50 charts diferentes — e garantir consistência manual é praticamente impossível.
Estrutura e Criação de um Library Chart
Um library chart mantém a mesma estrutura de um chart normal, mas a flag type: library no Chart.yaml indica seu propósito. O conteúdo fica em templates/ e charts/ segue as mesmas convenções. A diferença crucial: você quase nunca coloca recursos em templates/ — em vez disso, coloca helpers Helm (funções Go template) que outros charts usam.
Crie um library chart básico com helm create meu-library --type library. Estrutura:
meu-library/
├── Chart.yaml
├── values.yaml
├── templates/
│ ├── _helpers.tpl # Helpers reutilizáveis
│ ├── _deployment.tpl # Template de deployment genérico
│ ├── _service.tpl # Template de service genérico
│ └── _ingress.tpl # Template de ingress genérico
└── README.md
No Chart.yaml:
apiVersion: v2
name: meu-library
description: Library chart com templates comuns para microsserviços
type: library
version: 1.0.0
appVersion: "1.0"
maintainers:
- name: Seu Time
email: seu-time@empresa.com
Implementação de Templates Reutilizáveis
Templates em library charts usam a convenção de underscore (ex: _helpers.tpl) e são incluídos em outros charts, não renderizados diretamente. Vamos criar helpers para labels e um template de deployment genérico:
{{/* templates/_helpers.tpl */}}
{{/*
Cria o nome completo do release
*/}}
{{- define "meu-library.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 padrão para toda a organização
*/}}
{{- define "meu-library.labels" -}}
helm.sh/chart: {{ include "meu-library.chart" . }}
{{ include "meu-library.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
organization: acme-corp
team: {{ .Values.team | default "devops" }}
{{- end }}
{{/*
Selector labels para matchLabels
*/}}
{{- define "meu-library.selectorLabels" -}}
app.kubernetes.io/name: {{ include "meu-library.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Template genérico de Deployment
Uso: {{ include "meu-library.deployment" .Values.deploymentConfig }}
*/}}
{{- define "meu-library.deployment" -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "meu-library.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "meu-library.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount | default 2 }}
selector:
matchLabels:
{{- include "meu-library.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
labels:
{{- include "meu-library.selectorLabels" . | nindent 8 }}
spec:
serviceAccountName: {{ include "meu-library.fullname" . }}
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy | default "IfNotPresent" }}
ports:
- name: http
containerPort: 8080
protocol: TCP
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 10
periodSeconds: 5
resources:
limits:
cpu: {{ .Values.resources.limits.cpu | default "500m" }}
memory: {{ .Values.resources.limits.memory | default "512Mi" }}
requests:
cpu: {{ .Values.resources.requests.cpu | default "250m" }}
memory: {{ .Values.resources.requests.memory | default "256Mi" }}
{{- end }}
Usando Library Charts em Charts Dependentes
Para usar seu library chart em outro chart, declare-o como dependência. No Chart.yaml do seu chart consumidor:
apiVersion: v2
name: meu-microsservico
description: Microsserviço usando templates de library
type: application
version: 2.1.0
appVersion: "1.5"
dependencies:
- name: meu-library
version: "1.0.0"
repository: file://../meu-library
alias: lib
Execute helm dependency update para trazer a dependência. Agora no values.yaml do seu microsserviço:
lib:
team: backend
replicaCount: 3
image:
repository: seu-registry.azurecr.io/seu-microsservico
tag: "1.5.0"
resources:
limits:
cpu: "1000m"
memory: "1Gi"
requests:
cpu: "500m"
memory: "512Mi"
E no templates/deployment.yaml do seu chart:
{{ include "lib.deployment" . }}
Pronto — você tem um deployment completo, com segurança, probes e labels padrão, sem escrever uma linha de YAML novo.
Chart Museum: Hospedando e Gerenciando Repositórios Helm
ChartMuseum é um servidor HTTP open-source projetado especificamente para hospedar, indexar e servir charts Helm. Ele abstrai a complexidade de manter um repositório em S3, GCS, Azure Blob ou sistema de arquivos. Sem ChartMuseum, você precisaria gerenciar manualmente arquivos .tgz, atualizar index.yaml, configurar CORS, e lidar com versionamento — tudo manualmente e propenso a erros. ChartMuseum automatiza tudo: você faz push de um chart e ele se registra sozinho.
A razão pela qual ChartMuseum é crítico em ambientes corporativos é simples: você tem dezenas ou centenas de charts internos, múltiplas equipes atualizando-os constantemente, e precisa de versionamento automático, rastreamento de metadados, segurança (autenticação/autorização) e replicação entre ambientes. ChartMuseum fornece tudo isso com uma API clean.
Instalação e Configuração de ChartMuseum
A forma mais comum é instalar ChartMuseum via Helm (sim, existe um chart para ChartMuseum). Primeiro, adicione o repositório oficial:
helm repo add chartmuseum https://chartmuseum.github.io/charts
helm repo update
Depois, crie um values-chartmuseum.yaml com sua configuração:
replicaCount: 2
image:
repository: ghcr.io/chartmuseum/chartmuseum
tag: "v0.15.0"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 8080
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: chartmuseum-auth
nginx.ingress.kubernetes.io/auth-realm: "ChartMuseum"
hosts:
- host: charts.sua-empresa.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: chartmuseum-tls
hosts:
- charts.sua-empresa.com
env:
STORAGE: local
DEBUG: "false"
ALLOW_OVERWRITE: "true"
DISABLE_API: "false"
DISABLE_METRICS: "false"
persistence:
enabled: true
storageClass: standard
accessMode: ReadWriteOnce
size: 50Gi
resources:
limits:
cpu: "1000m"
memory: "1Gi"
requests:
cpu: "500m"
memory: "512Mi"
securityContext:
runAsNonRoot: true
runAsUser: 65534
fsGroup: 65534
Instale com:
helm install chartmuseum chartmuseum/chartmuseum \
--namespace chartmuseum \
--create-namespace \
-f values-chartmuseum.yaml
Configurando Armazenamento Externo e Autenticação
Para ambientes produção, use armazenamento externo (S3, Azure Blob, etc.) em vez de local. Modifique o values-chartmuseum.yaml:
env:
STORAGE: amazon
STORAGE_AMAZON_BUCKET: seu-bucket-s3
STORAGE_AMAZON_PREFIX: charts/
STORAGE_AMAZON_REGION: us-east-1
ALLOW_OVERWRITE: "true"
DISABLE_API: "false"
DISABLE_METRICS: "false"
# Autenticação básica via Secret
authSecret:
enabled: true
login: seu-usuario
password: sua-senha-segura
# Alternativa: autenticação Bearer Token para CI/CD
bearerAuth:
enabled: true
secret: seu-token-secreto
persistence:
enabled: false # Não precisa com S3
Crie o secret de autenticação antes:
kubectl create secret generic chartmuseum-auth \
--from-literal=username=seu-usuario \
--from-literal=password=$(openssl rand -base64 12) \
-n chartmuseum
Integração com Pipeline CI/CD
Configurar seu pipeline para fazer push de charts para ChartMuseum é trivial. Usando um Helm plugin chamado helm-push:
# Instale o plugin
helm plugin install https://github.com/chartmuseum/helm-push.git
# No seu pipeline (ex: GitHub Actions)
- name: Push chart to ChartMuseum
run: |
helm cm-push ./seu-chart \
--username=${{ secrets.CHARTMUSEUM_USERNAME }} \
--password=${{ secrets.CHARTMUSEUM_PASSWORD }} \
https://charts.sua-empresa.com
Ou usando curl direto (útil se não quiser plugin):
#!/bin/bash
CHART_NAME="seu-chart"
CHART_VERSION="1.2.3"
CHARTMUSEUM_URL="https://charts.sua-empresa.com"
USERNAME="${CHARTMUSEUM_USERNAME}"
PASSWORD="${CHARTMUSEUM_PASSWORD}"
# Empacote o chart
helm package ./seu-chart
# Faça upload via API
curl -u "$USERNAME:$PASSWORD" \
--data-binary @${CHART_NAME}-${CHART_VERSION}.tgz \
${CHARTMUSEUM_URL}/api/charts
Consumindo Charts do ChartMuseum
Seus usuários/times adicionam seu repositório:
helm repo add minha-empresa https://seu-usuario:sua-senha@charts.sua-empresa.com
helm repo update
# Instalam charts como normal
helm install meu-app minha-empresa/meu-microsservico --values values-prod.yaml
Para ambientes que não suportam credenciais na URL (ou por segurança preferem evitar), use um Bearer token:
helm repo add minha-empresa https://charts.sua-empresa.com \
--username=token \
--password=seu-token-secreto
helm repo update
helm search repo minha-empresa/
helm install meu-app minha-empresa/meu-microsservico -n producao
Conclusão
Neste artigo exploramos três tópicos avançados do Helm que transformam-o de uma ferramenta de empacotamento para uma plataforma robusta de gerenciamento de aplicações Kubernetes. Primeiro, Hooks permitem orquestrar operações críticas (validações, migrations, backups) em momentos precisos do ciclo de vida de um release, garantindo que pré-requisitos sejam atendidos antes que recursos sejam criados. Segundo, Library Charts eliminam duplicação e garantem consistência ao centralizar templates, helpers e padrões de segurança em um único lugar que todos os charts consomem, transformando manutenção de 50 charts em manutenção de 1. Terceiro, ChartMuseum profissionaliza seu repositório com versionamento automático, autenticação, armazenamento escalável e APIs que integram naturalmente em pipelines CI/CD.
Esses três pilares — automação de eventos, reutilização de código e distribuição segura — são o que separa equipes Helm amadoras de times enterprise operando centenas de aplicações com confiabilidade e agilidade.