kustomize est un outil conçu pour permettre aux utilisateurs de :

Personnaliser des fichiers YAML bruts et sans modèle à des fins multiples, en laissant le YAML d'origine intact et utilisable tel quel

Je n'invente rien, ce texte est tiré de la présentation de l'outil sur sa page GitHub 😂

Inclus directement dans Kubernetes depuis sa version 1.14, Kustomize va donc nous permettre de customiser la configuration de nos déploiements en gardant une base commune. Par exemple nous allons pouvoir utiliser le même fichier de déploiement YAML pour un environnement de développement et de production, en n'utilisatant Kustomize pour définir les éléments propres à chaque environnement ! Top ?!

Tout ceci semble prometteur mais dans le concret ça donne quoi ? Un exemple ?

Pourquoi ?

Comment passer d'un environnement de développement à la production sans modifier de façon systématique tous mes fichiers YAML de configuration ? Appliquez une configuration en fonction des environnements, tout en gardant une base commune m'aiderait tout simplement à maintenir plus facilement mes solutions !

Dans son utilisation la plus simple, kustomize n'est tout simplement qu'un ensemble de ressources ( les fichiers YAML qui définissent vos objets Kubernetes comme les déploiements, les services, etc.) et un ensemble d'instructions sur les modifications à apporter à ces ressources ( des patches ).

Nous pourrions comparer cette pratique à la façon dont make utilise un fichier Makefile pour effectuer une compilation. Kustomize utilise lui un fichier kustomization.yaml afin de stocker les instructions qui vont modifier vos ressources.

Mais d'abord regardons un fichier kustomization.yaml :

namePrefix: dev-
namespace: development
commonLabels:
  environment: development
resources:
- deployment.yaml
- service.yaml

Voici une explication rapide de cet exemple :      

  • Le champ namePrefix va permettre de préfixer tous les noms de ressources définies avec la valeur que vous aurez pu définir. Dans notre cas, je vais préfixer tous mes noms avec "dev-". Par exemple un déploiement nommé traefik-deployment deviendra dev-traefik-deployement.
  • L'instruction namespace est très intéressante, grâce à elle, Kustomize va ainsi créer nos éléments dans le namespace spécifié. Dans mon exemple : "development".
  • Nous pouvons également spécifier un label pour toutes les ressources générées. Je vais ici mettre un label "environment" avec la valeur "development" pour chaque ressource.
  • Enfin, je vais pouvoir appliquer des patches pour des ressources spécifiques que je vais préciser dans le champ : resources . Dans notre cas, les fichiers deployment.yaml et service.yaml.

Enfin Kustomize nous offre principalement la possibilité de sur-charger une ressources avec de nouvelles valeurs. Et de ce fait, la patcher, avec des éléments précis d'un cas d'utilisation.

L'utilisation de variantes avec une base commune est appelée overlays. Voyons ensemble l'utilisation d'un cas concret d'overlays !

Overlays

Vous avez dû le remarquer, j'utilise beaucoup Traefik comme reverse-proxy !

Cet outil m'apporte une solution rapide et efficace pour gérer les nombreux conteneurs - web - que je mets en production et dont je maintiens également un environnement de développement, voir de staging.


Mais comment passer d'un environnement de développement à la production sans modifier de façon systématique mes fichiers YAML de configuration ? Appliquez une configuration en fonction des environnements, tout en gardant une base commune m'aiderait tout simplement à maintenir plus facilement mes solutions !


Voici donc ce que j'envisage pour notre exemple :

  1. Une base Traefik, qui sera semblable à un environnement de développement :
  • Uniquement du HTTP,
  • API et Dashboard accessible sans sécurité.

2. Un environnement de production :

  • Une redirection HTTP vers HTTPS,
  • Dashboard et API désactivés.

Je pourrais bien évidement mettre bien plus d'options dans mon environnement de production, comme un middleware par défaut pour ajouter des en-têtes de sécurité par exemple, mais il s'agit là d'un exemple et je souhaite le conserver simple afin de garder ma configuration lisible et compréhensible pour tous !

Commençons immédiatement avec notre base !

Base

Tout d'abord voici l'arborescence des dossiers de mon projet :

.
├── base
├── development
└── production

Je ne présente pas encore chaque fichiers.

Votre base doit être commune à votre environnement de développement mais aussi de production. Il faut donc en extraire les éléments communs.

Déclarons donc déjà les éléments essentiels afin de lancer Traefik dans mon environnement Kubernetes.

🚩 Pour mes essais, j'utilise K3S que j'installe tout simplement avec la commande :

curl -sfL https://get.k3s.io | sh -s - --no-deploy=traefik

Je ne déploie pas Traefik car nous allons le faire avec Kustomize 🚩

Je vais reprendre une bonne partie de la configuration présente dans cet article :

