Docker & Kubernetes

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

17 min de leitura

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

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

<h2>Hooks no Helm: Automatizando Eventos do Ciclo de Vida</h2>

<p>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.</p>

<p>Os hooks disponíveis no Helm são: <code>pre-install</code>, <code>post-install</code>, <code>pre-upgrade</code>, <code>post-upgrade</code>, <code>pre-delete</code>, <code>post-delete</code>, <code>pre-rollback</code> e <code>post-rollback</code>. Cada um representa um momento estratégico. Por exemplo, você pode usar um hook <code>pre-install</code> para criar um namespace customizado ou um <code>post-install</code> 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.</p>

<h3>Estrutura e Anotação de Hooks</h3>

<p>Todo hook no Helm é um recurso Kubernetes convencional (Pod, Job, ConfigMap, etc.) que recebe a anotação <code>helm.sh/hook</code> com o tipo de evento. Você também pode especificar <code>helm.sh/hook-weight</code> para controlar a ordem de execução entre múltiplos hooks do mesmo tipo e <code>helm.sh/hook-delete-policy</code> para definir o que fazer com o recurso após execução.</p>

<p>Veja um exemplo prático de um hook <code>pre-install</code> que valida a conectividade com um banco de dados antes de instalar a aplicação:</p>

<pre><code class="language-yaml">apiVersion: v1

kind: Pod

metadata:

name: {{ include &quot;minha-app.fullname&quot; . }}-db-check

namespace: {{ .Release.Namespace }}

annotations:

helm.sh/hook: pre-install

helm.sh/hook-weight: &quot;0&quot;

helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded

spec:

serviceAccountName: {{ include &quot;minha-app.serviceAccountName&quot; . }}

restartPolicy: Never

containers:

  • name: db-check

image: postgres:15-alpine

command:

  • sh
  • -c

- |

echo &quot;Verificando conectividade com banco de dados...&quot;

pg_isready -h {{ .Values.database.host }} -p {{ .Values.database.port }} -U {{ .Values.database.user }}

if [ $? -eq 0 ]; then

echo &quot;Banco de dados está acessível!&quot;

exit 0

else

echo &quot;Falha ao conectar ao banco de dados!&quot;

exit 1

fi</code></pre>

<p>Neste exemplo, o pod executa antes de qualquer instalação (<code>pre-install</code>), com peso 0 (executa primeiro se houver múltiplos hooks), e é deletado automaticamente após sucesso ou falha (<code>hook-succeeded</code> ou <code>hook-failed</code>). Se o comando <code>pg_isready</code> falhar, todo o release falha — é uma validação crítica.</p>

<h3>Hooks com Jobs para Operações Complexas</h3>

<p>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:</p>

<pre><code class="language-yaml">apiVersion: batch/v1

kind: Job

metadata:

name: {{ include &quot;minha-app.fullname&quot; . }}-db-migrate-{{ .Release.Revision }}

namespace: {{ .Release.Namespace }}

annotations:

helm.sh/hook: pre-upgrade

helm.sh/hook-weight: &quot;-5&quot;

helm.sh/hook-delete-policy: before-hook-creation

spec:

backoffLimit: 3

template:

metadata:

name: db-migrate

spec:

serviceAccountName: {{ include &quot;minha-app.serviceAccountName&quot; . }}

restartPolicy: Never

containers:

  • name: migrate

image: &quot;{{ .Values.image.repository }}:{{ .Values.image.tag }}&quot;

command: [&quot;/bin/sh&quot;, &quot;-c&quot;]

args:

- |

echo &quot;Iniciando migrations...&quot;

./migrations.sh

echo &quot;Migrations concluídas com sucesso!&quot;

env:

  • name: DATABASE_URL

valueFrom:

secretKeyRef:

name: {{ include &quot;minha-app.fullname&quot; . }}-db-secret

key: url

volumeMounts:

  • name: migrations

mountPath: /migrations

volumes:

  • name: migrations

configMap:

name: {{ include &quot;minha-app.fullname&quot; . }}-migrations

defaultMode: 0755</code></pre>

