Accueil > Comment maintenir une plateforme Kubernetes en condition opérationnelle ?
Jean-François Le Lezec

Comment maintenir une plateforme Kubernetes en condition opérationnelle ?

Comment maintenir une plateforme Kubernetes en condition opérationnelle ?

Article corédigé par Jean-François le Lezec (Cellenza) et Sébastien Dillier (Squadra)

 

La maintenance d’une plateforme basée sur des conteneurs impose de changer quelques réflexes acquis au cours de cette dernière décennie. Les conteneurs sont arrivés avec des orchestrateurs qui gèrent leurs cycles ainsi qu’un nouveau découpage entre le code applicatif et les couches système.

Kubernetes est aujourd’hui la plateforme d’orchestration de référence. Pour atteindre cette maturité, les équipes du projet Kubernetes ont choisi de déléguer un certain nombre de problématiques à des outils tiers. Cela implique que Kubernetes s’appuie et vienne avec des composants tiers qu’il faudra faire évoluer en même temps que les releases importantes de Kubernetes pour accompagner les « breaking changes » et les APIs dépréciées.

 

Le bon maintien en condition opérationnelle d’une plateforme Kubernetes nécessite d’augmenter son niveau d’industrialisation afin de répondre aux enjeux suivants :

  • La résilience ;
  • La mise à jour de la plateforme ;
  • La supervision ;
  • La gestion des tâche récurrentes.

 

Livre blanc Run Cellenza

 

La résilience, un enjeu stratégique

L’un des enjeux, lorsque l’on met en place un service applicatif, est de le rendre résilient. En effet, l’orchestrateur prend des décisions en « autonomie » afin de s’assurer du bon fonctionnement de la plateforme. Par exemple, l’orchestrateur peut décider de déplacer un service applicatif afin de libérer de la place sur le nœud courant. Le service sera ainsi déplacé sur un autre nœud et pourra même changer d’identité.

Kubernetes offre une boite à outils étendue pour rendre nos applications résilientes et éviter une dégradation de nos SLAs.

 

La résilience se traduit par 3 principes majeurs :

  • Une forte tolérance aux pannes ;
  • Une résistance à l’augmentation de charge ;
  • Une résistance aux menaces de sécurité.

 

 

La résilience aux pannes

Kubernetes propose avec son orchestrateur une grande variété de paramétrages permettant d’atteindre nos objectifs de résilience, mais pour cela quelque règles doivent être respectées :

  1. La première règle est d’implémenter les sondes nécessaires qui permettent à Kubernetes de savoir si l’application est toujours fonctionnelle et si elle peut recevoir du trafic. En effet, si un conteneur met deux minutes à démarrer, toutes les requêtes qui lui seront adressées échoueront pendant ces deux minutes dans le cas où les sondes ne sont pas configurées. L’implémentation par défaut des sondes des différents sdk peut convenir dans un premier temps. Si vous avez une API avec une dépendance forte à une partie de l’infrastructure, il faudra les personnaliser afin de les prendre en compte. Dans le cas d’application lente au démarrage, l’utilisation d’une StartUp Probe permet de différer l’exécution des sondes de vie et rendra ainsi possible le démarrage de l’application sans compromettre la sonde de vie.
  2. La deuxième est d’avoir un nombre de réplicas suffisant, deux étant le minimum. Cela implique de privilégier des services stateless et d’externaliser le stockage des données dans des services tiers à la plateforme. L’utilisation d’objets statefull est possible mais la gestion du stockage doit être anticipée en fonction de leur résilience et performance.
  3. La troisième est de paramétrer ses applications de façon à ce qu’elles s’exécutent sur des nœuds différents. La mise en place de ces stratégies peut être faite via les “topology spread constraint” ou les “node affinity”. Par exemple, on pourra déployer plusieurs réplicas d’une même application sur des nœuds localisés dans différentes Availability zones Azure. Attention : l’utilisation des nodeselector/taint contraignant fortement l’orchestrateur, ce type de paramétrage doit être réservé pour isoler des workloads en fonction leurs besoins matériels (besoin GPU par exemple).
  4. Le quatrième point consiste à bien calibrer les ressources utilisables par nos applications. Cela permettra à Kubernetes de demander l’ajout d’un nœud afin d’y placer les nouvelles charges de travail.
  5. Spécifier la QoS et des priorités sur les pods permettra à l’orchestrateur de prioriser la création des ressources.
  6. Implémenter correctement le processus d’arrêt de vos conteneurs afin de fermer proprement vos ressources.
  7. L’utilisation contrôlée et raisonnée des Pod Distribution Budget permettra de s’assurer qu’un nombre suffisant de réplicas sera disponible pendant les opérations de maintenance. Bien que puissant, cet outil doit être utilisé en prenant en compte les besoins des applications et du paramétrage de la plateforme (le max surge dans le cas d’opération de rolling update sur les nœuds) et les contraintes appliquées à l’orchestrateur. Un mauvais paramétrage peut occasionner des blocages lors de mises à jour de la plateforme.
  8. La création / l’utilisation d’opérateurs permet de gérer des cas complexes comme des clusters de données.

 

 

