[12/24] H is for Helm: Package Management for Kubernetes
This is Post #11 in the Kubernetes A-to-Z Series
Reading Order: Previous: Ingress | Next: Operators
Series Progress: 12/24 complete | Difficulty: Intermediate | Time: 20 min | Part 4/6: Advanced Concepts
Welcome to the eleventh post in our Kubernetes A-to-Z Series! Now that you understand Ingress, let’s explore Helm - the package manager for Kubernetes. Helm simplifies deploying and managing complex applications through reusable, versioned charts.
What is Helm?
Helm is often called “the package manager for Kubernetes.” It packages Kubernetes resources into charts - reusable, versioned bundles that can be easily shared and deployed.
Without Helm:
┌─────────────────────────────────────────────────┐
│ Manual Deployment │
│ kubectl apply -f deployment.yaml │
│ kubectl apply -f service.yaml │
│ kubectl apply -f configmap.yaml │
│ kubectl apply -f secret.yaml │
│ kubectl apply -f ingress.yaml │
│ ... (20+ more files) │
│ Manual version tracking │
│ Manual rollbacks │
└─────────────────────────────────────────────────┘
With Helm:
┌─────────────────────────────────────────────────┐
│ Helm Deployment │
│ helm install myapp ./mychart │
│ - Versioned releases │
│ - Easy rollbacks │
│ - Templated configurations │
│ - Dependency management │
└─────────────────────────────────────────────────┘
Helm Benefits
- Simplified Deployments: Single command deploys entire applications
- Version Control: Track releases and rollback easily
- Templating: Parameterize manifests for different environments
- Dependency Management: Bundle related charts together
- Sharing: Public repositories for common applications
Helm Architecture
┌─────────────────────────────────────────────────┐
│ Helm CLI │
│ ┌─────────────────────────────────────┐ │
│ │ helm install/upgrade/rollback │ │
│ └──────────────┬──────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ Chart + Values │ │
│ │ templates/ + values.yaml │ │
│ └──────────────┬──────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ Kubernetes API Server │ │
│ │ (manifests applied) │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
Installing Helm
# macOS
brew install helm
# Linux
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# Windows (Chocolatey)
choco install kubernetes-helm
# Verify installation
helm version
Helm Charts
Chart Structure
mychart/
├── Chart.yaml # Chart metadata
├── values.yaml # Default configuration values
├── charts/ # Chart dependencies
├── templates/ # Kubernetes manifest templates
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── _helpers.tpl # Template helpers
│ └── NOTES.txt # Post-install notes
└── .helmignore # Files to ignore
Chart.yaml
# Chart.yaml
apiVersion: v2
name: webapp
description: A Helm chart for deploying web applications
type: application
version: 1.0.0
appVersion: "2.0.0"
keywords:
- webapp
- nodejs
maintainers:
- name: Platform Team
email: [email protected]
dependencies:
- name: postgresql
version: "12.x.x"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
values.yaml
# values.yaml
replicaCount: 3
image:
repository: myapp
tag: "v1.0.0"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
targetPort: 8080
ingress:
enabled: true
className: nginx
hosts:
- host: myapp.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: myapp-tls
hosts:
- myapp.example.com
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
postgresql:
enabled: true
auth:
database: webapp
username: webapp
env:
LOG_LEVEL: info
CACHE_ENABLED: "true"
Template Examples
deployment.yaml:
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "webapp.fullname" . }}
labels:
{{- include "webapp.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "webapp.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "webapp.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: {{ .Values.service.targetPort }}
env:
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
service.yaml:
# templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ include "webapp.fullname" . }}
labels:
{{- include "webapp.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
protocol: TCP
selector:
{{- include "webapp.selectorLabels" . | nindent 4 }}
_helpers.tpl:
# templates/_helpers.tpl
{{- define "webapp.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- define "webapp.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- define "webapp.labels" -}}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app.kubernetes.io/name: {{ include "webapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{- define "webapp.selectorLabels" -}}
app.kubernetes.io/name: {{ include "webapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
Working with Charts
Installing Charts
# Install from local directory
helm install myapp ./mychart
# Install with custom values
helm install myapp ./mychart -f custom-values.yaml
# Install with inline values
helm install myapp ./mychart --set replicaCount=5 --set image.tag=v2.0.0
# Install in specific namespace
helm install myapp ./mychart -n production --create-namespace
# Install from repository
helm install nginx ingress-nginx/ingress-nginx
# Dry run (preview without installing)
helm install myapp ./mychart --dry-run --debug
Upgrading Releases
# Upgrade with new values
helm upgrade myapp ./mychart -f production-values.yaml
# Upgrade with inline values
helm upgrade myapp ./mychart --set image.tag=v2.0.0
# Install or upgrade (idempotent)
helm upgrade --install myapp ./mychart
# Upgrade with atomic rollback on failure
helm upgrade myapp ./mychart --atomic --timeout 5m
Rolling Back
# View release history
helm history myapp
# Rollback to previous version
helm rollback myapp
# Rollback to specific revision
helm rollback myapp 2
Managing Releases
# List releases
helm list
helm list -A # All namespaces
# Get release status
helm status myapp
# Get release values
helm get values myapp
helm get values myapp --all
# Get rendered manifests
helm get manifest myapp
# Uninstall release
helm uninstall myapp
helm uninstall myapp --keep-history
Helm Repositories
# Add repository
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add stable https://charts.helm.sh/stable
# Update repositories
helm repo update
# Search repositories
helm search repo nginx
helm search repo bitnami/postgresql --versions
# Search Artifact Hub
helm search hub wordpress
Environment-Specific Values
# values-development.yaml
replicaCount: 1
image:
tag: "latest"
resources:
requests:
memory: "64Mi"
cpu: "50m"
ingress:
hosts:
- host: dev.myapp.example.com
# values-production.yaml
replicaCount: 5
image:
tag: "v1.0.0"
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "512Mi"
cpu: "500m"
ingress:
hosts:
- host: myapp.example.com
# Deploy to different environments
helm install myapp ./mychart -f values-development.yaml -n development
helm install myapp ./mychart -f values-production.yaml -n production
Chart Dependencies
# Chart.yaml
dependencies:
- name: postgresql
version: "12.x.x"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
- name: redis
version: "17.x.x"
repository: "https://charts.bitnami.com/bitnami"
condition: redis.enabled
# Update dependencies
helm dependency update ./mychart
# Build dependencies
helm dependency build ./mychart
Creating Charts
# Create new chart
helm create mychart
# Package chart
helm package ./mychart
# Lint chart
helm lint ./mychart
# Template locally (debug)
helm template myapp ./mychart --debug
Helm Hooks
Execute actions at specific points in release lifecycle:
# templates/pre-install-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "webapp.fullname" . }}-db-migrate
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "0"
"helm.sh/hook-delete-policy": hook-succeeded
spec:
template:
spec:
containers:
- name: migrate
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
command: ["./migrate.sh"]
restartPolicy: Never
Hook Types
| Hook | Description |
|---|---|
| pre-install | Before resources installed |
| post-install | After resources installed |
| pre-upgrade | Before upgrade |
| post-upgrade | After upgrade |
| pre-rollback | Before rollback |
| post-rollback | After rollback |
| pre-delete | Before deletion |
| post-delete | After deletion |
Troubleshooting
# Debug installation
helm install myapp ./mychart --debug --dry-run
# Check release status
helm status myapp
# View release history
helm history myapp
# Get rendered templates
helm get manifest myapp
# Lint chart
helm lint ./mychart
# Template locally
helm template myapp ./mychart > rendered.yaml
Commands Reference
# Repository Management
helm repo add NAME URL
helm repo update
helm repo list
helm search repo KEYWORD
# Release Management
helm install RELEASE CHART
helm upgrade RELEASE CHART
helm rollback RELEASE [REVISION]
helm uninstall RELEASE
helm list
helm status RELEASE
helm history RELEASE
# Chart Development
helm create NAME
helm package CHART
helm lint CHART
helm template RELEASE CHART
helm dependency update CHART
# Values
helm get values RELEASE
helm install RELEASE CHART -f values.yaml
helm install RELEASE CHART --set key=value
Key Takeaways
- Helm simplifies Kubernetes application deployment
- Charts package related resources together
- Values enable environment-specific configuration
- Templates use Go templating for dynamic manifests
- Releases track deployed chart versions
- Hooks execute actions at lifecycle points
- Dependencies manage chart relationships
Next Steps
Now that you understand Helm, you’re ready to explore Operators in the next post. We’ll learn how to extend Kubernetes functionality with custom controllers and resources.
Resources for Further Learning
Series Navigation:
Complete Series: Kubernetes A-to-Z Series Overview