Il s'est passé ce que nous (et pas seulement nous) avons longtemps attendu : , notre outil Open Source pour la construction d'applications et leur déploiement dans Kubernetes prend désormais en charge la mise en œuvre de modifications grâce à des patches en 3-way-merge ! En plus de cela, il est maintenant possible d'adopter des ressources K8s existantes dans des releases Helm sans avoir à recréer ces ressources.

Pour faire court, on met WERF_THREE_WAY_MERGE=enabled — on obtient un déploiement « comme dans kubectl apply», compatible avec les installations existantes sur Helm 2 et même un peu plus.
Mais commençons par la théorie : qu'est-ce que les patches en 3-way-merge, comment les gens en sont-ils arrivés à cette approche pour leur génération et pourquoi sont-ils importants dans les processus CI/CD avec une infrastructure basée sur Kubernetes ? Ensuite, nous verrons ce qu'est réellement le 3-way-merge dans werf, quels modes sont utilisés par défaut et comment les gérer.
Qu'est-ce qu'un patch en 3-way-merge ?
Alors, commençons par la tâche de déploiement des ressources décrites dans des manifestes YAML dans Kubernetes.
Pour travailler avec les ressources, l'API Kubernetes propose les opérations principales suivantes : create, patch, replace et delete. Il est prévu que celles-ci permettent de construire un déploiement continu des ressources dans le cluster. Comment ?
Commandes impératives kubectl
La première approche pour gérer des objets dans Kubernetes consiste à utiliser des commandes kubectl impératives pour créer, modifier et supprimer ces objets. En d'autres termes :
- la commande
kubectl runpeut lancer un Deployment ou un Job :kubectl run --generator=deployment/apps.v1 NOM_DU_DEPLOIEMENT --image=IMAGE - la commande
kubectl scale— changer le nombre de réplicas :kubectl scale --replicas=3 deployment/mysql - etc.
Cette approche peut sembler pratique au premier abord. Cependant, il y a des problèmes :
- Il est difficile de l'automatiser.
- Comment de refléter la configuration dans Git ? Comment faire une revue des modifications qui se produisent dans le cluster ?
- Comment assurer la reproductibilité de la configuration lors du redémarrage ?
- …
Il est clair que cette approche s'accorde mal avec le stockage du code de l'application et de l'infrastructure comme code (IaC ; ou même comme une variante plus moderne, en gagnant en popularité dans l'écosystème Kubernetes). C'est pourquoi ces commandes n'ont pas été davantage développées dans kubectl.
Opérations create, get, replace et delete
Avec la création initiale, c'est simple : on envoie le manifeste à l'opération au kube api et la ressource est créée. La représentation YAML du manifeste peut être stockée dans Git, et pour la création, on utilise la commande create kubectl create -f manifest.yaml pour la suppression,.
Avec c'est aussi simple : on passe le même manifest.yaml de Git à la commande kubectl delete -f manifest.yaml Opération.
Opération remplacer permet de remplacer complètement la configuration d'une ressource par une nouvelle, sans recréer la ressource. Cela signifie qu'avant de modifier la ressource, il est logique de demander la version actuelle par l'opération obtenir, de la modifier et de mettre à jour avec l'opération remplacer. Dans kube apiserver, il y a une intégration de et, si après l'opération obtenir l'objet a changé, alors l'opération remplacer échouera.
Pour stocker la configuration dans Git et mettre à jour en utilisant replace, il faut effectuer l'opération obtenir, fusionner la configuration de Git avec celle que nous avons reçue, et exécuter remplacer. Par défaut, kubectl permet seulement d'utiliser la commande kubectl replace -f manifest.yaml, où de Git à la commande — un manifeste déjà entièrement préparé (dans notre cas — fusionné) qui doit être installé. En fin de compte, l'utilisateur doit réaliser la fusion des manifestes, ce qui n'est pas trivial...
Il convient également de noter que bien que de Git à la commande soit stocké dans Git, nous ne pouvons pas savoir à l'avance s'il faut créer l'objet ou le mettre à jour — cela doit être fait par le logiciel utilisateur.
Total : pouvons-nous construire un déploiement continu uniquement avec create, replace et delete, en garantissant le stockage de la configuration de l'infrastructure dans Git avec le code et un CI/CD convivial ?
En principe, oui... Pour cela il faudra implémenter l'opération de fusion des manifestes et une certaine enveloppe qui:
- vérifie la présence de l'objet dans le cluster,
- effectue la création initiale de la ressource,
- la met à jour ou la supprime.
Lors de la mise à jour, il faut prendre en compte que la ressource a pu changer depuis la dernière obtenir et traiter automatiquement le cas de verrouillage optimiste — en faisant des tentatives de mise à jour à nouveau.
Mais pourquoi réinventer la roue quand kube-apiserver propose une autre méthode de mise à jour des ressources : l'opération patch, qui soulage l'utilisateur de certaines des problématiques décrites ?
Patch
Nous voilà arrivés aux patchs.
Les patchs sont le moyen principal d'appliquer des modifications aux objets existants dans Kubernetes. L'opération patch fonctionne de telle manière que :
- l'utilisateur du kube-apiserver doit envoyer un patch au format JSON et indiquer l'objet,
- et l'apiserver s'occupera de l'état actuel de l'objet et le mettra dans l'état requis.
Le verrouillage optimiste n'est pas nécessaire ici. Cette opération est plus déclarative par rapport à replace, bien que cela puisse d'abord paraître contraire.
Ainsi :
- avec l'opération
createnous créons un objet à partir du manifeste de Git, - à l'aide de
supprimer— nous le supprimons, si l'objet n'est plus nécessaire, - à l'aide de
patch— nous modifions l'objet pour le mettre dans l'état décrit dans Git.
Cependant, pour ce faire, il est nécessaire de créer un correctif approprié!
Comment fonctionnent les correctifs dans Helm 2 : 2-way-merge
Lors de la première installation d'une version, Helm effectue une opération create pour les ressources du chart.
Lors de la mise à jour d'une version, Helm pour chaque ressource :
- calcule le correctif entre la version de la ressource de l'ancien chart et la version actuelle du chart,
- applique ce correctif.
Nous appellerons ce correctif le correctif 2-way-merge, car sa création implique 2 manifestes :
- le manifeste de la ressource de l'ancienne version,
- le manifeste de la ressource de la version actuelle.
Lors de la suppression, l'opération supprimer dans kube apiserver est appelée pour les ressources qui ont été déclarées dans l'ancienne version, mais ne sont pas déclarées dans la version actuelle.
L'approche avec le correctif 2-way-merge a un problème : elle entraîne une désynchronisation entre l'état réel de la ressource dans le cluster et le manifeste dans Git..
Illustration du problème avec un exemple
- Dans Git, le chart contient un manifeste où le champ
imagedu Deployment a la valeurubuntu:18.04. - L'utilisateur a changé la valeur de ce champ en
kubectl editubuntu:19.04Lors de la réinstallation du chart Helm,. - aucun correctif n'est généré , car le champdans la version précédente de la version et dans le chart actuel sont identiques.
imageAprès la réinstallation, - il reste
image, bien que le chart indiqueLors de la réinstallation du chart Helm,Nous avons obtenu une désynchronisation et perdu la déclarativité.ubuntu:18.04.
Qu'est-ce qu'une ressource synchronisée ?
En général,
il est impossible d'obtenir une correspondance complète entre le manifeste de la ressource dans le cluster en fonctionnement et le manifeste de Git. Car dans le manifeste réel, il peut y avoir des annotations/labels de service, des conteneurs supplémentaires et d'autres données ajoutées et supprimées dynamiquement par certains contrôleurs. Ces données, nous ne pouvons pas et ne voulons pas les garder dans Git. Cependant, nous voulons que lors du déploiement, les champs que nous avons explicitement spécifiés dans Git prennent des valeurs correspondantes.
On obtient donc cette règle générale d'une ressource synchronisée: lors du déploiement d'une ressource, il est possible de modifier ou de supprimer uniquement les champs qui sont explicitement indiqués dans le manifeste de Git (ou qui ont été indiqués dans la version précédente, mais maintenant supprimés).
3-way-merge patch
L'idée principale : génère un correctif entre la dernière version appliquée du manifeste de Git et la version cible du manifeste de Git, en tenant compte de la version actuelle du manifeste dans le cluster en fonctionnement. Le correctif final doit respecter la règle de la ressource synchronisée :
- Les nouveaux champs ajoutés à la version cible sont intégrés via un patch;
- Les champs précédemment existants dans la dernière version appliquée et qui n'existent pas dans la cible sont remis à zéro via un patch;
- Les champs de la version actuelle de l'objet, qui diffèrent de la version cible du manifeste, sont mis à jour via un patch.
C'est exactement selon ce principe que les patches sont générés. kubectl apply:
- La dernière version appliquée du manifeste est conservée dans l'annotation de l'objet lui-même,
- la cible est extraite du fichier YAML spécifié,
- la version actuelle provient du cluster en fonctionnement.
Maintenant que nous avons compris la théorie, il est temps de parler de ce que nous avons fait dans werf.
Application des changements dans werf
Auparavant, werf, tout comme Helm 2, utilisait des patches de fusion à deux voies.
Patch de réparation
Pour passer à un nouveau type de patches — fusion à trois voies — la première étape a été d'introduire ce que l'on appelle des patches de réparation..
Lors du déploiement, un patch de fusion à deux voies standard est utilisé, mais werf génère en plus un patch qui synchronise l'état réel de la ressource avec ce qui est écrit dans Git (ce patch est créé en utilisant la même règle de ressource synchronisée décrite ci-dessus).
En cas de désynchronisation, à la fin du déploiement, l'utilisateur reçoit un AVERTISSEMENT avec un message correspondant et un patch qui doit être appliqué pour ramener la ressource à un état synchronisé. Ce patch est également noté dans une annotation spéciale werf.io/repair-patch. Il est prévu que l'utilisateur lui-même applique ce patch : werf ne l'appliquera pas en principe.
La génération de patches de réparation est une mesure temporaire qui permet de tester la création de patches selon le principe de la fusion à trois voies, mais sans appliquer automatiquement ces patches. Actuellement, ce mode de fonctionnement est activé par défaut.
Patch de fusion à trois voies uniquement pour les nouvelles versions
À partir du 1er décembre 2019, les versions beta et alpha de werf commencent par défaut à utiliser des patches de fusion à trois voies complets pour l'application des changements uniquement pour les nouvelles versions Helm déployées via werf. Les versions existantes continueront à utiliser l'approche avec des patches de fusion à deux voies et des patches de réparation.
Ce mode de fonctionnement peut être activé explicitement par la configuration WERF_THREE_WAY_MERGE_MODE=onlyNewReleases dès maintenant.
Remarque: la fonctionnalité a été introduite dans werf au fil de plusieurs versions : dans le canal alpha, elle est devenue prête avec la version , et dans le canal beta — avec .
patch de fusion à trois voies pour toutes les versions.
À partir du 15 décembre 2019, les versions beta et alpha de werf commenceront à utiliser par défaut des patchs de fusion 3 voies pour appliquer des modifications à toutes les versions.
Ce mode de fonctionnement peut être activé explicitement par la configuration WERF_THREE_WAY_MERGE_MODE=enabled dès maintenant.
Comment gérer l'autoscaling des ressources ?
Dans Kubernetes, il existe deux types d'autoscaling : HPA (horizontal) et VPA (vertical).
L'horizontal choisit automatiquement le nombre de réplicas, le vertical détermine la quantité de ressources. Tant le nombre de réplicas que les exigences relatives aux ressources sont spécifiés dans le manifeste des ressources (voir spec.replicas ou spec.containers[].resources.limits.cpu, spec.containers[].resources.limits.memory et ).
Problème : si un utilisateur configure une ressource dans le chart de manière à ce que des valeurs spécifiques pour les ressources ou les réplicas soient indiquées et que des autoscalers soient activés pour cette ressource, alors à chaque déploiement, werf remettra ces valeurs à celles inscrites dans le manifeste du chart.
Il existe deux solutions au problème. Tout d'abord, il est préférable de ne pas indiquer explicitement les valeurs autoscalables dans le manifeste du chart. Si toutefois cette option n'est pas envisageable pour une raison ou une autre (par exemple, parce qu'il est pratique de définir les limites de ressources initiales et le nombre de réplicas dans le chart), werf propose les annotations suivantes :
-
werf.io/set-replicas-only-on-creation=true -
werf.io/set-resources-only-on-creation=true
Avec cette annotation, werf ne remettra pas à zéro les valeurs correspondantes à chaque déploiement, mais les définira uniquement lors de la création initiale de la ressource.
Pour plus de détails, voir la documentation du projet sur et .
Interdire l'utilisation des patchs de fusion 3 voies
Pour l'instant, l'utilisateur peut interdire l'utilisation des nouveaux patchs dans werf à l'aide de la variable d'environnement WERF_THREE_WAY_MERGE_MODE=disabled. Cependant, à partir du 1er mars 2020, cette interdiction cessera d'être effective et seuls des patchs de fusion 3 voies seront utilisables.
Adoption des ressources dans werf
La maîtrise de la méthode d'application des modifications par patchs de fusion 3 voies nous a permis de mettre en œuvre immédiatement une fonctionnalité telle que l'adoption des ressources existantes dans le cluster dans un Helm release.
Helm 2 a un problème : on ne peut pas ajouter dans les manifestes du chart une ressource qui existe déjà dans le cluster sans recréer cette ressource de zéro (voir , ). Nous avons appris à werf à accepter les ressources existantes dans le release. Pour cela, vous devez ajouter à la version actuelle de la ressource du cluster en fonctionnement une annotation (par exemple, à l'aide de kubectl edit):
"werf.io/allow-adoption-by-release": RELEASE_NAMEVous devez maintenant décrire la ressource dans le chart et lors du prochain déploiement avec werf de la version portant le nom correspondant, la ressource existante sera acceptée dans cette version et restera sous sa gestion. De plus, au cours de l'adoption de la ressource dans la version, werf mettra l'état actuel de la ressource à partir du cluster fonctionnel dans l'état décrit dans le chart, en utilisant les mêmes patches de fusion à 3 voies et la règle de ressource synchronisée.
Remarque: configuration WERF_THREE_WAY_MERGE_MODE n'affecte pas l'adoption des ressources — en cas d'adoption, un patch de fusion à 3 voies est toujours utilisé.
Les détails sont dans .
Conclusions et plans futurs
J'espère qu'après cet article, il est devenu plus clair ce que sont les patches de fusion à 3 voies et pourquoi ils ont été introduits. D'un point de vue pratique, le développement de werf a vu leur mise en œuvre comme une étape supplémentaire vers l'amélioration du déploiement similaire à Helm. On peut désormais oublier les problèmes de synchronisation de la configuration qui se produisaient souvent avec Helm 2. En parallèle, une nouvelle fonctionnalité utile d'adoption des ressources Kubernetes déjà récupérées dans les releases Helm a été ajoutée.
Dans le déploiement similaire à Helm, certaines problématiques et difficultés demeurent, telles que l'utilisation de modèles Go, et nous continuerons à les résoudre.
Des informations sur les méthodes de mise à jour des ressources et d'adoption sont également disponibles sur .
Helm 3
Il convient de noter séparément une migration Werf, de son côté, s'est déjà débarrassé de l'utilisation de Tiller, a opté pour la fusion à 3 voies et ajouté
beaucoup d'autres fonctionnalités Cependant, la transition de werf vers le code de Helm 3 est inévitable et se produira dans un avenir proche. Cela devrait se faire lors des versions werf 1.1 ou 1.2 (actuellement, la version principale de werf est 1.0 ; pour plus de détails sur le système de versionnage de werf, voir
). D'ici là, Helm 3 devrait avoir stabilisé. Cycle de notes sur les nouvelles fonctionnalités dans werf :
P.S.
Lisez aussi dans notre blog :
- Utilisation de werf pour le déploiement de charts Helm complexes
- «»;
- «»;
- «».
- «»;
- «»;
- «».
Source : habr.com