Répondre à la charge

Pour répondre aux enjeux de charge, Kubernetes propose deux outils natifs :

  • Le cluster autoscaler: ce composant déclenche l’ajout/la suppression de nœuds sur votre cluster en fonction de seuils, notamment en fonction des demande CPU et mémoire.
  • L’Horizontal Pod Autoscaler (HPA) permet d’ajouter des réplicas en fonction des métriques telles quel le CPU ou la mémoire consommée.

L’activation de ces deux composants permet d’avoir une infrastructure adaptative en fonction de l’usage de la plateforme. Les demandes d’ajout de pods de la part du HPA vont permettre d’activer la partie autoscaler. Ainsi quand la charge va augmenter, le HPA demandera de nouveaux pods en forçant le cluster à créer de nouveaux nœuds pour placer ces pods.

 

D’autres outils issus de la communauté permettent également d’adresser d’autre use cases. Parmi ceux-ci citons :

  • KEDA (Kubernetes Event Driven Autoscaler) pilote le HPA en fonction de plusieurs sources de métriques et en fonction de plusieurs seuils. On peut par exemple augmenter le nombre de réplicas en fonction du nombre d’éléments dans une queue. KEDA prend en charge un grand nombre de sources, dont Azure Monitor et Prometheus.

 

exemple de l'outil KEDA qui pilote le HPA

 

    1. A l’état initial notre service a un seul réplica (ligne orange dans le graphique du bas) et notre infrastructure a trois nœuds (ligne jaune dans le graphique du bas). Dans notre scénario de test, nous allons injecter 60 utilisateurs simultanés progressivement et demander à KEDA d’ajouter un nouveau réplica dès que nous atteignons le seuil de 10 rps (requêtes par seconde) supplémentaires dans la limite de 10 réplicas.
    2. Au début de la phase de Scale out, KEDA demande de nouveau en suivant l’évolution des demandes. Pendant cette phase, les nouveau pods passent en attente : en effet, le cluster ne peut satisfaire les nouvelles demandes. Le mécanisme cluster autoscaler se met alors en place et de nouveaux nœuds sont créés. Dès qu’un nouveau pod est prêt, il est ajouté au service et prend en moyenne une charge de 10 requêtes par seconde.
    3. Phase de stabilisation: pendant cette phase, KEDA maintient un nombre de pods suffisant pour répondre à la charge.
    4. Phase de Scale down: KEDA maintient pendant un temps défini les réplicas ajoutés et les supprime s’il n’y pas de nouvelles requêtes. Par la suite, le cluster autoscaler  supprime les nœuds ajoutés.

 

 

  • VPA (Vertical Pod Autoscaler) permet de scaler verticalement un pod en y ajoutant des ressources. La VPA n’est pas compatible avec le HPA, hormis chez GCP avec le MPA (Multiple Pod Autoscaler).

 

 

Se protéger

Kubernetes ne propose pas de couche de sécurité permettant de parer à une attaque. Cependant, un certain nombre d’actions peuvent être mises en place pour atténuer les risques.

 

