Qu'est-ce Kustomize ?
Tags: french, devops, kubernetes
Published: 2022-05-03 21:13:26

Quand on débute sur Kubernetes et qu'on utilise des fichiers de configuration en YAML, généralement on crée les fichiers un par un en utilisant la commande suivante
```
kubectl apply -f <file>
```

Puis plus tard, on découvre que l'on peut utiliser la même commande avec un dossier pour créer l'ensemble des éléments définis dedans.
```
kubectl apply -f <folder>
```

Cependant, quand on commence à déployer les éléments dans différents environnements, on se trouve rapidement embêté car on doit changer les configs manuellement avant de déployer dans l'environnement voulu. C'est pour cela que dans ces cas-là on commence à utiliser des outils afin de pouvoir générer/modifier dynamiquement des templates. 

Aujourd'hui nous allons parler de l'un d'entre eux, **Kustomize**.

---

## Qu'est-ce que Kustomize ?
**Kustomize** est un outil de gestion de configuration intégré dans Kubernetes! 

Par conséquent, si vous avez **kubectl** de présent sur votre machine, vous pouvez l'utiliser dès maintenant.

Contrairement à **Helm**, il ne s'agit pas d'un outil de templating, mais bien d'un outil de gestion de configuration.

Pour donner un exemple rapide pour comparer les deux outils, Helm va avoir un template comme suit et l'utilisateur va pouvoir définir chacune des variables comme bon lui semble.
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: the-deployment
spec:
  replicas: 5
  template:
    containers:
      - name: {{ .Values.containerName }}
        image: {{ .Values.containerImage }}
```
Ici, l'utilisateur peut donc facilement redéfinir le nom du container et son image. Cependant, il ne pourra pas modifier le nombre de replicas. 

Helm, une fois appelé, va donc générer le fichier yaml à partir de ce template et des valeurs données par l'utilisateur.

Kustomize, lui, permet à l'utilisateur de surcharger n'importe quelle valeur de la configuration de base. *(On va voir un peu plus tard comment)*

---

Avant d'aller plus loin, voici le contexte dans lequel les exemples seront.
```
- /kubernetes
    - /application
        - deployment.yaml
        - configmap.yaml
```

*deployment.yaml*
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deployment
spec:
  replicas: 1
  template:
    containers:
      - name: my-container
        image: ubuntu:latest
        env:
        - name: TEST
          value: TOTO
        volumeMounts:
        - name: config-volume
          mountPath: /configs/
      volumes:
      - name: config-volume
        configMap:
          name: example-config
```

*configmap.yaml*
```yaml
 apiVersion: v1
 kind: ConfigMap
 metadata:
   name: example-config
   namespace: example
 data:
   config.json: |
     {
       "environment" : "test"
     }
```

---

## Comment fonctionne Kustomize ?
Pour pouvoir fonctionner, **Kustomize** se base sur un fichier **kustomization.yaml** qui doit se trouver dans le dossier cible de la commande.

### Base
Dans notre contexte, ajoutons dans le dossier **/application** le fichier kustomization avec le contenu suivant
```yaml
resources:
  - deployment.yaml
  - configmap.yaml
```

Dans cet exemple, on indique à Kustomize quels sont les fichiers de configurations que l'on souhaite appliquer quand ce fichier kustomization est appelé. Ce dossier devient alors notre **base** pour l'ensemble des futures configurations. 

Par conséquent, si nous utilisons l'une des commandes suivantes, nous allons générer le déploiement et la configmap telles que définies dans les fichiers YAML.

```
# Génère le yaml avec toutes les configs avant de les appliquer
kubectl kustomize .\kubernetes\application\ | kubectl apply -f -

# Ou

# Applique directement les configs générées
kubectl apply -k .\kubernetes\application\
```

### Customisation
Maintenant que l'on a notre base, on va pouvoir créer nos configurations customisées pour chacun des environnements que l'on a.

Dans le dossier **/kubernetes**, nous allons maintenant créer un dossier **/environments** dans lequel nous allons créer deux nouveaux dossiers **/dev** et **/prod** *(Correspondant à nos environnements)*, puis créer un fichier **kustomization.yaml** dans /dev et /prod.

Dans chacun de ces fichiers, nous allons ajouter le code suivant 
```yaml
bases:
  - ../../application
```
Cette configuration va permettre de définir quelle est la configuration de base que l'on va utiliser pour nos customisations. *(Ici les configurations du dossier application définies plus tôt)*

