Déployer des applications Kubernetes packagées avec Helm

Qu'est-ce que Helm ? Comment fonctionne Helm ? Comment pouvons-nous utiliser Helm pour déployer et gérer des applications Kubernetes packagées ? Familiarisons-nous avec Helm et apprenons à créer et distribuer nos propres packages Helm.

Déployer des applications Kubernetes packagées avec Helm
Photo par Kira sur la bruyère / Unsplash

Qu'est-ce que Helm

Helm est un outil qui peut être utilisé pour créer et installer des packages prêts à l'emploi, configurables et partageables de manifestes de ressources Kubernetes pour des applications spécifiques.

Ces manifestes de ressources Kubernetes empaquetés sont appelés Charts Helm. Les Charts Helm sont distribués via des référentiels ou dépôts de Charts.

ArtefactHub est une application Web qui peut être utilisée pour explorer les Charts Helm à partir de nombreux référentiels publics de Charts Helm.

L'installation d'un Chart Helm spécifique dans un cluster Kubernetes crée une Release Helm..

Comment fonctionne Helm

Paramètres et fichiers de configuration de Helm

Lorsque l'utilitaire de ligne de commande Helm est invoqué pour gérer (installer, désinstaller, obtenir l'état...) des Releases Helm, par défaut, il utilise le fichier de configuration $HOME/.kube/config pour découvrir les points de terminaison des clusters Kubernetes actuellement configurés et obtenir les informations nécessaires pour communiquer en toute sécurité avec leurs serveurs d'API.

Le même fichier de configuration est utilisé par défaut par « kubectl » et par conséquent, les configurations effectuées avec « kubectl config » seront par défaut réutilisées par Helm.

La variable d'environnement $KUBECONFIG, ou l'option de ligne de commande « --kubeconfig » peuvent être utilisés pour modifier le chemin par défaut vers le fichier de configuration des clusters Kubernetes.

Pour répertorier les autres variables d'environnement de configuration ainsi que les options de ligne de commande disponibles pour la configuration de Helm, ainsi que les chemins de répertoire par défaut où Helm stocke ses données, configurations et caches, utilisez « helm --help ».

États des Releases Helm

Les états des Releases Helm sont stockés sous forme de ressources « secrets » Kubernetes dans les clusters cibles. Chaque fois qu'une Release Helm est créée ou mise à jour, une nouvelle version de la ressource « secret » est créée dans l'espace de noms de la Release.

Helm récupère l'historique des Releases à partir de ces « secrets » et les utilise pour effectuer des restaurations. La suppression de ces ressources « secrets » fera oublier à Helm tout ce qui concernait les Releases précédemment gérées.

Voici des exemples de ressources « secrets » d'état Helm pour deux versions d'une Release Helm nommée « grafana » :

$ kubectl get secret
NAME                            TYPE                 DATA   AGE
sh.helm.release.v1.grafana.v1   helm.sh/release.v1   1      3m1s
sh.helm.release.v1.grafana.v2   helm.sh/release.v1   1      5s

Installation de Helm

  • Les assets des versions de Helm peuvent être trouvés ici
  • Pour installer Helm sous Linux, procédez comme suit :
$ helm_version=v3.15.3 # choose version
$ linux_arch=amd64 # choose OS arch

# Download Helm binary
$ wget https://get.helm.sh/helm-${helm_version}-linux-${linux_arch}.tar.gz

# Install Helm binary
$ sudo tar xzvf helm-${helm_version}-linux-${linux_arch}.tar.gz -C /usr/local/bin/

# Verify
$ helm version

Gestion des dépôts de Charts Helm

# Adding Helm Charts repositories
$ repo_name=grafana ; repo_url=https://grafana.github.io/helm-charts
$ helm repo add $repo_name $repo_url 
"grafana" has been added to your repositories

# Listing added repositories
$ helm repo list
NAME    URL                                  
grafana https://grafana.github.io/helm-charts

# Synchronize/update local repo info from remote
$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "grafana" chart repository
Update Complete. ⎈Happy Helming!⎈

# Removing repositories
$ helm repo remove $repo_name
"grafana" has been removed from your repositories

Recherche de Charts Helm

