Plusieurs clusters
Pourquoi
Jusqu'ici, recette et production de Signalements vivaient dans le même cluster qu'Argo CD, séparées par un namespace. C'est le point de départ de beaucoup d'équipes, et il a des limites nettes : un incident de cluster (un nœud saturé, une mise à jour ratée, un contrôleur d'admission en panne) touche la recette et la production ; une erreur de droits dans la recette peut atteindre la production ; et certains clients exigent que leur production tourne sur un cluster qui leur est propre, parfois dans un autre projet ou une autre organisation cloud.
Argo CD sait piloter plusieurs clusters depuis une seule instance. Mais enregistrer un cluster, c'est confier à Argo CD des identifiants capables de le modifier : cette leçon s'attache autant à ce que l'on donne à Argo CD qu'à la manière de répartir les applications.
Les concepts
Hub et rayons
flowchart LR
G[(Dépôts Git)] --> A
subgraph hub["Cluster d'Argo CD (hub)"]
A[Argo CD] --> R[signalements-recette]
end
A -->|"API Kubernetes,<br/>jeton du cluster cible"| P
subgraph prod["Cluster de production"]
P[signalements-production]
end
Dans le modèle le plus courant, une instance d'Argo CD (le hub) se connecte à l'API des clusters cibles (les rayons) et y applique les manifestes. Conséquences directes :
- le hub doit joindre l'API de chaque cluster cible ;
- le hub détient un identifiant par cluster cible, stocké dans un secret Kubernetes ;
- les clusters cibles n'ont pas besoin d'accéder à Git : c'est le repo-server du hub qui lit les dépôts. Ils doivent seulement pouvoir tirer leurs images.
L'alternative est une instance d'Argo CD par cluster, chacune ne gérant que le sien : aucun identifiant ne quitte le cluster, mais il y a autant d'instances à exploiter. Entre les deux, le projet argocd-agent inverse le sens de la connexion : un agent installé dans chaque cluster cible se connecte au hub. Il est encore hébergé dans argoproj-labs et en version 0.x (0.10.0 en août 2026) : à suivre, pas encore à mettre en production sans précaution.
Un cluster, c'est un secret
Argo CD ne connaît un cluster que par un secret de son namespace portant l'étiquette argocd.argoproj.io/secret-type: cluster. Ses clés principales :
| Clé | Rôle |
|---|---|
name | Nom du cluster, utilisé par destination.name |
server | URL de l'API, telle que le hub la joint |
config | JSON des identifiants : bearerToken, tlsClientConfig (caData, certData, keyData), ou execProviderConfig / awsAuthConfig |
namespaces | Liste des namespaces gérés, séparés par des virgules ; vide = tout le cluster |
clusterResources | "true" pour gérer aussi les ressources de portée cluster quand namespaces est renseigné |
Les étiquettes du secret servent ensuite au générateur de clusters des ApplicationSet.
En pratique
Pour cette leçon, un second cluster kind, gitops-prod, a été créé sur le même réseau Docker que le premier, avec la même image de nœud. Les deux contextes sont dans le kubeconfig isolé du laboratoire : kind-gitops (le hub) et kind-gitops-prod.
kind create cluster --name gitops-prod --image "$NOEUD" --kubeconfig "$KUBECONFIG" --wait 120s
kind load docker-image --name gitops-prod registre.lyneko.example/signalements:1.2.0Warning
Sous Linux, un second cluster kind dépasse souvent la limite fs.inotify.max_user_instances de l'hôte (128 par défaut sous Ubuntu). Le cluster démarre, mais kube-proxy redémarre en boucle avec too many open files, et les Services ne fonctionnent plus : CoreDNS reste non prêt, et un test de fumée échoue sur Resolving timed out. C'est ce qui est arrivé pendant la préparation de cette leçon. La documentation de kind recommande sudo sysctl fs.inotify.max_user_instances=512 (et fs.inotify.max_user_watches=524288) avant de créer le cluster.
Ce que fait argocd cluster add
La méthode documentée en premier est la commande argocd cluster add, qui prend un contexte du kubeconfig :
$ argocd cluster add kind-gitops-prod --name production --yes
{"level":"info","msg":"ServiceAccount \"argocd-manager\" created in namespace \"kube-system\"","time":"2026-10-02T10:19:14+02:00"}
{"level":"info","msg":"ClusterRole \"argocd-manager-role\" created","time":"2026-10-02T10:19:14+02:00"}
{"level":"info","msg":"ClusterRoleBinding \"argocd-manager-role-binding\" created","time":"2026-10-02T10:19:14+02:00"}
{"level":"info","msg":"Created bearer token secret \"argocd-manager-long-lived-token\" for ServiceAccount \"argocd-manager\"","time":"2026-10-02T10:19:14+02:00"}
{"level":"fatal","msg":"rpc error: code = Unknown desc = error getting server version: failed to get server version: Get \"https://127.0.0.1:43675/version?timeout=32s\": dial tcp 127.0.0.1:43675: connect: connection refused","time":"2026-10-02T10:19:15+02:00"}
Deux enseignements dès ce premier essai :
- La commande a déjà modifié le cluster cible (un compte, un rôle de cluster, un jeton) avant d'échouer.
- Elle échoue parce qu'elle enregistre l'URL telle qu'elle figure dans votre kubeconfig (
127.0.0.1:43675, le port publié par kind sur votre poste), et que le serveur Argo CD, qui tourne dans le hub, ne peut évidemment pas la joindre. C'est le même problème avec un cluster joint par un tunnel, un bastion ou une adresse privée différente de celle que voit le hub.
L'option --cluster-endpoint kube-public utilise à la place l'adresse publiée par le cluster lui-même dans le ConfigMap kube-public/cluster-info, ici https://gitops-prod-control-plane:6443, joignable sur le réseau Docker :
$ argocd cluster add kind-gitops-prod --name production --cluster-endpoint kube-public --label environnement=production --yes
...
Cluster 'https://gitops-prod-control-plane:6443' added
$ argocd cluster list
SERVER NAME VERSION STATUS MESSAGE PROJECT
https://gitops-prod-control-plane:6443 production v1.36.4 Successful
https://kubernetes.default.svc in-cluster v1.36.4 Successful
Regardons ce qui a été donné, d'abord au compte créé dans le cluster cible :
$ kubectl --context kind-gitops-prod get clusterrole argocd-manager-role -o jsonpath='{.rules}'
[{"apiGroups":["*"],"resources":["*"],"verbs":["*"]},{"nonResourceURLs":["*"],"verbs":["*"]}]
Tous les droits, sur tout le cluster. Puis au secret enregistré côté hub (valeurs remplacées par <...> ici) :
$ kubectl -n argocd get secrets -l argocd.argoproj.io/secret-type=cluster --show-labels
NAME TYPE DATA AGE LABELS
cluster-gitops-prod-control-plane-2294550256 Opaque 3 6s argocd.argoproj.io/secret-type=cluster,environnement=production
Le champ config de ce secret, décodé, a la forme suivante (valeurs masquées) :
{
"tlsClientConfig": {
"insecure": false,
"certData": "<...>",
"keyData": "<...>",
"caData": "<...>"
}
}Il n'y a pas de jeton dans ce secret, mais un certificat client et sa clé privée : ceux du contexte kind-gitops-prod de votre kubeconfig, c'est-à-dire l'identité d'administration du cluster. Le code de la version 3.5.3 l'explique (cmd/util/cluster.go, fonction NewCluster) :
// Bearer token will preferentially be used for auth if present,
// Even in presence of key/cert credentials
// So set bearer token only if the key/cert data is absent
if len(tlsClientConfig.CertData) == 0 || len(tlsClientConfig.KeyData) == 0 {
clst.Config.BearerToken = managerBearerToken
}Quand votre contexte s'authentifie par certificat client (kind, kubeadm, beaucoup de clusters installés à la main), argocd cluster add recopie votre certificat d'administration dans Argo CD, et le compte argocd-manager qu'il vient de créer ne sert à rien. Un tel certificat ne se révoque pas individuellement dans Kubernetes : il reste valide jusqu'à son expiration. Quand le contexte s'authentifie par jeton ou par un programme externe (exec), c'est bien le jeton de argocd-manager qui est enregistré, avec ses droits illimités.
Et pour défaire :
$ argocd cluster rm production --yes
Cluster 'production' removed
{"level":"fatal","msg":"context production does not exist in kubeconfig","time":"2026-10-02T10:20:09+02:00"}
$ kubectl --context kind-gitops-prod -n kube-system get sa,secret -o name | grep argocd
serviceaccount/argocd-manager
secret/argocd-manager-long-lived-token
$ kubectl --context kind-gitops-prod get clusterrole,clusterrolebinding -o name | grep argocd
clusterrole.rbac.authorization.k8s.io/argocd-manager-role
clusterrolebinding.rbac.authorization.k8s.io/argocd-manager-role-binding
argocd cluster rm cherche, pour nettoyer le cluster cible, un contexte portant le nom donné au cluster (production), qui n'existe pas dans le kubeconfig : il retire l'enregistrement, puis échoue, et laisse dans le cluster cible un compte administrateur avec un jeton de longue durée. Il faut les supprimer à la main.
Warning
argocd cluster add est pratique pour une démonstration. Pour un cluster qui compte, préférez l'enregistrement déclaratif qui suit : vous choisissez les droits, et vous savez exactement quel identifiant est confié à Argo CD.
Un enregistrement déclaratif, limité à un namespace
L'équipe plateforme prépare, dans le cluster cible, un compte dédié et ses droits, limités au namespace de Signalements :
# acces-argocd.yaml, à appliquer sur le cluster de production par l'équipe plateforme.
# Argo CD n'y reçoit que les droits sur le namespace de Signalements.
apiVersion: v1
kind: Namespace
metadata:
name: signalements-production
---
apiVersion: v1
kind: Namespace
metadata:
name: argocd-acces
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: argocd
namespace: argocd-acces
---
# Jeton de longue durée, lié au compte : il est révoqué si le compte ou ce secret est supprimé.
apiVersion: v1
kind: Secret
metadata:
name: argocd-jeton
namespace: argocd-acces
annotations:
kubernetes.io/service-account.name: argocd
type: kubernetes.io/service-account-token
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: argocd
namespace: signalements-production
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: admin
subjects:
- kind: ServiceAccount
name: argocd
namespace: argocd-accesLe namespace signalements-production est créé par l'équipe plateforme, pas par Argo CD : un compte limité à un namespace ne peut pas créer de namespace. Le rôle admin intégré à Kubernetes donne, à travers un RoleBinding, tous les droits usuels dans ce seul namespace.
Côté hub, le secret de cluster référence ce jeton :
apiVersion: v1
kind: Secret
metadata:
name: cluster-production
namespace: argocd
labels:
argocd.argoproj.io/secret-type: cluster
environnement: production
type: Opaque
stringData:
name: production
server: https://gitops-prod-control-plane:6443
namespaces: signalements-production
clusterResources: "false"
config: |
{
"bearerToken": "<jeton du secret argocd-acces/argocd-jeton>",
"tlsClientConfig": {"caData": "<ca.crt du même secret, en base64>"}
}Ce secret contient un identifiant : il ne se commite pas en clair dans le dépôt gitops. La leçon 8 montre comment le faire venir d'un gestionnaire de secrets. Une fois appliqué :
$ argocd cluster list
SERVER NAME VERSION STATUS MESSAGE PROJECT
https://gitops-prod-control-plane:6443 (1 namespaces) production Unknown Cluster has no applications and is not being monitored.
https://kubernetes.default.svc in-cluster v1.36.4 Successful
Unknown n'est pas une erreur : Argo CD ne surveille un cluster qu'à partir du moment où une application le cible.
Étiqueter aussi le cluster local
Le générateur de clusters sélectionne les clusters par leurs étiquettes. Le cluster local (in-cluster) n'a pas de secret par défaut, donc pas d'étiquette : on le déclare explicitement.
# Le cluster local d'Argo CD, déclaré pour pouvoir porter une étiquette.
apiVersion: v1
kind: Secret
metadata:
name: cluster-in-cluster
namespace: argocd
labels:
argocd.argoproj.io/secret-type: cluster
environnement: recette
type: Opaque
stringData:
name: in-cluster
server: https://kubernetes.default.svc
config: '{"tlsClientConfig": {"insecure": false}}'Chaque environnement sur son cluster
L'ApplicationSet de la leçon 6 devient une matrice : pour chaque cluster portant une étiquette environnement, le générateur Git ne retient que le répertoire du même nom.
# Une application par environnement, sur le cluster qui porte son étiquette.
spec:
generators:
- matrix:
generators:
- clusters:
selector:
matchExpressions:
- key: environnement
operator: Exists
- git:
repoURL: http://forgejo.forge.svc.cluster.local:3000/plateforme/signalements-deploiement.git
revision: main
directories:
- path: 'environnements/{{ index .metadata.labels "environnement" }}'
template:
spec:
destination:
name: '{{ .name }}'
namespace: 'signalements-{{ .path.basename }}'
# ... le reste du modèle de la leçon 6, inchangéLe second générateur utilise un paramètre du premier (.metadata.labels) : c'est permis dans une matrice. Avant de pousser, la vérification habituelle :
$ argocd appset generate applications/signalements.yaml -o json | jq -r '.[]|"\(.metadata.name) \(.spec.destination.name) \(.spec.destination.namespace)"'
signalements-recette in-cluster signalements-recette
signalements-production production signalements-production
Ce qui se passe quand une application change de cluster
Le commit poussé, l'Application signalements-production change de destination en quelques secondes. Puis :
$ argocd app get signalements-production --refresh
...
Sync Status: Unknown
Health Status: Healthy
CONDITION MESSAGE
ComparisonError Failed to load live state: failed to get cluster info for "https://gitops-prod-control-plane:6443": error synchronizing cache state : failed to sync cluster https://gitops-prod-control-plane:6443: failed to load initial state of resource CSIStorageCapacity.storage.k8s.io: failed to list resources: csistoragecapacities.storage.k8s.io is forbidden: User "system:serviceaccount:argocd-acces:argocd" cannot list resource "csistoragecapacities" in API group "storage.k8s.io" in the namespace "signalements-production"
Le cache d'Argo CD ne se contente pas des types de ressources que l'application utilise : il liste tous les types de ressources namespacées que l'API annonce, dans chaque namespace géré. Le rôle admin de Kubernetes n'en couvre pas certains (csistoragecapacities, podtemplates...), et une seule liste refusée bloque tout le cache du cluster. Il faut donc ajouter, dans le namespace, un droit de lecture sur tout :
# Le cache d'Argo CD liste tous les types de ressources du namespace :
# le rôle admin intégré n'en couvre pas certains (csistoragecapacities...).
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: argocd-lecture
namespace: signalements-production
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: [get, list, watch]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: argocd-lecture
namespace: signalements-production
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: argocd-lecture
subjects:
- kind: ServiceAccount
name: argocd
namespace: argocd-accesCe rôle donne la lecture des secrets du namespace, ce que admin donnait déjà. L'alternative est d'exclure de la surveillance les types inutiles (resource.exclusions dans argocd-cm), au risque d'en oublier. Le droit ajouté, l'application se synchronise sur le nouveau cluster :
$ kubectl --context kind-gitops-prod -n signalements-production get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
signalements 3/3 3 3 2s
Deux surprises restent à voir. La première, sur l'ancien cluster :
$ kubectl --context kind-gitops -n signalements-production get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
signalements 3/3 3 3 52m
L'ancienne production tourne toujours. L'élagage ne concerne que le cluster de destination : en changeant de destination, l'application a simplement oublié ses ressources de l'ancien cluster. Les voilà orphelines, et plus rien ne les met à jour. Pour un vrai déménagement, c'est d'ailleurs souhaitable pendant la bascule (le trafic peut continuer d'y aller), mais il faut les supprimer explicitement ensuite.
La seconde surprise est dans l'opération qui a déployé la production sur le nouveau cluster :
$ kubectl -n argocd get application signalements-production -o json | jq '{op: .status.operationState | {startedAt, phase, initiatedBy: .operation.initiatedBy, results: [.syncResult.resources[] | "\(.kind) \(.hookType // "-") \(.message)"]}, history: [.status.history[] | {id, deployedAt, revision}]}'
{
"op": {
"startedAt": "2026-10-02T08:23:05Z",
"phase": "Succeeded",
"initiatedBy": {
"automated": true
},
"results": [
"Service - service/signalements created",
"Deployment - deployment.apps/signalements created"
]
},
"history": [
{
"id": 0,
"deployedAt": "2026-10-02T08:15:47Z",
"revision": "07a1de5bb01a5bc9cde97c675db1ef4692d83b63"
}
]
}
Le hook PostSync de test de fumée n'a pas été exécuté, et l'historique n'a pas de nouvelle entrée. La révision 07a1de5 n'a pas changé : pour le contrôleur, la synchronisation automatique de cette révision a déjà été tentée (leçon 3). Il ne relance donc qu'une synchronisation d'auto-réparation, et le code de controller/appcontroller.go la restreint aux ressources désynchronisées :
op.Sync.SelfHealAttemptsCount++
for _, resource := range resources {
if resource.Status != appv1.SyncStatusCodeSynced {
op.Sync.Resources = append(op.Sync.Resources, appv1.SyncOperationResource{ ... })
}
}Une synchronisation partielle n'exécute pas les hooks (le filtre de controller/sync.go ne retient que les ressources listées) et n'est pas inscrite dans l'historique. Sans selfHeal, il n'y aurait même rien eu : la synchronisation automatique aurait été ignorée (Skipping auto-sync: already attempted). Pour une application avec une migration de base en hook, déplacer l'application sans changer de révision déploie l'application sans sa migration. Après un changement de destination, lancez une synchronisation complète (argocd app sync signalements-production), qui exécute tous les hooks.
Fenêtres de synchronisation
Plus il y a de clusters, plus il y a de raisons de ne pas déployer à certains moments : la nuit, pendant une période de forte charge, pendant un gel des mises en production. Les fenêtres de synchronisation se déclarent dans un AppProject (la leçon 9 traite les projets en détail) :
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: production
namespace: argocd
spec:
description: Applications de production
sourceRepos:
- http://forgejo.forge.svc.cluster.local:3000/plateforme/*
destinations:
- name: production
namespace: signalements-production
syncWindows:
# Pas de mise en production le soir, la nuit et le week-end, sauf geste manuel.
- kind: allow
schedule: '0 9 * * 1-4'
duration: 8h
timeZone: Europe/Paris
applications: ['*']
manualSync: true
description: Du lundi au jeudi, de 9 h à 17 h
# Gel de fin d'année, sans exception.
- kind: deny
schedule: '0 0 20 12 *'
duration: 336h
timeZone: Europe/Paris
clusters: [production]
description: Gel du 20 décembre au 2 janvier inclusLes règles, d'après la documentation :
- sans fenêtre correspondante, tout est permis ;
- dès qu'une fenêtre
allowcorrespond à une application, les synchronisations ne sont permises que pendant une fenêtreallowactive ; - une fenêtre
denyactive interdit les synchronisations, et l'emporte sur uneallowactive ; manualSync: truelaisse passer les synchronisations manuelles ; les fenêtres bloquent sinon les automatiques comme les manuelles ;- les sélecteurs
applications,namespacesetclustersacceptent des jokers et sont combinés par un OU, saufandOperator: true; - sans
timeZone, l'horaire est en UTC.
L'état est visible dans argocd app get (ligne SyncWindow, qui affiche Sync Allowed, Sync Denied ou Manual Allowed, ce dernier quand seule une synchronisation manuelle passerait) et dans argocd proj windows list production. Une fenêtre bloque aussi l'auto-réparation : une dérive pendant un gel reste en place jusqu'à la réouverture.
Sous le capot
Pour chaque cluster ciblé par au moins une application, le contrôleur d'applications maintient un cache : il liste puis surveille (watch) chaque type de ressource, sur tout le cluster ou dans les seuls namespaces déclarés. C'est ce cache qui rend les comparaisons rapides, et c'est lui qui consomme la mémoire du contrôleur : elle croît avec le nombre de clusters et d'objets. C'est aussi pourquoi un seul droit de liste manquant met tout le cluster en erreur : le cache ne peut pas se construire partiellement.
Au-delà de quelques dizaines de clusters, on répartit les clusters entre plusieurs répliques du contrôleur d'applications (le sharding) : chaque réplique ne gère que ses clusters, selon un algorithme de répartition (legacy par défaut ; round-robin et consistent-hashing, tous deux expérimentaux), ou un numéro de réplique imposé par la clé shard du secret de cluster.
Pièges courants
dial tcp 127.0.0.1:... connection refused. L'URL du kubeconfig n'est pas celle que voit le hub. Utilisez --cluster-endpoint kube-public, ou un secret déclaratif avec la bonne URL.
Un certificat d'administration dans Argo CD. argocd cluster add sur un contexte à certificat client recopie ce certificat. Vérifiez le contenu de config du secret de cluster.
Un compte administrateur oublié dans le cluster cible. Après argocd cluster rm, vérifiez kube-system/argocd-manager et le ClusterRole argocd-manager-role.
cannot list resource ... in the namespace. Le compte d'Argo CD doit pouvoir lister tous les types de ressources des namespaces gérés, pas seulement ceux de l'application.
Cluster has no applications and is not being monitored. Normal tant qu'aucune application ne cible le cluster.
Une application déplacée sans ses hooks, et des orphelins sur l'ancien cluster. Synchronisation complète manuelle après le déplacement, puis suppression explicite des anciennes ressources.
Une synchronisation urgente bloquée par une fenêtre. C'est son rôle. Prévoyez manualSync: true sur les fenêtres où une intervention humaine doit rester possible, ou retirez la fenêtre par un commit tracé.
Sécurité
Le hub est la cible la plus précieuse de votre infrastructure : il détient un identifiant d'écriture sur chaque cluster qu'il pilote. Quiconque compromet Argo CD (son serveur, son repo-server, un administrateur) atteint tous les clusters. D'où :
- le moindre privilège par cluster : un compte limité aux namespaces gérés,
clusterResources: "false"quand c'est possible, jamais le certificat d'administration ; - des identifiants révocables : un jeton de compte de service se révoque en supprimant son secret ; un certificat client ne se révoque pas ;
- des identifiants à durée courte quand le fournisseur le permet :
execProviderConfigouawsAuthConfigobtiennent un jeton à la demande au lieu d'en stocker un ; - l'API des clusters cibles filtrée : n'autoriser que l'adresse sortante du hub ;
- désactiver
in-cluster(cluster.inClusterEnabled: "false"dansargocd-cm) quand le hub ne doit héberger aucune application : on ne déploie alors plus par erreur dans le cluster d'Argo CD lui-même.
En production
Chez Lyneko, un seul cluster Kapsule héberge Argo CD et les applications : toutes les Applications ciblent in-cluster. Ce choix est raisonnable à cette taille, et la leçon montre ce qu'il faudrait le jour où un client exige son propre cluster : un secret de cluster déclaratif, un compte limité à ses namespaces, préparé dans son cluster, et un identifiant distribué par le gestionnaire de secrets plutôt que par Git.
Un hub ou un Argo CD par cluster ? Le hub donne une vue unique et une seule instance à mettre à jour, au prix d'un point central qui détient tous les accès et doit joindre toutes les API. Un Argo CD par cluster isole les incidents et les identifiants, au prix de N instances. Beaucoup d'organisations combinent : un hub par grande zone de confiance (un par client, ou un pour la production et un pour le reste).
Le contrôleur grossit avec les clusters. Surveillez sa mémoire ; le sharding devient nécessaire bien avant des centaines de clusters si ceux-ci sont chargés.
Exercices
1. Pourquoi le secret de cluster créé par argocd cluster add sur un cluster kind ne contenait-il aucun jeton ?
Solution
Le contexte kind s'authentifie par certificat client. NewCluster recopie le certificat et la clé du contexte et n'ajoute le jeton de argocd-manager que si l'un d'eux est absent. Argo CD utilise donc l'identité d'administration du kubeconfig, et le compte argocd-manager créé n'est jamais utilisé.
2. Écrivez le secret de cluster et les droits nécessaires pour qu'Argo CD gère deux namespaces, signalements-production et annuaire-production, et puisse aussi créer des ClusterRole.
Solution
Dans le secret de cluster : namespaces: signalements-production,annuaire-production et clusterResources: "true". Dans le cluster cible : les RoleBinding admin et le Role de lecture dans chacun des deux namespaces, plus un ClusterRole permettant get, list, watch, create, update, patch, delete sur clusterroles (et clusterrolebindings si besoin), lié par un ClusterRoleBinding. Attention : Kubernetes empêche l'escalade de privilèges, et refuse la création d'un ClusterRole qui contiendrait des droits que le compte n'a pas lui-même (« attempting to grant RBAC permissions not currently held »). Pour gérer des rôles quelconques, il faudrait les verbes escalate (sur les rôles) et bind (sur les liaisons), qui équivalent à se donner tous les droits du cluster. Ne les accordez qu'en connaissance de cause.
3. Une fenêtre allow du lundi au jeudi de 9 h à 17 h existe sur le projet. Un développeur fusionne une correction le vendredi à 10 h. Que se passe-t-il, avec et sans manualSync: true ?
Solution
Aucune fenêtre allow n'est active le vendredi : la synchronisation automatique n'a pas lieu, l'application reste OutOfSync jusqu'au lundi 9 h. Avec manualSync: true, une personne autorisée peut lancer argocd app sync ; sans, même la synchronisation manuelle est refusée, et il faut modifier la fenêtre (par un commit sur l'AppProject) pour déployer.
4. Vous déplacez signalements-recette, qui a une base PostgreSQL et un hook de migration, vers un nouveau cluster, dans le même commit qu'une nouvelle version de l'image. Les hooks seront-ils exécutés ?
Solution
Oui : la révision du dépôt de déploiement change (nouvelle version de l'image), donc la synchronisation automatique est une synchronisation complète, hooks compris. Le piège ne concerne que le déplacement à révision inchangée. Mais attention à l'ordre : la base du nouveau cluster est vide, et les données de l'ancien cluster ne suivent pas. Déplacer une application avec état est un projet de migration de données, pas un changement de destination.
5. Après le déménagement, comment supprimez-vous proprement l'ancienne production du hub ?
Solution
Plus aucune Application ne gère ces ressources : kubectl --context kind-gitops delete namespace signalements-production, après avoir vérifié que le trafic va bien au nouveau cluster. Si l'on veut que ce soit tracé dans Git, on peut aussi le faire par une Application temporaire, mais un namespace orphelin se supprime en général à la main, par une procédure revue.
Récapitulatif
- Argo CD connaît un cluster par un secret étiqueté
argocd.argoproj.io/secret-type: cluster: URL, identifiants, namespaces gérés, étiquettes. argocd cluster addcrée un compte administrateur dans le cluster cible, et peut recopier votre certificat d'administration ;argocd cluster rmpeut laisser le compte derrière lui. Préférez l'enregistrement déclaratif avec un compte limité.- Un compte limité à des namespaces a besoin de lister tous les types de ressources de ces namespaces.
- Le générateur de clusters, en matrice avec le générateur Git, place chaque environnement sur son cluster.
- Changer la destination d'une application laisse des orphelins sur l'ancien cluster, et, à révision inchangée, ne déclenche qu'une synchronisation partielle sans hooks.
- Les fenêtres de synchronisation d'un AppProject bloquent les synchronisations hors créneau ;
denyl'emporte surallow.
Pour aller plus loin
- Argo CD, Declarative Setup, Clusters : toutes les clés du secret de cluster, et les authentifications par fournisseur.
- argocd-agent : le modèle inverse, où chaque cluster se connecte au hub.
- Leçon suivante : Les secrets.
Sources
- Argo CD, Declarative Setup (Clusters)
- Argo CD, argocd cluster add
- argoproj/argo-cd v3.5.3, cmd/util/cluster.go (NewCluster) et cmd/argocd/commands/cluster.go
- argoproj/argo-cd v3.5.3, controller/appcontroller.go (autoSync, synchronisation partielle)
- Argo CD, Cluster Generator
- Argo CD, Sync Windows
- Argo CD, High Availability (sharding du contrôleur)
- argoproj-labs/argocd-agent
- Kubernetes, Using RBAC Authorization (rôles par défaut admin, edit, view)