Introduction
Kubernetes Server-Side Apply (SSA) est une fonctionnalité qui déplace la logique de fusion des modifications de configuration du client (kubectl) vers le serveur d'API Kubernetes. Dans l'approche traditionnelle côté client, kubectl calcule localement un correctif de fusion à trois voies et l'envoie au serveur d'API. Avec l'application côté serveur, le serveur d'API lui-même stocke la configuration appliquée dans un champ dédié (metadata.managedFields) et résout les conflits en fonction de la propriété des champs. Ce changement améliore la cohérence, permet une meilleure détection des conflits et permet à plusieurs contrôleurs de mettre à jour en toute sécurité différents champs du même objet.
Cet article explique l'architecture de Kubernetes Server-Side Apply avec des exemples pratiques. Nous nous concentrons sur la sécurité opérationnelle : observer avant de modifier, limiter le rayon d'impact, utiliser des espaces réservés au lieu de secrets, vérifier les résultats et documenter les chemins de récupération. Vous apprendrez à activer et utiliser l'application côté serveur, à inspecter la gestion des champs, à gérer les conflits et à opérationnaliser SSA dans votre cluster.
Inventaire des versions et de l'environnement
Avant d'utiliser l'application côté serveur, vérifiez que votre cluster Kubernetes et vos outils clients la prennent en charge. L'application côté serveur est devenue bêta dans Kubernetes 1.16 et est généralement disponible et activée par défaut à partir de Kubernetes 1.18. Pour une utilisation en production, exécutez Kubernetes 1.18 ou une version ultérieure. Assurez-vous que votre version de kubectl est au moins 1.18 pour utiliser le drapeau --server-side.
Vérifiez la version de votre cluster :
kubectl version --short
La sortie attendue inclut la version du serveur, par exemple Server Version: v1.25.4.
Vérifiez la porte de fonctionnalité du serveur d'API pour l'application côté serveur (elle devrait être activée par défaut) :
kubectl get --raw /metrics | grep server_side_apply
Vous devriez voir une métrique comme apiserver_request_total{...,resource="apply"...} indiquant que le point de terminaison d'application est actif.
Kubernetes Server-Side Apply utilise le verbe apply sur le serveur d'API. Le point de terminaison pour l'application avec correctif est PATCH /api/v1/namespaces/{namespace}/configmaps/{name}?fieldManager=my-manager&force=false avec le type de contenu application/apply-patch+yaml. Le serveur d'API stocke la configuration appliquée dans le champ metadata.managedFields de l'objet. Chaque entrée dans managedFields suit le nom du gestionnaire, l'opération (Apply ou Update), la version de l'API utilisée et les champs possédés par ce gestionnaire.
Pour voir comment l'application côté serveur suit la propriété, créez un déploiement en utilisant l'application côté serveur et inspectez managedFields. Tout d'abord, créez un espace de noms :
kubectl create namespace ssa-demo
Ensuite, créez un déploiement avec l'application côté serveur :
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: ssa-demo
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80
Appliquez avec l'option côté serveur :
kubectl apply --server-side -f deployment.yaml --field-manager=deployment-manager
Sortie attendue :
deployment.apps/nginx-deployment serverside-applied
Maintenant, inspectez les managedFields. Utilisez la commande suivante pour afficher la représentation YAML avec managedFields :
kubectl get deployment nginx-deployment -n ssa-demo -o yaml
Dans la sortie, sous metadata.managedFields, vous verrez une entrée avec manager: deployment-manager, operation: Apply et la structure fieldsV1 décrivant quels champs ce gestionnaire possède. Par exemple :
managedFields:
- apiVersion: apps/v1
fieldsType: FieldsV1
fieldsV1:
f:spec:
f:replicas: {}
f:selector: {}
f:template:
f:metadata:
f:labels:
f:app: {}
f:spec:
f:containers:
k:{"name":"nginx"}:
f:image: {}
f:name: {}
f:ports: {}
manager: deployment-manager
operation: Apply
time: "2023-10-01T12:00:00Z"
Cela montre que deployment-manager possède les champs qu'il a définis. Si un autre gestionnaire tente de modifier un champ possédé par deployment-manager sans forcer, le serveur d'API renverra une erreur de conflit.
Pour lister tous les gestionnaires et leurs opérations pour le déploiement, utilisez :
kubectl get deployment nginx-deployment -n ssa-demo -o json | jq '.metadata.managedFields[] | {manager: .manager, operation: .operation}'
Sortie attendue :
{
"manager": "deployment-manager",
"operation": "Apply"
}
Chemin de configuration sécurisé
L'application côté serveur introduit un mécanisme de résolution de conflits basé sur la propriété des champs. Lorsqu'un gestionnaire applique une modification à un champ déjà possédé par un autre gestionnaire, le serveur d'API rejette la modification avec une erreur de conflit à moins que le drapeau force ne soit défini. L'utilisation de force transfère la propriété du champ au nouveau gestionnaire, ce qui peut écraser des valeurs involontairement. Par conséquent, il est plus sûr d'éviter force en production et de résoudre plutôt les conflits explicitement.
Considérons un scénario où deux gestionnaires tentent de configurer le même déploiement. Le gestionnaire A (deployment-manager) possède spec.replicas. Le gestionnaire B (autoscaler) souhaite modifier spec.replicas de 2 à 3. Si le gestionnaire B applique sans forcer, le serveur d'API renvoie un conflit. Simulons cela.
Créez un deuxième fichier YAML qui tente de mettre à jour les réplicas :
# deployment-replicas-update.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: ssa-demo
spec:
replicas: 3
Appliquez en tant que gestionnaire de champ différent :
kubectl apply --server-side -f deployment-replicas-update.yaml --field-manager=autoscaler
La sortie attendue inclut une erreur de conflit similaire à :
error: Apply failed with 1 conflict: conflict with "deployment-manager": .spec.replicas
Cette erreur indique que autoscaler ne peut pas modifier spec.replicas car il est possédé par deployment-manager. Pour résoudre le conflit, vous avez quelques options :
- Utiliser force pour prendre possession (non recommandé en production à moins d'être certain) :
kubectl apply --server-side -f deployment-replicas-update.yaml --field-manager=autoscaler --force
Sortie attendue : deployment.apps/nginx-deployment serverside-applied. Après cela, autoscaler possède spec.replicas et la valeur est définie sur 3.
- Coordonner la propriété des champs : Assurez-vous qu'un seul gestionnaire est responsable d'un champ donné. Par exemple, l'autoscaler ne devrait pas essayer de définir directement les réplicas ; il devrait plutôt utiliser un contrôleur personnalisé ou une annotation.
- Utiliser l'application côté client pour cette mise à jour, ce qui n'applique pas la propriété des champs et utilise l'annotation last-applied-configuration pour la fusion. Cependant, cela peut entraîner des écrasements silencieux.
Pour vérifier la propriété actuelle après le conflit, utilisez :
kubectl get deployment nginx-deployment -n ssa-demo -o json | jq '.metadata.managedFields[] | select(.manager=="autoscaler") | .fieldsV1'
Si le conflit n'a pas été forcé, la sortie sera vide. S'il a été forcé, vous verrez f:spec avec f:replicas: {}.
Meilleures pratiques pour une configuration sécurisée avec SSA :
- Spécifiez toujours
--field-managerpour identifier le gestionnaire de manière unique. - Utilisez des gestionnaires de champ distincts pour différents composants afin d'éviter les conflits involontaires.
- Surveillez les conflits en vérifiant les journaux d'audit du serveur d'API ou les métriques.
- Testez les modifications dans un cluster non productif d'abord.
- Utilisez
--dry-run=serverpour prévisualiser les modifications sans les appliquer :
kubectl apply --server-side -f deployment-replicas-update.yaml --field-manager=autoscaler --dry-run=server
Sortie attendue : deployment.apps/nginx-deployment serverside-applied (dry run) et le conflit sera signalé le cas échéant.
Vérification et diagnostics
Après avoir appliqué des modifications avec l'application côté serveur, vérifiez que la ressource reflète l'état prévu et que managedFields sont mis à jour correctement. Utilisez des commandes en lecture seule pour observer l'état actuel avant et après les modifications.
Vérifiez l'état du déploiement :
kubectl rollout status deployment/nginx-deployment -n ssa-demo
Sortie attendue :
deployment "nginx-deployment" successfully rolled out
Inspectez les managedFields pour vous assurer que le bon gestionnaire possède les champs pertinents :
kubectl get deployment nginx-deployment -n ssa-demo -o json | jq '.metadata.managedFields'
Exemple de sortie montrant deux gestionnaires :
[
{
"apiVersion": "apps/v1",
"fieldsType": "FieldsV1",
"fieldsV1": {
"f:spec": {
"f:replicas": {},
"f:selector": {},
"f:template": {}
}
},
"manager": "deployment-manager",
"operation": "Apply",
"time": "2023-10-01T12:00:00Z"
},
{
"apiVersion": "apps/v1",
"fieldsType": "FieldsV1",
"fieldsV1": {
"f:spec": {
"f:replicas": {}
}
},
"manager": "autoscaler",
"operation": "Apply",
"time": "2023-10-01T12:05:00Z"
}
]
Notez qu'après l'application forcée, deployment-manager possède toujours spec.selector et spec.template, tandis que autoscaler possède spec.replicas. Si le deployment-manager applique ultérieurement une modification aux réplicas, il obtiendra un conflit à moins d'utiliser force ou de renoncer à la propriété.
Pour afficher la configuration appliquée par un gestionnaire spécifique, utilisez la commande kubectl apply view-last-applied (uniquement pour l'application côté serveur) :
kubectl apply view-last-applied deployment nginx-deployment -n ssa-demo
La sortie attendue montre le YAML qui a été appliqué en dernier par le gestionnaire de champ kubectl (ou vous pouvez spécifier --field-manager). Par exemple :
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: ssa-demo
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80
Diagnostiquez les problèmes en vérifiant les événements :
kubectl describe deployment nginx-deployment -n ssa-demo
Recherchez les événements liés aux opérations d'application en cas de conflit. Le serveur d'API émet également des métriques pour les demandes d'application. Vérifiez la métrique apiserver_request_total filtrée par verb="PATCH" et code pour voir les demandes d'application et les compteurs de succès/échec :
kubectl get --raw /metrics | grep 'apiserver_request_total{.*verb="PATCH".*code="409"'
Un compteur non nul pour 409 (Conflit) indique que des conflits d'application se sont produits. Cela peut aider à la surveillance.
Modes de défaillance et récupération
Comprendre les modes de défaillance courants avec l'application côté serveur aide à une récupération rapide. Voici des scénarios et comment récupérer.
Défaillance : Erreur de conflit lors de l'application
Symptôme : kubectl apply --server-side renvoie une erreur de conflit similaire à :
error: Apply failed with 1 conflict: conflict with "deployment-manager": .spec.replicas
Cause : Un autre gestionnaire possède le champ que vous essayez de modifier.
Récupération :
- Identifiez le propriétaire actuel en utilisant
kubectl get <ressource> -o yamlet en inspectantmanagedFields. - Si la modification est nécessaire, coordonnez-vous avec le propriétaire pour qu'il renonce à la propriété. Vous pouvez forcer la propriété avec
--force, mais cela peut écraser la valeur du champ. - Alternativement, mettez à jour le champ en utilisant une mise à jour régulière (PUT) ou un correctif (strategic merge patch) car la propriété peut ne pas être appliquée pour ces opérations (bien que ce ne soit pas recommandé car cela contourne la sémantique SSA).
Défaillance : Perte de propriété de champ après force
Symptôme : Après avoir forcé une application, les mises à jour du gestionnaire précédent sur le champ sont ignorées ou provoquent des conflits.
Cause : L'opération de force a transféré la propriété au nouveau gestionnaire et le gestionnaire précédent n'a plus de droits sur ce champ.
Récupération :
- Le gestionnaire précédent peut également utiliser force pour regagner la propriété, mais cela peut provoquer une lutte acharnée. Corrigez la cause première en attribuant une propriété de champ claire.
- Pour renoncer explicitement à la propriété d'un champ, appliquez un correctif avec
$setElementOrder? En fait, il n'y a pas de renonciation directe ; vous pouvez appliquer une valeur vide pour le champ et laisser l'autre gestionnaire prendre le relais. Ou vous pouvez supprimer l'entrée du gestionnaire de managedFields en utilisant un appel API brut (non recommandé en production car cela peut entraîner un comportement inattendu).
Défaillance : Nom de gestionnaire de champ incorrect
Symptôme : Plusieurs utilisateurs ou contrôleurs utilisent le même nom de gestionnaire de champ (par exemple, kubectl par défaut), provoquant des conflits inattendus.
Cause : Le --field-manager par défaut est kubectl pour kubectl apply --server-side. Si plusieurs scripts d'automatisation utilisent kubectl sans spécifier de gestionnaire, ils partagent tous la propriété.
Récupération :
- Spécifiez toujours un
--field-managerunique pour chaque acteur logique. Pour CI/CD, utilisez le nom du pipeline ou le nom du compte de service. - Pour corriger la propriété existante, réappliquez avec le bon gestionnaire et forcez si nécessaire, mais assurez la cohérence à l'avenir.
Défaillance : Ressource non prise en charge pour l'application côté serveur
Symptôme : L'application à une ressource personnalisée ou intégrée renvoie une erreur comme :
error: Apply failed with 1 conflict: conflicts with "..." using "...": .metadata.managedFields
Cause : Certaines ressources peuvent ne pas prendre entièrement en charge SSA ou avoir des problèmes de validation.
Récupération :
- Vérifiez la documentation de l'API de la ressource pour la prise en charge de SSA. La plupart des ressources intégrées prennent en charge SSA.
- Si la ressource est une CustomResourceDefinition, assurez-vous que la CRD a
spec.preserveUnknownFields: false(ou utilise un schéma structurel) car SSA nécessite des types structurés.
Liste de contrôle des opérations
Utilisez cette liste de contrôle pour opérationnaliser l'application côté serveur dans votre environnement.
- Vérifiez les versions du cluster et du client : Assurez-vous que Kubernetes >=1.18 et kubectl >=1.18. Utilisez
kubectl version --short. - Choisissez les noms des gestionnaires de champ : Définissez une convention de dénomination pour les gestionnaires de champ. Par exemple :
platform-teampour les déploiements d'infrastructureautoscalerpour le contrôleur HorizontalPodAutoscalerci-cd-pipelinepour les déploiements d'applications
- Appliquez les ressources avec l'option côté serveur pour les nouvelles ressources :
kubectl apply --server-side -f manifest.yaml --field-manager=platform-team
- Inspectez managedFields après l'application : Exécutez
kubectl get <ressource> -o yamlet vérifiez le gestionnaire et l'opération. - Testez les scénarios de conflit dans un cluster de préproduction : Simulez deux gestionnaires modifiant le même champ pour comprendre les messages de conflit et la récupération.
- Mettez en place une surveillance des conflits d'application : Utilisez Prometheus pour alerter sur l'augmentation de
apiserver_request_total{verb="PATCH", code="409"}ou récupérez les journaux d'audit pour les événements de conflit. - Documentez la propriété des ressources critiques : Maintenez un tableau comme :
| Ressource | Champ | Gestionnaire propriétaire |
|---|---|---|
| Deployment nginx-deployment | spec.replicas | autoscaler |
| Deployment nginx-deployment | spec.template | platform-team |
| Service nginx-service | spec.selector | platform-team |
- Utilisez dry-run dans CI/CD : Avant d'appliquer en production, exécutez :
kubectl apply --server-side -f manifest.yaml --field-manager=ci-cd --dry-run=server
Si le dry run réussit sans conflit, procédez à l'application réelle.
- Assurez-vous que les secrets ne sont pas gérés uniquement via SSA : Utilisez une gestion externe des secrets (par exemple, SealedSecrets, External Secrets) et référencez les secrets dans les pods.
- Examinez régulièrement managedFields : Auditez périodiquement managedFields pour détecter les entrées obsolètes. Supprimez les entrées de gestionnaire inutilisées en appliquant un correctif vide avec l'ancien gestionnaire ? En fait, vous pouvez supprimer l'entrée du gestionnaire en appliquant avec le même gestionnaire un objet vide ? Il n'y a pas de commande directe ; vous pouvez utiliser l'API pour supprimer l'entrée managedFields. Pour simplifier, utilisez
kubectl apply --server-side --field-manager=<ancien-gestionnaire> -f <manifeste sans champs possédés>puis supprimez l'entrée du gestionnaire ? Pas simple. Une alternative est d'utiliser un outil commekubectl patchavec unapplication/merge-patch+jsonbrut pour supprimer l'entrée managedFields, mais c'est avancé. Il est souvent sûr de laisser les anciennes entrées car elles n'affectent pas le fonctionnement.
Conclusion
Kubernetes Server-Side Apply fournit un mécanisme robuste pour gérer la configuration en suivant la propriété au niveau des champs sur le serveur d'API. En comprenant son architecture, vous pouvez éviter les pièges courants tels que les conflits involontaires et la perte de propriété. Les meilleures pratiques opérationnelles incluent l'utilisation de gestionnaires de champ uniques, les tests en non-production, la surveillance des conflits et des procédures de récupération claires.
Commencez avec une ressource à faible risque comme une ConfigMap dans un espace de noms de test. Pratiquez l'application avec l'option côté serveur, inspectez managedFields et simulez des conflits. Ensuite, déployez progressivement l'application côté serveur vers des charges de travail plus critiques, en vous assurant que votre équipe est familière avec la résolution des conflits. Avec une gestion minutieuse, l'application côté serveur peut rendre vos flux de travail de configuration Kubernetes plus sûrs et plus prévisibles.