# Searching through public artifacthub.io repositories
$ search_keyword=grafana
$ helm search hub $search_keyword
URL                                                     CHART VERSION           APP VERSION             DESCRIPTION                                       
https://artifacthub.io/packages/helm/grafana/gr...      8.3.6                   11.1.0                  The leading tool for querying and visualizing t...
https://artifacthub.io/packages/helm/saurabh6-g...      0.2.0                   1.1                     This is a Helm Chart for Grafana Setup.
(...)

# Searching through locally added repositories

$ search_keyword=grafana/grafana

## Search for lastest versions of Charts matching the search keyword
$ helm search repo $search_keyword
NAME                            CHART VERSION   APP VERSION     DESCRIPTION                                       
grafana/grafana                 8.3.6           11.1.0          The leading tool for querying and visualizing t...
grafana/grafana-agent           0.42.0          v0.42.0         Grafana Agent                                     
grafana/grafana-agent-operator  0.4.1           0.42.0          A Helm chart for Grafana Agent Operator           
grafana/grafana-sampling        0.1.1           v0.40.2         A Helm chart for a layered OTLP tail sampling a...
(...)

## Search for all versions of Charts matching the search keyword
$ helm search repo $search_keyword -l
NAME                            CHART VERSION   APP VERSION     DESCRIPTION                                       
grafana/grafana                 8.3.6           11.1.0          The leading tool for querying and visualizing t...
grafana/grafana                 8.3.5           11.1.0          The leading tool for querying and visualizing t...
(...)
grafana/grafana-agent           0.42.0          v0.42.0         Grafana Agent                                     
grafana/grafana-agent           0.41.0          v0.41.1         Grafana Agent
(...)
grafana/grafana-agent-operator  0.4.1           0.42.0          A Helm chart for Grafana Agent Operator           
grafana/grafana-agent-operator  0.4.0           0.41.1          A Helm chart for Grafana Agent Operator
(...)

# Download Charts files

$ helm pull --untar $chart_url

## Download from locally added repositories
$ helm pull --untar grafana/grafana
$ ls grafana/
Chart.yaml  ci  dashboards  README.md  templates  values.yaml

Gestion des Releases Helm

Afficher les valeurs par défaut des Charts Helm

  • Les « valeurs » des Charts Helm sont des variables/paramètres que nous pouvons définir afin de personnaliser les manifestes qui seront déployés par les Charts, lors de l'installation ou des mises à jour
  • Pour obtenir toutes les « valeurs » possibles qui peuvent être fournies aux Charts, procédez comme suit :
$ chart_name=grafana/grafana
$ helm show values $chart_name
global:
  # -- Overrides the Docker registry globally for all images
  imageRegistry: null
  (...)
  # global:
  #   imagePullSecrets:
  #   - pullSecret1
  #   - pullSecret2
  imagePullSecrets: []

rbac:
  create: true
(...)

La sortie de la commande précédente pourrait être redirigée vers un fichier « values.yaml », qui sera ensuite personnalisé et utilisé comme fichier de valeurs d'entrée lors de la création des Releases de Charts.

Voir les valeurs, manifestes, hooks et notes des Releases Helm

Nous pouvons obtenir les données suivantes à partir d'une Release Helm :

  • values: valeurs fournies par l'utilisateur
  • hooks: hooks Helm utilisés
  • manifest: les manifestes des ressources créées par la Release
  • notes: le contenu du fichier NOTES.txt du Chart

Nous pourrions également utiliser all pour toutes les valeurs et manifestes actuellement utilisés, ainsi que les hooks actuellement utilisés et le contenu du fichier NOTES.txt du Chart.

Voici des exemples :

$ chart_name=grafana/grafana
$ release_name=grafana

$ helm get values $release_name [-n $namespace]
USER-SUPPLIED VALUES:
null

$ helm get manifest $release_name
---
# Source: grafana/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
automountServiceAccountToken: false
metadata:
  labels:
    helm.sh/chart: grafana-8.4.4
    app.kubernetes.io/name: grafana
    app.kubernetes.io/instance: grafana
    app.kubernetes.io/version: "11.1.3"
    app.kubernetes.io/managed-by: Helm
  name: grafana
  namespace: default
---
# Source: grafana/templates/secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: grafana
(...)

