Dominando Helm Avançado: Hooks, Library Charts e Chart Museum em Projetos Reais Já leu

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: , , , , , , e . Cada um representa um momento estratégico. Por exemplo, você pode usar um hook para criar um namespace customizado ou um 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

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.

Referências


Artigos relacionados