Introduction
Dans l’article précédent, nous avons vu comment mettre en place FluxCD sur un cluster Kubernetes afin de créer automatiquement des ressources lorsque des manifestes YAML sont poussés sur un répertoire de code. Dans cette deuxième partie, nous allons voir comment utiliser Helm pour créer des ressources plus rapidement à l’aide de templates variabilisés. Premièrement, nous allons voir son utilisation seule dans un cluster Kubernetes, puis son utilisation dans le cadre d’une synchronisation de répertoire Git avec un cluster lorsqu’on utilise FluxCD.
Pour rappel, voici l’infrastructure que nous avons mise en place sur notre cluster Kubernetes.
Mon installation:
- Instance AWS Cloud9
- Instance type: m5.large (8 GiB RAM + 2 vCPU)
- OS: Ubuntu Server 18.04 LTS
- Minikube v1.27.1
- Helm v3.7
- Flux v2
Helm
Helm, à quoi ça sert ?
Helm est un package manager pour Kubernetes. Il permet de déployer des applications conteneurisées à partir de templates, ce qui permet d’avoir une cohérence au niveau des fichiers de configuration entre différentes applications. Il est également utile lorsque l’on souhaite déployer une même application sous différents environnements (développement, staging, pré-production, production) avec des configurations relatives avec ces derniers.
Depuis sa V3, Helm n’a plus besoin de passer par un agent (Tiller) installé sur le cluster Kubernetes pour créer des ressources, le client est en capacité de communiquer directement avec le Kube Api Server via le port 6443.
Terminologie
- Un chart: un package Helm qui contient toutes les définitions des ressources nécessaires pour exécuter une application.
- Une Release: une instance d’un chart tournant sur un cluster Kubernetes. On peut installer plusieurs releases à partir d’un même chart sur un même cluster.
- HelmRelease : Ressource qui permet de faire le pont entre Helm et Flux.
- Dossier Template: Le fichier qui contient les manifestes variabilisés des ressources Kubernetes dans le chart
- Fichier Values: Un fichier qui contient les valeurs attribuées aux variables contenues dans le dossier des templates.
Helm Quickstart
Installer le client Helm sur votre machine :
$ curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
$ sudo apt-get install apt-transport-https --yes
$ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
$ sudo apt-get update
$ sudo apt-get install helm
Voir la version de helm installée :
$ helm version
Helm repose sur le concept de chart, c'est-à-dire, des templates que nous pouvons utiliser pour créer un ensemble de ressources.
Lister les charts à disposition localement :
$ helm list
Utiliser des releases officielles
Pour démarrer et comprendre le principe de Helm, il peut être utile d’utiliser des charts officiels
Ajouter un répertoire publique (ici bitnami):
$ helm repo add bitnami https://charts.bitnami.com/bitnami
Afficher toutes les releases disponibles chez bitnami:
$ helm search repo bitnami
Installer le chart mysql développé par bitnami:
$ helm install my-release bitnami/mysql
Cette commande déploie une release du chart mysql sur le cluster kubernetes.
On peut observer les releases présentes sur notre cluster en utilisant la commande suivante :
$ helm ls
NAME NAMESPACE REVISION STATUS CHART APP VERSION
my-release default 1 deployed nginx-13.2.10 1.23.1
Si on regarde l'état des pods :
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
my-release-nginx-6ff47c9598-2php6 1/1 Running 0 36s
Désinstaller sa release du cluster :
$ helm uninstall my-release
Tous les répertoires sont centralisés sur artifacthub.io .
Créer sa release custom
On peut utiliser des charts officiels mais il peut être utile de créer les siens dans certains cas. Pour ce faire, il existe la commande suivante :
$ helm create mychart
La structure d’un chart Helm ressemble à la suivante:
- ./template: Le dossier dans lequel seront stockés les manifestes de création de ressource (ici un fichier deployment.yaml et service.yaml à titre d’exemple).
- ./template/deployment.yaml: un manifest permettant de créer une ressource deployment
- ./template/service.yaml: Un manifest permettant de créer une ressource service
- ./template/_helpers.tpl: Le fichier template dans lequel les valeurs par défaut du chart sont déclarées
- Chart.yaml: Manifest permettant de créer le chart qui est de type “application” ou “library”
- values.yaml: Fichier contenant les valeurs à appliquer dans le chart
Ce chart de démonstration étant un peu dense, effaçons le et recréons en un plus simple. Disons que nous souhaitons créer le même déploiement que celui de l’article précédent. A savoir:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
minReadySeconds: 5
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
Un appel au fichier de valeur se manifeste dans une expression entourée par des accolades “{{“ et “}}”. Ici par exemple, le nom et un label du déploiement sont variabilisés, ainsi que le nombre de replicas.
./templates/pod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.deployment.name }}-deployment
labels:
app: {{ .Values.deployment.name }}
spec:
replicas: {{ .Values.deployment.replicas }}
selector:
matchLabels:
app: {{ .Values.deployment.name }}
template:
metadata:
labels:
app: {{ .Values.deployment.name }}
spec:
containers:
- name: {{ .Values.deployment.name }}
image: {{ .Values.deployment.image }}
ports:
- containerPort: {{ .Values.deployment.port }}
Des valeurs peuvent ensuite être initiées à ces variables, depuis le fichier de values:
values.yaml
deployment:
name: nginx
replicas: 3
image: nginx:1.14.2
port: 80
Avant de créer une release à partir de ce dossier template et ce fichier de valeurs, on peut vouloir tester le template au préalable, avec la commande suivante:
$ helm template mychart-release ./ --debug
Avec :
- mychart-release: Le nom que vous souhaitez donner à votre release (attention que des lettres minuscules, chiffres ainsi que les caractères “-” et “.” ne sont autorisés)
- ./ : Le chemin de votre chart. Ici nous sommes dedans.
- --debug: Un des flags
Une fois que celui-ci est validé, le template chart peut être considéré comme utilisable et nous pouvons donc créer la release. Pour ce faire, utilisons la commande suivante:
$ helm install mychart-release ./
Nous pouvons vérifier ensuite que les 3 pods ont bien été créés avec kubectl:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-7fb96c846b-7djq7 1/1 Running 0 11s
nginx-deployment-7fb96c846b-b69qz 1/1 Running 0 11s
nginx-deployment-7fb96c846b-z47bm 1/1 Running 0 11s
Pour vérifier le comportement de Helm, créons un deuxième fichier de valeur en passant le nombre de replicas à 2 cette fois-ci et en changeant le nom et l'image utilisée pour le déploiement:
values2.yaml
deployment:
name: redis
replicas: 2
image: redis
port: 8080
Pour créer une nouvelle release à partir de ce nouveau fichier de valeur, utilisons la commande suivante:
$ helm install mychart-release-2 ./ -f values2.yaml
Vérifions le nombre de pods:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-7fb96c846b-7djq7 1/1 Running 0 40m
nginx-deployment-7fb96c846b-b69qz 1/1 Running 0 40m
nginx-deployment-7fb96c846b-z47bm 1/1 Running 0 40m
redis-deployment-78fb488fd5-9bcdd 1/1 Running 0 3s
redis-deployment-78fb488fd5-wsks2 1/1 Running 0 3s
Nous avons bien 2 nouveau pods redis qui ont été lancés, et cela à partir du même template mais en passant un fichier de valeurs différent.
L’utilisation du helpers.tpl
Jusqu’à présent, nous avons pu récupérer des valeurs depuis un fichier afin de personnaliser des ressources créées à partir de manifestes communs à d’autres applications. Dans ce cadre d’utilisation, il peut être utile d’inclure des valeurs par défaut dans les manifestes (gain de temps et homogénéisation dans la structure des ressources), valeurs qui seraient adaptées à chaque application mais non renseignées dans le fichier de valeur. Ces valeurs pourraient par exemple être le numéro de version de la Release, le namespace dans lequel la Release est déployée, la concaténation du nom de l’application et de sa version pour des impératifs de labellisation (pour du monitoring par exemple). Certaines de ces valeurs sont récupérables grâce à des fonctions pré-intégrées dans Helm (par exemple “Release.Name”, “Release.Namespace”, “Release.Revision”) et sont injectables dans un manifeste en passant par des blocs balisés par des accolades “{{“ et “}}”. Par exemple:
{{ .Release.Name }}
L’avantage de passer par ces variables, est qu’elles sont réutilisables dans tout le chart. Vous pouvez retrouver la liste des fonctions et objets présents dans Helm sur le lien suivant:
Pour les variables qui ne sont pas prévues ou qui sont spécifiques à la politique informatique d’une entreprise, il existe le fichier “_helper.tpl” (tpl pour template) qui permet de créer ses propres fonctions et variables. Par exemple, dans le cadre d’un déploiement, imaginons qu'il est impératif que tous les pods contiennent un label contenant le nom de la personne qui déploie l’application. Il est possible de le faire en définissant une variable dans le fichier helper et en injectant la variable dans le manifeste comme suit:
_helpers.tpl
{{- define "mychart.labels" -}}
deployedby: paul
{{- end }}
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.deployment.name }}-deployment
labels:
app: {{ .Values.deployment.name }}
spec:
replicas: {{ .Values.deployment.replicas }}
selector:
matchLabels:
app: {{ .Values.deployment.name }}
template:
metadata:
labels:
app: {{ .Values.deployment.name }}
{{- include "mychart.labels" . | nindent 8 }}
spec:
containers:
- name: {{ .Values.deployment.name }}
image: {{ .Values.deployment.image }}
ports:
- containerPort: {{ .Values.deployment.port }}
En regardant de plus prêt notre pod, nous pouvons voir que le label lui a bien été ajouté :
$ kubectl describe pod redis-deployment-64d969dc4-hjskh
Name: redis-deployment-64d969dc4-hjskh
Namespace: default
Priority: 0
Service Account: default
Node: minikube/192.168.49.2
Start Time: Thu, 13 Oct 2022 09:12:13 +0000
Labels: app=redis
deployedby=paul
pod-template-hash=64d969dc4
Dans ce cas là, l’intérêt d’utiliser ce helper n'est pas bien clair, il aurait été plus simple d'écrire la mention “deployedby=paul” directement dans le template. Mais le helper permet de créer des conventions de nommage.
Par exemple, si au sein d'une entreprise, chaque pod doit être nommé par le nom et la version de l’application (qui eux sont indiqués dans le fichier de valeur), la variable podname peut être construite comme suit:
_helpers.tpl
{{- define "mychart.podname" -}}
{{- printf "%s-%s" .Values.deployment.name .Values.deployment.version }}
{{- end }}
Chaque équipe utilisant ce template se verra imposer cette même convention de nommage.
Il est possible de faire de nombreuses choses avec ce fichier de template, de nombreuses fonctions existent pour créer de nouvelles variables. L’objectif derrière cette création de valeur par défaut est encore une fois d’homogénéiser les ressources Kubernetes au sein d’un groupe ou d’une entreprise.
Et si on combine FluxCD et Helm ?
Nous avons vu le fonctionnement de FluxCD dans le premier article de cette série, puis le fonctionnement de Helm, le meilleur des mondes voudrait que l’on puisse utiliser les deux en même temps, c’est à dire qu’à chaque fois qu’un fichier de valeur serait modifié (le nombre de replicas dans un déploiement par exemple), les ressources présentes sur un cluster serait mises à jour avec ces valeurs. Il s’avère que Helm vient nativement avec FluxCD, via le Helm-Controller, lorsque celui-ci est installé sur un cluster.
Reprenons une nouvelle infrastructure, je vais détailler à nouveau toutes les étapes qui permettent d’avoir une telle synchronisation.
Lancement d’un cluster Kubernetes en local
Je recréée d’abord une instance Cloud9 sur laquelle j’installe Minikube.
$ curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
$ sudo install minikube-linux-amd64 /usr/local/bin/minikube
Une fois installée, j’ai un cluster Kubernetes accessible sur mon instance. Je peux le lancer avec la commande suivante:
$ minikube start
N’oublions pas de créer un alias pour simuler l’utilisation de la commande kubectl
$ alias kubectl="minikube kubectl --"
Installation de la CLI Flux
Absolument rien ne change ici par rapport au premier article:
$ curl -s https://fluxcd.io/install.sh | sudo bash
Installation de Flux et Helm dans le cluster
La seconde étape consiste en la génération d’un token d’accès depuis Gitlab et de l’utiliser comme variable d’environnement dans l’instance. Ce token permettra à flux de s’authentifier auprès de Gitlab pour créer le projet d’installation ou alors de le récupérer s’il existe déjà.
$ export GITLAB_TOKEN=<your-token>
Bootstraper le projet qui va permettre d’installer Helm et FluxCD v2 sur notre cluster, dans un namespace “flux-system”
$ flux bootstrap gitlab \
--owner=paulboisson \
--repository=article-gitops-fluxcd-setup \
--branch=main \
--path=clusters \
--token-auth \
--personal
Cette commande est la même que celle utilisée dans le premier article.
Nous pouvons observer les ressources qui ont été créées en affichant les pods dans le namespace “flux-system”:
$ kubectl get pods -n flux-system
NAME READY STATUS RESTARTS AGE
helm-controller-68b799b589-8hz6f 1/1 Running 0 59s
kustomize-controller-7ddb8d8f7-cp5w9 1/1 Running 0 59s
notification-controller-56bd788f9-sdrtn 1/1 Running 0 59s
source-controller-7d98d6688c-j4c6j 1/1 Running 0 59s
Mise en place de la livraison continue
Regardons de plus près ce que nous voulons mettre en place, nous souhaitons faire de la livraison continue, c’est à dire qu’à chaque modification du code, les nouvelles configurations des ressources vont être poussées sur le cluster.
Nous avons 2 répertoires impliqués (ils peuvent être confondus et être seulement des dossiers dans un même répertoire), l’un contient le chart avec le template de notre application, l’autre contient ce qu’on appelle le helmrelease (différent de la release helm qui est la ressource créée à partir d’un chart helm).
Récupération du projet
Tout d’abord, clonons le projet que j’ai créé à l’avance
$ git clone https://gitlab.com/paulboisson/article-gitops-fluxcd-helmrelease
Dans ce projet, nous avons 4 dossiers:
- chart: contient le chart et le dossier de template (même que précédemment)
- config: contient le fichier de configuration de Kustomize
- releases: contient les fichiers helmrelease qui seront les fichiers dont la détection devra être détectée
- setup: contient les fichiers à appliquer manuellement dans le cluster (pour la création d’une source Git et la préparation de Kustomize)
Nous verrons ces différents fichiers plus en détails par la suite.
Création d’une source
FluxCD est constitué de différents outils GitOps qui permettent notamment de faire de la livraison continue sur Kubernetes. L’un de ces outils, ou composant, est le Source Controller. Il permet de construire des artefacts utilisables depuis Kubernetes à partir de différentes sources:
- Un répertoire Git
- Un répertoire Helm
- Un bucket S3
Dans le cadre de cet article, nous souhaitons pouvoir récupérer un projet depuis Gitlab et devons donc passer par l’API “GitRepository” via l’utilisation d’une ressource de type “GitRepository” comme décrit ci-dessous:
gitRepo.yaml
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
name: myappgitsource
namespace: default
spec:
interval: 1m0s
url: https://gitlab.com/paulboisson/article-gitops-helmrelease.git
ref:
branch: main
Cette ressource nommée “myappgitssource” va sonder le répertoire Gitlab indiqué, toutes les minutes et créer un artefact composé du code contenu sur la branche “main”.
Pour créer cette ressource, on peut soit
- créer ce fichier YAML et l’appliquer avec la commande:
$ kubectl apply -f gitRepo.yaml
- ou alors utiliser la commande flux dédiée:
$ flux create source git myappsource \
--url=https://gitlab.com/paulboisson/article-gitops-fluxcd-helmreleaset \
--branch=main
On peut observer la ressource correspondante et le nom de l’artefact:
$ kubectl get gitrepository
NAME URL AGE READY
myappgitsource https://gitlab.com/paulboisson/article…. 7s True
STATUS
stored artifact for revision 'main/2f41a6b7d5c69bd27e3ad7caa85c7c8a875b3fae'
Création d’une ressource Kustomization
Le kustomize-controller est un opérateur Kubernetes, qui permet de faire de la livraison continue, il permet notamment de surveiller un répertoire et de déclencher une action lorsque des modifications y sont apportées. C’est le controller que nous avons utilisé dans le premier article. Ici nous devons créer une ressource de type “Kustomization” pour faire appel au Kustomize-Controller:
Kustomize.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
name: app
spec:
sourceRef:
kind: GitRepository
name: myappsource
path: ../config
prune: true
interval: 1m
Cette ressource va se baser sur les artefacts générés par la ressource “myappsource” pour déterminer si une modification a été apportée dans le code. Il est possible de créer cette ressource à la main en utilisant la commande :
$ kubectl apply -f Kustomize.yaml
Vérifions que cette ressource a bien été créée et qu’elle est prête à être utilisée:
$ kubectl get kustomization
NAME AGE READY STATUS
app 3s True Applied revision: main/2f41a6b7d5c69bd27e3ad7caa85c7c8a875b3fae
Cependant il faut qu’une autre ressource Kustomization soit présente dans le répertoire pour indiquer les ressources à surveiller. Ici il s’agit du fichier “app.yaml” présent dans le dossier “/releases”. Cette ressource sera automatiquement créé à partir du manifeste suivant par la Kustomize-Controller
/config/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
metadata:
name: myappkustomize
commonLabels:
app: hello
resources:
- ../releases/app.yaml
La HelmRelease
Le Helm-Controller permet de faire du templating à partir de charts et de ressources générées par Flux. La ressource de type HelmRelease est une ressource que le Helm Controller est capable de manipuler dans son utilisation avec FluxCD.
Dans notre cas, c’est ce fichier que nous allons modifier pour faire varier notre configuration. Cette ressource va être automatiquement créée par le Kustomize-Controller.
/releases/app.yaml
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: my-app-release
namespace: default
spec:
interval: 1m
chart:
spec:
sourceRef:
kind: GitRepository
name: myappgitsource
namespace: default
chart: chart
interval: 1m
values:
deployment:
name: redis
version: 3630
replicas: 3
image: redis
port: 8080
On y renseigne la source du code (ici la ressource “myappsource” de type “GitRepository” qui est présente dans le namespace par défaut). Le chart est contenu dans le dossier “chart” de la source et la mise à jour de la helmrelease devra se faire toutes les minutes. Il est à noter que comme vu précédemment, la source pourrait être de type “HelmRepository” ou “Bucket”
Le champ spec.values est l’équivalent du fichier values.yaml dans un chart Helm. Ces valeurs viendront écraser les valeurs renseignées dans le fichier de valeur du chart.
Afin de souligner cet écrasement de valeur, regardons les valeurs dans le chart:
/chart/values.yaml
deployment:
name: ToBeOverride
version: ToBeOverride
replicas: ToBeOverride
image: ToBeOverride
port: ToBeOverride
Elles seront écrasées par les valeurs de la HelmRelease.
Bilan
Dans cet article nous avons vu comment faire du templating avec Helm qui vient nativement lorsque l’on installe Flux v2. Il semblerait que cette nouvelle version permette de faire plus de choses que la première et notamment la génération de notification que nous aborderons dans un prochain article.
Voici un schéma qui résume ce que nous avons réussi à mettre en place:
Source
- kubernetes : https://fr.wikipedia.org/wiki/Kubernetes
- Helm : https://helm.sh/docs/intro/quickstart/
- Installation de FluxCD : https://fluxcd.io/flux/installation/#gitlab-and-gitlab-enterprise
- Les composants de FluxCD : https://fluxcd.io/flux/components/
Les répertoires Gitlab
- Article GitOps - HelmRelease : https://gitlab.com/paulboisson/article-gitops-fluxcd-helmrelease