<p>Este Job roda antes de qualquer upgrade (<code>pre-upgrade</code>), com peso <code>-5</code> para executar antes de outros hooks com peso neutro. A política <code>before-hook-creation</code> garante que se o Job falhar, um novo será criado na próxima tentativa sem conflito de nome. O <code>backoffLimit: 3</code> permite até 3 tentativas automáticas.</p>

<p>---</p>

<h2>Library Charts: Reutilização de Código em Charts Helm</h2>

<p>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.</p>

<p>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.</p>

<h3>Estrutura e Criação de um Library Chart</h3>

<p>Um library chart mantém a mesma estrutura de um chart normal, mas a flag <code>type: library</code> no <code>Chart.yaml</code> indica seu propósito. O conteúdo fica em <code>templates/</code> e <code>charts/</code> segue as mesmas convenções. A diferença crucial: você quase nunca coloca recursos em <code>templates/</code> — em vez disso, coloca helpers Helm (funções Go template) que outros charts usam.</p>

<p>Crie um library chart básico com <code>helm create meu-library --type library</code>. Estrutura:</p>

<pre><code>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</code></pre>

<p>No <code>Chart.yaml</code>:</p>

<pre><code class="language-yaml">apiVersion: v2

name: meu-library

description: Library chart com templates comuns para microsserviços

type: library

version: 1.0.0

appVersion: &quot;1.0&quot;

maintainers:

  • name: Seu Time

email: seu-time@empresa.com</code></pre>

<h3>Implementação de Templates Reutilizáveis</h3>

<p>Templates em library charts usam a convenção de underscore (ex: <code>_helpers.tpl</code>) e são incluídos em outros charts, não renderizados diretamente. Vamos criar helpers para labels e um template de deployment genérico:</p>

<pre><code class="language-yaml">{{/ templates/_helpers.tpl /}}

{{/*

Cria o nome completo do release

*/}}

{{- define &quot;meu-library.fullname&quot; -}}

{{- if .Values.fullnameOverride }}

{{- .Values.fullnameOverride | trunc 63 | trimSuffix &quot;-&quot; }}

{{- else }}

{{- $name := default .Chart.Name .Values.nameOverride }}

{{- if contains $name .Release.Name }}

{{- .Release.Name | trunc 63 | trimSuffix &quot;-&quot; }}

{{- else }}

{{- printf &quot;%s-%s&quot; .Release.Name $name | trunc 63 | trimSuffix &quot;-&quot; }}

{{- end }}

{{- end }}

{{- end }}

{{/*

Labels padrão para toda a organização

*/}}

{{- define &quot;meu-library.labels&quot; -}}

helm.sh/chart: {{ include &quot;meu-library.chart&quot; . }}

{{ include &quot;meu-library.selectorLabels&quot; . }}

{{- 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 &quot;devops&quot; }}

{{- end }}

{{/*

Selector labels para matchLabels

*/}}

{{- define &quot;meu-library.selectorLabels&quot; -}}

app.kubernetes.io/name: {{ include &quot;meu-library.name&quot; . }}

app.kubernetes.io/instance: {{ .Release.Name }}

{{- end }}

{{/*

Template genérico de Deployment

Uso: {{ include &quot;meu-library.deployment&quot; .Values.deploymentConfig }}

*/}}

{{- define &quot;meu-library.deployment&quot; -}}

apiVersion: apps/v1

kind: Deployment

metadata:

name: {{ include &quot;meu-library.fullname&quot; . }}

namespace: {{ .Release.Namespace }}

labels:

{{- include &quot;meu-library.labels&quot; . | nindent 4 }}

spec:

replicas: {{ .Values.replicaCount | default 2 }}

selector:

matchLabels:

{{- include &quot;meu-library.selectorLabels&quot; . | nindent 6 }}

template:

metadata:

annotations:

prometheus.io/scrape: &quot;true&quot;

prometheus.io/port: &quot;8080&quot;

labels:

{{- include &quot;meu-library.selectorLabels&quot; . | nindent 8 }}

spec:

serviceAccountName: {{ include &quot;meu-library.fullname&quot; . }}

securityContext:

runAsNonRoot: true

runAsUser: 1000

fsGroup: 2000

containers:

  • name: {{ .Chart.Name }}

