Aller au contenu
Edouard Topin's Blog
VKS sur VCF 9 / Série 05/05

GitOps sur VKS avec Argo CD : du bootstrap au multi-tenant

Transformer un cluster VKS en plateforme self-service. Bootstrap Argo CD, ApplicationSets multi-cluster, secrets externes et patterns multi-tenant — le guide pratique.

Edouard Topin
7 min de lecture
Illustration éditoriale abstraite d'un arc en forme d'Argo enjambant plusieurs compartiments de tenants disposés en roue.

Un cluster VKS provisionné et opéré ne devient une plateforme que lorsque les équipes applicatives peuvent y déployer sans dépendre du platform team pour chaque release. GitOps est le pattern qui rend ça possible — Argo CD est l’implémentation qui s’est imposée.

Cet article retrace la mise en place pas à pas : bootstrap, App-of-Apps, ApplicationSets pour le multi-cluster, secrets via External Secrets Operator, et patterns multi-tenant. L’objectif est de poser une fondation qui ne se renie pas dans 6 mois quand le périmètre triple.

Audience supposée : tu opères VKS, tu connais kubectl, tu as déjà entendu parler d’Argo CD ou Flux mais tu veux un parcours concret pour démarrer proprement.

Pourquoi GitOps sur VKS

Trois raisons concrètes, au-delà de la mode.

Auditabilité. Toute modification du cluster passe par un commit Git. Le git log est la source de vérité opérationnelle. Quand un incident remonte “qui a déployé quoi à quelle heure”, la réponse est dans le repo, pas dans la mémoire des équipes.

Cohérence multi-cluster. Avec deux clusters (dev + prod), la dérive est inévitable en mode push manuel. Avec dix clusters (dev + staging + prod × régions), elle est garantie. ApplicationSets éliminent la dérive en synchronisant un manifest unique sur N clusters.

Self-service applicatif. Les équipes applicatives gèrent leur propre repo et leur propre namespace. Elles déclenchent des déploiements sans intervention du platform team. Le platform team conserve la maîtrise du framework — RBAC, politiques, sécurité — mais sort du chemin critique des releases.

Argo CD vs Flux : le choix assumé

Les deux fonctionnent. Choisir l’un ou l’autre engage sur des années — les patterns d’organisation du repo, les outils tiers, les compétences de l’équipe se construisent autour.

Forces — UI riche, App-of-Apps natif, ApplicationSets puissants, Sync Waves intégrés, écosystème large (Notifications, Image Updater, Rollouts).

Limites — composants nombreux, courbe d’apprentissage côté CRDs. La UI peut donner une fausse impression de simplicité — penser GitOps d’abord.

Pour qui — équipes qui veulent une UX lisible pour les développeurs, ou qui ont besoin de patterns multi-cluster avancés.

Forces — design plus modulaire (Source/Kustomize/Helm controllers séparés), pas d’UI par défaut (philosophie “Git c’est l’UI”), intégration native CNCF.

Limites — pas d’UI native (à compenser par d’autres outils), patterns multi-cluster moins explicites qu’avec ApplicationSets.

Pour qui — équipes très orientées CLI/code, contextes où l’absence d’UI est une feature plutôt qu’un manque.

Recommandation assumée. Argo CD pour la plupart des plateformes VKS. La UI réduit drastiquement le ticket-volume vers le platform team (les équipes applicatives debug elles-mêmes leurs sync errors), et ApplicationSets sont le pattern multi-cluster de référence. Flux reste un excellent choix pour des contextes où la philosophie “no UI” est prioritaire.

Bootstrap : la première fois

Démarrer Argo CD sur un cluster VKS prend une vingtaine de minutes. Mais la structure choisie ce jour-là conditionne les six mois suivants.

Étape 1 — Installation. Via Helm, dans un namespace dédié argocd avec les paramètres de base.

# Ajout du repo
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update