$ helm get hooks $release_name
(...)
---
# Source: grafana/templates/tests/test-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: grafana-test
  namespace: default
  annotations:
    "helm.sh/hook": test
    "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
  labels:
    helm.sh/chart: grafana-8.4.4
    app.kubernetes.io/name: grafana
    app.kubernetes.io/instance: grafana
    app.kubernetes.io/version: "11.1.3"
    app.kubernetes.io/managed-by: Helm
data:
  run.sh: |-
    @test "Test Health" {
      url="http://grafana/api/health"

      code=$(wget --server-response --spider --timeout 90 --tries 10 ${url} 2>&1 | awk '/^  HTTP/{print $2}')
      [ "$code" == "200" ]
    }
---
(...)

$ helm get all $release_name
COMPUTED VALUES:
admin:
  existingSecret: ""
  passwordKey: admin-password
  userKey: admin-user
adminUser: admin
affinity: {}
alerting: {}
assertNoLeakedSecrets: true
automountServiceAccountToken: true
autoscaling:
  behavior: {}
  enabled: false
  maxReplicas: 5
  minReplicas: 1
  targetCPU: "60"
  targetMemory: ""
containerSecurityContext:
  allowPrivilegeEscalation: false
  capabilities:
    drop:
    - ALL
  seccompProfile:
    type: RuntimeDefault
(...)

Créer ou mettre à jour des Releases Helm à partir d'un Chart

helm upgrade --install $release_name $chart_name --create-namespace [-n $namespace] [--version $chart_version] [-f values.yaml] [--set key=value] [--wait] 
  • Les options « -n » et « --version » peuvent être utilisées pour spécifier respectivement l'espace de noms de la Release ainsi que la version du Chart à utiliser
  • L'option « --create-namespace » est utilisée pour créer automatiquement l'espace de noms cible pour la Release s'il n'existe pas
  • L'option « -f » ou « --values » permet de définir des valeurs personnalisées pour le Chart. Cette option peut être utilisée plusieurs fois avec plusieurs fichiers de valeurs. Les valeurs des derniers fichiers spécifiés auront une priorité plus élevée.
  • Les valeurs des Charts peuvent également être spécifiées directement en ligne de commande à l'aide de « --set key1=value1,key2=value2... ». Les valeurs spécifiées avec l'option « --set » ont une priorité supérieure à celles spécifiées avec « -f » ou « --values ». La barre oblique inverse peut être utilisée pour échapper certains caractères.
    Exemple : --set nodeSelector."kubernetes\.io/role"=master
  • L'option « --wait » permet d'attendre que tous les pods soient prêts. Helm attendra le temps spécifié par l'option « --timeout » (valeur par défaut : « 5 minutes »).

Lister et obtenir le statut des Releases Helm

$ helm list [-n $namespace]
NAME    NAMESPACE       REVISION        UPDATED                       STATUS          CHART           APP VERSION
grafana default         2               <release_creation_date>       deployed        grafana-8.3.6   11.1.0

$ release_name=grafana
$ helm status $release_name [-n $namespace]
NAME: grafana
(...)
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
1. Get your 'admin' user password by running:

   kubectl get secret --namespace default grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo
(...)

Voir l'historique des versions de Helm

$ release_name=grafana
$ helm history $release_name [-n $namespace]
REVISION        UPDATED                         STATUS          CHART           APP VERSION     DESCRIPTION     
1               <release_creation_date>         superseded      grafana-8.3.6   11.1.0          Install complete
2               <release_creation_date>         deployed        grafana-8.3.6   11.1.0          Upgrade complete

Rollback des Releases Helm

# Rolling back

$ release_name=grafana

# Rollback to previous release
$ helm rollback $release_name [-n $namespace]

# Rollback to specific revision of a release
# Revision numbers are shown in release history
$ revision_number=1
$ helm rollback $release_name $revision_number [-n $namespace]
Rollback was a success! Happy Helming!

# Show release created after rollback
$ helm history $release_name
REVISION        UPDATED                         STATUS          CHART           APP VERSION     DESCRIPTION     
(...)
3               <release_creation_date>         deployed        grafana-8.3.6   11.1.0          Rollback to 1

Désinstaller des Releases Helm

$ release_name=grafana
$ helm uninstall $release_name [--keep-history -n $namespace]
release "grafana" uninstalled

L'option facultative « --keep-history » permet de conserver les enregistrements de suppression. Cela permet d'afficher les Releases supprimées avec les options « --uninstalled » ou « --all » lors de l'affichage des Releases avec la commande « helm list ».

