Upgrade d'une plateforme K8S: Etape 01 - logique et préparatifs

Introduction

Maintenir son infrastructure et ses applications à jours est devenu une nécessité ( et aurait dû toujours l'être..Avant même la mode de la cybersécurité...).

Mais il n’est pas toujours simple de suivre le rythme. Obnubilé par le besoin de nouveautés et la course aux fonctionnalités (pas toujours utiles), les releases se suivent, entremêlées parfois de bugs et de failles de sécurités.

Kubernetes n’échappe pas à la règle avec environ trois versions principales par an et des sorties tous les quatre mois.

Avant de se lancer sur l’opération d’upgrade en tant que telle, il est important d’avoir une bonne connaissance du cycle de vie de Kubernetes et de mettre en place sa stratégie de mise à jour.

Cycle de vie de Kubernetes

La numérotation de K8S démarre par le numéro de la version principale. (Jusqu’à présent la 1).

Puis vient le numéro de release, par exemple 1.30, soit la trentième itération de la version 1.

À chaque changement de release correspondent des modifications fonctionnelles importantes. Certains objets peuvent passer à un état de stabilité différent dans l’API.

De nouveaux objets peuvent être introduits...et d’autres être retirés. De même les exigences de compatibilités avec d’autres composants peuvent être revues.

Il faut toujours être attentifs aux releases notes et se maintenir un minimum informé des évolutions de Kubernetes.

Chaque objet K8S respecte toujours un cycle en trois phases:

  • Alpha (Préfixe : vXalphaY (ex.: v1alpha1)): branche de l’API hébergeant des objets proposant des fonctionnalités expérimentales, pouvant encore subir des modifications importantes.
  • Beta (Préfixe : vXbetaY (ex.: v1beta1)): branche de l’API hébergeant des objets disponibles par défaut et comportant des fonctionnalités jugées suffisamment stables pour un usage plus large, mais toujours susceptibles de subir quelques ajustements.
  • Stable (Préfixe vX (ex.: v1, v2, etc.)): branche de l’API hébergeant des objets associés à des fonctionnalités considérées comme matures et prêtes pour une utilisation en production à grande échelle.

Un dernier état, non déclaré dans l’API, mais intéressant de connaitre est l’état Deprecated….Généralement un objet apparaissant ainsi dans une release note signifie qu’il finira par être retiré dans une version future…Mais qu’il est temps dès à présent d’apprendre à s’en passer si celui-ci est utilisé dans sa plateforme.

Il n’y a pas de règles quand au passage d’un objet d’une phase à une autre.

Ce n’est pas parce qu’une nouvelle release apparait que tous les objets de la branche Alpha passe en Beta.

Cela dépend du niveau d’avancement du développement propre à chaque objet, certains pouvant rester en alpha ou en beta durant de nombreuses versions.

Le support de chaque release est limité à 14 mois. Les 12 premiers mois sont considérés comme "actif" et peuvent acceuillir des améliorations et des évolutions fonctionnelles. Les deux derniers mois sont uniquement pris en compte pour des corrections de bugs et/ou des failles de sécurité. La plateforme évolue donc rapidement et on peut vite sortir du cadre.

Une troisième numérotation peut apparaitre par exemple,1.30.5. Ce dernier numéro est associé à un ensemble de patchs incluant le plus souvent des corrections de bugs ou de sécurité. Passé 14 mois après la sortie d'une release, il n’est plus possible de voir ce dernier numéro évoluer pour cette derniere.

Exemple du cycle de vie des release K8S

Cliquez sur l'image pour l'agrandir.

Le schéma ci-dessus est donné à titre d'illustration, les versions ont été choisies arbitrairement et l'exemple part du premier mois de l'année uniquement pour simplifier l'explication.

Tout l’historique de version de Kubernetes est consultable ici.

L’approche de développement de K8S est particulière, elle fait appel à une rotation des équipes par release.

Pour chaque version, une nouvelle équipe est formée. Cette dernière est responsable de superviser tout le cycle de développement et de planification jusqu’à la publication de l’application (incluant les patchs).

Lorsqu’on passe à une nouvelle version par exemple de la 1.30.9 à la 1.31 , c’est une nouvelle équipe qui prend le relais. L’objectif est de distribuer les responsabilités et d’éviter la centralisation des connaissances au sein d’un petit groupe d’individu.

A chaque release son équipe

Cliquez sur l'image pour l'agrandir.

Par contre cela a pour conséquence des changements parfois importants entre les releases et maintenir à jour Kubernetes a longtemps été considéré comme pénible et complexe.

C’est notamment le cas dans l’usage d’une installation Vanilla comme j’ai pu le décrire dans mon cookbook sur le sujet.

Une installation Vanilla est une installation de Kubernetes directement réalisée à partir des composants de bases.

Contrairement à l’usage de distribution Kubernetes, comme OpenShift, avec une plateforme Vanilla, c’est à vous de gérer chaque dépendance entre les composants que vous avez déployé. Il n’y a pas de mises à jour intégrées ou prépackagées.

N’hésitez pas à faire un tour dans cette partie du site pour plus d’informations sur le sujet.

Néanmoins, on va voir à travers cette série d’articles qu’il est tout à fait possible de mettre à jour son cluster Kubernetes Vanilla. Il suffit de respecter certaines bonnes pratiques et d’être méthodique dans son approche.

Recommandations

Voici quelques recommandations issues de mon expérience:

  • N’attendez jamais trop longtemps entre vos updates et privilégiez des montées de version régulières pour ne pas vous laisser distancer dans les releases. Un rythme d’upgrade tous les 6 mois est une bonne moyenne. (Sauf urgence et passage d’un correctif suite à des bugs ou des problèmes de sécurités).
  • Essayer de toujours rester dans la dernière version patchée de la précédente release et de conserver une version d’écart de 1 entre votre version et la dernière disponible en ligne. Par exemple, si vous êtes en 1.29.9, attendez la sortie de la 1.31 pour upgrader vers la 1.30.5. De cette manière vous avez toujours une version considérée comme très stable (car déjà patchée à de nombreuses reprises) sans avoir un écart trop important avec la dernière release. Vous évitez aussi les problématiques possibles autour d’une version fraichement sortie.
  • Écrivez votre procédure d’upgrade et documentez la un maximum, en la faisant évoluer au fur à mesure.
  • Privilégier toujours un essai d’upgrade sur un cluster de tests, se rapprochant le plus possible de votre cluster de production. Par exemple, si votre cluster de production dispose de plusieurs control plane, montez un cluster test avec plusieurs control plane.
  • Fixez votre planning d’upgrade : préréserver à l’année vos dates auxquelles vous procéderez aux mises à jour.
  • Assurez-vous d’avoir toutes les sauvegardes nécessaires à commencer par la la base etcd…et de savoir les restaurer. Controler vos solutions de sauvegardes, si vous en avez déployée, comme Velero par exemple.
  • Upgradez toujours de 1 en 1 (ou de patch en patch sur une meme release), par exemple de 1.28 à 1.29. N’essayez jamais de sauter des versions. Si vous avez pris du retard, passer par les versions intermédiaires. Par exemple si vous êtes en 1.28 et que vous voulez basculer en 1.31, passez d’abord par la 1.29.x, puis la 1.30.x ou x est toujours la dernière version patchée de la release. Dans l’idéal, démarrez même par une montée en1.28.x.

Ce qu’il est important de retenir, c’est qu’il est préférable d’upgrader souvent que de laisser sa version prendre le large. Vous limitez ainsi les risques d’écart fonctionnel trop important et vous vous rassurez sur la procédure en augmentant votre maitrise. Rien ne vous empêche d’industrialiser une partie des actions et d’automatiser au fil de l’eau.

Procédures

En termes de procédure, voici une proposition. Elle est bien sûr à détailler de votre côté et à adapter en fonction de votre configuration.

  1. Choix de la version cible: je regarde les releases disponibles et je fixe ma cible. Dans le cadre de cette série d’articles, je vais partir d’un cluster en 1.29.4 pour aller vers un cluster en 1.30.5. (La dernière version disponible au moment de la rédaction étant la 1.31.1.)
  2. Contrôle des matrices de compatibilité: maintenant que j’ai ma version cible, je vérifie la compatibilité de cette dernière avec les composants additionnels de mon cluster. Je m’appuie sur ce que j’ai pu décrire dans mon cookbook. Il est possible que ces composants ne soient pas les mêmes pour vous, mais la logique demeure
    1. Contrôle de mon OS: dans mon cas PhotonOS 5.0
    2. Contrôle du runtime de conteneur: dans mon cas containerd. La matrice de compatibilité est disponible ici. Dans mon exemple, je vais devoir upgrader vers la version 1.7.22 (je pars d’une 1.7.16)
    3. Contrôle du CPI (Cloud Provider Interface): dans mon cas celui de vSphere. Je passe par le repo de l’éditeur.Le CPI est un composant spécifique à mon usage de Kubernetes avec vSphere, il n’est pas forcement utilisé de votre côté. Cela dépend de votre plateforme d’exécution de votre cluster. Si vous souhaitez davantage d’information sur le CPI vSphere, n’hésitez pas à consulter cet article.
    4. Contrôle des CSI (Container Storage Interface): dans mon cas celui de vSphere, NFS et SMB. Les CSI permettent de proposer des solutions de stockage persistent à un cluster K8S. N’hésitez à parcourir cet article et celui-ci pour plus d’informations sur le sujet. Il existe plusieurs drivers CSI. À vous de voir le(s)quel(s) vous utiliser et comment le(s) mettre à jour. Dans mon cas, je vais devoir faire un upgrade de celui de vSphere pour basculer de la version 3.3.0 à 3.3.1. La matrice de compatibilité est disponible ici.
    5. Contrôle de mon provider réseau: dans mon cas Cilium. Là aussi, il faut passer par la lecture de la matrice disponible ici. Dans mon cas, je vais upgrader vers la version 1.16.2 (je pars d’une version 1.15.4)
    6. Contrôle de mes applications tierces internes au cluster: dans mon cas Traefik, CertManager… Pour ces derniers, la matrice est moins problématique, mais il est toujours bon de regarder les releases notes et de s’assurer de la compatibilité de ces derniers avec la version cible de l’update Kubernetes. Il n’est pas obligé d’upgrader ces composants, mais en synchronisant vos montées de version avec vos mises à jour Kubernetes, vous vous assurez de rester cohérents sur votre plateforme. Il n’est pas nécessaire de prendre les dernières releases. À vous de fixer vos règles sur ce point.
    7. Contrôle de toutes les applications externes aux clusters, mais qui peuvent être impactées par la montée de version. Tous applicatifs, type CICD, GUI, supervision ou autres. Beaucoup d’outils tiers peuvent appeler l’API Kubernetes, il est donc logique que lors d’une montée de version de K8S, il faille s’assurer de la compatibilité de ces derniers avec la nouvelle release de l’API.

NB : concernant le CPI et le CSI vSphere, j’ai également une dépendance à mon infrastructure vMware. Par exemple, je dois m’assurer que la version de mes ESXi et de mon vCenter soient compatibles. C’est un élément à considérer également même si vous n’utilisez pas vSphere de votre côté. Si vous exploitez un driver CSI propre à un fournisseur de baie, comme Netapp par exemple, a vous de vous assurez de la compatibilité du triptyque version de Kubernetes/Driver CSI/Firmware de votre baie.

Plan d'actions

Arrivé à cette étape, j’ai normalement ma feuille de route concernant les mises à jour de tous les composants tiers que j’aurais à réaliser avant mon upgrade de Kubernetes ou juste après.

Voici une roadmap simplifiée des opérations à réaliser. Celle-ci reprend les points évoqués précédemment et s’appuie sur une logique d’usage d’un cluster Kubernetes déployé avec kubeadm, comme j’ai pu le décrire dans mon cookbook.

  1. Contrôle des prérequis: est-ce que mes sauvegardes sont OK, est-ce que mes procédures de restauration sont connues et maitrisées ?
  2. Contrôle de l’état du cluster et de mes pods avant la mise à jour. Est-ce que mes apps sont OK, est-ce que je n’ai pas d’erreur en amont de l’opération d’upgrade.
  3. Mise à jour du CPI si nécessaire (dans mon cas vSphere).
  4. Mise à jour des CSI si nécessaire (dans mon cas vSphere, NFS, SMB).
  5. Mise à jour du provider réseau (dans mon cas Cilium).
  6. Mise en maintenance du premier control plane.
  7. Mise à jour du runtime de container sur le premier control plane.
  8. Mise à jour de l’OS sur le premier control plane.
  9. Reboot du premier control plane.
  10. Changement de repo pour les sources vanilla correspondant à ma version cible sur le premier control plane.
  11. Upgrade des composants K8S sur le premier control plane
    1. Mise à jour de kubeadm sur le premier control plane.
    2. Lancement de la commande kubeadm avec les options d'update.
    3. Mise à jour de l’agent kubelet sur le premier control plane.
    4. Mise à jour de kubectl sur le premier control plane.
  12. Sortie du mode maintenance sur le premier control plane
  13. Passage au contrôle plane suivant (même stratégie que pour le premier)

Je traite ainsi tous mes control plane, un par un pour ensuite poursuivre sur les worker. De la même manière je procède worker par worker avec toujours:

  1. Mise en maintenance du node
  2. Mise à jour du runtime de container
  3. Mise à jour de l’OS
  4. Reboot
  5. Changement de repo pour les sources vanilla correspondant à ma version cible
  6. Mise à jour de kubeadm
  7. Lancement de la mise à jour de kubernetes
  8. Mise à jour de l’agent kubelet (mais pas de kubectl, absent des worker)
  9. Sortie du mode maintenance
  10. Passage au worker suivant

En fin d’opération, je devrais avoir tous mes nodes à la version cible.

Je peux poursuivre par la mise à jour des applications additionnelles:

  1. Traefik
  2. Certmanager

(la liste est susceptible d’évoluer avec le temps).

Résumé de la stratégie d'upgrade

Cliquez sur l'image pour l'agrandir.

Conclusion

Cet article détaillant la logique et les préparatifs à établir dans le cas d’un upgrade K8S est fortement lié à mon cookbook sur l’installation d’un cluster Kubernetes vanilla avec kubeadm.

Il est fort possible que vos clusters diffèrent du mien, et que les composants varient entre mon exemple et votre infrastructure. Cet article est donc à adapter à vos besoins, mais la logique globale peut s’appliquer à d’autres configurations. Cela reste vrai uniquement dans un usage de cluster type vanilla. Si vous utilisez un cluster managé chez un cloud provider ou une distribution K8S type OpenShift, il faut vous rapprocher de votre fournisseur/éditeur pour suivre leur guideline.

On peut procéder aux actions en live, mais je vous conseille fortement de réserver des plages d’interventions fonctions des possibilités de votre production. Les temps d’indisponibilité dépendent du déploiement de vos applications et de vos architectures applicatives. Si vous êtes dans des environnements type micro services avec une forte redondance de vos pods, alors les opérations devraient se faire en toute transparence. À l’inverse si vous avez des conteneurs unitaires, ils seront amenés à redémarrer et donc à provoquer des coupures. Dans tous les cas, il est toujours intéressant de se laisser une marge de manœuvre dans vos opérations au cas ou un incident se produirait.

Mon but est ici de mettre en avant la nécessité d’être rigoureux pour upgrader son cluster. Il est important de bien comprendre l’ordre des actions et de préparer son intervention. La plus grosse difficulté est de s’assurer de la matrice de compatibilité entre les différents composants. S’il faut obtenir une concordance avec la version cible, il faut aussi maintenir un fonctionnement avec la version actuelle.

Si vous upgradez un élément en prérequis de votre montée de version de k8S, mais que vous cassez le fonctionnement en place, ce n’est pas l’idéal. Encore une fois, si vous gardez un rythme de mise à jour régulier, ce type de problème ne devrait pas arriver.

Il ne faut pas avoir peur de se lancer dans un usage d’un cluster vanilla. Au fur à mesure des versions, les opérations se simplifient et il suffit souvent d’une bonne préparation pour que l’opération se fasse sans soucis. C’est le revers de la médaille de l’aspect modulaire de Kubernetes.

Il y’a les briques de bases et tous le restes…et quand on upgrade le cœur de la plateforme, il faut s’assurer que toute la suite continue de fonctionner. D’ailleurs après la théorie, on va pouvoir passer à la pratique, mais cela se fait dans cet article.