Segmenter les périmètres

L’une des premières choses à faire est de définir une politique d’accès, une politique de gestion des ressources et une politique des ségrégations au niveau du network. Pour cela, Kubernetes offre divers outils pour solutionner ces problématiques :

  • L’utilisation des RBAC au niveau des namespaces ;
  • La mise en place de quotas au niveau des namespaces pour limiter l’utilisation des ressources ;
  • La mise en place d’une priority class pour prioriser certains applicatifs. Par exemple, le contrôleur d’ingress est un composant essentiel de la plateforme : celui-ci ne doit pas être affecté par une fuite mémoire d’un autre composant ;
  • Isolation de la couche réseau via les Networks policies ;
  • Exposer les sondes et les métriques Prometheus sur un port différent afin de ne pas les exposer avec l’ingress.

 

Limiter les privilèges des containers

L’autre point est de veiller à limiter l’accès des conteneurs aux machines virtuelles sur lesquelles ils résident. Pour cela, on met en place des politiques de moindre accès :

  • En supprimant les shells des images ;
  • En interdisant l’accès aux ressource physiques de l’hôte ;
  • En interdisant l’utilisation des comptes à privilèges ;
  • En contrôlant les comptes de service utilisés par le conteneur.

 

Gouverner la plateforme avec les admissions :

Depuis la version 1.21 de Kubernetes, les Pod Security Policies sont dépréciées. La communauté a mis en place des projets basés sur des contrôleurs d’admission qui permettent un contrôle flexible des points cités au paragraphe précédent.

Ce contrôle s’effectue par deux biais : les contrôleurs d’admission en validation et en mutation. En validation pour contrôler, par exemple, la présence des sondes, et en mutation pour forcer l’application de tag ou bien le contexte de sécurité.

Le scope d’application de ces règles va du cluster au selector en passant par le type d’objet. En coordination avec les Role-Based Access Control (RBAC), nous avons un outil puissant pour sécuriser les déploiements et garder le contrôle des plateformes mutualisé.

Azure intègre Gatekeeper à Azure Policy afin d’avoir un niveau de gestion globale sur votre infrastructure. Azure Policy contient un grand nombre de politiques standard et il également possible de créer les vôtres. Cependant l’implémentation faite par Azure ne donne pas accès à certaines fonctionnalités de Gatekeeper. Kyverno, un autre projet qui se démarque, propose également un grand nombre de politiques disponibles.

 

 

K8S, une plateforme en évolution constante

 

Maintenance des conteneurs

La conteneur registry est l’autre élément primordial de nos architectures à base de conteneurs : c’est en effet dans celle-ci que nous retrouverons les images prêtes être déployées sur un cluster.

Pour rappel, une image doit être immutable : cela signifie que le code applicatif et ses dépendances sont définis statiquement dans un définition OCI et installés lors de la création de l’image. Cela permet de s’assurer qu’une image testée en environnement de qualité aura le même comportement en production, et d’avoir un démarrage rapide. Chaque dépendance sera décrite dans une instruction et donnera lieu à la création d’un layer.

Bien souvent, la base de nos images fait référence à des distributions Linux telles que Debian, Alpine, Ubuntu, etc. Dans les entités matures, ces images sont « hardenées » par des équipes systèmes et sécurité mais cela n’est pas le cas bien souvent.

 

Maintenance des images

Comme rappelé précédemment, nos images se basent souvent sur une distribution de Linux dont on ne gère pas le cycle de vie. Il faudra donc veiller à mettre à jour ces images afin d’appliquer les derniers correctifs de sécurité.

Azure Container Registry propose une solution élégante qui traque les modifications sur les images de base et reconstruit les images qui en dépendent.

 

Gestion de la container registry

L’utilisation des pipelines CI génère bien souvent un grand nombre d’images. Il est donc nécessaire de contrôler la volumétrie de notre registry. Pour cela, il faut mettre en place une politique de purge basée sur des tags ou sur des dates. Il faudra également veiller à ne pas supprimer les images utilisées en production.

 

Public / Privé