$ helm list --uninstalled
NAME    NAMESPACE       REVISION        UPDATED                                 STATUS          CHART           APP VERSION
grafana default         1               <release_creation_date>                 uninstalled     grafana-8.4.4   11.1.3

Création de Charts Helm

Structure des Charts Helm

Voici la structure de base des sources de Charts Helm :

$ tree mychart/
mychart/
├── charts
├── Chart.yaml
├── NOTES.txt
├── templates
│   ├── deployment.yaml
│   └── _helpers.tpl
└── values.yaml
  • Préférez utiliser l'extension « .yaml » plutôt que « .yml » lors du développement de Charts Helm
  • Pour générer rapidement un exemple de Chart pouvant servir de base de développement, utilisez la commande suivante :
helm create $chart_directory_path

Le fichier Chart.yaml

Le fichier « Charts.yaml » contient les métadonnées des Charts. Voici un exemple :

apiVersion: v2
version: 0.0.1 # Sementic Versioning (SemVer)
name: myapp
description: Helm Chart for myapp
type: application # or 'library'
appVersion: 10.2.3

Tous les champs disponibles pour ce fichier sont répertoriés ici. Le champ « type » indique le type de Chart que nous créons. Il existe deux types de Charts : « application » et « library ».

Les Charts d'application sont les Charts classiques que nous utilisons pour déployer des applications Kubernetes. C'est celui que nous allons créer dans cet article.

Les Charts de librairie ne sont utiles que lors du développement de Charts pour ajouter de nouveaux utilitaires ou fonctions dans le pipeline de rendu. Ils ne contiennent aucun template de ressources Kubernetes et ne peuvent donc pas être déployés.

Les champs apiVersion, name et version sont obligatoires.

Le répertoire charts

Le répertoire « charts » est utilisé pour stocker les packages des Charts de dépendances. Les Charts de dépendances requis sont déclarés via le champ « dependencies » à l'intérieur du fichier Chart.yaml.

Les « valeurs » personnalisées pour les dépendances sont définies sous la clé correspondant au nom des dépendances. Voici un exemple :

# File: Charts.yaml

(...)
dependencies:
  - name: ingress-nginx
    repository: https://helm.nginx.com/stable
  (...)
(...)

# File: values.yaml

nginx:
  # Charts values from the ingress-nginx Helm Charts repository:
  # https://github.com/kubernetes/ingress-nginx/blob/main/charts/ingress-nginx/values.yaml
  namespaceOverride: mynamespace
  controller:
    name: mycontroller
  (...)

Le répertoire templates

Le répertoire « templates » contient les fichiers manifestes « .yaml » des ressources Kubernetes. Ces fichiers contiennent la définition des ressources Kubernetes pouvant être déployées à l'aide du Chart.

La syntaxe, ainsi que les fonctions du moteur de templating Go peuvent être utilisées à l'intérieur de ces fichiers. Les Fonctions Sprig ainsi que des Objets natifs Helm supplémentaires peuvent également être utilisés. Pour obtenir une liste de toutes les fonctions disponibles dans les templates de manifestes de ressources Kubernetes, consultez Liste des fonctions de templating de Charts Helm.

Notez que les fichiers de templates préfixés par un underscore (_) ne génère pas de sortie interprétée, mais leur contenu peut être utilisé dans d'autres templates de manifestes. C'est le cas, par exemple, du fichier « _helpers.tpl », utilisé pour stocker des blocs de templates réutilisables. Voici un exemple :

# File: templates/_helpers.tpl

{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride }}
{{- end -}}

# File: templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "name" . }}
  (...)

Le fichier NOTES.txt

Le contenu du fichier « NOTES.txt » sera affiché sur la sortie standard après la création d'une Release à partir du Chart. Ce fichier est également compatible avec le langage de templating Go . Voici un exemple de contenu :

=> Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
  {{- range .paths }}
  http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
  {{- end }}
{{- end }}
{{- else if contains "ClusterIP" .Values.service.type }}
  export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "..name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
  export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}

Le fichier values.yaml