Traefik 2.2 + K3S
Après avoir essayé quelques nouveautés de cette version 2.2 sur Docker, il était temps de mettre en place cette nouvelle version sur un environnement Kubernetes. J’ai donc sans hésité, sauter le pas avec K3S !

Voici les fichiers que je vais utiliser pour déployer Traefik :

  • Un fichier traefik-rbac.yaml pour les droits clusters/autorisations.
  • traefik-service.yaml pour déclarer mon service.
  • Un fichier traefik-configmap.yaml pour définir ma configuration statique.
  • Enfin mon fichier de déploiement : traefik-deployment.yaml.

Je ne vais pas détailler le fichier RBAC, vous le trouverez ici.

Mon fichier de service que je vais utiliser pour la base :

apiVersion: v1
kind: Service
metadata:
  name: traefik

spec:
  ports:
    - protocol: TCP
      name: web
      port: 80
  type: LoadBalancer
  selector:
    app: traefik

Je ne déclare que les éléments communs à mon environnement de production et de développement. Je retire donc le HTTPS et le dashboard.

Mon fichier traefik-configmap.yaml :

apiVersion: v1
kind: ConfigMap
metadata:
  name: traefik-config-static
  namespace: default
  labels:
    app: traefik
data:
  traefik.yml: |
    entryPoints:
      web:
        address: :80
      
    providers:
      kubernetesCRD: {} 

Enfin mon fichier de traefik-deployment :

apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: default
  name: traefik-ingress-controller
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: default
  name: traefik
  labels:
    app: traefik

spec:
  replicas: 1
  selector:
    matchLabels:
      app: traefik
  template:
    metadata:
      labels:
        app: traefik
    spec:
      serviceAccountName: traefik-ingress-controller
      containers:
        - name: traefik
          image: traefik:v2.2
          imagePullPolicy: Always
          volumeMounts:
            - name: traefik-config-static
              mountPath: /etc/traefik/
          ports:
            - name: web
              containerPort: 80
      volumes:
        - name: traefik-config-static
          configMap:
            name: traefik-config-static

Je vais pouvoir utiliser Kustomize en déclarant dans mon fichier kustomization.yaml mes ressources :

resources:
- traefik-rbac.yaml
- traefik-configmap.yaml
- traefik-service.yaml
- traefik-deployment.yaml

Ma structure de projet ressemble maintenant à :

.
├── base
│   ├── kustomization.yaml
│   ├── traefik-configmap.yaml
│   ├── traefik-deployment.yaml
│   ├── traefik-rbac.yaml
│   └── traefik-service.yaml
├── development
└── production

Vous pouvez vérifier que tout fonctionne en appliquant vos fichiers :

$ kubectl apply -k ./
customresourcedefinition.apiextensions.k8s.io/ingressroutes.traefik.containo.us created
customresourcedefinition.apiextensions.k8s.io/ingressroutetcps.traefik.containo.us created
customresourcedefinition.apiextensions.k8s.io/ingressrouteudps.traefik.containo.us created
customresourcedefinition.apiextensions.k8s.io/middlewares.traefik.containo.us created
customresourcedefinition.apiextensions.k8s.io/tlsoptions.traefik.containo.us created
customresourcedefinition.apiextensions.k8s.io/tlsstores.traefik.containo.us created
customresourcedefinition.apiextensions.k8s.io/traefikservices.traefik.containo.us created
serviceaccount/traefik-ingress-controller created
clusterrole.rbac.authorization.k8s.io/traefik-ingress-controller created
clusterrolebinding.rbac.authorization.k8s.io/traefik-ingress-controller created
configmap/traefik-config-static created
service/traefik created
deployment.apps/traefik created

Vous pouvez supprimer les objets créés avec la commande :

$ kubectl delete -k ./

Nous allons pouvoir appliquer les modifications pour notre environnement de développement. Que nous manque t-il ?

  • Le dashboard !

Développement

Pour déclarer l'utilisation du dashboard je dois :

  • Le déclarer dans ma configuration statique : api.insecure=true
  • Déclarer le port dans mon service et dans mon container.

Je vais donc dans le dossier development modifier mon fichier de configuration :

apiVersion: v1
kind: ConfigMap
metadata:
  name: traefik-config-static
  namespace: default
  labels:
    app: traefik
data:
  traefik.yml: |
    entryPoints:
      web:
        address: :80

    api:
      insecure: true
      
    providers:
      kubernetesCRD: {}

🚩 Oui je réécris mon fichier en grande partie, pour ne pas le faire, il faudrait tirer partie d'autres directives que je ne souhaite pas aborder ici 🚩

Enfin dans mon fichier de service :

apiVersion: v1
kind: Service
metadata:
  name: traefik

spec:
  ports:
    - protocol: TCP
      name: admin
      port: 8080
  type: LoadBalancer
  selector:
    app: traefik

Et le fichier de deployment :

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: default
  name: traefik
  labels:
    app: traefik

