Happy Dussehra! May good always win. Happy Diwali! Wishing you a bright year ahead. Merry Christmas! Warm wishes from Vizag. Happy New Year! Here’s to a great year ahead.

Archive · Kubernetes

Helm: Packaging and Installing Applications

Use Helm 3 to install, configure, upgrade and roll back applications, write a small chart of your own, and avoid the common release-management mistakes.

4 min read

  • Helm 3
  • Kubernetes 1.22
  • Chart.yaml
  • values.yaml

Real applications are a dozen YAML files that must change together: Deployment, Service, Ingress, ConfigMap, ServiceAccount. Helm packages them as a chart, a template plus default values, and manages installs, upgrades and rollbacks as named releases. It is also how most third-party software (ingress controllers, Prometheus, databases) is distributed.

Versions in use on this date. Helm 3 (3.6 is the current series; 3.7 is expected in the coming weeks). Helm 2 and its server component Tiller reached end of life in November 2020: Helm 3 needs no server-side component and keeps release state in Secrets in the cluster. Support for storing charts in OCI registries is still experimental (HELM_EXPERIMENTAL_OCI=1).

Chart to cluster

flowchart TB
  CH[Chart<br/>templates and default values] --> R
  V[Your values files<br/>values-prod.yaml and --set] --> R[helm template rendering]
  R --> YAML[Kubernetes manifests]
  YAML --> API[Kubernetes API server]
  API --> OBJ[Deployment Service Ingress ...]
  H[helm install or upgrade] --> REL[(Release record<br/>Secret sh.helm.release.v1.name.vN)]
  REL -->|helm history and rollback| H
  REPO[Chart repository or OCI registry] -->|helm pull| CH

Using other people’s charts

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm search repo prometheus-community/kube-prometheus-stack --versions | head
helm show values prometheus-community/kube-prometheus-stack --version <pinned-version> > values-default.yaml
helm install monitoring prometheus-community/kube-prometheus-stack -n monitoring --create-namespace \
   --version <pinned-version> -f values-prod.yaml --atomic --timeout 10m
helm list -A
helm status monitoring -n monitoring
helm get values monitoring -n monitoring            # what you overrode
helm get manifest monitoring -n monitoring | less   # what is actually deployed

Habits: always pin --version, keep your overrides in a values file in Git (not scattered --set flags), and read helm show values and the chart README before installing. --atomic rolls back automatically if the install or upgrade fails, and --wait waits for resources to be ready.

Upgrade and roll back

helm diff upgrade monitoring prometheus-community/kube-prometheus-stack -n monitoring --version <next-version> -f values-prod.yaml   # plugin: helm plugin install https://github.com/databus23/helm-diff
helm upgrade monitoring prometheus-community/kube-prometheus-stack -n monitoring --version <next-version> -f values-prod.yaml --atomic
helm history monitoring -n monitoring
helm rollback monitoring 3 -n monitoring
helm uninstall monitoring -n monitoring

The diff step shows what will change before you apply it. Note that Helm does not upgrade CustomResourceDefinitions placed in a chart’s crds/ directory; large charts document manual CRD steps in their upgrade notes.

A chart of your own

helm create shop-web
tree shop-web
# shop-web/Chart.yaml  values.yaml  templates/{deployment,service,ingress,_helpers.tpl,NOTES.txt}

values.yaml:

replicaCount: 2
image:
  repository: registry.example.com/shop/web
  tag: "1.4.2"
service: { port: 80 }
ingress:
  enabled: true
  host: shop.example.com
resources:
  requests: { cpu: 100m, memory: 128Mi }

A template fragment, templates/deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "shop-web.fullname" . }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels: {{- include "shop-web.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels: {{- include "shop-web.selectorLabels" . | nindent 8 }}
      annotations:
        checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
    spec:
      containers:
        - name: web
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          resources: {{- toYaml .Values.resources | nindent 12 }}

The checksum/config annotation changes whenever the ConfigMap changes, so pods restart on configuration updates.

Test before releasing:

helm lint shop-web
helm template shop-web ./shop-web -f values-prod.yaml | kubectl apply --dry-run=server -f -
helm upgrade --install shop-web ./shop-web -n shop --create-namespace -f values-prod.yaml --atomic

Package and share with helm package shop-web and a repository or registry; version the chart (version) separately from the application (appVersion).

Practices

  • Keep one release per application per environment, with values files per environment (values-staging.yaml, values-prod.yaml).
  • Don’t put secrets in values files committed to Git; use Sealed Secrets, SOPS (helm-secrets) or an external secrets operator.
  • Review rendered output (helm template) in pull requests, or use a GitOps tool such as Argo CD or Flux to apply charts from Git.
  • Avoid editing live objects with kubectl edit on Helm-managed resources; the next upgrade overwrites the change.
  • Release state lives in Secrets; large releases can hit size limits, so split huge charts.

Troubleshooting

  • UPGRADE FAILED: another operation is in progress: a previous run was interrupted; check helm history, and helm rollback to the last good revision.
  • rendered manifests contain a resource that already exists: the object was created outside Helm; adopt it by adding Helm labels and annotations, or delete and re-install.
  • no matches for kind ... in version ...: the chart uses an API that your cluster no longer serves (see the 1.22 removals post); upgrade the chart.
  • Wrong values applied: helm get values --all shows merged values; later -f files and --set win.