Le fichier « values.yaml » contient les paramètres de configuration par défaut du Chart, que les utilisateurs peuvent définir pour personnaliser leurs Releases. Ces personnalisations peuvent consister, par exemple, à attribuer un nom personnalisé à toutes les ressources de la Release, à activer/désactiver des ressources ou fonctionnalités spécifiques, ou encore à définir des valeurs personnalisées pour des champs spécifiques de certaines ressources. Voici un exemple de contenu :

# values.yaml
nameOverride: myapp
image:
  repository: selenium/node-firefox
  tag: 3.141.59
replicaCount: 3
autoscaling:
  enabled: false
(...)

Tous les paramètres du Chart spécifiés comme valeurs (via values.yaml ou l'option « --set » de la CLI Helm) sont exportés vers l'objet Helm « .Values », accessible depuis les fichiers de templates. Voici un exemple d'utilisation des paramètres de values de Charts Helm dans un template de manifeste de ressources de type deployment :

# File: templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  (...)
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  (...)

Notions de base pour templating de Charts Helm

  • Pour afficher les manifestes des ressources Kubernetes à partir d'un répertoire Helm Chart, utilisez :
    • helm template $path_to_chart_directory --debug
  • Les directives et fonctions de création de templates sont placées entre des accolades doubles. {{ }}
  • Voici comment nous pouvons utiliser des commentaires à l'intérieur de fichiers de templates, sur une ou plusieurs lignes : {{/* my comment */}}
  • La routine shell - est utilisé au début des doubles accolades comme ceci {{- }} ou à la fin {{ -}} afin de supprimer respectivement tous les caractères d'espacement avant ou après les directives de création de templates. Voici quelques exemples :
# Without '-', whitespaces before and after 
# the double curly braces '{{ }}' are preserved

# File: templates/test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ "myapp" }}
  labels:
    {{ "app.kubernetes.io/name: myapp" }}

# Result
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:
    app.kubernetes.io/name: myapp
    
# Removing all whitespaces before the label

# File: templates/test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ "myapp" }}
  labels:
    {{- "app.kubernetes.io/name: myapp" }}
    
# Result
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:app.kubernetes.io/name: myapp
  • La routine shell {{- }} est couramment utilisé pour les blocs de templates définis dans le fichier « _helpers.tpl ». Il est également couramment utilisé dans les templates de ressources Kubernetes, en combinaison avec la fonction nindent qui ajoute une nouvelle ligne et le nombre d'espaces souhaité pour l'indentation. Voici un exemple :
# Removing all whitespaces before the label

# File: templates/test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ "myapp" }}
  labels:
    {{- "app.kubernetes.io/name: myapp" }}
    
# Result
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:app.kubernetes.io/name: myapp
  
# Adding a new line + 4 spaces for indentation

# File: templates/test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ "myapp" }}
  labels:
    {{- "app.kubernetes.io/name: myapp" | nindent 4 }}
    
# Result
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:
    app.kubernetes.io/name: myapp
  • La routine shell {{ -}} est généralement utilisé lors de la définition de blocs de templates dans le fichier « _helpers.tpl ». Voici un exemple :
# File: templates/_helpers.tpl
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride }}
{{- end }}
  • Le point {{ . }} est un objet contenant toutes les valeurs ainsi que d'autres objets natifs Helm ou une portion des valeurs en fonction de l'emplacement à partir duquel il est appelé à l'intérieur d'un fichier template. Voici un exemple où nous obtenons tous les objets :
# File: values.yaml
(...)
autoscaling:
  enabled: true
env:
  - name: HUB_HOST
    value: "selenium-hub"
  - name: HUB_PORT
    value: "4444"
  - name: NODE_MAX_INSTANCES
    value: "1"
  - name: NODE_MAX_SESSION
    value: "1"
(...)

# File: templates/test.yaml
{{ toYaml . }}

# Result
$ helm template . --debug
install.go:178: [debug] Original chart version: ""
install.go:195: [debug] CHART PATH: /home/gmkziz/helm-charts/mychart

---
# Source: myapp/templates/test.yaml
Capabilities:
  APIVersions:
  - v1
  (...)
  HelmVersion:
    git_commit: 414ff28d4029ae8c8b05d62aa06c7fe3dee2bc58
    (...)
  KubeVersion:
    Major: "1"
    (...)
  IsRoot: true
  apiVersion: v2
  appVersion: 10.2.3
  description: Helm Chart for myapp
  name: myapp
  type: application
  version: 0.0.1
