Helm Charts
Ziel
In diesem Projekt geht es darum, aus normalen Kubernetes Manifesten Schritt für Schritt ein eigenes Helm Chart zu bauen. Sie werden:
- ein Chart mit
helm createvorbereiten - plain Kubernetes Manifeste in ein Chart übernehmen
- Werte aus
values.yamlverwenden - Namen, Labels und Umgebungsvariablen templaten
- optionale Ressourcen mit
ifein- und ausschalten - das Chart mit
helm template,helm lint,helm installundhelm upgradetesten
Hilfsmittel
- Versuchen Sie, die unten stehenden Aufgaben mit Hilfe der Folien und des Helm Cheatsheets eigenständig zu lösen.
- Sollten Sie dabei Probleme haben, finden Sie bei jeder Aufgabe einen ausklappbaren Block, in dem der Lösungsweg beschrieben wird.
- Prüfen Sie Ihr Chart nach jeder größeren Änderung mit
helm template. So sehen Sie direkt, welche Kubernetes Manifeste Helm erzeugt.
Vorbereitung
In diesem Hands-on bauen Sie ein Helm Chart für die Demoanwendung
corewire/docker-demoapp. Die Anwendung läuft im Container auf Port 5000.
Stellen Sie sicher, dass Sie sich im Workspace-Ordner befinden:
cd /home/coder/workspace
Erstellen Sie einen neuen Projektordner und wechseln Sie hinein:
mkdir helm-demoapp
cd helm-demoapp
Aufgabe 1 - Erstes Helm Chart erstellen
In dieser Aufgabe werden die bestehenden Manifeste in ein Helm Chart überführt. Im ersten Schritt soll das Chart noch möglichst wenig Helm-Logik enthalten.
1.1: Chart-Grundstruktur erzeugen
- Erzeugen Sie mit Helm ein neues Chart mit dem Namen
docker-demoappmithelm create. - Schauen Sie sich die erzeugte Ordnerstruktur an.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
helm create docker-demoapp
Schauen Sie sich anschließend die erzeugte Struktur an:
tree docker-demoapp
Helm legt unter anderem folgende Dateien und Ordner an:
docker-demoapp
├── Chart.yaml
├── templates
└── values.yaml
1.2: Beispieltemplates entfernen
Das automatisch erzeugte Chart enthält Beispieltemplates. Diese werden für dieses Hands-on nicht benötigt.
- Löschen Sie im Ordner
docker-demoapp/templatesalle automatisch angelegten Dateien.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
rm -rf docker-demoapp/templates/*
1.3: Chart-Metadaten anpassen
- Öffnen Sie die Datei
docker-demoapp/Chart.yamlund passen Sie die Metadaten an.
apiVersion: v2
name: docker-demoapp
description: A simple Helm chart for the docker-demoapp
type: application
version: 0.1.0
appVersion: "1.1.1"
versionbeschreibt die Version des Charts.appVersionbeschreibt die Version der Anwendung.
1.4: Values Datei löschen
- Löschen Sie den Inhalt der Datei
docker-demoapp/values.yaml, da die Standardwerte später selbst definiert werden.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
> docker-demoapp/values.yaml
Aufgabe 2 - Plain Manifeste in Templates übernehmen
In diesem Schritt werden Kubernetes Manifeste in den templates-Ordner kopiert.
Die Dateien sind dadurch bereits Teil des Charts, verwenden aber noch keine Helm
Variablen.
2.1: Deployment Template erstellen
Erstellen Sie die Datei docker-demoapp/templates/deployment.yaml mit folgendem
Inhalt:
apiVersion: apps/v1
kind: Deployment
metadata:
name: docker-demoapp
labels:
app: docker-demoapp
spec:
replicas: 1
selector:
matchLabels:
app: docker-demoapp
template:
metadata:
labels:
app: docker-demoapp
spec:
containers:
- name: docker-demoapp
image: docker.io/corewire/docker-demoapp:1.1.1
ports:
- containerPort: 5000
env:
- name: DOCKERDEMO_NOTES_DIR
value: "/data/notes"
- name: DATABASE_HOST
value: "database"
- name: DATABASE_PORT
value: "3306"
- name: DATABASE_USER
value: "example-user"
- name: DATABASE_USER_PASSWORD
value: "password"
Namespace im Chart
Im Chart wird kein fester Namespace in die Ressourcen geschrieben. Der Namespace
wird später beim Installieren mit --namespace gesetzt. Dadurch kann dasselbe
Chart in verschiedenen Namespaces installiert werden.
2.2: Service Template erstellen
- Erstellen Sie die Datei
docker-demoapp/templates/service.yamlmit folgendem Inhalt:
apiVersion: v1
kind: Service
metadata:
name: docker-demoapp
labels:
app: docker-demoapp
spec:
type: ClusterIP
selector:
app: docker-demoapp
ports:
- port: 5000
targetPort: 5000
2.3: Templates rendern
- Lassen Sie sich anzeigen, welche Manifeste Helm aus dem Chart erzeugt.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
helm template demoapp ./docker-demoapp
Sie sollten ein Deployment und einen Service sehen. Inhaltlich entsprechen diese noch den plain Manifesten.
2.4: Chart installieren
- Installieren Sie das Chart in den Namespace
demoapp. - Prüfen Sie, ob die Ressourcen angelegt wurden.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
helm install demoapp ./docker-demoapp --namespace demoapp --create-namespace
Prüfen Sie anschließend die erzeugten Ressourcen:
helm list --namespace demoapp
kubectl -n demoapp get all
Aufgabe 3 - Namen mit Helm dynamisch machen
Bisher heißen Deployment und Service immer docker-demoapp. Das ist unpraktisch,
wenn dasselbe Chart mehrfach installiert werden soll. Deshalb wird nun der
Release-Name genutzt.
3.1: Namen mit .Release.Name ersetzen
Passen Sie in templates/deployment.yaml und templates/service.yaml den Namen
von docker-demoapp an.
Verwenden Sie für metadata.name:
name: {{ .Release.Name }}
Passen Sie außerdem alle app Labels und Selector auf denselben Wert an.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
templates/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}
labels:
app: {{ .Release.Name }}
spec:
replicas: 1
selector:
matchLabels:
app: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ .Release.Name }}
spec:
containers:
- name: docker-demoapp
image: docker.io/corewire/docker-demoapp:1.1.1
ports:
- containerPort: 5000
env:
- name: DOCKERDEMO_NOTES_DIR
value: "/data/notes"
- name: DATABASE_HOST
value: "database"
- name: DATABASE_PORT
value: "3306"
- name: DATABASE_USER
value: "example-user"
- name: DATABASE_USER_PASSWORD
value: "password"
templates/service.yaml:
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}
labels:
app: {{ .Release.Name }}
spec:
type: ClusterIP
selector:
app: {{ .Release.Name }}
ports:
- port: 5000
targetPort: 5000
3.2: Unterschiedliche Release-Namen testen
- Rendern Sie das Chart mit unterschiedlichen Release-Namen mit
helm template.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
helm template demoapp ./docker-demoapp
helm template testapp ./docker-demoapp
Achten Sie darauf, wie sich metadata.name, Labels und Selector ändern.
3.3: Bestehenden Release aktualisieren
- Wenden Sie die Änderung auf den bestehenden Release an.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
helm upgrade demoapp ./docker-demoapp --namespace demoapp
Aufgabe 4 - values.yaml verwenden
Feste Werte im Template sind unflexibel. Deshalb werden nun Replikate, Image und
Service-Port in die Datei values.yaml ausgelagert.
4.1: Werte definieren
Ersetzen Sie den Inhalt von docker-demoapp/values.yaml durch:
replicaCount: 1
image:
repository: docker.io/corewire/docker-demoapp
tag: "1.1.1"
service:
type: ClusterIP
port: 5000
4.2: Werte im Deployment verwenden
Passen Sie templates/deployment.yaml so an, dass replicaCount und image
aus values.yaml gelesen werden.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}
labels:
app: {{ .Release.Name }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ .Release.Name }}
spec:
containers:
- name: docker-demoapp
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: 5000
env:
- name: DOCKERDEMO_NOTES_DIR
value: "/data/notes"
- name: DATABASE_HOST
value: "database"
- name: DATABASE_PORT
value: "3306"
- name: DATABASE_USER
value: "example-user"
- name: DATABASE_USER_PASSWORD
value: "password"
4.3: Werte im Service verwenden
Passen Sie templates/service.yaml so an, dass Type und Port aus values.yaml
gelesen werden.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}
labels:
app: {{ .Release.Name }}
spec:
type: {{ .Values.service.type }}
selector:
app: {{ .Release.Name }}
ports:
- port: {{ .Values.service.port }}
targetPort: 5000
4.4: Release aktualisieren
Aktualisieren Sie das installierte Chart, damit die Änderungen wirksam werden.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
helm upgrade demoapp ./docker-demoapp \
--namespace demoapp
Aufgabe 5 - Umgebungsvariablen als Map templaten
Die Anwendung nutzt mehrere Environment Variablen. Diese werden nun ebenfalls in
die values.yaml verschoben und im Template mit einer Schleife erzeugt.
5.1: Environment Werte definieren
Ergänzen Sie values.yaml um den Abschnitt env:
env:
DOCKERDEMO_NOTES_DIR: "/data/notes"
DATABASE_HOST: "database"
DATABASE_PORT: "3306"
DATABASE_USER: "example-user"
DATABASE_USER_PASSWORD: "password"
5.2: Environment Variablen mit range erzeugen
Ersetzen Sie im Deployment den bisherigen env-Block durch eine range-Schleife.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
Der Container-Abschnitt sieht danach so aus:
containers:
- name: docker-demoapp
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: 5000
env:
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
quote sorgt dafür, dass Werte wie 3306 als String ausgegeben werden.
5.3: Template prüfen
Prüfen Sie, ob Helm gültiges YAML erzeugt.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
helm template demoapp ./docker-demoapp
Wenn die Einrückung nicht stimmt, ist die Ausgabe häufig kein gültiges YAML.
Achten Sie besonders auf die Leerzeichen vor {{- range ... }} und - name.
Aufgabe 6 - Helper Templates und Labels einführen
In größeren Charts sollen Namen und Labels nicht mehrfach von Hand gepflegt werden. Dafür werden Helper Templates verwendet.
6.1: _helpers.tpl erstellen
Erstellen Sie die Datei docker-demoapp/templates/_helpers.tpl mit folgendem
Inhalt:
{{/*
Generate a fullname.
*/}}
{{- define "docker-demoapp.fullname" -}}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Common labels.
*/}}
{{- define "docker-demoapp.labels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
Warum trunc 63?
Viele Kubernetes Namen dürfen maximal 63 Zeichen lang sein. Der Helper kürzt
den erzeugten Namen deshalb auf 63 Zeichen und entfernt ein mögliches - am
Ende.
6.2: Fullname im Deployment verwenden
Passen Sie das Deployment so an, dass metadata.name, Labels und Selector den
Helper verwenden.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "docker-demoapp.fullname" . }}
labels:
{{- include "docker-demoapp.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ include "docker-demoapp.fullname" . }}
template:
metadata:
labels:
app: {{ include "docker-demoapp.fullname" . }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: 5000
env:
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
6.3: Fullname im Service verwenden
Passen Sie den Service ebenfalls an.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
apiVersion: v1
kind: Service
metadata:
name: {{ include "docker-demoapp.fullname" . }}
labels:
{{- include "docker-demoapp.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
selector:
app: {{ include "docker-demoapp.fullname" . }}
ports:
- port: {{ .Values.service.port }}
targetPort: 5000
6.4: Ausgabe prüfen
Rendern Sie das Chart und prüfen Sie die erzeugten Namen und Labels.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
helm template demoapp ./docker-demoapp
Der Name der Ressourcen sollte nun ungefähr so aussehen:
demoapp-docker-demoapp
Aufgabe 7 - Optionalen Ingress einbauen
Ein Ingress soll nur erzeugt werden, wenn er in values.yaml aktiviert wird.
Dafür wird ein if-Block verwendet.
7.1: Ingress Werte definieren
Ergänzen Sie values.yaml:
ingress:
enabled: false
host: demoapp.example.local
annotations: {}
7.2: Ingress Template erstellen
Erstellen Sie die Datei docker-demoapp/templates/ingress.yaml:
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "docker-demoapp.fullname" . }}
labels:
{{- include "docker-demoapp.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
rules:
- host: {{ .Values.ingress.host }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ include "docker-demoapp.fullname" . }}
port:
number: {{ .Values.service.port }}
{{- end }}
7.3: Ohne und mit Ingress rendern
Prüfen Sie die Ausgabe einmal ohne und einmal mit aktiviertem Ingress.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
Ohne Ingress:
helm template demoapp ./docker-demoapp
Mit Ingress:
helm template demoapp ./docker-demoapp \
--set ingress.enabled=true
Beim ersten Befehl sollte kein Ingress erzeugt werden. Beim zweiten Befehl
sollte ein zusätzliches kind: Ingress erscheinen.
Aufgabe 8 - Qualität prüfen und Chart paketieren
Zum Abschluss prüfen Sie das Chart und erstellen ein Chart-Paket.
8.1: Helm Lint ausführen
Führen Sie helm lint aus.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
helm lint ./docker-demoapp
Die Ausgabe sollte ungefähr so aussehen:
==> Linting ./docker-demoapp
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, 0 chart(s) failed
Die Info zum Icon ist unkritisch.
8.2: Chart paketieren
Erstellen Sie ein .tgz-Paket aus dem Chart.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
helm package ./docker-demoapp
Danach sollte im aktuellen Ordner eine Datei ähnlich zu dieser liegen:
docker-demoapp-0.1.0.tgz
8.3: Paket installieren
- Installieren Sie das Chart testweise aus dem Paket in einem anderen Namespace.
- Prüfen Sie, ob die Ressourcen angelegt wurden.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
helm install packaged-demoapp ./docker-demoapp-0.1.0.tgz \
--namespace packaged-demoapp \
--create-namespace
Prüfen Sie danach:
helm list --namespace packaged-demoapp
kubectl -n packaged-demoapp get all
Ergebnis
Sie haben aus einfachen Kubernetes Manifesten Schritt für Schritt ein Helm Chart gebaut. Das Chart enthält nun:
- ein Deployment für die Demoanwendung
- einen Service für die Demoanwendung
- konfigurierbare Werte in
values.yaml - Helper Templates für Namen und Labels
- eine
range-Schleife für Environment Variablen - einen optionalen Ingress
Damit haben Sie die wichtigsten Bausteine kennengelernt, um eigene Helm Charts zu erstellen und bestehende Kubernetes Manifeste in Helm zu überführen.