Comprendre ArgoCD
Ce guide explique ArgoCD en profondeur : ce qu'il fait, comment il raisonne, et les options qu'on utilise dans le projet.
ADR lié : GitOps avec ArgoCD. Voir aussi le guide Observabilité (distinction plan installation / plan runtime).
En une phrase
ArgoCD est un moteur GitOps : il lit un dépÎt Git (l'état voulu) et fait en sorte que le cluster Kubernetes (l'état réel) lui corresponde, en permanence.
C'est déclaratif (on décrit la cible, pas les étapes) et continu (il réconcilie en boucle, pas juste une fois).
L'objet central : Application
Tout part d'un objet kind: Application. C'est une fiche qui répond à 3 questions :
spec:
source: # QUOI déployer
repoURL: ... # depuis quel dépÎt
targetRevision: develop # quelle branche/version
path: k8s/base # quel dossier (ou chart Helm)
destination: # Oà déployer
server: https://kubernetes.default.svc
namespace: fastapi
syncPolicy: # COMMENT rester synchronisé
automated:
prune: true
selfHeal: true
ArgoCD compare en boucle le contenu de la source (Git) avec ce qui tourne dans la
destination (cluster).
Les deux états que tu liras tout le temps
| Ătat | Signification |
|---|---|
| Synced | Le cluster correspond Ă Git |
| OutOfSync | Le cluster diffÚre de Git (drift, ou nouveau commit pas encore appliqué) |
| Healthy | Les ressources tournent correctement (pods Ready, etc.) |
| Progressing | En cours de déploiement (rollout pas fini) |
| Degraded | Une ressource est en échec |
Le combo idéal : Synced + Healthy. Si une Application reste Progressing sans se stabiliser, c'est souvent un conflit (cf. INC-054, HPA vs selfHeal).
La syncPolicy : prune et selfHeal
syncPolicy:
automated:
prune: true # supprime du cluster ce qui disparaĂźt de Git
selfHeal: true # réécrase toute modif manuelle faite dans le cluster
- Sans
prune: si tu retires un fichier de Git, la ressource reste dans le cluster (orpheline).prune: truela supprime. Ăa corrige le dĂ©faut dukubectl applyimpĂ©ratif (qui ne supprime jamais). selfHeal: true: si quelqu'un fait unkubectl editĂ la main, ArgoCD remet l'Ă©tat de Git. Git est la seule source de vĂ©ritĂ©.
Attention :
selfHealse bat avec tout autre contrĂŽleur qui modifie le mĂȘme champ. Exemple vĂ©cu (INC-054) : un HPA changespec.replicas, ArgoCD le réécrase â boucle. Solution :ignoreDifferences(voir plus bas).
Le pattern app-of-apps
On installe une seule Application à la main (le root, nommée apps). Son
path pointe vers un dossier qui contient... d'autres Applications.
root-app (apps)
âââ surveille k8s/platform/argocd-apps/
âââ fastapi.yaml
âââ kube-prometheus-stack.yaml
âââ loki.yaml
âââ alloy.yaml
Résultat : pour ajouter une brique, on dépose un fichier dans ce dossier, ArgoCD le découvre et le déploie tout seul. C'est ce qu'on a fait pour l'observabilité (#74/#75) et l'exposition (#76).
Les options qu'on utilise dans le projet
sync-wave : l'ordre d'installation
metadata:
annotations:
argocd.argoproj.io/sync-wave: "0" # vague 0 = installé avant la vague 1
Sert Ă ordonner : kube-prometheus-stack (wave 0) pose les CRD, puis l'Application
monitoring (wave 1) qui les utilise. Loki (wave 1) avant Alloy (wave 2) qui
pousse vers lui.
multi-source : recette d'internet + nos réglages
sources:
- repoURL: https://prometheus-community.github.io/helm-charts # le chart
chart: kube-prometheus-stack
targetRevision: 86.2.2
helm:
valueFiles:
- $values/k8s/platform/monitoring/values.yaml # nos values
- repoURL: https://gitlab.com/yk-devops/fastapi-eks-project.git
targetRevision: develop
ref: values # fournit $values
Le chart vient d'internet (figé par targetRevision), nos réglages vivent dans
notre Git (diffables en review). On ne modifie jamais la recette publique.
syncOptions
syncOptions:
- CreateNamespace=true # crée le namespace cible s'il n'existe pas
- ServerSideApply=true # applique cÎté serveur API
ServerSideApply est indispensable pour kube-prometheus-stack : ses CRD dépassent
la limite d'annotation du kubectl apply classique (262144 octets).
ignoreDifferences : cohabiter avec un autre contrĂŽleur
ignoreDifferences:
- group: apps
kind: Deployment
name: fastapi
jsonPointers:
- /spec/replicas # ce champ est géré par le HPA, pas par Git
On dit Ă ArgoCD d'ignorer un champ. Indispensable quand un HPA (ou un autre
opérateur) pilote dynamiquement un champ : sinon selfHeal lui fait la guerre
(INC-054).
Refresh vs Sync (et le polling)
- Refresh : ArgoCD relit Git pour détecter un changement. Par défaut il le fait par polling toutes les ~3 minutes (ou instantanément via un webhook GitLab).
- Sync : ArgoCD applique le changement détecté au cluster.
En auto-sync, les deux s'enchaĂźnent. Quand on merge sur develop, il y a donc un
délai (jusqu'à ~3 min) avant qu'ArgoCD agisse. On peut forcer (bouton Refresh
puis Sync dans l'UI, ou argocd app sync <nom>).
Le point qui perd tout le monde : ArgoCD â pipeline
ArgoCD ne regarde ni le pipeline GitLab, ni les MR, ni les branches de feature.
Il regarde uniquement : « le contenu de la branche que je surveille
(develop) a-t-il changé ? ».
Conséquences concrÚtes :
- Un
git pushsur une branche de feature ne déclenche rien dans ArgoCD (ce n'est pas develop). - Une MR ouverte ne change rien tant qu'elle n'est pas mergée dans develop.
- Un pipeline ratĂ© ou absent n'empĂȘche pas ArgoCD d'agir (et inversement).
C'est pour ça qu'une modif ne prend effet qu'au merge dans develop, et que « l'infra Ă©teinte » n'empĂȘche pas de livrer : on Ă©crit dans Git, ArgoCD applique au prochain dĂ©marrage du cluster.
Comment ArgoCD est installé (lien avec le bootstrap)
ArgoCD lui-mĂȘme est posĂ© par le bootstrap Ansible (une fois, Ă l'aws-start) :
il installe le chart ArgoCD via Helm, puis applique le root-app.yaml. Ensuite
ArgoCD prend le relais et déploie tout le reste.
Ansible = impératif, one-shot (amorce). ArgoCD = déclaratif, continu (pilote automatique). Voir Observabilité.
Le mot de passe admin (géré via ESO)
Par défaut, le chart ArgoCD génÚre un mot de passe admin aléatoire et le pose
dans le secret argocd-initial-admin-secret. Pratique tant que l'accĂšs est local
(port-forward), mais on veut exposer l'UI publiquement (#105/MR2), donc on prend
la main sur ce mot de passe : on le gĂšre via ESO, comme celui de Grafana (#76).
Sauf que ArgoCD a deux particularités par rapport à Grafana :
1. ArgoCD stocke un hash bcrypt, pas un mot de passe en clair. Le mot de passe
admin vit dans argocd-secret, sous deux clés : admin.password (le hash bcrypt)
et admin.passwordMtime (la date depuis laquelle il est valide). On stocke donc le
hash dans AWS Secrets Manager, pas un mot de passe brut. Et comme la fonction
bcrypt() de Terraform regénÚre un sel à chaque apply (le hash changerait sans
arrĂȘt), on gĂ©nĂšre le hash une fois en local et on le fournit comme valeur externe
(TF_VAR_argocd_admin_bcrypt), exactement comme l'URL du webhook Slack (#77) :
# Générer le hash bcrypt à passer en TF_VAR_argocd_admin_bcrypt
argocd account bcrypt --password 'MON_MOT_DE_PASSE'
# ou, sans la CLI argocd :
htpasswd -nbBC 10 "" 'MON_MOT_DE_PASSE' | tr -d ':\n' | sed 's/$2y/$2a/'
2. argocd-secret appartient déjà au chart ArgoCD. Il contient aussi d'autres
clés (server.secretkey...). Si notre ExternalSecret essayait de le créer
(creationPolicy: Owner, comme Grafana qui crée son secret), ESO et ArgoCD se
battraient pour la propriété. On utilise donc creationPolicy: Merge +
template.mergePolicy: Merge : ESO patche le secret existant en ajoutant juste
admin.password + admin.passwordMtime, sans toucher au reste.
Le tout est déployé par une Application ArgoCD dédiée argocd-config
(path: k8s/platform/argocd) : ArgoCD gÚre sa propre config en GitOps. Il déploie
l'ExternalSecret (pas argocd-secret directement), c'est ESO qui fait le patch
â aucun conflit. Le pourquoi complet : ADR 022.
Retenir :
Ownerquand tu crées le secret (Grafana) ;Mergequand tu enrichis un secret possédé par quelqu'un d'autre (ArgoCD). Confondre les deux = bataille de contrÎleurs.
L'exposition publique (argocd.devopsyouss.com)
Une fois le mot de passe gĂ©rĂ© (ci-dessus), on expose l'UI via le mĂȘme Gateway
partagé que Grafana (voir Exposition Grafana) : un
HTTPRoute argocd.devopsyouss.com (ns argocd) qui pointe argocd-server:80,
attaché au fastapi-gateway cross-namespace. Le cert wildcard couvre le sous-domaine,
ExternalDNS crée le CNAME tout seul.
Le piĂšge Ă connaĂźtre : le TLS-sur-TLS. Par dĂ©faut, argocd-server fait lui-mĂȘme
du TLS. Si on le met derriÚre un Gateway qui termine déjà le TLS, on a deux couches de
TLS qui s'empilent â boucle de redirection HTTPâHTTPS, ou 502 sur le gRPC. La solution
standard d'ArgoCD derriĂšre un ingress : passer le serveur en server.insecure: true.
Il sert alors l'UI / l'API / le gRPC-Web en clair sur le port 80, et c'est le
Gateway qui fait le HTTPS public.
navigateur ââHTTPS (cert wildcard)âââș Gateway (Envoy) ââHTTPâââș argocd-server:80
TLS terminĂ© ici ââââââââââ
OĂč vit quoi (Ă retenir) : server.insecure est un paramĂštre d'installation du
serveur â il est dans les values du helm install ArgoCD, donc dans le bootstrap
Ansible. Le HTTPRoute, lui, est une ressource d'exposition â il est en GitOps
(k8s/platform/argocd/, Application argocd-config). Le pourquoi complet :
ADR 023.
Point ouvert (Ă valider live) : le CLI
argocdparle gRPC. Avecserver.insecureet un seul port, ArgoCD sert le gRPC-Web sur le 80 ; reste Ă confirmer queargocd login argocd.devopsyouss.compasse bien Ă travers le Gateway.
Le SSO (connexion via GitLab)
Maintenant qu'ArgoCD est exposĂ©, on ne veut plus se connecter avec le mot de passe admin au quotidien : on dĂ©lĂšgue l'authentification Ă GitLab (l'IdP qu'on utilise dĂ©jĂ ). ArgoCD embarque pour ça Dex, un petit serveur qui sait parler Ă des fournisseurs d'identitĂ© (GitLab, GitHub, GoogleâŠ).
Le flux quand tu cliques « Log in via GitLab » :
toi âââș ArgoCD âââș Dex âââș GitLab (tu t'authentifies) âââș Dex âââș ArgoCD (connectĂ©)
â
ââ GitLab renvoie : qui tu es + tes groupes
La config vit dans argocd-cm (configs.cm.dex.config, donc dans les values du
helm install â bootstrap Ansible) : un connecteur type: gitlab, le clientID
(public, en clair) et clientSecret: $dex.gitlab.clientSecret. Le $ dit Ă ArgoCD
d'aller lire la valeur dans argocd-secret (la clĂ© y est injectĂ©e par ESO, mĂȘme
mécanisme que le mot de passe admin). Le champ groups: [yk-devops] filtre :
seuls les membres du groupe GitLab yk-devops peuvent se connecter.
Le RBAC (qui a le droit de faire quoi) est dans argocd-rbac-cm
(configs.rbac) :
policy.default: role:readonly # par défaut, lecture seule
policy.csv: |
g, yk-devops, role:admin # les membres du groupe yk-devops -> admin
GitLab renvoie le groupe dans le token, ArgoCD applique la rĂšgle. Le pourquoi complet (GitLab vs GitHub, une seule app multi-redirect, secret via ESO) : ADR 024.
Le mot de passe admin devient un « break-glass » : on se connecte via GitLab au quotidien, l'admin ne sert plus qu'en secours (si le SSO tombe). Ă terme on peut mĂȘme le dĂ©sactiver (
admin.enabled: false). MĂȘme logique pour Grafana (MR2 de #106).
Teardown
Les Applications ont un finalizer :
metadata:
finalizers:
- resources-finalizer.argocd.argoproj.io
Supprimer une Application supprime aussi ce qu'elle a créé (cascade). Au
teardown, on supprime les Applications en premier : ça stoppe le selfHeal
avant le reste, sinon il recrĂ©erait des ressources (ex: HTTPRoute â ELB orphelin,
classe INC-016).
Récap des piÚges
| SymptÎme | Cause probable | Réflexe |
|---|---|---|
Application reste Progressing |
Conflit sur un champ (HPA vs selfHeal) | ignoreDifferences (INC-054) |
CRD Too long ... 262144 bytes |
apply client-side sur gros CRD | ServerSideApply=true |
| Ma modif n'est pas appliquée | Pas (encore) sur develop |
Merger ; ArgoCD suit develop, pas les branches |
| Rien ne bouge aprĂšs le merge | Polling ~3 min | Attendre, ou Refresh+Sync |
| ELB orphelin au teardown | selfHeal recrée l'HTTPRoute | Supprimer les Applications en premier |