Skip to content

Helm — comandi e troubleshooting

Helm fa due cose: renderizza template in manifest Kubernetes e tiene traccia di cosa ha applicato, in un Secret nel namespace della release. Quasi tutti i problemi si spiegano con una delle due: o il template produce qualcosa di diverso da quello che pensi, o lo stato registrato non corrisponde a quello che c’è nel cluster.

Repository

Terminal window
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo list
helm repo update
helm repo remove <nome>
helm search repo <parola>
helm search repo <parola> --versions # tutte le versioni disponibili
helm search hub <parola> # cerca su Artifact Hub

helm repo update aggiorna l’indice locale: senza, helm search e helm install continuano a vedere le versioni del giorno in cui hai aggiunto il repo.

Ambienti air-gapped — scaricare il chart per portarlo dentro:

Terminal window
helm pull bitnami/redis --version 18.1.5
helm pull bitnami/redis --version 18.1.5 --untar --untardir ./charts
helm show values bitnami/redis --version 18.1.5 > values-default.yaml
helm show chart bitnami/redis
helm show readme bitnami/redis

Registry OCI, il formato ormai standard:

Terminal window
helm registry login registry.example.it -u <user>
helm pull oci://registry.example.it/charts/mychart --version 1.0.0
helm push mychart-1.0.0.tgz oci://registry.example.it/charts
helm install myrel oci://registry.example.it/charts/mychart --version 1.0.0

Install e upgrade

Terminal window
helm install <release> <chart> -n <ns>
helm install <release> <chart> -n <ns> --create-namespace
helm install <release> ./chart-locale -n <ns>
helm upgrade <release> <chart> -n <ns>
helm upgrade --install <release> <chart> -n <ns> # idempotente: installa se non c'è

helm upgrade --install è la forma da usare in pipeline: stesso comando alla prima esecuzione e a tutte le successive.

Flag che contano davvero:

Terminal window
helm upgrade --install myrel ./chart -n myns \
--values values-prod.yaml \
--set image.tag=1.4.2 \
--atomic \
--timeout 10m \
--wait
  • --atomic — se l’upgrade fallisce, fa rollback automatico allo stato precedente. Implica --wait.
  • --wait — aspetta che le risorse siano pronte prima di dichiarare successo.
  • --timeout — quanto aspettare; il default di 5 minuti è spesso corto per StatefulSet con PVC.
  • --create-namespace — crea il namespace se manca.
  • --dry-run — non applica nulla, mostra cosa farebbe.
  • --force — sostituisce le risorse invece di aggiornarle. Da usare con cautela: ricrea gli oggetti e può causare downtime.
  • --reset-values / --reuse-values — vedi sotto, è la fonte di sorprese più comune.

Values: la parte che genera più errori

Terminal window
helm show values <chart> > values-default.yaml # tutti i default documentati
helm get values <release> -n <ns> # solo i values sovrascritti da te
helm get values <release> -n <ns> --all # values effettivi, default inclusi

Precedenza, dalla più debole alla più forte: values.yaml del chart → file passati con -f nell’ordine in cui compaiono → --set--set-string / --set-file.

Terminal window
helm upgrade myrel ./chart -n myns \
-f values-common.yaml \
-f values-prod.yaml \
--set replicaCount=3 \
--set-string image.tag="1.4" # forza stringa: evita che 1.4 diventi float

Sintassi di --set per i casi non banali:

Terminal window
--set nodeSelector."kubernetes\.io/hostname"=worker-0 # punti nelle chiavi vanno escapati
--set tolerations[0].key=dedicated # array per indice
--set 'ingress.hosts={a.example.it,b.example.it}' # lista inline
--set-file config.script=./setup.sh # contenuto di un file in un value

Il comportamento che sorprende tutti: dalla versione 3, helm upgrade riusa i values della release precedente solo per i valori che non ridefinisci, ma se passi -f con un file diverso i valori assenti tornano ai default del chart. Per essere espliciti:

Terminal window
helm upgrade myrel ./chart -n myns --reuse-values --set image.tag=1.4.3 # parte dai values attuali
helm upgrade myrel ./chart -n myns --reset-values -f values-prod.yaml # riparte dai default del chart

Se non specifichi né l’uno né l’altro e usi solo --set, Helm si comporta come --reuse-values. Con -f, no. In pipeline conviene sempre passare il file completo ed essere espliciti con --reset-values, così il risultato non dipende dalla storia della release.

Ispezionare una release