Donc, si on utilise la même commande que précédemment mais en ciblant le dossier de l'environnement souhaité, on sera capable de créer les éléments définis dans la base.
```
kubectl kustomize .\kubernetes\environments\dev | kubectl apply -f -

# Ou

kubectl apply -k .\kubernetes\environments\dev
```

---

## Modifications possibles
En fouillant dans la documentation de **Kustomize**, vous pourrez voir l'ensemble des éléments possibles, mais ici on va regarder les éléments principaux.


### Patch
Un patch dans Kustomize est un fichier qui va contenir une configuration partielle d'un composant ayant pour but de surcharger la configuration de base.

Par exemple, en production nous souhaitons augmenter le nombre de pods dans le déploiement et définir les ressources qu'un pod peut utiliser. On va alors créer les deux fichiers suivants dans le dossier **/prod**.

*replica_count.yaml* *(Note: Le nom n'a pas d'importance, il pourrait s'appeler toto.yaml que ça fonctionnerait aussi très bien)*
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deployment # Avec le nom, on vient préciser sur quel déploiement on souhaite agir
spec:
  replicas: 6 # Définition du nombre de pods pour la production.
``` 

*resources.yaml*
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deployment
spec:
  template:
    containers:
      - name: my-container
        resources:
          requests:
            memory: "50Mi"
            cpu: "50m"
          limits:
            memory: "500Mi"
            cpu: "500m"
```

Ensuite, pour qu'ils soient bien définis comme étant des patches, il faut ajouter le bloc suivant dans le fichier **kustomization.yaml** du dossier **/prod**.

```yaml
patches:
  - replica_count.yaml
  - resources.yaml
```
On y retrouve donc les deux fichiers contenant des configurations que l'on veut remplacer.

---

### Patch stratégique de fusion
Dans certains cas, on ne souhaite pas remplacer le contenu d'une liste, on souhaite ajouter une valeur dans la liste. Dans ce cas, il faut utiliser un **patchesStrategicMerge**.

Par exemple, si je veux ajouter une variable d'environnement pour mon container, il faudrait :
- que je crée le fichier suivant dans **/prod**

*env.yaml*
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deployment
spec:
  template:
    containers:
      - name: my-container
        env:
        - name: ENVIRONMENT
          value: Production
```

- puis que j'ajoute le bloc suivant dans **kustomization.yaml**
```yaml
patchesStrategicMerge:
  - env.yaml
```

Dans notre exemple, le container en production va donc avoir deux variables d'environnement : 
- TEST : TODO
- ENVIRONMENT : Production

---

### Générateurs
Dans Kustomize, il existe des paramètres qui permettent de générer dynamiquement des configmaps et des secrets. Les deux sont très similaires et on va se focaliser sur la génération de configmap pour notre exemple.

```yaml
configMapGenerator:
- name: example-config
  namespace: example 
  #behavior: replace
  files:
    - configs/config.json
```

Dans l'exemple précédent du bloc se trouvant dans le fichier kustomization.yaml, une configmap "**example-config**" sera créée dans le namespace **example** à partir du fichier **configs/config.json**. 

En commenté, on voit le paramètre **behavior** que l'on peut définir pour remplacer le contenu d'une configmap créée dans la configuration de base.

---

### Images
Le dernier bloc que l'on va voir aujourd'hui est celui pour mettre à jour des paramètres associés aux images utilisées.

*Exemple de définition dans kustomization.yaml*
```yaml
images:
- name: hello-world 
  newTag: linux
  newName: ubuntu 
```

Dans cet exemple, on trouve 3 champs :
- **name** qui permet d'aller récupérer toutes les images définies dans la base ayant cette image.
- **newTag** qui permet de redéfinir la version de l'image à utiliser *(S'applique sur toutes les images ayant été récupérées par le champ name)*
- **newName** qui permet de redéfinir le nom de l'image *(S'applique sur toutes les images ayant été récupérées par le champ name)*

---

Si vous êtes curieux et souhaitez aller plus loin dans l'utilisation de Kustomize, sachez qu'il y a encore pas mal d'éléments dont on n'a pas parlé dont **[labels](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/labels/)**, **[vars](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/vars/)**...

N'hésitez pas à aller faire un tour dans la [documentation](https://kustomize.io), regarder d'autres articles en parlant ou voir des tutoriels sur Youtube.

J'espère que ça vous aidera! 🍺

*Tout commentaire et feedback est le bienvenue que ça soit ici, sur [Twitter](https://twitter.com/glt_mx) ou sur [LinkedIn](https://www.linkedin.com/in/maxime-guilbert-14b6a5128/).*