Files:
  (...)
Release:
  IsInstall: true
  (...)
Subcharts: {}
Template:
  BasePath: myapp/templates
  Name: myapp/templates/test.yaml
Values:
  autoscaling:
    enabled: true
  env:
  - name: HUB_HOST
    value: selenium-hub
  - name: HUB_PORT
    value: "4444"
  - name: NODE_MAX_INSTANCES
    value: "1"
  - name: NODE_MAX_SESSION
    value: "1"
  (...)
  • Maintenant, changeons la portée avec un block range et vérifions que le {{ . }} contient uniquement les objets à l'intérieur de cette portée :
# File: values.yaml
(...)
autoscaling:
  enabled: true
env:
  - name: HUB_HOST
    value: "selenium-hub"
  - name: HUB_PORT
    value: "4444"
  - name: NODE_MAX_INSTANCES
    value: "1"
  - name: NODE_MAX_SESSION
    value: "1"
(...)

# File: templates/test.yaml
{{- range .Values.env }}
{{ toJson . }}
{{- end }}

# Result
$ helm template . --debug
install.go:178: [debug] Original chart version: ""
install.go:195: [debug] CHART PATH: /home/gmkziz/helm-charts/mychart

---
# Source: myapp/templates/test.yaml
{"name":"HUB_HOST","value":"selenium-hub"}
{"name":"HUB_PORT","value":"4444"}
{"name":"NODE_MAX_INSTANCES","value":"1"}
{"name":"NODE_MAX_SESSION","value":"1"}
  • Les objets que nous obtenons en utilisant {{ . }} dépendent de la portée ou du contexte dans lequel nous nous trouvons actuellement
  • La portée à partir de laquelle nous obtenons tous les objets natifs Helm s'appelle portée racine (root scope)
  • Nous pouvons obtenir les objets de portée racine à partir d'un emplacement quelconque depuis les fichiers de template en utilisant le signe $ . Exemple :
# File: values.yaml
(...)
autoscaling:
  enabled: true
env:
  - name: HUB_HOST
    value: "selenium-hub"
  - name: HUB_PORT
    value: "4444"
  - name: NODE_MAX_INSTANCES
    value: "1"
  - name: NODE_MAX_SESSION
    value: "1"
(...)

# File: templates/test.yaml
{{- with .Values.autoscaling  }}
{{ toJson . }}
{{ toJson $.Values.env }}
{{- end }}

# Result
$ helm template . --debug
install.go:178: [debug] Original chart version: ""
install.go:195: [debug] CHART PATH: /home/gmkziz/helm-charts/mychart

---
# Source: myapp/templates/test.yaml
{"enabled":true}
[{"name":"HUB_HOST","value":"selenium-hub"},{"name":"HUB_PORT","value":"4444"},{"name":"NODE_MAX_INSTANCES","value":"1"},{"name":"NODE_MAX_SESSION","value":"1"}]

Conditions et variables dans les Charts Helm

  • Les fonctions de comparaison peuvent être trouvées ici et ici
# File: templates/deployment.yaml

# if autoscaling.enabled is not set to true,
# set the number of replicas from the replicaCount value
apiVersion: apps/v1
kind: Deployment
metadata:
  (...)
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  (...)

# Set resource type depending on the podController value
apiVersion: apps/v1
{{- if eq .Values.podController "Deployment" }}
kind: Deployment
{{- else if eq .Values.podController "Statefulset" }}
kind: Statefulset
{{- else }}
kind: Deployment
{{- end }}
metadata:
  (...)
  
# File: templates/ingress.yaml