image: &quot;{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}&quot; imagePullPolicy: {{ .Values.image.pullPolicy | default &quot;IfNotPresent&quot; }}

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 &quot;500m&quot; }} memory: {{ .Values.resources.limits.memory | default &quot;512Mi&quot; }}

requests:

cpu: {{ .Values.resources.requests.cpu | default &quot;250m&quot; }} memory: {{ .Values.resources.requests.memory | default &quot;256Mi&quot; }}

{{- end }}</code></pre>

<h3>Usando Library Charts em Charts Dependentes</h3>

<p>Para usar seu library chart em outro chart, declare-o como dependência. No <code>Chart.yaml</code> do seu chart consumidor:</p>

<pre><code class="language-yaml">apiVersion: v2

name: meu-microsservico

description: Microsserviço usando templates de library

type: application

version: 2.1.0

appVersion: &quot;1.5&quot;

dependencies:

  • name: meu-library

version: &quot;1.0.0&quot;

repository: file://../meu-library

alias: lib</code></pre>

<p>Execute <code>helm dependency update</code> para trazer a dependência. Agora no <code>values.yaml</code> do seu microsserviço:</p>

<pre><code class="language-yaml">lib:

team: backend

replicaCount: 3

image:

repository: seu-registry.azurecr.io/seu-microsservico

tag: &quot;1.5.0&quot;

resources:

limits:

cpu: &quot;1000m&quot;

memory: &quot;1Gi&quot;

requests:

cpu: &quot;500m&quot;

memory: &quot;512Mi&quot;</code></pre>

<p>E no <code>templates/deployment.yaml</code> do seu chart:</p>

<pre><code class="language-yaml">{{ include &quot;lib.deployment&quot; . }}</code></pre>

<p>Pronto — você tem um deployment completo, com segurança, probes e labels padrão, sem escrever uma linha de YAML novo.</p>

<p>---</p>

<h2>Chart Museum: Hospedando e Gerenciando Repositórios Helm</h2>

<p>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 <code>.tgz</code>, atualizar <code>index.yaml</code>, 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.</p>

<p>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.</p>

<h3>Instalação e Configuração de ChartMuseum</h3>

<p>A forma mais comum é instalar ChartMuseum via Helm (sim, existe um chart para ChartMuseum). Primeiro, adicione o repositório oficial:</p>

<pre><code class="language-bash">helm repo add chartmuseum https://chartmuseum.github.io/charts

helm repo update</code></pre>

<p>Depois, crie um <code>values-chartmuseum.yaml</code> com sua configuração:</p>

<pre><code class="language-yaml">replicaCount: 2

image:

repository: ghcr.io/chartmuseum/chartmuseum

tag: &quot;v0.15.0&quot;

pullPolicy: IfNotPresent

service:

type: ClusterIP

port: 8080

annotations:

prometheus.io/scrape: &quot;true&quot;

prometheus.io/port: &quot;8080&quot;

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

hosts:

  • host: charts.sua-empresa.com

paths:

  • path: /

pathType: Prefix

tls:

  • secretName: chartmuseum-tls

hosts:

  • charts.sua-empresa.com

env:

STORAGE: local

DEBUG: &quot;false&quot;

ALLOW_OVERWRITE: &quot;true&quot;

DISABLE_API: &quot;false&quot;

DISABLE_METRICS: &quot;false&quot;

persistence:

enabled: true

storageClass: standard

accessMode: ReadWriteOnce

size: 50Gi

resources:

limits:

cpu: &quot;1000m&quot;

memory: &quot;1Gi&quot;

requests:

cpu: &quot;500m&quot;

memory: &quot;512Mi&quot;

securityContext:

runAsNonRoot: true

runAsUser: 65534

fsGroup: 65534</code></pre>

<p>Instale com:</p>

<pre><code class="language-bash">helm install chartmuseum chartmuseum/chartmuseum \

--namespace chartmuseum \

--create-namespace \

-f values-chartmuseum.yaml</code></pre>

<h3>Configurando Armazenamento Externo e Autenticação</h3>

<p>Para ambientes produção, use armazenamento externo (S3, Azure Blob, etc.) em vez de local. Modifique o <code>values-chartmuseum.yaml</code>:</p>

<pre><code class="language-yaml">env:

STORAGE: amazon

STORAGE_AMAZON_BUCKET: seu-bucket-s3