Terminal window
helm list -n <ns>
helm list -A # tutti i namespace
helm list -A --pending # release bloccate in transizione
helm list -A --failed
helm status <release> -n <ns>
helm history <release> -n <ns>

helm history mostra le revisioni con stato e descrizione: è il primo comando da lanciare quando “ieri funzionava”.

Terminal window
helm get manifest <release> -n <ns> # i manifest realmente applicati
helm get notes <release> -n <ns>
helm get hooks <release> -n <ns>
helm get all <release> -n <ns>

helm get manifest è la verità: mostra cosa Helm ha mandato all’API server, dopo il rendering dei template. Confrontarlo con quello che c’è nel cluster individua subito le modifiche fatte a mano fuori da Helm.

Terminal window
helm get manifest myrel -n myns > applied.yaml
oc -n myns get deploy myapp -o yaml > actual.yaml
diff <(yq '.spec.template.spec.containers[0].image' applied.yaml) \
<(yq '.spec.template.spec.containers[0].image' actual.yaml)

Dove Helm tiene lo stato:

Terminal window
oc -n <ns> get secret -l owner=helm
oc -n <ns> get secret sh.helm.release.v1.<release>.v<N> -o jsonpath='{.data.release}' | \
base64 -d | base64 -d | gunzip | jq .info

Il doppio base64 non è un refuso: il payload è compresso e codificato due volte.

Rollback

Terminal window
helm history <release> -n <ns>
helm rollback <release> -n <ns> # alla revisione precedente
helm rollback <release> <N> -n <ns> # a una revisione specifica
helm rollback <release> <N> -n <ns> --wait --timeout 10m

Il rollback crea una nuova revisione che riporta i manifest a quelli della revisione scelta. Non cancella la storia: dopo un rollback dalla 5 alla 3, ti ritrovi con la revisione 6 identica alla 3.

Il numero di revisioni conservate si controlla in fase di upgrade:

Terminal window
helm upgrade myrel ./chart -n myns --history-max 10

Debug dei template

Il comando più utile dell’intero toolkit:

Terminal window
helm template <release> <chart> -n <ns> -f values-prod.yaml
helm template <release> <chart> -n <ns> -f values-prod.yaml --debug
helm template <release> <chart> -n <ns> -s templates/deployment.yaml # un solo file

helm template renderizza in locale senza toccare il cluster: è dove si vedono i valori sbagliati, gli if che non scattano e le indentazioni rotte.

Terminal window
helm install myrel ./chart -n myns --dry-run --debug
helm upgrade myrel ./chart -n myns --dry-run --debug

--dry-run fa il rendering e valida contro l’API server, quindi intercetta anche campi inesistenti e CRD mancanti. helm template da solo no. Per la validazione lato server:

Terminal window
helm template myrel ./chart | oc apply --dry-run=server -f -

Vedere le differenze prima di applicare, con il plugin più utile in circolazione:

Terminal window
helm plugin install https://github.com/databus23/helm-diff
helm diff upgrade myrel ./chart -n myns -f values-prod.yaml
helm diff upgrade myrel ./chart -n myns -f values-prod.yaml --context 3
helm diff rollback myrel 3 -n myns

Validazione del chart e delle sue convenzioni:

Terminal window
helm lint ./chart
helm lint ./chart -f values-prod.yaml --strict

Chart locale e dipendenze

Struttura minima:

mychart/
├── Chart.yaml # nome, version, appVersion, dependencies
├── values.yaml # valori di default
├── templates/ # i manifest con i placeholder
│ ├── _helpers.tpl # funzioni e nomi riusabili
│ └── NOTES.txt # messaggio post-install
└── charts/ # dipendenze scaricate
Terminal window
helm create mychart
helm dependency list ./mychart
helm dependency update ./mychart # scarica le dipendenze e scrive Chart.lock
helm dependency build ./mychart # usa Chart.lock senza riaggiornare
helm package ./mychart
helm package ./mychart --version 1.2.0 --app-version 2.4.1

update risolve i vincoli e riscrive il lock, build si attiene al lock esistente. In pipeline si usa build, per avere risultati riproducibili.

Distinzione da tenere a mente: version è la versione del chart, appVersion è quella dell’applicazione che il chart installa. Sono indipendenti.

Hook

annotations:
"helm.sh/hook": pre-upgrade,pre-install
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded

Fasi disponibili: pre-install, post-install, pre-upgrade, post-upgrade, pre-delete, post-delete, pre-rollback, post-rollback, test.