{{/* Defining variables */}}
{{- $svcPort := .Values.service.port -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  (...)

{{/* Referencing variables */}}
(...)
spec:
  (...)
  rules:
    - host: myhost.local
      http:
        paths:
          - path: /
            backend:
              service:
                name: myservice
                port:
                  number: {{ $svcPort }}

Actions couramment utilisées dans les Charts Helm

  • define
  • include
  • with
  • range

Fonctions couramment utilisées dans les Charts Helm

Liste des fonctions de templating Helm

  • nindent
  • toYaml
  • trunc
  • trimSuffix
  • replace
  • default
  • printf

Template de Chart Helm pour le nommage des ressources

Une manière courante de définir les noms des ressources de Charts Helm est la suivante :

  • « .Values.fullnameOverride » si défini, sinon
  • une combinaison de « .Release.Name » et « .Values.nameOverride » si « .Values.nameOverride » est défini et ne contient pas « .Release.Name », sinon
  • Une combinaison de « .Release.Name » et « .Chart.Name » si « .Chart.Name » ne contient pas « .Release.Name », sinon
  • « .Release.name »

Les noms des ressources doivent également être tronqués à 63 caractères en raison d'une limitation de certains champs de noms Kubernetes (spécification de nommage DNS) et le suffixe - supprimé.

Voici un exemple de bloc de template de Chart Helm pour définir les noms de ressources :

# File: templates/_helpers.tpl

{{- define "fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}

Template de Chart Helm pour la labellisation des ressources

Voici les clés d'étiquettes (labels) courantes que nous pouvons utiliser dans nos Charts :

  • helm.sh/chart: une combinaison du nom et de la version du Chart
  • app.kubernetes.io/version: la version de l'application gérée par le Chart
  • app.kubernetes.io/managed-by:
  • app.kubernetes.io/name: le nom de l'application gérée par le Chart
  • app.kubernetes.io/instance: le nom de la release

app.kubernetes.io/name et app.kubernetes.io/instance sont couramment utilisés comme labels de sélecteur pour les ressources de type Deployments et StatefulSets.

Voici quelques exemples d'utilisation de labels dans les Charts Helm :

# File: templates/_helpers.tpl
# Define reusable templating blocks

{{/* 
Return .Values.nameOverride if defined, 
otherwise .Chart.name
*/}}
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride }}
{{- end -}}

{{- define "labels" -}}
helm.sh/chart: {{ .Chart.Name }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{ include "selectorLabels" . }}
{{- end }}

{{- define "selectorLabels" -}}
app.kubernetes.io/name: {{ include "name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

# File: templates/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  (...)
  labels:
    {{- include "labels" . | nindent 4 }}
spec:
  (...)
  selector:
    matchLabels:
      {{- include "selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "selectorLabels" . | nindent 8 }}
(...)

Création et packaging d'un Chart Helm simple

Créons un exemple de Chart Helm qui servira à créer une ressource de type deployment. Voici le contenu du dossier du Chart :

$ tree helm-chart-example/
helm-chart-example/
├── charts
├── Chart.yaml
├── NOTES.txt
├── templates
│   ├── deployment.yaml
│   └── _helpers.tpl
└── values.yaml
  • Contenu du fichier Chart.yaml
apiVersion: v2
version: 0.0.1
name: myapp
description: Helm Chart for myapp
type: application
appVersion: 10.2.3
  • Contenu du fichier _helpers.tpl
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride }}
{{- end -}}

{{/* 
The fullname tempating block will be 
used to set resources names
*/}}
{{- define "fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := include "name" . }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name }}
{{- end }}
{{- end }}
{{- end }}

{{- define "chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{- define "labels" -}}
helm.sh/chart: {{ include "chart" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{ include "selectorLabels" . }}
{{- end }}

{{- define "selectorLabels" -}}
app.kubernetes.io/name: {{ include "name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
  • Contenu du fichier deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "fullname" . }}
  labels:
    {{- include "labels" . | nindent 4 }}
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "selectorLabels" . | nindent 8 }}
    spec:
      {{- with .Values.volumes }}
      volumes:
        {{- toYaml . | nindent 8 }}
      {{- end }}  
      containers:
      - name: {{ include "name" . }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
        {{- with .Values.volumeMounts }}
        volumeMounts:
          {{- toYaml . | nindent 8 }}
        {{- end }}  
        {{- with .Values.env }}
        env:
          {{- toYaml . | nindent 8 }}
        {{- end }}
        {{- with .Values.resources }}
        resources:
          {{- toYaml . | nindent 10 }}
        {{- end }}
  • Contenu du fichier values.yaml
nameOverride: myapp2
fullnameOverride: 
image:
  repository: selenium/node-firefox
  tag: 3.141.59
replicaCount: 3
autoscaling:
  enabled: true
volumes:
- name: dshm
  emptyDir:
    medium: Memory
volumeMounts:
  - name: dshm
    mountPath: /dev/dshm
env:
  - name: HUB_HOST
    value: "selenium-hub"
  - name: HUB_PORT
    value: "4444"
  - name: NODE_MAX_INSTANCES
    value: "1"
  - name: NODE_MAX_SESSION
    value: "1"
resources:
  limits:
    memory: "2000Mi"
  requests:
    memory: "2000Mi"
  • Pour afficher le contenu des manifestes résultants sur la sortie standard à partir du Chart, nous utilisons :
# From inside the helm-chart-example directory
$ helm template --debug .
  • Pour créer un package de Chart, nous utilisons :
$ helm package helm-chart-example
Successfully packaged chart and saved it to: /home/gmkziz/helm-charts/myapp-0.0.1.tgz

Création d'une Release à l'aide du Chart Helm précédent

$ helm show values myapp-0.0.1.tgz > values.yaml
nameOverride: primary
fullnameOverride: 
image:
  repository: selenium/node-firefox
  tag: 3.141.59
replicaCount: 3
(...)

# Set the custom Charts values as desired inside the values.yaml file,
# then:

$ helm upgrade --install myapp -f values.yaml myapp-0.0.1.tgz 
NAME: myapp
(...)
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

$ helm list
NAME    NAMESPACE       REVISION        UPDATED                                 STATUS          CHART           APP VERSION
myapp   default         1               <release_creation_date>                 deployed        myapp-0.0.1     10.2.3

$ kubectl get deploy
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
myapp-primary   1/1     1            1           84s

$ kubectl get pods
NAME                            READY   STATUS    RESTARTS   AGE
myapp-primary-65596d9948-k7m2z   1/1     Running   0          79s

$ kubectl get deploy -o yaml
apiVersion: v1
items:
- apiVersion: apps/v1
  kind: Deployment
  metadata:
    annotations:
      deployment.kubernetes.io/revision: "1"
      meta.helm.sh/release-name: myapp
      meta.helm.sh/release-namespace: default
    creationTimestamp: "***"
    generation: 1
    labels:
      app.kubernetes.io/instance: myapp
      app.kubernetes.io/managed-by: Helm
      app.kubernetes.io/name: primary
      app.kubernetes.io/version: 10.2.3
      helm.sh/chart: myapp
    name: myapp-primary
    namespace: default
    resourceVersion: "990"
    uid: bf577704-2d19-4b43-88ac-a7450c32ced1
  spec:
    progressDeadlineSeconds: 600
    replicas: 1
    revisionHistoryLimit: 10
    selector:
      matchLabels:
        app.kubernetes.io/instance: myapp
        app.kubernetes.io/name: primary
    strategy:
      rollingUpdate:
        maxSurge: 25%
        maxUnavailable: 25%
      type: RollingUpdate
    template:
      metadata:
        creationTimestamp: null
        labels:
          app.kubernetes.io/instance: myapp
          app.kubernetes.io/name: primary
      spec:
        containers:
        - env:
          - name: HUB_HOST
            value: selenium-hub
          - name: HUB_PORT
            value: "4444"
          - name: NODE_MAX_INSTANCES
            value: "1"
          - name: NODE_MAX_SESSION
            value: "1"
          image: selenium/node-firefox:3.141.59
          imagePullPolicy: IfNotPresent
          name: primary
          resources:
            limits:
              memory: 2000Mi
            requests:
              memory: 2000Mi
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
          volumeMounts:
          - mountPath: /dev/dshm
            name: dshm
        dnsPolicy: ClusterFirst
        restartPolicy: Always
        schedulerName: default-scheduler
        securityContext: {}
        terminationGracePeriodSeconds: 30
        volumes:
        - emptyDir:
            medium: Memory
          name: dshm
  status:
    availableReplicas: 1
    conditions:
    - lastTransitionTime: "***"
      lastUpdateTime: "***"
      message: Deployment has minimum availability.
      reason: MinimumReplicasAvailable
      status: "True"
      type: Available
    - lastTransitionTime: "***"
      lastUpdateTime: "***"
      message: ReplicaSet "myapp-primary-74fdbf6bd" has successfully progressed.
      reason: NewReplicaSetAvailable
      status: "True"
      type: Progressing
    observedGeneration: 1
    readyReplicas: 1
    replicas: 1
    updatedReplicas: 1
kind: List
metadata:
  resourceVersion: ""