STORAGE_AMAZON_PREFIX: charts/

STORAGE_AMAZON_REGION: us-east-1

ALLOW_OVERWRITE: &quot;true&quot;

DISABLE_API: &quot;false&quot;

DISABLE_METRICS: &quot;false&quot;

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</code></pre>

<p>Crie o secret de autenticação antes:</p>

<pre><code class="language-bash">kubectl create secret generic chartmuseum-auth \

--from-literal=username=seu-usuario \

--from-literal=password=$(openssl rand -base64 12) \

-n chartmuseum</code></pre>

<h3>Integração com Pipeline CI/CD</h3>

<p>Configurar seu pipeline para fazer push de charts para ChartMuseum é trivial. Usando um Helm plugin chamado <code>helm-push</code>:</p>

<pre><code class="language-bash"># 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</code></pre>

<p>Ou usando curl direto (útil se não quiser plugin):</p>

<pre><code class="language-bash">#!/bin/bash

CHART_NAME=&quot;seu-chart&quot;

CHART_VERSION=&quot;1.2.3&quot;

CHARTMUSEUM_URL=&quot;https://charts.sua-empresa.com&quot;

USERNAME=&quot;${CHARTMUSEUM_USERNAME}&quot;

PASSWORD=&quot;${CHARTMUSEUM_PASSWORD}&quot;

Empacote o chart

helm package ./seu-chart

Faça upload via API

curl -u &quot;$USERNAME:$PASSWORD&quot; \

--data-binary @${CHART_NAME}-${CHART_VERSION}.tgz \

${CHARTMUSEUM_URL}/api/charts</code></pre>

<h3>Consumindo Charts do ChartMuseum</h3>

<p>Seus usuários/times adicionam seu repositório:</p>

<pre><code class="language-bash">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</code></pre>

<p>Para ambientes que não suportam credenciais na URL (ou por segurança preferem evitar), use um Bearer token:</p>

<pre><code class="language-bash">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</code></pre>

<p>---</p>

<h2>Conclusão</h2>

<p>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. <strong>Primeiro, Hooks permitem orquestrar operações críticas</strong> (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. <strong>Segundo, Library Charts eliminam duplicação e garantem consistência</strong> 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. <strong>Terceiro, ChartMuseum profissionaliza seu repositório</strong> com versionamento automático, autenticação, armazenamento escalável e APIs que integram naturalmente em pipelines CI/CD.</p>

<p>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.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://helm.sh/docs/topics/charts_hooks/" target="_blank" rel="noopener noreferrer">Documentação oficial do Helm - Hooks</a></li>

<li><a href="https://helm.sh/docs/topics/chart_template_guide/#the-chart-type-field" target="_blank" rel="noopener noreferrer">Documentação oficial do Helm - Chart Types (Library Charts)</a></li>

<li><a href="https://github.com/chartmuseum/chartmuseum" target="_blank" rel="noopener noreferrer">ChartMuseum GitHub Repository</a></li>

<li><a href="https://helm.sh/docs/chart_best_practices/" target="_blank" rel="noopener noreferrer">Helm Chart Best Practices and Patterns</a></li>

<li><a href="https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy" target="_blank" rel="noopener noreferrer">Kubernetes Official Documentation - Job and Pod Hooks</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em Docker & Kubernetes

Disaster Recovery em Kubernetes: Velero, Backups e Restore de Cluster na Prática
Disaster Recovery em Kubernetes: Velero, Backups e Restore de Cluster na Prática

Entendendo Disaster Recovery em Kubernetes Disaster Recovery (DR) é a capacid...

O que Todo Dev Deve Saber sobre Volumes em Docker: bind mounts, named volumes e tmpfs Comparados
O que Todo Dev Deve Saber sobre Volumes em Docker: bind mounts, named volumes e tmpfs Comparados

O Problema: Persistência de Dados em Containers Quando você cria um container...

O que Todo Dev Deve Saber sobre Kubernetes Events e Auditoria: Rastreando Mudanças no Cluster
O que Todo Dev Deve Saber sobre Kubernetes Events e Auditoria: Rastreando Mudanças no Cluster

Introdução: Por que Rastrear Mudanças no Kubernetes? No dia a dia de um clust...