Mise en place d’un GitOps multi-environnement auto-hébergé avec Argo CD, Kustomize et cert-manager sur MicroK8s : architecture, défis, erreurs, et automatisation des déploiements Kubernetes.
🌍 Contexte : Pourquoi ce projet ?
Ce projet est le prolongement logique d'une démarche amorcée plusieurs mois auparavant. Initialement, mon infrastructure était gérée de façon artisanale : quelques fichiers Kubernetes posés dans un repository, un Ingress pour gérer le trafic entrant, un certificat Let’s Encrypt, un déploiement de mon app exposée en interne via un service ClusterIP, ainsi qu'un NodePort pour l'exposition externe.
Mais rapidement, cette approche a montré ses limites :
Pas de gestion multi-environnement
Pas de vision d'ensemble de l'état du cluster
Mises à jour via pipelines mais limitées (uniquement création ou mise à jour de ressources, le reste devant se faire manuellement), sans historique clair
Je voulais un setup plus robuste, plus proche de ce qui se fait en entreprise : versionnement complet de l'infrastructure, déploiement multi-env, GitOps, monitoring plus complet (traces, logs) et sécurité.
🚀 Objectifs techniques
Ce nouveau projet vise à construire une base solide et maintenable pour déployer n'importe quelle application dans un cluster Kubernetes auto-hébergé. Les piliers :
GitOps avec Argo CD pour des déploiements déclaratifs
Multi-environnement (dev, staging, prod...) via kustomize
Certificats TLS gérés automatiquement par cert-manager
Ingress NGINX pour l'exposition publique en HTTPS
Versionnement complet de l'infra : Ingress, Cert-Manager, Argo CD sont installés via Helm, mais pilotés via Kustomize
Namespace par environnement
📊 Stack technique
MicroK8s : distribution légère de Kubernetes, mono-nœud
Helm : gestionnaire de chart pour l'installation d'outils comme Argo CD, cert-manager, ingress-nginx
Kustomize : overlay d'environnements (dev, prod)
Argo CD : moteur GitOps pour le déploiement continu
cert-manager + ClusterIssuer Let's Encrypt : certificats TLS publics
📂 Organisation du repo
Le repository est structuré comme suit :
.
├── environments/
│ ├── dev/ # Déploiement dédié au développement
│ ├── staging/ # Environnement de préproduction
│ └── prod/ # Production simulée
├── base/ # Composants K8s communs à tous les environnements
Chaque outil est installé via Helm avec un values.yaml versionné, et les environnements dev/, staging/ et prod/ sont des overlays Kustomize avec des patchs ciblés.
Le repo est disponible ici : github.com/Wooulf/devops-bootcamp-ippon.
📦 Gestion multi-environnement avec Kustomize
Kustomize
est l’outil que j’utilise pour gérer proprement mes différents environnements (dev
, staging
, prod
) à partir d’une base commune de ressources Kubernetes. Contrairement à Helm, il ne nécessite pas de moteur de templating externe. On parle ici de composition de fichiers déclaratifs plutôt que de génération dynamique.
Chaque environnement hérite des ressources communes de base/
, et vient appliquer des patches spécifiques (ex : nom de domaine, image Docker, namespace...).
Exemple :
.
├── base/
│ └── portfolio/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ └── ingress.yaml
├── environments/
│ ├── dev/
│ │ ├── kustomization.yaml
│ │ └── patch-ingress-host.json
│ ├── staging/
│ └── prod/
Chaque répertoire (qu’il s’agisse de base
ou d’un environnement comme dev
, staging
ou prod
) contient un fichier kustomization.yaml
obligatoire. Ce fichier décrit les ressources Kubernetes à assembler ainsi que les éventuelles modifications spécifiques à l’environnement (comme un patch JSON pour changer le host de l’Ingress).
Cette approche permet une composition claire et modulaire des ressources, tout en gardant un contrôle précis sur les différences entre environnements.
Exemple :
environments/dev/kustomization.yaml
:
namespace: dev
resources:
- ../../base/portfolio
patches:
- path: patch-ingress-host.json
target:
kind: Ingress
name: portfolio-ingress
patch-ingress-host.json
:
[
{ "op": "replace", "path": "/spec/tls/0/hosts/0", "value": "dev.woulf.fr" },
{ "op": "replace", "path": "/spec/rules/0/host", "value": "dev.woulf.fr" }
]
👉 Avec cette structure, je peux déployer le même projet dans plusieurs environnements en changeant uniquement le contexte et quelques variables cibles.
🔍 Petit tip : pour valider la sortie Kustomize en local avant de confier le déploiement à Argo CD, on peut utiliser :
kubectl kustomize environments/dev
Cela génère le YAML final tel qu’il sera appliqué dans le cluster.Et on peut l'appliquer comme ça :
kubectl apply -k environments/dev
🚀 Pourquoi j’utilise Argo CD ?
Argo CD est le cœur de ma stratégie GitOps : c’est lui qui veille à ce que l’état réel du cluster Kubernetes soit toujours synchronisé avec les manifests Git.
Ce que j’apprécie dans Argo CD :
- Interface visuelle claire de l’état des déploiements
- Déploiement automatique (pull-based) dès qu’un commit est détecté
- Historique des modifications, gestion des rollback
- Vérification automatique de la dérive de configuration
- Facilité de ciblage d’environnements/namespaces spécifiques
⚙️ Installation d'Argo CD avec HTTPS
Argo CD a été installé via Helm dans le namespace argocd
, avec cette configuration :
global:
domain: argocd.woulf.fr
configs:
params:
server.insecure: "true"
server:
certificate:
enabled: true
secretName: argocd-tls
domain: argocd.woulf.fr
issuer:
group: cert-manager.io
kind: ClusterIssuer
name: letsencrypt-prod
ingress:
enabled: true
ingressClassName: nginx
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
hosts:
- argocd.woulf.fr
tls:
- hosts:
- argocd.woulf.fr
secretName: argocd-tls
Pourquoi server.insecure: true
? Car la terminaison TLS est gérée au niveau de l'Ingress. Le trafic interne reste en HTTP, ce qui est acceptable dans un cluster local/VPS non mutualisé.
🌐 Accès public via Ingress
Le certificat TLS est généré automatiquement par cert-manager à partir du ClusterIssuer letsencrypt-prod
. Argo CD est maintenant accessible publiquement via https://argocd.woulf.fr.
📌 Définir un déploiement GitOps avec Argo CD
Pour connecter Argo CD à mon dépôt Git et lui indiquer quoi synchroniser dans le cluster, j'ai créé une ressource Kubernetes de type Application
:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: portfolio-dev
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/Wooulf/devops-bootcamp-ippon
targetRevision: HEAD
path: environments/dev
destination:
server: https://kubernetes.default.svc
namespace: dev
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
👉 Le fichier Application Argo CD est également versionné dans mon dépôt Git, visible ici : argocd/applications/portfolio-dev.yaml.
🔍 Comment ça fonctionne
source.repoURL + path + targetRevision
Argo CD surveille le répertoireenvironments/dev
du dépôt Githttps://github.com/Wooulf/devops-bootcamp-ippon
. Toute modification (commit, push) dans ce dossier déclenche une synchronisation automatique.destination
L’application est déployée dans le cluster local (MicroK8s) via l’URLhttps://kubernetes.default.svc
, dans le namespacedev
.-
syncPolicy.automated
-
automated
: synchronisation automatique sans action manuelle. -
prune
: suppression des ressources obsolètes qui ne sont plus déclarées dans Git. -
selfHeal
: restauration automatique si une ressource est modifiée manuellement dans le cluster.
-
syncOptions.CreateNamespace=true
Le namespacedev
est créé automatiquement s’il n’existe pas encore, rendant le déploiement autonome et idempotent.
🔁 Résultat final
- Déploiement GitOps complet et automatique dans l’environnement
dev
- Conformité assurée en permanence entre Git et le cluster
- Mise à jour ou suppression de ressources pilotée uniquement depuis le dépôt Git
- Namespace
dev
créé dynamiquement sans préconfiguration manuelle
Le portfolio dev
est maintenant déployé automatiquement avec la dernière image Docker taggée latest
, et accessible via https://dev.woulf.fr.
🎯 Interface Argo CD : état de santé et synchronisation des environnements dev, staging et prod.
🔍 Vue détaillée de l'application portfolio-dev déployée dans le namespace dev — chaque ressource est traquée, versionnée, et synchronisée à Git.
🧨 Problèmes rencontrés (et résolus)
-
Certificats auto-signés persistants
J’ai eu des certificats non valides persistants (auto-signés) à cause de deux mécanismes de création simultanés : une annotation
cert-manager.io/cluster-issuer
sur l’Ingress et une configurationserver.certificate
dans lesvalues.yaml
de Helm. 👉 Solution : ne conserver qu’une seule source de vérité. Ici, la configuration Helm viaserver.certificate
.
- Certificat temporaire actif après provisionnement Let's Encrypt Cert-manager installe parfois un certificat temporaire avant que Let's Encrypt émette le définitif. 👉 Il suffit d’attendre la mise à jour, c’est un comportement normal.
- MicroK8s qui "perd" des add-ons (Helm, DNS, etc.) après redémarrage J’ai constaté que certains add-ons de MicroK8s se désactivaient au reboot. 👉 Nécessité de relancer manuellement certains services.
- ClusterIssuer manquant après reboot du cluster Après redémarrage, mon ClusterIssuer n’était plus reconnu. Cela venait d’un oubli de le versionner dans mon GitOps au début. 👉 Résolu en l’ajoutant explicitement dans les manifests surveillés par Argo CD.
- Erreurs d’accès HTTPS malgré une config a priori correcte Liées dans mon cas à un certificat temporaire encore actif, ou à une mauvaise terminaison TLS côté Ingress. 👉 En fixant la gestion TLS uniquement au niveau Ingress, le problème a disparu.
🧾 En résumé
Dans cet article, j’ai amorcé la transition vers une infrastructure GitOps multi-environnement, avec :
L’installation d’Argo CD via Helm dans un namespace dédié
La gestion du HTTPS avec cert-manager et un ClusterIssuer Let’s Encrypt
La résolution de problèmes classiques liés aux certificats (self-signed, temporaires, double création de certificat)
L’exposition d’Argo CD en HTTPS via Ingress NGINX
Le premier déploiement GitOps d’une application (
portfolio
) dans l’environnementdev
, patché dynamiquement viakustomize
Ce socle technique me permet désormais de tester, versionner et sécuriser mes déploiements comme en production, tout en gardant un maximum de contrôle sur l'infrastructure.
🔜 À venir dans le prochain article
On poursuivra en intégrant Argo CD Image Updater pour assurer la mise à jour automatique des images déployées, en ajoutant une couche de sécurité avec SealedSecrets pour protéger le token d'API permettant d'interagir avec Argo CD. On verra aussi comment rendre ce système autonome, sans intervention manuelle, toujours dans une logique GitOps.