ArgoCD¶
ArgoCD est l'outil GitOps qui synchronise en continu les manifestes Kubernetes stockés dans Git avec l'état réel des clusters. Il est installé sur le cluster service (namespace
argocd) et pilote l'ensemble des clusters ADEME depuis ce point unique. L'authentification se fait directement via Keycloak (OIDC).
Déploiement Terraform¶
| Élément | Emplacement |
|---|---|
| Run Terraform | main-infra/scw-service/terraform/030.argocd |
| Module | main-infra/scw-tf-modules/argocd |
| Values du chart Helm | main-infra/scw-tf-modules/argocd/helm_values/argocd_values.yaml |
Version du chart argo-cd |
scw-init-config/001.config_infrastructure/terraform.tfvars → k8s_helm_charts_versions["service"].argocd |
Les values sont un template Terraform (templatefile) : les variables ${CLUSTER_FQDN}, ${KEYCLOAK_URL}, etc. sont injectées par le module à partir de Secret Manager et de la configuration commune.
Composants¶
graph LR
U["Utilisateurs / GitLab CI"] --> S["argocd-server (x8)"]
S --> R["argocd-repo-server (x8)"]
S --> H["redis-ha-haproxy (x3)"]
C["application-controller (x3 shards)"] --> R
C --> H
A["applicationset-controller (x8)"] --> S
N["notifications-controller (x1)"] --> H
H --> RS["redis-ha-server (x3, Sentinel)"]
C --> K["Clusters gérés"]
R --> G["Dépôts Git"]
| Composant | Type | Réplicas | Rôle |
|---|---|---|---|
| application-controller | StatefulSet | 3 | Cœur d'ArgoCD : compare l'état désiré (Git) et l'état réel des clusters, calcule le statut Synced/Healthy et applique les synchronisations. Les clusters gérés sont répartis entre les réplicas (sharding) : chaque pod ne surveille que les clusters de son shard. |
| repo-server | Deployment | 8 | Clone les dépôts Git et génère les manifestes (Helm, Kustomize, YAML). Consommation mémoire ponctuelle lors des générations. |
| server | Deployment | 8 | API et interface web (argocd.<fqdn service>), exposés via Traefik avec liste blanche IP. Gère l'authentification OIDC Keycloak et les comptes locaux (gitlab pour la CI). |
| applicationset-controller | Deployment | 8 | Génère des Application à partir des ApplicationSet. Un seul réplica est actif à la fois (élection de leader), les autres sont en attente. |
| notifications-controller | Deployment | 1 | Envoie les notifications sur les événements des applications (sync, échec, dégradation). |
| redis-ha-server | StatefulSet | 3 | Cache Redis répliqué (1 maître, 2 réplicas) surveillé par Sentinel, qui élit un nouveau maître en cas de panne. Chaque pod contient les conteneurs redis, sentinel et split-brain-fix. |
| redis-ha-haproxy | Deployment | 3 | Point d'entrée unique de Redis (argocd-redis-ha-haproxy:6379) : redirige toujours vers le maître courant. |
| redis-secret-init | Job (hook Helm) | – | Génère le secret argocd-redis (mot de passe Redis) à l'installation. |
| dex-server | – | désactivé | Plus utilisé : le SSO passe directement par Keycloak (configs.cm.oidc.config). |
Modifications de novembre 2026¶
| Sujet | Avant | Après |
|---|---|---|
| application-controller | 1 réplica | 3 réplicas (1 shard par cluster géré, sharding automatique via ARGOCD_CONTROLLER_REPLICAS) |
| Dex | déployé | désactivé (dex.enabled: false) |
| Redis | 1 pod argocd-redis |
Redis HA : 3 serveurs + Sentinel, 3 HAProxy |
| Tolérances | multiaz uniquement |
multiaz + unreachable / not-ready à 30 s, pour tous les composants |
| Répartition par zone | aucune | topologySpreadConstraints sur tous les composants |
| PodDisruptionBudgets | aucun | PDB sur tous les composants multi-réplicas |
| Ressources | aucune request ni limit | requests / limits mémoire dimensionnées sur l'historique Thanos |
Tolérances¶
Définies une seule fois dans global.tolerations (ancre YAML &argocd_tolerations) et réutilisées pour redis-ha et redis-ha.haproxy, car le sous-chart Redis HA ne lit pas global.tolerations.
- { key: k8s.scaleway.com/multiaz, operator: Equal, value: "true", effect: NoSchedule }
- { key: node.kubernetes.io/unreachable, operator: Exists, effect: NoExecute, tolerationSeconds: 30 }
- { key: node.kubernetes.io/not-ready, operator: Exists, effect: NoExecute, tolerationSeconds: 30 }
Les deux dernières réduisent de 300 s (valeur par défaut Kubernetes) à 30 s le délai avant éviction des pods d'un nœud injoignable : en cas de perte d'un nœud ou d'une zone, les pods sont replanifiés beaucoup plus vite.
Répartition par zone (topologySpreadConstraints)¶
Chaque composant ArgoCD porte la contrainte suivante (label adapté au composant) :
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
nodeTaintsPolicy: Honor
matchLabelKeys: # Deployments uniquement
- pod-template-hash
labelSelector:
matchLabels:
app.kubernetes.io/name: argocd-server
maxSkew: 1: l'écart entre la zone la plus chargée et la moins chargée ne peut pas dépasser 1 pod (ex. 8 réplicas sur 3 zones → 3/3/2). AvecDoNotSchedule, un pod qui violerait la règle restePending.nodeTaintsPolicy: Honor: les nœuds portant un taint non toléré sont exclus du calcul. Conséquences :- lors d'une panne de zone, les nœuds
unreachablene comptent plus et les pods évincés peuvent être replanifiés dans les zones restantes ; - lors d'un drain, le nœud cordonné est ignoré et le drain ne bloque pas ;
- la zone fr-par-3, dont l'unique nœud porte le taint
k8s.scaleway.com/par-3, est ignorée : les composants se répartissent aujourd'hui sur fr-par-1 et fr-par-2.
- lors d'une panne de zone, les nœuds
matchLabelKeys: [pod-template-hash](Deployments uniquement) : seuls les pods d'une même révision (ReplicaSet) sont comptés, ce qui garantit une répartition équilibrée à chaque rollout. Non applicable à l'application-controller (StatefulSet, pas de labelpod-template-hash).
Redis HA : contrainte en ScheduleAnyway
Le sous-chart redis-ha génère lui-même sa contrainte et n'accepte que maxSkew, topologyKey et whenUnsatisfiable : impossible d'y ajouter nodeTaintsPolicy ou matchLabelKeys. Sans Honor, la zone fr-par-3 compte comme une zone vide ; en DoNotSchedule, le 3ᵉ pod Redis et le 3ᵉ HAProxy resteraient Pending indéfiniment (et le helm_release Terraform échouerait en timeout).
Redis HA utilise donc whenUnsatisfiable: ScheduleAnyway : la répartition par zone est une préférence, et l'anti-affinité obligatoire du chart impose toujours un pod par nœud. Si le taint par-3 est un jour remplacé par le taint multiaz, la zone fr-par-3 deviendra utilisable et l'on pourra repasser en DoNotSchedule.
Retour d'une zone après une panne¶
Les contraintes de répartition ne sont évaluées qu'à la planification : quand une zone revient, les pods déjà en cours d'exécution ne sont pas déplacés (ex. 4/4/0 au lieu de 3/3/2). Le service fonctionne normalement, mais n'est plus réparti. L'équilibre revient au prochain déploiement, ou immédiatement avec :
kubectl -n argocd rollout restart \
deploy/argocd-server deploy/argocd-repo-server deploy/argocd-applicationset-controller \
sts/argocd-application-controller
Les nouveaux pods ne peuvent être placés que dans la zone sous-peuplée tant qu'elle n'a pas rattrapé les autres.
PodDisruptionBudgets¶
| PDB | maxUnavailable |
Réplicas | Justification |
|---|---|---|---|
argocd-server |
4 | 8 | Drains rapides, la moitié des pods reste disponible |
argocd-repo-server |
4 | 8 | idem |
argocd-applicationset-controller |
4 | 8 | idem |
argocd-application-controller |
1 | 3 | Un seul shard (donc un seul cluster géré) indisponible à la fois |
argocd-redis-ha-pdb |
1 | 3 | 2 serveurs sur 3 toujours disponibles : Sentinel garde son quorum |
argocd-redis-ha-haproxy-pdb |
1 | 3 | Point d'entrée Redis toujours disponible |
Le notifications-controller n'a volontairement pas de PDB : avec un seul réplica, tout PDB protecteur bloquerait les drains de nœuds (mises à jour Kapsule, autoscaler). Un redémarrage pendant un drain est sans impact.
maxUnavailable est préféré à minAvailable car il reste valable si le nombre de réplicas change.
Ressources¶
Dimensionnées à partir de l'historique Thanos du cluster service (p95/p99 sur 7 jours, max sur 30 jours, pire pod de chaque composant). Pas de limite CPU (le throttling ralentirait la réconciliation) ; la mémoire est réservée à hauteur de la limite.
| Conteneur | Mesuré CPU (p95 / max) | Mesuré mémoire (p95 / max 30 j) | Request CPU | Request = limit mémoire |
|---|---|---|---|---|
| application-controller (x3) | 2,44 / 4,58 cores* | 4,8 Gi / 7,2 Gi* | 1 | 8Gi |
| repo-server (x8) | 0,19 / 0,22 | 504 Mi / 1,06 Gi | 100m | 1536Mi |
| server (x8) | 0,02 / 0,14 | 213 Mi / 363 Mi | 50m | 512Mi |
| applicationset-controller (x8) | 0,012 / 0,022 | 118 Mi / 169 Mi | 25m | 256Mi |
| notifications-controller (x1) | 0,013 / 0,023 | 161 Mi / 255 Mi | 25m | 384Mi |
redis-ha redis (x3) |
0,015 / 0,022** | 784 Mi / 1,06 Gi** | 100m | 1536Mi |
redis-ha haproxy (x3) |
non mesuré | non mesuré | 25m | 128Mi |
* Mesuré avec un seul controller gérant tous les clusters. Les shards ne sont pas équilibrés (le cluster horsprod porte la majorité des objets), d'où une marge large. ** Mesuré sur l'ancien Redis mono-nœud. Chaque serveur Redis HA contient l'intégralité des données, la mémoire par pod est donc identique.
Total réservé sur le namespace : environ 4,8 cores et 47 Gi de mémoire.
Redis HA sans volume persistant¶
Redis n'est qu'un cache pour ArgoCD (arbre des ressources, manifestes générés, cache des clusters) : tout est reconstructible depuis Git et les clusters. Le chart le déploie donc sans PVC (persistentVolume.enabled: false, volume emptyDir, snapshots désactivés save "").
- Redémarrage d'un pod : il repart vide et se resynchronise depuis le maître ; Sentinel élit un nouveau maître si besoin. Transparent pour ArgoCD.
- Redémarrage des 3 pods : cache vide, UI et réconciliation plus lentes quelques minutes. Aucune perte de configuration (stockée dans les CRDs et secrets ArgoCD).
Un PVC serait contre-productif : les volumes Block Storage Scaleway sont zonaux et figeraient chaque pod Redis dans sa zone.
Points de vigilance¶
- Application du changement : le passage à Redis HA modifie l'adresse Redis (
argocd-redis-ha-haproxy:6379) ; tous les composants redémarrent et le cache se reconstruit. Prévoir quelques minutes de lenteur. - Re-mesurer l'application-controller environ deux semaines après la mise en place du sharding, puis réduire la mémoire si possible.
- Dimensionner HAProxy et Sentinel après quelques jours de fonctionnement (valeurs de départ / non définies).
- Nombre de réplicas : server et applicationset-controller sont largement surdimensionnés à 8 (quelques millicores par pod ; un seul applicationset-controller actif). 3 réplicas suffiraient.
- Zone fr-par-3 : tant que son nœud porte le taint
par-3, ArgoCD tourne sur deux zones seulement. Le remplacer par le taintmultiazpermettrait une répartition sur les trois zones.
Liens utiles¶
- Chart Helm
argo-cd: https://github.com/argoproj/argo-helm/tree/main/charts/argo-cd - Sharding du controller : https://argo-cd.readthedocs.io/en/stable/operator-manual/high_availability/#argocd-application-controller
- Topology spread constraints : https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/