# Install Argo CD avec High Availability activée
helm install argocd argo/argo-cd \
  --namespace argocd \
  --create-namespace \
  --version 7.7.0 \
  --set controller.replicas=2 \
  --set repoServer.replicas=2 \
  --set redis-ha.enabled=true \
  --set server.ingress.enabled=true \
  --set server.ingress.hosts[0]=argocd.platform.example.com

Étape 2 — Premier accès. Récupérer le password admin initial puis le rotater immédiatement vers un secret généré ou une intégration SSO.

kubectl -n argocd get secret argocd-initial-admin-secret \
  -o jsonpath="{.data.password}" | base64 -d

Étape 3 — Connecter le repo Git. Argo CD a besoin d’accéder au repo de configuration. Via SSH key (à privilégier) ou personal access token. Stocker la credential dans un Secret typé argocd-repo.

Étape 4 — App-of-Apps. Créer une première Application qui pointe vers un dossier du repo contenant… d’autres Applications. C’est le pattern fondateur.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root
  namespace: argocd
spec:
  project: default
  source:
    repoURL: git@github.com:org/platform-config.git
    targetRevision: main
    path: bootstrap
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

À partir de cette Application racine, tout le reste se déploie en cascade. C’est ce qui rend l’écosystème scalable.

Structure de repo : la décision qui dure

Une mauvaise structure de repo se paie pendant des mois. Trois patterns dominent.

Modèle — un seul repo qui contient tout : platform configs + applications + environnements.

Forces — atomicité des changements transverses, vue unique. Bonne pour les petites équipes.

Limites — RBAC Git complexe (qui peut éditer quoi), CI lent, conflits de merge fréquents quand les équipes grandissent. À abandonner au-delà de 5 équipes applicatives.

Modèle — un repo par équipe ou par application, plus un repo platform.

Forces — RBAC naturel (chaque équipe possède son repo), CI rapide.

Limites — coordination des changements transverses (changer une politique sur N repos = N PRs), risque de drift entre les configs.

Modèle — un repo platform-config (politiques, ApplicationSets, namespaces, RBAC) + un repo par équipe applicative pour leurs manifests.

Forces — sépare les responsabilités platform/app, RBAC clair, changements transverses centralisés. Le sweet spot pour la plupart des plateformes.

Limites — il faut maintenir la cohérence entre les deux niveaux via convention (templates, OPA/Kyverno), un peu plus de plomberie initiale.

Recommandation assumée. Pattern hybride. Le platform team possède platform-config (le source de vérité du cluster). Chaque équipe applicative possède son repo applicatif. ApplicationSets dans platform-config instancient automatiquement une Application Argo CD pour chaque repo applicatif découvert. C’est le modèle qui scale jusqu’à plusieurs dizaines d’équipes sans renégocier la structure.

ApplicationSets : multi-cluster sans douleur

Le composant qui change la perspective. Plutôt qu’écrire 30 Applications à la main pour 30 clusters, on écrit une ApplicationSet qui les génère.

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: cert-manager-fleet
  namespace: argocd
spec:
  generators:
    - clusters:
        selector:
          matchLabels:
            env: prod         # tous les clusters labellisés "env=prod"
  template:
    metadata:
      name: 'cert-manager-{{name}}'
    spec:
      project: platform
      source:
        repoURL: git@github.com:org/platform-config.git
        targetRevision: main
        path: platform/cert-manager
      destination:
        server: '{{server}}'  # injecté par le generator
        namespace: cert-manager
      syncPolicy:
        automated:
          prune: true
          selfHeal: true

Le pattern. Cluster generator + template = N Applications créées automatiquement. Ajouter un nouveau cluster ne demande qu’une chose : le déclarer comme cluster Argo CD avec les bons labels. L’ApplicationSet le détecte et déploie le bundle de référence dessus.

Combinaisons utiles. Cluster generator + Git generator (un dossier par environnement) = matrice complète. SCM Provider generator pour découvrir automatiquement tous les repos d’une org GitHub avec un label spécifique. Le pattern matrix permet d’éviter le copier-coller massif.

