Skip to main content

Exercises and Projects


1 - Hands-on exercises

Exercise 1: Installation and configuration

Objective: Install a chart from a public repository.

# Tâches :
# 1. Ajouter le repository Bitnami
# 2. Rechercher le chart nginx
# 3. Voir les valeurs disponibles
# 4. Installer avec 3 réplicas et un service NodePort
# 5. Vérifier le déploiement
Solution
# 1. Ajouter le repository
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

# 2. Rechercher nginx
helm search repo nginx

# 3. Voir les valeurs
helm show values bitnami/nginx > nginx-values.yaml

# 4. Installer
helm install my-nginx bitnami/nginx \
--set replicaCount=3 \
--set service.type=NodePort

# 5. Vérifier
helm list
kubectl get pods,svc

Exercise 2: Create a basic chart

Objective: Create a chart for a simple application.

# Tâches :
# 1. Créer un nouveau chart "hello-world"
# 2. Modifier pour déployer l'image hashicorp/http-echo
# 3. Configurer le message via values
# 4. Installer et tester
Solution
# 1. Créer le chart
helm create hello-world

# 2-3. Modifier values.yaml
# values.yaml
replicaCount: 1
image:
repository: hashicorp/http-echo
tag: "0.2.3"
pullPolicy: IfNotPresent

message: "Hello from Helm!"

service:
type: ClusterIP
port: 5678
# templates/deployment.yaml (extrait)
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
args:
- "-text={{ .Values.message }}"
ports:
- containerPort: 5678
# 4. Installer et tester
helm install hello ./hello-world
kubectl port-forward svc/hello-hello-world 8080:5678
curl localhost:8080

Exercise 3: Advanced templates

Objective: Use the templating functions.

Create a template that:

  1. Generates standard labels
  2. Configures resources conditionally
  3. Iterates over a list of hosts for the Ingress
Solution
# _helpers.tpl
{{- define "app.labels" -}}
app.kubernetes.io/name: {{ include "app.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
{{- end }}

# deployment.yaml
metadata:
labels:
{{- include "app.labels" . | nindent 4 }}
spec:
template:
spec:
containers:
- name: app
{{- if .Values.resources }}
resources:
{{- toYaml .Values.resources | nindent 10 }}
{{- end }}

# ingress.yaml
{{- if .Values.ingress.enabled }}
spec:
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ $.Release.Name }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- end }}

Exercise 4: Release management

Objective: Practice the release lifecycle.

# Tâches :
# 1. Installer une release
# 2. Faire un upgrade avec nouvelles values
# 3. Voir l'historique
# 4. Faire un rollback
# 5. Désinstaller proprement
Solution
# 1. Installer
helm install myapp bitnami/nginx --set replicaCount=1

# 2. Upgrade
helm upgrade myapp bitnami/nginx --set replicaCount=3 --set service.type=LoadBalancer

# 3. Historique
helm history myapp

# 4. Rollback
helm rollback myapp 1

# 5. Désinstaller
helm uninstall myapp

Exercise 5: Hooks and Tests

Objective: Create a pre-install hook and a test.

Solution
# templates/hooks/pre-install.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: "{{ .Release.Name }}-init"
annotations:
"helm.sh/hook": pre-install
"helm.sh/hook-delete-policy": hook-succeeded
spec:
template:
spec:
containers:
- name: init
image: busybox
command: ['sh', '-c', 'echo "Initializing..."']
restartPolicy: Never

# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ .Release.Name }}-test"
annotations:
"helm.sh/hook": test
spec:
containers:
- name: test
image: busybox
command: ['wget', '-q', '-O-', '{{ include "app.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never
# Tester
helm test myapp

2 - Complete project: Web Application

Description

Create a complete Helm chart for a web application with:

  • Frontend (Nginx with custom configuration)
  • Backend API (custom image or http-echo)
  • PostgreSQL database (dependency)
  • Redis cache (dependency)
  • Ingress with TLS

Chart structure

webapp-chart/
├── Chart.yaml
├── values.yaml
├── values-production.yaml
├── templates/
│ ├── _helpers.tpl
│ ├── frontend/
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ └── configmap.yaml
│ ├── backend/
│ │ ├── deployment.yaml
│ │ └── service.yaml
│ ├── ingress.yaml
│ ├── networkpolicy.yaml
│ ├── hooks/
│ │ └── db-migrate.yaml
│ └── tests/
│ └── test-endpoints.yaml
└── ci/
└── test-values.yaml

Chart.yaml

apiVersion: v2
name: webapp
version: 1.0.0
appVersion: "1.0.0"
description: Application web complète avec frontend, backend et dépendances

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

values.yaml

# Global
global:
imageRegistry: ""

# Frontend
frontend:
replicaCount: 2
image:
repository: nginx
tag: "1.25"
service:
type: ClusterIP
port: 80
resources:
limits:
cpu: 200m
memory: 256Mi

# Backend
backend:
replicaCount: 2
image:
repository: hashicorp/http-echo
tag: "0.2.3"
message: "API Response"
service:
type: ClusterIP
port: 5678
resources:
limits:
cpu: 500m
memory: 512Mi

# Ingress
ingress:
enabled: false
className: nginx
hosts:
- host: webapp.local
paths:
- path: /
pathType: Prefix
service: frontend
- path: /api
pathType: Prefix
service: backend

# Dependencies
postgresql:
enabled: true
auth:
database: webapp
username: webapp

redis:
enabled: true
architecture: standalone

Deployment

# Dépendances
helm dependency update ./webapp-chart

# Installation développement
helm install webapp-dev ./webapp-chart \
--namespace dev \
--create-namespace

# Installation production
helm install webapp-prod ./webapp-chart \
--namespace production \
--create-namespace \
-f values-production.yaml \
--atomic \
--wait

# Tests
helm test webapp-dev

3 - Review quiz

  1. What is the difference between helm template and helm install --dry-run?

  2. How do you override a nested value with --set?

  3. What is the priority order of values?

  4. Which annotation defines a pre-upgrade hook?

  5. How do you view the values used in a release?

Answers
  1. helm template renders the manifests locally without connecting to the cluster. --dry-run contacts the cluster for validation.

  2. --set parent.child.key=value

  3. values.yaml < -f files (in order) < --set/--set-string

  4. "helm.sh/hook": pre-upgrade

  5. helm get values <release-name> or --all to include the defaults


Course summary

Congratulations! You have completed the Helm course. You now have a firm grasp of:

  • The installation and configuration of Helm
  • The fundamental concepts (Charts, Releases, Repositories)
  • The creation of custom charts
  • Go templates and Helm functions
  • Managing HTTP and OCI repositories
  • The release lifecycle
  • Hooks and tests
  • The best practices

Next steps

  • Practice with existing charts
  • Create charts for your applications
  • Explore Helmfile for multi-release management
  • Integrate Helm into your CI/CD pipelines

← Back to the table of contents