spec:
  replicas: 1
  selector:
    matchLabels:
      app: traefik
  template:
    metadata:
      labels:
        app: traefik
    spec:
      containers:
        - name: traefik
          ports:
            - name: admin
              containerPort: 8080

Par contre pour ces deux fichiers, je ne modifie, ou ajoute, que les éléments nécessaires !

Je vais pouvoir créer mon fichier kustomization.yaml :

bases:
- ../base
patches:
- traefik-configmap.yaml
- traefik-deployment.yaml
- traefik-service.yaml

Vous pouvez maintenant lancer votre reverse-proxy de développement :

$ kubectl apply -k ./

Vérifier votre installation en accédant au dashboard :

dashboard kustomize

Et mon environnement de production dans tout ça ?!

Rien de plus simple !

Production

On repart donc de notre base et on modifie la configuration pour effectuer une redirection HTTP vers HTTPS :

apiVersion: v1
kind: ConfigMap
metadata:
  name: traefik-config-static
  namespace: default
  labels:
    app: traefik
data:
  traefik.yml: |
    entryPoints:
      web:
        address: :80
        http:
          redirections:
            entryPoint:
              to: websecure
              scheme: https
              
      websecure:
        address: :443
        http:
          tls:
            certResolver: myresolver
            
    accessLog: {}
      
    providers:
      kubernetesCRD: {} 
      
    certificatesResolvers:
      myresolver:
        acme:
          email: "foo.bar@mydomain.com"
          storage: "/letsencrypt/acme.json"
          tlsChallenge: {} 

J'en profite pour ajouter les accessLog et bien sûr retirer l'accès au dashboard.

Mon fichier pour la déclaration du service :

apiVersion: v1
kind: Service
metadata:
  name: traefik

spec:
  ports:
    - protocol: TCP
      name: websecure
      port: 443
  type: LoadBalancer
  selector:
    app: traefik

Et enfin le déploiement :

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: default
  name: traefik
  labels:
    app: traefik

spec:
  replicas: 1
  selector:
    matchLabels:
      app: traefik
  template:
    metadata:
      labels:
        app: traefik
    spec:
      containers:
        - name: traefik
          ports:
            - name: websecure
              containerPort: 443

🚩 Oui oui il manque quelques petites choses, mais j'ai supprimé quelques elements afin de gagner en lisibilité de l'article. J'ajouterai ces éléments par la suite sur GitHub : Exemple, aucun volume pour les certificats LE 🚩

Le fichier kustomization ressemble très fortement à celui de développement :

bases:
- ../base
patches:
- traefik-configmap.yaml
- traefik-deployment.yaml
- traefik-service.yaml

Voici enfin à quoi ressemble ma structure de projet une fois terminée :

.
├── base
│   ├── kustomization.yaml
│   ├── traefik-configmap.yaml
│   ├── traefik-deployment.yaml
│   ├── traefik-rbac.yaml
│   └── traefik-service.yaml
├── development
│   ├── kustomization.yaml
│   ├── traefik-configmap.yaml
│   ├── traefik-deployment.yaml
│   └── traefik-service.yaml
└── production
│   ├── kustomization.yaml
│   ├── traefik-configmap.yaml
│   ├── traefik-deployment.yaml
└── └── traefik-service.yaml

Il ne reste plus qu'à essayer tout ça :

🚩 Je n'ai pas utilisé de Prefix dans cet exemple, ne lancez pas la production et le développement en même temps, sinon vous allez avoir quelques erreurs ! 🚩

$ kubectl apply -k ./

En vous rendant sur http://monip/ vous devriez être automatiquement redirigé vers le https.

Vous pouvez bien sûr déployer une image comme containous/whoami pour valider le bon fonctionnement !


Nous avons donc pu voir ensemble comment utiliser `Kustomize` et le mettre en pratique avec la création de deux environnements pour Traefik.

Il est bien évidement possible d'ajouter plus d'éléments à la production. Comme par exemple l'ouverture du Dashboard avec des restrictions IP ou accès par mot de passe, ajouter des middlewares, utiliser un KV store uniquement en production, etc. Les possibilités sont multiples et vont dépendre de votre couche applicative !

L'intérêt de Kustomize ne s'arrête bien sûr pas ici ! Il est par exemple possible de l'utiliser lors d'un changement configuration ( une surcharge de paramètre par exemple ). Vous pourrez ainsi réaliser cette modification sans altérer votre configuration de base.

Et cela sera encore plus intéressant si vous travaillez en équipe sur votre infrastructure. Chacun pourra patcher son environnement et proposer sur un dépôt les modifications par exemple mais en gardant votre base commune !

Et vous ! Est-ce que vous envisagez d'utiliser Kustomize prochainement ?!

En tout cas  n'hésitez pas à m'apporter des remarques ou des commentaires sur Twitter ! C'est toujours un plaisir d'avoir des retours !