La construction d’une plateforme Kubernetes comporte généralement un grand nombre de COTS (Commercial Off The Shelf) publics tels que NGINX, cert-manager, Prometheus et bien d’autres. Ces différentes images sont disponibles sur des registries publics. Cependant, dans le cadre d’un DRP (Disaster Recovery Plan), il est préférable de limiter notre dépendance à des sources extérieures dans le cas de plateformes avec haut niveau de criticité. Les Cloud Solution Providers proposent des solutions géo-répliquées offrant un haut niveau de disponibilité.

 

Sécuriser nos images

Au-delà de Kubernetes, la sécurité des images de base utilisées pour vos déploiements est aussi un point à prendre en compte. La détections de vulnérabilités sur nos images pourra être faite au plus tôt dans nos pipelines de CI mais également au niveau de la container registry via Microsoft Defender for Cloud.

Sécuriser ses images sur K8S

 

Il faudra donc, pour ce type de cas, apporter une attention particulière à la configuration des déploiements afin de permettre à vos applications d’avoir un cycle de vie maitrisé au sein de l’environnement Kubernetes.

schéma de la configuration de déploiement Kubernetes

 

Le déploiement

 

Configuration :

Un autre mantra des Twelve Factor App demande à dissocier la configuration applicative de notre binaire. En effet, si l’on veut respecter le principe d’immutabilité, nous devons externaliser les paramètres de nos applications. Kubernetes propose deux objets pour ce faire : le configmap et les secrets. Ces mécanismes sont assez similaires et doivent être traités avec le même niveau de sensibilité.

Les configmap et les secrets peuvent être injectés en tant que volume ou alors directement dans les variables d’environnement de l’application.

La configuration de l’application est stockée avec ces manifestes de déploiement dans un contrôleur git.

Les variables sensibles peuvent alors être injectées lors du déploiement ou bien stockées directement dans git à condition que les valeurs soient chiffrées.

 

GitOps

L’indépendance des équipes collaborant sur une plateforme Kubernetes implique que chacune d’entre elles puisse avoir des cycles séparés. C’est à dire qu’une équipe métier ne doit pas dépendre de l’équipe technique pour déployer une application, et inversement l’équipe technique ne doit pas dépendre des équipes métiers pour opérer la plateforme.

 

Afin de garantir cette séparation, il faudra respecter la résilience, la ségrégation des accès ainsi que des processus automatisés pour déployer les applicatifs. À grande échelle, il est compliqué pour les équipes techniques opérationnelles de maintenir une cartographie applicative des différents services et versions déployés sur une plateforme. Il est également compliqué de coordonner les différentes équipes métiers pour redéployer une plateforme dans le cadre d’une migration AKS, d’un DRP (Disaster Recovery Plan – plan de reprise d’activité) ou la reconstruction d’un cluster (modification du plan d’adressage).

 

Pour cela, il est nécessaire de s’adosser à une usine logicielle performante. À ce jour, deux solutions se détachent pour déployer sur un cluster Kubernetes :

  • Le mode push consiste à pousser les manifestes sur nos clusters. Cette technique est bien adaptée pour de petits déploiements. Pour des déploiements industrialisés, elle atteint plusieurs limites :
    • Maintenance : il faudra maintenir un compte de service sur chaque cluster ;
    • Temps : en fonction du nombre d’agents de déploiement utilisables en parallèle, par exemple, une mise à jour des déploiements contenant des volumes peut prendre plusieurs minutes ;
    • Connaissance et coordination entre les équipes afin de déployer les différents applicatifs.
  • Le mode pull permet de résoudre une grande partie de ces problématiques. GitOps en mode pull permet d’automatiser les déploiements sur le cluster de différentes configurations git provenant de « repository » différents. De cette manière, chaque équipe décrit l’état souhaité de leur application et laisse l’agent GitOps opérer les déploiements pour eux. Prenons par exemple le scénario d’une migration AKS mettant en œuvre un pattern actif-passif. A la construction du cluster passif, celui-ci s’abonne à un premier repository contenant la configuration des repositories des équipes métiers (pattern app for apps). Ensuite, l’agent va cloner les différents repositories et déployer les états applicatifs décrits dans chacun de ces repositories. Avec cette méthode, les équipes métiers n’ont plus besoin d’accéder au cluster et les équipes opérationnelles n’ont plus besoin de connaître les procédures de déploiement de chaque applicatif.