Secrets : External Secrets Operator

Un pattern critique. Argo CD lit Git, et Git ne doit jamais contenir de secrets en clair. ESO résout le problème en récupérant les secrets depuis un store externe au moment du déploiement.

# Le SecretStore pointe vers Vault (ou autre)
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: vault-backend
  namespace: app-namespace
spec:
  provider:
    vault:
      server: "https://vault.example.com"
      path: "kv-v2"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes-prod-cluster"
          role: "app-namespace"

---
# L'ExternalSecret matérialise un Secret Kubernetes depuis Vault
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
  namespace: app-namespace
spec:
  refreshInterval: 5m
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: db-credentials       # le Secret généré
  data:
    - secretKey: username
      remoteRef:
        key: prod/db
        property: username
    - secretKey: password
      remoteRef:
        key: prod/db
        property: password

Le manifest dans Git ne contient que la référence. Les valeurs réelles vivent dans Vault (ou AWS Secrets Manager, GCP Secret Manager, vSphere Secret Store si ESO s’y connecte). Argo CD synchronise la référence, ESO matérialise le Secret. La rotation côté Vault est répercutée automatiquement après le refreshInterval.

Alternatives. Sealed Secrets de Bitnami (chiffrement avec clé du cluster — simple mais le secret reste in-Git, juste chiffré). SOPS via le plugin Argo CD (chiffre/décrypte au vol). ESO est plus opérationnellement satisfaisant à mon sens, mais Sealed Secrets reste un excellent point de départ pour les petites plateformes.

Multi-tenant : RBAC et AppProjects

Argo CD propose le concept d’AppProject pour isoler les tenants au niveau Argo CD lui-même.

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: team-data
  namespace: argocd
