Une option ajoutée dans spec.syncPolicy.syncOptions, un sync relancé, et le comportement ne change pas. Ou il change pour toutes les ressources sauf une, celle précisément qui posait problème.
Les sync options ne forment pas une liste plate d’interrupteurs. Elles se posent à deux étages, elles ne modifient pas toutes la même chose — certaines changent la commande kubectl réellement exécutée, d’autres seulement le périmètre — et pour une partie d’entre elles, l’étage du bas gagne.
Deux étages, et une règle de précédence explicite
Le premier étage est l’Application : spec.syncPolicy.syncOptions, une liste de chaînes qui vaut par défaut pour toutes les ressources de cette Application.
# Application level: the default for every resource of this Application
apiVersion: argoproj.io/v1alpha1
kind: Application
spec:
syncPolicy:
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
Le second étage est la ressource elle-même, via l’annotation argocd.argoproj.io/sync-options. Plusieurs options s’y concatènent dans une seule valeur, séparées par des virgules ; les espaces autour sont supprimés.
# Resource level: applies to this object only. Comma-separated values are
# supported in a single annotation.
kind: PersistentVolumeClaim
metadata:
annotations:
argocd.argoproj.io/sync-options: Prune=false,Delete=false
La documentation est explicite sur Prune et sur Delete : une option posée sur la ressource écrase toujours la politique définie au niveau de l’Application. C’est la première chose à vérifier quand un réglage global semble ignoré sur un objet précis — il ne l’est pas, il est surchargé.
Toutes les options ne sont pas disponibles aux deux étages. CreateNamespace et FailOnSharedResource se posent sur l’Application. Force se pose en annotation de ressource. Replace, ServerSideApply, PruneLast, Prune, Delete acceptent les deux.
Les options qui changent la commande exécutée
Par défaut, Argo CD applique les manifestes avec kubectl apply côté client, qui s’appuie sur l’annotation kubectl.kubernetes.io/last-applied-configuration pour retenir l’état précédent. Trois options remplacent cette commande, et c’est ce qui les rend risquées.
Replace=true fait basculer sur kubectl replace ou kubectl create. Le motif documenté est la taille : un manifeste trop gros ne tient pas dans l’annotation last-applied-configuration, plafonnée à 262 144 octets. La documentation accompagne l’option d’un avertissement en propre — des ressources peuvent devoir être recréées, avec l’interruption que ça implique.
Force=true, en pratique combiné à Replace=true, passe par kubectl delete puis create. C’est le cas des Jobs qu’on veut rejouer à chaque sync.
# Delete + recreate on every sync. Documented as destructive.
metadata:
annotations:
argocd.argoproj.io/sync-options: Force=true,Replace=true
ServerSideApply=true fait exécuter kubectl apply --server-side --force-conflicts. Elle répond à trois besoins distincts : dépasser la limite d’annotation sans les effets de bord de Replace, patcher une ressource que l’Application ne gère pas entièrement, et s’appuyer sur la propriété de champs (managedFields) plutôt que sur le dernier état appliqué. Deux détails comptent. D’abord, l’option se désactive ressource par ressource avec ServerSideApply=false en annotation, même quand elle est active sur l’Application. Ensuite, Replace=true prend le pas sur ServerSideApply=true — les deux ensemble ne donnent pas ce qu’on croit.
Le patch partiel mérite son exemple, parce qu’il demande une seconde option. Fournir à Argo CD un manifeste qui ne contient qu’un champ n’est pas valide au regard du schéma Kubernetes :
# Partial manifest: valid input for server-side apply, invalid for the
# Deployment schema. Validation must be disabled alongside.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
spec:
replicas: 3
spec:
syncPolicy:
syncOptions:
- ServerSideApply=true
- Validate=false
Validate=false seule sert un autre cas : les types Kubernetes qui utilisent RawExtension et que kubectl refuse de valider.
Les options qui décident de ce qui disparaît
Deux familles cohabitent ici, et les confondre est l’erreur classique. Prune gouverne la suppression pendant un sync, quand une ressource n’est plus décrite dans Git. Delete gouverne la suppression quand l’Application est supprimée. Une PVC qu’on veut conserver dans les deux cas a besoin des deux annotations.
Prune=false empêche la suppression. Conséquence directe et souvent mal vécue : l’Application reste OutOfSync tant qu’Argo CD estime que la ressource devrait être élaguée. Le panneau de statut affiche que l’élagage a été sauté et pourquoi.
Prune=confirm et Delete=confirm, depuis Argo CD 2.14, ajoutent une confirmation humaine pour les ressources critiques — un Namespace, typiquement. La confirmation passe par l’interface, la CLI, ou l’apposition manuelle de l’annotation argocd.argoproj.io/deletion-approved avec un horodatage ISO — sur l’Application, pas sur la ressource, ce qui est contre-intuitif juste après un paragraphe où l’option se pose en annotation de ressource. Tant que la confirmation n’arrive pas, l’opération reste en Syncing.
Le plancher de version compte ici plus qu’ailleurs : sur une version antérieure, l’annotation se pose sans effet et sans message. Une option de sûreté qui échoue en silence est pire que pas d’option du tout.
PrunePropagationPolicy choisit entre background, foreground et orphan ; le défaut est foreground. PruneLast=true repousse l’élagage en vague implicite finale, après que les autres ressources sont déployées et saines.
Les options qui réduisent le périmètre
ApplyOutOfSyncOnly=true change le comportement par défaut, qui est d’appliquer tous les objets de l’Application à chaque sync. Sur une Application qui en contient des milliers, ça pèse sur l’API server et ça gonfle le champ status.operationState.syncResult.resources, avec un impact sur la base derrière l’API. La différence avec un sync sélectif est documentée et elle est décisive : avec ApplyOutOfSyncOnly, les hooks continuent de s’exécuter et le sync est enregistré dans l’historique. Un sync sélectif, lui, ne joue pas les hooks et n’est pas historisé, donc il interdit le rollback.
FailOnSharedResource=true fait échouer le sync quand une ressource de l’Application est déjà appliquée dans le cluster par une autre Application. Sans elle, Argo CD applique sans rien dire — c’est le mode de défaillance qui apparaît quand les Applications sont générées, et c’est le même terrain que le piège du tenant dans les ApplicationSets.
L’option qui relie le diff au sync
RespectIgnoreDifferences=true est celle qu’on cherche sans le savoir. Par défaut, spec.ignoreDifferences ne sert qu’à calculer le diff, c’est-à-dire à décider si l’Application est synchronisée. Au moment du sync, l’état désiré est appliqué tel quel, et le patch est calculé par un merge à trois voies entre l’état live, l’état désiré et l’annotation last-applied-configuration — le merge que personne ne regarde vraiment.
# Without RespectIgnoreDifferences, spec.replicas is ignored when computing
# the diff, then written back anyway on the next sync.
spec:
ignoreDifferences:
- group: 'apps'
kind: 'Deployment'
jsonPointers:
- /spec/replicas
syncPolicy:
syncOptions:
- RespectIgnoreDifferences=true
Avec l’option, l’état désiré est pré-patché avant d’être appliqué. Une réserve documentée : elle n’est effective que si la ressource existe déjà dans le cluster. À la création, aucun état live n’existe, et l’état désiré part tel quel.
Ce qui ressemble à une sync option sans en être une
Trois mécanismes voisins se règlent ailleurs, et les chercher dans syncOptions fait perdre du temps.
Les phases et les vagues passent par argocd.argoproj.io/hook et argocd.argoproj.io/sync-wave, des annotations distinctes. À noter au passage : pendant l’élagage, l’ordre des vagues est inversé, les ressources des vagues hautes sont supprimées en premier.
argocd.argoproj.io/compare-options: IgnoreExtraneous est une autre annotation, qui exclut une ressource du statut de synchronisation de l’application. Elle n’affecte que le statut de sync — si la santé de la ressource se dégrade, l’application se dégrade quand même.
Enfin spec.syncPolicy.automated et spec.syncPolicy.retry sont des champs frères de syncOptions, pas des options. Ils décident quand un sync part, pas comment il s’exécute — la mécanique de la boucle de réconciliation est détaillée dans le déploiement déclaratif avec ArgoCD.
La bonne question avant d’ajouter une option n’est donc pas « laquelle corrige le symptôme », mais « à quel étage se pose-t-elle, et qu’est-ce qu’elle change dans la commande ». Une option destructive posée au niveau de l’Application s’applique à des ressources auxquelles personne ne pensait en l’écrivant.
Pour la suite : le choix entre ArgoCD et Flux revient sur ce que ce modèle d’options implique côté opérateur, et le parcours la chaîne CI/CD replace le sujet dans le pipeline complet.