Notons également que chaque équipe pourra choisir la façon de structurer son repository comme elle le souhaite. Certains opteront pour une gestion par branches, d’autres passeront par la création de folders et de tags. Chaque équipe pourra ainsi choisir sa façon de travailler. Dans tous les cas, la mise en place d’un processus pull request ou de merge request est nécessaire afin de tracer les actions, limiter les risques et de faciliter le rollback.

 

Exemple d’organisation multi tenantExemple d’organisation multi tenant

 

 

Pattern de déploiement

Au niveau applicatif, trois standards sont utilisés pour faire une mise à jour :

  • le rollout
  • le canary release
  • Le blue/green

 

Par défaut, les déploiement Kubernetes utilisent le mode rollout. Pendant la phase de rollout, les nouveaux pods sont ajoutés au déploiement et reçoivent le trafic dès qu’il passe en état « Ready ». Le rollout ne permet pas un déploiement progressif en contrôlant le taux d’erreur des nouveaux pods et le trafic réseau.

Le mode canary permet de déployer progressivement en envoyant une portion de trafic réseau sur les nouveaux pods et de contrôler leur taux d’erreur. Le mode canary release nécessite l’utilisation de composants additionnels tels qu’un controller NGINX par exemple ou la mise en place de proxy utilisant le traffic shifting. Le traffic shifting est dans la définition d’Open Service Mesh et est implémenté par défaut dans les services mesh. Certains outils tels que Flagger ou Argo Rollouts permettent d’automatiser cette partie-là : il suffira alors de définir les conditions de succès pour que le déploiement soit mis en ligne.

Enfin, le mode Blue/Green fait cohabiter les deux versions d’un applicatif : la version N qui est publiée et la nouvelle version N+1. Une fois la version N+1 testée, celle-ci sera activée aux dépens de la version N.

 

 

Le monitoring d’une plateforme K8S

La mise sous monitoring d’une plateforme Kubernetes sert plusieurs objectifs :

  • Comprendre l’usage ;
  • Détecter et prévenir les défaillances ;
  • Détecter et prévenir des comportements hostiles (malicieux).

 

Le monitoring mis à disposition doit aussi pouvoir répondre aux besoins des équipes d’infrastructures, des équipes métiers et sécurité.

En effet, les équipes infrastructures se concentreront sur la disponibilité globale des infrastructures alors que les équipes métiers se focaliseront sur la disponibilité et les KPIs (Key Performance Indicators) exposés par leurs applicatifs. Enfin, les équipes sécurité suivront de près les logs d’accès. Il est donc indispensable que la solution choisie puisse répondre aux besoins de ces trois populations.

 

Pour atteindre ces objectifs, nous aurons besoin de collecter les métriques d’infrastructure et applicatives, les journaux d’événements des infrastructures et des applications ainsi que les traces publiées par ces applications. Nous devrons également avoir à disposition une solution de dashboarding et d’alerting. Par ailleurs, une solution de Stream processing permettra de corréler différents événements en fonction des sources de données.

 

Bien que nous parlions de supervision de plateforme, on s’aperçoit vite que nous devrons superviser plus que la plateforme Kubernetes mais bien son écosystème complet, c’est-à-dire :

  • Les événements émis par Azure Health ou émis par la plateforme Kubernetes, par exemple les incidents sur un stockage Azure ;
  • Les métriques des composants PaaS utilisés par Kubernetes ou par les différents services de stockage, par exemple les métriques sur le nombre de ports ouverts sur un load balancer ;
  • Les métriques, les événements et les journaux fournis par Kubernetes, par exemple l’utilisation de l’API port-forward ;
  • Les métriques et les journaux fournis par les applications, par exemple les traces des http appels reçus, le taux d’erreur et la latence ;
  • Les traces fournies par les applications, par exemple les appels entre services ou à un stockage.

 