Il peso ordina gli hook della stessa fase, dal più basso al più alto. La hook-delete-policy decide se il Job resta nel cluster: senza hook-succeeded ti ritrovi decine di Job completati.

Terminal window
helm get hooks <release> -n <ns>

Un hook che fallisce blocca l’upgrade: se una release resta in pending-upgrade, il primo posto dove guardare sono i Job di hook nel namespace.

Terminal window
oc -n <ns> get jobs
oc -n <ns> logs job/<hook-job>

Test della release:

Terminal window
helm test <release> -n <ns>
helm test <release> -n <ns> --logs

Release bloccate e recupero

Sintomo classico: helm list mostra pending-install, pending-upgrade o pending-rollback e ogni comando successivo si rifiuta con “another operation is in progress”.

Succede quando il processo Helm è stato interrotto (pipeline killata, timeout della shell) lasciando il Secret di stato a metà.

Terminal window
helm list -A --pending
helm history <release> -n <ns>

Recupero, in ordine di invasività:

Terminal window
# 1. rollback all'ultima revisione buona
helm rollback <release> <N-buona> -n <ns>
# 2. se il rollback si rifiuta, il plugin dedicato
helm plugin install https://github.com/Nightapes/helm-mapkubeapis
oc -n <ns> get secret -l owner=helm,name=<release>
# 3. ultima spiaggia: cancellare il Secret della revisione bloccata
oc -n <ns> delete secret sh.helm.release.v1.<release>.v<N-bloccata>

Il punto 3 fa dimenticare a Helm l’operazione in corso, ma non tocca le risorse nel cluster: dopo, verifica con helm get manifest che lo stato registrato corrisponda alla realtà.

Altri errori ricorrenti e cosa significano:

ErroreCausaRimedio
cannot re-use a name that is still in userelease esistente, anche fallitahelm list -a -n <ns>, poi upgrade o uninstall
has no deployed releasesla prima install è fallitahelm uninstall e reinstalla
field is immutablemodifica a selector o a un campo non aggiornabile--force, o delete e reinstall della risorsa
rendered manifests contain a resource that already existsrisorsa creata fuori da Helmadottala con le annotation meta.helm.sh/release-name e -namespace più la label app.kubernetes.io/managed-by: Helm
UPGRADE FAILED: timed out waiting for the condition--wait scaduto, pod non prontioc get events, oc describe pod — il problema è applicativo

Adozione di una risorsa preesistente:

Terminal window
oc -n <ns> annotate <kind>/<nome> meta.helm.sh/release-name=<release> --overwrite
oc -n <ns> annotate <kind>/<nome> meta.helm.sh/release-namespace=<ns> --overwrite
oc -n <ns> label <kind>/<nome> app.kubernetes.io/managed-by=Helm --overwrite

Disinstallazione

Terminal window
helm uninstall <release> -n <ns>
helm uninstall <release> -n <ns> --keep-history # mantiene lo storico per un futuro rollback
helm uninstall <release> -n <ns> --wait

I PVC creati da un StatefulSet non vengono rimossi da Helm: vanno cancellati a mano.

Terminal window
oc -n <ns> get pvc

Specificità OpenShift

Le SecurityContextConstraints sono la causa più frequente di chart pubblici che non partono: molti impostano runAsUser fisso o fsGroup, incompatibili con l’SCC restricted-v2 che assegna gli UID dal range del namespace.

Terminal window
oc get scc
oc -n <ns> get sa
oc adm policy add-scc-to-user nonroot-v2 -z <serviceaccount> -n <ns>

Nei values dei chart Bitnami e simili, la soluzione pulita è svuotare i campi invece di aggiungere privilegi:

podSecurityContext:
fsGroup: null
containerSecurityContext:
runAsUser: null
runAsGroup: null

Verificare cosa impedisce l’avvio:

Terminal window
oc -n <ns> get events --sort-by=.lastTimestamp | tail -20
oc -n <ns> describe pod <pod> | sed -n '/Events/,$p'
oc -n <ns> get pod <pod> -o jsonpath='{.metadata.annotations.openshift\.io/scc}{"\n"}'

Se il chart crea Ingress e ti serve una Route, quasi tutti i chart supportano annotation custom; in alternativa OpenShift genera la Route automaticamente dall’Ingress.

Comandi di servizio

Terminal window
helm version
helm env # dove Helm cerca cache, config e data
helm plugin list
helm completion bash > /etc/bash_completion.d/helm