spec:
  description: "Data team workloads"
  sourceRepos:
    - 'git@github.com:org/team-data-*.git'   # repos autorisés
  destinations:
    - namespace: 'data-*'                     # namespaces autorisés
      server: 'https://kubernetes.default.svc'
  clusterResourceWhitelist:
    - group: ''
      kind: Namespace
  namespaceResourceBlacklist:
    - group: ''
      kind: ResourceQuota                     # géré par le platform team
  roles:
    - name: dev
      policies:
        - p, proj:team-data:dev, applications, sync, team-data/*, allow
        - p, proj:team-data:dev, applications, get, team-data/*, allow
      groups:
        - org:team-data-dev                    # groupe SSO

Le pattern. Un AppProject par équipe applicative. Source repos restreints, namespaces restreints, certaines ressources interdites (ResourceQuota, NetworkPolicy gérés par le platform team). Les roles mappent vers les groupes SSO. Les développeurs ont accès uniquement à leurs Applications via la UI.

Combiner avec RBAC Kubernetes. AppProjects contrôlent ce qu’Argo CD peut faire au nom de l’équipe. RBAC Kubernetes contrôle ce que l’équipe peut faire avec son kubectl direct. Les deux doivent être cohérents — un dev qui peut bypass Argo CD pour kubectl apply directement casse le modèle GitOps.

Gotchas et patterns à connaître

Auto-prune en prod = couteau à double tranchant
Activer prune: true sur une Application supprime automatiquement les ressources retirées du Git. C'est exactement ce que GitOps doit faire. Mais une PR mal mergée qui supprime un namespace par erreur est exécutée en quelques secondes. Pour les environnements critiques, désactiver l'auto-prune et exiger un manual sync sur les suppressions, ou mettre en place des PreSync hooks de validation.
Sync waves mal ordonnées = cascade d'échecs
Les CRDs doivent être appliqués avant les CRs qui les utilisent. Les Operators doivent être prêts avant les ressources qu'ils gèrent. Les Sync Waves (-2, -1, 0, 1, 2) ordonnent l'application. Sans elles, le premier sync d'un cluster échoue partiellement et donne l'illusion d'une plateforme instable. Auditer les sync waves de tous les bundles platform avant le premier déploiement multi-cluster.
ApplicationSets : drift entre clusters
Si un cluster est temporairement inaccessible (maintenance, network issue), l'ApplicationSet ne peut pas le sync. Au retour, le cluster doit rattraper les changements intermédiaires en une fois — risque de surcharge si le delta est important. Monitorer les Applications en état OutOfSync persistant et investiguer rapidement.
ExternalSecret refresh : risque de rate-limit
Avec un refreshInterval bas (1m) et des centaines d'ExternalSecrets, le store externe peut être rate-limité. Vault et AWS Secrets Manager ont des quotas. Adapter le refreshInterval au taux de rotation réel : la plupart des secrets ne changent pas plus d'une fois par jour, refreshInterval: 1h suffit largement.
AppProject permissions trop larges
L'erreur classique : créer un seul AppProject 'default' que toutes les équipes utilisent, avec tous les sourceRepos et destinations en wildcard. Ça marche tant que la plateforme a deux équipes. Au-delà, c'est une dette de gouvernance qui se paie cher au moment d'un incident sécurité.
ImageUpdater : à activer avec discernement
Argo CD Image Updater peut détecter de nouvelles images Docker et mettre à jour les manifests automatiquement. C'est puissant mais cela court-circuite le workflow de PR review pour les changements d'image. À activer par projet, pas globalement, et seulement quand la maturité des tests automatisés le permet.
Webhooks Git : monitoring obligatoire
Argo CD réagit aux webhooks pour synchroniser plus rapidement. Si les webhooks sont down (firewall, DNS, certificat), Argo CD passe en mode polling lent (3 min par défaut). Les développeurs constatent des sync lents sans comprendre. Monitorer la santé des webhooks au niveau du provider Git et alerter sur la perte de connectivité.

Conclusion

Ce qu’il faut retenir. Argo CD transforme un cluster VKS en plateforme self-service à condition de respecter trois piliers : structure de repo hybride (platform-config + repos applicatifs), ApplicationSets pour le multi-cluster, External Secrets Operator pour les secrets. Les AppProjects et le RBAC SSO délimitent les tenants. Les Sync Waves et l’auto-prune sont les leviers à manier avec discernement. Le bootstrap initial prend quelques heures ; la structure choisie engage pour des années — d’où l’importance de ne pas l’improviser.

Pour la suite. La série de trois articles sur VCF 9 + VKS s’arrête ici. Le prochain cycle traitera de l’industrialisation Terraform/Pulumi sur la plateforme et des patterns Crossplane pour exposer des services VKS comme des ressources Kubernetes natives.

Ressources.

Pour aller plus loin :

Reçois le prochain par email

Les nouveaux articles et séries, envoyés à leur publication. Aucun autre courrier.

Désabonnement en un clic, à tout moment.

Retour au blog
Partager

Articles similaires

  1. 17 min de lecture

    Modèles de coûts FinOps : ce que facturent AWS, Azure et GCP — et ce que VCF calcule

    Un cluster-heure EKS, un palier AKS, une requête de pod GKE et un matériel VCF amorti ne sont pas quatre valeurs de la même variable. Ce que chaque plateforme facture, et ce que VCF calcule.

  2. 23 min de lecture

    Network policies et Cilium : construire un default-deny défendable

    L'API NetworkPolicy est livrée avec Kubernetes ; l'appliquer est le travail du CNI. Ce que Cilium ajoute, ce qui reste standard, et comment atteindre le default-deny sans casser la production.

  3. 18 min de lecture

    RBAC Kubernetes : les fondations, et les pièges qui survivent à l'audit

    Chacun de ces pièges est publié sur kubernetes.io. Ce qui manque, c'est leur mise en ordre — et le chemin qui mène d'un Namespace vSphere jusqu'à cluster-admin.

Suivre le blog

Nouveaux articles, réflexions et mises à jour.