Helm charts are essentially templated Kubernetes resource definitions that allow you to package, deploy, and manage complex applications.
Let’s see it in action. Imagine we’re deploying a basic Nginx web server.
First, we need a Helm chart. We can create a simple one:
helm create my-nginx
cd my-nginx
This creates a directory structure:
my-nginx/
├── charts/
├── charts.lock
├── .helmignore
├── Chart.yaml
├── ignore/.helmignore
├── templates/
│ ├── NOTES.txt
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── service.yaml
│ └── serviceaccount.yaml
└── values.yaml
The Chart.yaml file contains metadata about the chart:
apiVersion: v2
name: my-nginx
description: A Helm chart for Nginx
type: application
version: 0.1.0
appVersion: "1.16.0"
The values.yaml file is where we define configurable parameters for our deployment:
replicaCount: 1
image:
repository: nginx
pullPolicy: IfNotPresent
tag: ""
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
create: true
annotations: {}
name: ""
podAnnotations: {}
podLabels: {}
podSecurityContext: {}
securityContext: {}
service:
type: ClusterIP
port: 80
ingress:
enabled: false
className: ""
annotations: {}
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
resources: {}
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
The templates/ directory contains the Kubernetes manifest files that Helm will render. For example, templates/deployment.yaml:
{{- with .Values.image }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "my-nginx.fullname" . }}
labels:
{{- include "my-nginx.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "my-nginx.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "my-nginx.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "my-nginx.serviceAccountName" . }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- end }}
Notice the {{ ... }} syntax. This is Go templating. Helm processes these templates, substituting values from values.yaml (and potentially overrides) to generate the final Kubernetes manifests.
To deploy this chart:
helm install my-nginx ./my-nginx
Helm creates a "release," which is an instance of a chart deployed to your Kubernetes cluster. You can then manage this release:
helm list
helm upgrade my-nginx ./my-nginx --set replicaCount=3
helm uninstall my-nginx
The true power of Helm lies in its ability to abstract away the complexity of Kubernetes configurations. Instead of managing dozens of YAML files for a microservice architecture, you manage a single chart. This chart can include deployments, services, ingress rules, configmaps, secrets, and even custom resources. Dependencies between charts are also supported, allowing you to manage complex stacks like databases, message queues, and your application all from a single Chart.yaml.
A crucial, often overlooked, aspect of Helm’s templating is the _helpers.tpl file. This file defines named templates (partials) that are reused across your manifest files. For instance, you’ll find templates like {{ include "my-nginx.fullname" . }} and {{ include "my-nginx.labels" . }}. These are not just for generating names and labels; they encapsulate common logic for creating consistent and predictable Kubernetes object names and labels across all resources within a chart. By using these helpers, you ensure that related resources (like a Deployment and its Service) share a common naming convention and labels, which is essential for Kubernetes’ internal workings and for your own organizational clarity. This reduces duplication and makes your charts more maintainable.
The next step in mastering Helm is understanding how to create and manage dependencies between charts, allowing you to build sophisticated application stacks.