Pour arriver à ce but, quatre approches sont disponibles :

  • L’utilisation des capacités du Cloud provider, Azure Monitor, Azure Defender for Cloud, Azure Container insight
  • L’utilisation de projets open source: par exemple la stack Prometheus et Jaegger pour la partie tracing ;
  • L’utilisation de solution tierces comme Datadog, ELK, Splunk ;
  • Un mix des solutions précédentes: par exemple Prometheus pour la collecte de métriques liées à Kubernetes, Alertmanager pour l’envoi de notification, Grafana sur la partie dashboard. Les logs seront quant à eux envoyés à Azure Monitor ou Event Hubs via Fluentd/Fluent Bit.

 

La collecte de métriques applicatives se fera via l’exposition d’endpoint Prometheus qui est un standard sur cette plateforme et pris en compte par les différents acteurs.

 

 

schema de mise en oeuvre hybride du monitoring KubernetesSchémas de mise en œuvre hybride

 

 

Du fait de la diversité des implémentations de Kubernetes, chacun aura des exigences uniques en matière de monitoring.

L’approche à adopter doit être basée sur des facteurs tels que l’échelle, la topologie, les rôles et la location multi-cluster.

Le schéma ci-dessous présente une stratégie commune qui est une approche ascendante partant de l’infrastructure jusqu’aux applications.

schema approche ascendante du monitoring Kubernetes

 

Automatiser le récurrent

 

Doit-on backuper un cluster Kubernetes ?

À partir du moment où nous avons externalisé les données, que le code source des applications, des scripts d’infrastructures et les états souhaités sont sous Git, la question de la nécessité de backuper un cluster K8S devient pertinente.

 

La réponse est oui car votre applicatif hébergé Kubernetes n’a pas forcément le même niveau de criticité que votre chaîne CI/CD ou que votre repository Git. Dans le cadre d’un DRP, vous voulez être sûr de pouvoir restaurer votre application dans un laps de temps court et sans dépendance à un système externe dont vous ne maîtrisez pas toujours la disponibilité. Il faut noter également que certains déploiements modifient l’état d’un cluster : c’est le cas par exemple de la création d’un nouveau load balancer, ou de la modification d’un enregistrement de DNS alors que le backup restaurera l’état connu.

 

La gestion des certificats

Un autre point sensible est la gestion des certificats. En règle générale, nos applications sont sur le protocole https : elles ont donc besoin de gérer le cycle de vie de leurs certificats ainsi que les enregistrements DNS associés.

Les services mesh utilisent également les certificats pour s’authentifier. Il est donc important de gérer le cycle de vie des certificats. Des composants de la Cloud Native Computing Foundation permettent d’automatiser ces opérations en utilisant des composants qui vont gérer les enregistrements DNS au niveau d’une zone Azure et la rotation des certificats. Ces composant offrent également des métriques sur l’expiration des certificats, ce qui permet de vérifier le bon fonctionnement du mécanisme.

 

Mise à jour de COTS

La mise à jour de COTS sur une plateforme est un travail fondamental et ne doit pas être négligée. Les dépréciation d’AKS suivent le rythme des sorties des releases Kubernetes forçant les exploitants à migrer les clusters de versions mineures 2 à 3 fois par an.

Le changement de version de Kubernetes apporte ses lots de changements, d’APIs dépréciées. Il faudra mettre à jour en conséquence les COTS externes utilisés. La solution de monitoring peut également être fortement impactée en fonction des changements faits sur kube-state-metrics.

 

Aller plus loin sur Kubernetes

Comme nous l’avons vu, le maintien en conditions opérationnelles d’application sur Kubernetes impose de respecter un certain nombre de contraintes qui doivent être partagées par les équipes développement, de Run ainsi que  d’infrastructure.

 

Livre blanc Run Cellenza

 

Et pour tout savoir sur la façon dont le Build sert le Run, nous vous invitons à consulter les autres articles de cette série :

 

 

Nos autres articles
Commentaires
Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.

Restez au courant des dernières actualités !
Le meilleur de l’actualité sur le Cloud, le DevOps, l’IT directement dans votre boîte mail.