Hello les amis, Cela fait un petit moment que ça me démange de (re)parler de GKE Autopilot. J’ai encore fait mon détective Pikachu pour creuser le sujet et savoir si GKE Autopilot nous simplifie la vie et le porte-monnaie, ou si c’est encore un rebranding commercial de la Team Rocket !

Je vais balancer mes petites hypothèses, mes observations et un peu d’expérience de terrain sur ce service avec ma mauvaise foi légendaire !

Mais c’est quoi GKE Autopilot au juste ? Non, ce n’est pas Mario Kart 8 avec la direction assistée pour éviter de tomber.

Toad de l’univers Mario Kart, fusionné avec la vue autopilote de Tesla

On est encore partis pour un article deep tech, j’en ai peur !

Je vous mets des petits rappels pour les gens pas trop familiers de Kubernetes pour ne pas être trop perdus.

Plus d’infos sur Kubernetes

Kubernetes (souvent appelé K8s) est une plateforme qui gère automatiquement vos applications contenues dans des conteneurs.

  1. Le Node (Le Serveur) Le Node est la machine physique ou virtuelle qui constitue la puissance de calcul du cluster. On distingue le Control Plane (le cerveau qui dirige) et les Worker Nodes (les ouvriers qui exécutent les tâches).

  2. Le Pod (L’Unité de base) Le Pod est le plus petit objet de Kubernetes. Il sert d’enveloppe à un ou plusieurs conteneurs (comme Docker). C’est lui qui possède une adresse IP unique et qui permet aux conteneurs de partager des ressources réseau et de stockage.

  3. L’Autoscaling (L’Élasticité) L’Autoscaling permet au système de s’adapter dynamiquement à la charge. Il existe deux types principaux :

HPA (Horizontal Pod Autoscaler) : Ajoute ou supprime des Pods en fonction de l’usage CPU/RAM.

Cluster Autoscaler : Ajoute ou supprime des Nodes (serveurs) si les Pods n’ont plus de place pour s’exécuter.

GKE Autopilot, Kubernetes, mais sans la contrainte de gestion des nœuds

Quel problème résout GKE Autopilot par rapport à un GKE standard ?

GKE Standard nécessite des node pools avec des tailles de machine fixes et un scaling de nœuds rigide.

Ce que cela implique concrètement, c’est que les tailles des nœuds sont définies à l’avance dans les node pools et que l’unité de scheduling des nœuds est fixée à l’avance dans le node pool.

Cela implique, d’un point de vue opérationnel, que si le node pool utilise de petits nœuds, on augmente le nombre de nœuds plus rapidement que si l’on utilise de plus gros nœuds. On peut moduler plusieurs node pools et mettre de l’autoscaling de nœuds, mais quoi qu’il arrive, on paie pour les nœuds provisionnés, qu’on les utilise ou pas.

On paie aussi les pods techniques propres au fonctionnement du cluster, qui consomment des ressources, et également l’espace en trop réservé pour les pods qui ont besoin d’être reschedulés.

Si les nœuds sont surprovisionnés ou avec des tailles fixes et jamais réduites, on paie pour des ressources allouées et pour un truc peu élastique.

Cela implique de réfléchir à un capacity planning de la consommation prévisionnelle des ressources, et de calculer grossièrement le nombre de pods que l’on veut mettre par nœud. On peut aussi avoir le problème un peu frustrant d’avoir les ressources suffisantes sur les nœuds, mais d’avoir dépassé le quota maximum de pods schedulables par nœud, ce qui est donc une pure perte financière.

Bien sûr, il existe des outils pour optimiser les coûts des node pools en fonction des charges de travail en place, c’est déjà un bon début, mais cela nécessite quand même de s’occuper de la taille des nœuds, et franchement, c’est un travail dont on se passerait bien. Et si vous avez des pods un peu hétérogènes en consommation de ressources, vous pouvez avoir des problèmes de nœuds non schedulables même si, unitairement, il reste des ressources sur certains nœuds.

J’ai fait des setups avec des nœuds rigides, 3 réplicas par pod, un cluster régional, et cela marche plutôt bien. De toute façon, si l’infra n’est pas challengée par des pics de charge et que le nombre d’utilisateurs est stable, c’est déjà presque du luxe d’utiliser Kube si on ne dépasse pas 10 microservices et que les changements ne sont pas fréquents. Si les réservations de ressources et les probes sont bien dimensionnées, c’est déjà 80 % du boulot de fait. Autre considération, si le temps de réponse des services est lent, rajouter de l’autoscaling pour empiler des connexions est un pur gâchis d’argent et une source de frustration pour les utilisateurs.

En somme, ce n’est même pas si grave d’avoir un cluster rigide avec un nombre fixe de pods. Les coûts sont fixes et maîtrisés, mais cela devient vite compliqué quand on utilise massivement des charges de travail éphémères, quand on lance des jobs en quantité, avec des ressources coûteuses ou une application avec des pics de trafic qui nécessitent de l’autoscaling de pods et de nœuds.

plus d’infos sur le Capacity Planning

Le Capacity Planning est l’art d’anticiper les ressources informatiques nécessaires pour garantir la performance des applications tout en optimisant les coûts. Dans un univers Kubernetes, c’est ce qui permet d’éviter que vos serveurs ne saturent ou, à l’inverse, que vous ne payiez pour de la puissance inutilisée.

Note pro : Un bon capacity planning vise un taux d’utilisation cible (souvent entre 60 % et 80 %) pour garder une marge de sécurité en cas de pic soudain.

Meme comparant deux photos de souplesse : à gauche, “Cluster Rigide” montre un étirement difficile, à droite, “Horizontal Autoscaling” affiche un grand écart parfait, illustrant la flexibilité d’une infrastructure cloud.

La promesse de ne plus s’occuper des nœuds

C’est là que GKE Autopilot intervient, avec une promesse plutôt intéressante : payer pour la consommation des pods et non plus pour celle des nœuds. Qu’est-ce que cela implique concrètement ? Qu’est-ce que je paie vraiment ? Où sont les nœuds ? Est-ce que je peux encore faire du SSH pour faire du sale ? Si je ne vois pas les machines, sont-elles mélangées avec d’autres VM, mes pods sont-ils sécurisés ?

Un premier élément à prendre en compte quand on fait du GKE Autopilot : la réservation de ressources est reine. On paie la consommation des pods, et si le pod n’a pas de ressources réclamées, Autopilot créera un nœud avec des ressources par défaut, ce qui peut coûter très cher si aucune réservation n’est faite sur de petites charges de travail. Ce qui amène rapidement au second point : les quotas de calcul peuvent bloquer très vite et empêcher la création de nouveaux nœuds (sauf si on met en place les augmentations automatiques de quotas).

Ce qui m’amène à une erreur redoutable que j’ai faite pendant mon premier setup : installer ArgoCD et mon Ingress sans vérifier que les ressources étaient définies dans la configuration des services. J’ai eu une série de warnings, un scheduling très, très long de mes pods, et des ressources overkill par rapport à la faible consommation de mes pods ArgoCD. J’ai corrigé cela ensuite en vérifiant systématiquement les ressources définies sur les pods.

Un truc important à savoir : en ce qui concerne le placement des pods pour Autopilot, seule la request l’intéresse. Pour le CPU et la RAM, il vaut mieux faire request == limit pour éviter de se prendre des OOMKilled intempestifs à cause des chevauchements d’espace mémoire.

En Autopilot, la limit est automatiquement ajustée pour être égale à la request dans la plupart des cas. Google fait cela pour garantir que chaque pod a exactement ce pour quoi il a payé, sans “vol” de ressources entre voisins (noisy neighbors).

Note: We recommend that you explicitly set your resource requests for each container to meet your application requirements, as these default values might not be sufficient or optimal.

Autopilot applies the following default values to resources that are not defined in the Pod specification for Pods that run on compute classes. If you only set one of the requests and leave the other blank, GKE uses the CPU:memory ratio defined in the Minimum and maximum requests section to set the missing request to a value that complies with the ratio.

Default resource reservation

General-purpose (default) CPU 0.5 vCPU Memory 2 GiB

Source : https://docs.cloud.google.com/kubernetes-engine/docs/concepts/autopilot-resource-requests?hl=fr

Tips : Utilisez un outil comme Goldilocks pour vous aider à dimensionner au mieux vos ressources. Attention cependant, car les pods nécessitent parfois plus de puissance au démarrage que les ressources consommées en moyenne. Il faut être prudent sur la restriction de ressources.

Pour le bien du portefeuille de votre client, il convient de faire la chasse aux pods inutiles et aux jobs en erreur, qui peuvent polluer le cluster ou générer des coûts pour rien. Autre détail qui a toute son importance : on ne paie que les pods métiers. Les pods techniques de GCP ne sont pas facturés, ce qui veut dire qu’on s’affranchit de la taxe de 15 % des nœuds en dur pour la respiration des nœuds et les pods techniques de GCP (plus exactement, on n’en a plus rien à faire, puisque GKE crée des nœuds à la demande qu’il nous facture partiellement). Ce qui m’amène à parler du petit secret d’Autopilot, qui n’en est pas vraiment un : les balloon pods.

Réserver de la capacité pour gagner du temps de scheduling

Tonton GCP, le roi des bons tuyaux, gère lui-même le placement des pods sur son infra, ce qu’on appelle le bin packing, et le fait de la manière la plus agressive et rentable pour l’arranger lui. Bah oui, Chef, tu ne paies pas les espaces vides pour que tes pods soient placés, donc c’est juste que Google réduit au maximum l’espace “gâché”. Pour éviter de perdre trop de temps à planifier de nouveaux nœuds, GCP met en place des pods de respiration (Balloon pods), qui sont des pods placeholder qui viennent remplir les espaces de scheduling pour que GKE puisse les détruire et mettre des pods facturables à la place. L’idée, c’est que si une demande rapide de nouveau pod arrive avant que les nœuds soient prêts, on remplace un balloon pod par le pod arrivant.

Source : https://cloud.google.com/blog/products/containers-kubernetes/gke-features-to-optimize-resource-allocation?hl=en

C’est toute la beauté du système : on s’en tape complètement des nœuds, on s’en tape complètement de l’espace de réservation. Bien que les nœuds ne soient pas visibles dans l’interface Compute Engine, ils sont bien présents quand on fait un kubectl get nodes. On peut aussi voir les fameux gke-system-balloon-pod dans le namespace kube-system.

Ce n’est peut-être pas super green, mais ce n’est probablement pas pire que de laisser d’énormes nœuds allumés avec des charges de travail complètement vides.

Meme le clown de “ça” étiqueté “GKE Autopilot”, tenant des ballons nommés “Balloon Pods”. Il illustre l’usage de ressources factices pour pallier la lenteur du provisionnement automatique.

Devine quoi ? Il faut quand même savoir configurer Kubernetes

Comme dit plus haut, ce n’est pas parce qu’Autopilot gère les nœuds qu’il ne faut pas configurer correctement les réservations de ressources, les probes, et construire des images Docker proprement. Absolument aucune magie, je dirais même que c’est un devoir de vérifier que les ressources sont déclarées pour éviter les déconvenues. N’importez pas les mauvaises pratiques dans votre cluster. Ne rien déclarer et laisser faire le planificateur en mode “yolo”, cela peut fonctionner un temps, mais c’est une très mauvaise pratique pour la qualité de service de vos pods.

Si tu ne mets pas de probes liveness et readiness, toujours pas de magie : tu routes dans le vide, et Kubelet ne peut pas monitorer proprement l’état de tes pods. Si tu te trompes dans la configuration du port que tu dois router vers tes pods, comme sur un Kube normal, rien ne sauvera tes erreurs de setup. Donc, la première étape, c’est de sortir les poubelles et de régler les problèmes avant de les exporter dans Autopilot !

Et là où cela devient particulièrement important, c’est si tu utilises des workflows qui font popper des pods au kilomètre. Si tu ne mets pas de restrictions de retry ou de réservation de ressources, tu risques de voir tes factures grimper. C’est le prix à payer, littéralement, pour consommer des pods à la demande. Ne laissez pas traîner des pods qui ne sont plus utilisés, ils vous seront facturés.

À savoir : à ressource égale “purement en compute brut”, Autopilot est plus cher que du compute normal, mais avec ce dernier, il faut gérer les versions de cluster et payer l’overhead du fonctionnement du cluster. Donc finalement, on paie un peu plus pour la tranquillité de ne pas avoir à gérer les node pools et le scaling sans souci.

https://cloud.google.com/kubernetes-engine/pricing?hl=fr

Le scaling horizontal sera plus long s’il ne reste pas de balloon pod à dégager, mais ce serait pareil avec des node pools avec autoscaler.

plus d’infos sur le Scaling Horizontal

Le Scaling Horizontal (ou Scaling Out) consiste à ajouter des Pods dans Kubernetes plutôt que d’augmenter la puissance (CPU/RAM) d’un pod existant.

On utilise pour cela l’Horizontal Pod Autoscaler (HPA) et pour les nœuds, des node pools avec autoscaler.

Mais qu’est-ce que c’est que cette mascarade ?

Petit concept réseau rigolo : là où GKE Standard laisse la possibilité d’utiliser ou non les IP natives de VPC et l’IP masquerade, GKE Autopilot ne nous laisse pas le choix et décide arbitrairement des plages d’IP privées qui ne seront pas traduites.

Ce que je veux dire par là, c’est que par défaut, les pods de GKE Autopilot sortent avec des IP de la plage du VPC sur lequel le service est accroché, sauf si vous voulez parler à une machine sur un réseau qui est défini par Google comme une exclusion.

Petit tips de gredin : Google empêche d’appliquer les egressNatPolicies par défaut qui contiennent les fameuses listes d’exclusion, mais pose moins de difficulté à faire un kubectl edit. J’ai mal à mon YAML déclaratif, mais bon, rien à faire, tu ne m’empêcheras pas de le faire, c’est écrit dans la doc officielle !

Bon, OK, c’est hyper pointu et très niche, mais un peu agaçant si on veut parler avec du on-prem qui est dans la liste des plages exclues et qu’on doit ensuite gérer le bon pare-feu des familles qui attend des plages d’IP whitelistées.

Retour d’expérience : avant de trouver cette documentation et ce comportement agaçant, j’ai fait un HAProxy de fou pour faire du port forwarding de tous mes services Kube vers de l’on-prem. En plus de créer un magnifique SPOF dans mon infra si le proxy tombe, il faut sans arrêt maintenir quel pod forwarde vers quel service, gérer les pare-feu GCP pour autoriser les ports… Autant dire qu’à partir de 15 redirections de port, c’est compliqué de ne pas perdre la tête. J’étais bien content de me débarrasser de mon proxy et de contacter mes services en direct.

plus d’infos sur l’IP Masquerade

L’IP Masquerade (ou masquage d’adresse IP) est une forme de Network Address Translation (NAT) qui permet à plusieurs machines d’un réseau privé (comme vos Pods dans Kubernetes) d’accéder à un réseau externe (Internet) en utilisant une seule adresse IP publique.

Sortie : Le paquet quitte le Pod avec son IP source interne, mais en passant par le Node, l’IP est remplacée par celle du Node. Retour : Le Node garde une table de correspondance pour savoir que la réponse reçue doit être redirigée vers tel ou tel Pod spécifique.

Dans l’écosystème Kubernetes Dans un cluster, on utilise souvent l’ip-masq-agent. Son rôle est de décider quand masquer ou non :

Trafic Interne : Si un Pod parle à un autre Pod dans le cluster, le masquage est désactivé pour conserver l’identité de l’émetteur. Trafic Externe : Si le Pod contacte une API externe (ex: Google Maps API), le masquage s’active pour que la requête puisse circuler sur Internet.

Meme avec un homme masqué légendé “Devine mon IP hihihi” et “Random Pods”, illustrant le masquage d’adresse IP (IP Masquerade) dans un cluster Kubernetes.

La sécurité dans tout ça ?

Bon, c’est sympa, mais est-ce que je peux faire du SSH sur les nœuds ? La seule bonne réponse à ça, c’est : pourquoi diable aurais-tu besoin de faire ça ? Les nœuds sont “shieldés” avec des OS COS qui sont faits pour porter des conteneurs, et il est toujours autorisé de faire du kubectl exec pour réaliser des actions dans les pods, comme un gros sale. Pourquoi venir jardiner dans les nœuds qui sont, par design, faits pour être destructibles ?

Mais où sont planqués les nœuds ? Bien sûr que Google utilise des compute pour héberger les conteneurs, mais les machines sont rangées dans un projet géré par Google. Les pods clients ne sont pas mélangés entre les clients, et des ponts réseaux sont faits avec les VPC des projets clients de manière transparente pour le consommateur.

Ce qui implique que Google empêche l’utilisation de certaines directives kernel sur les pods comme CAP_NET_RAW, les conteneurs privilégiés ou les directives sysctl.

https://docs.cloud.google.com/kubernetes-engine/docs/concepts/autopilot-security

Ciao aussi les daemonsets, qu’on utilise en général pour faire des trucs sur les nœuds, comme du monitoring. Il faudra passer par le Prometheus managé pour ça. Adieu Zabbix ou Dynatrace, petits anges partis trop vite…

Retour d’expérience : j’ai voulu mettre un Elasticsearch en StatefulSet dans mon Kube Autopilot, mais comme je ne peux pas enlever la restriction kernel sur le nombre max de fichiers, je n’ai pas eu d’autre choix que de faire un compute à côté. C’est chiant, mais je peux vivre avec, je n’allais pas créer un cluster GKE Standard juste pour ça.

plus d’infos sur les DaemonSets Un DaemonSet, c’est un type de déploiement particulier qui garantit qu’une application tourne sur chaque serveur (Node) de votre cluster. Si le concept de Kubernetes est souvent de mettre les applications là où il y a de la place, le DaemonSet déroge à la règle : il impose une copie (un Pod) par machine.

Gollum, du Seigneur des Anneaux, avec l’inscription “Google qui ‘manage’ tes noeuds GKE”, illustrant le contrôle de Google sur l’infrastructure Kubernetes gérée.

Conclusion

Vous me voyez venir à 20 km ? Autopilot, ce n’est pas magique ! Tu ne sais pas faire du Kube ? Ce n’est pas Autopilot qui va régler tes problèmes !

Tu ne sais pas écrire des YAML propres, standards, et gérer correctement tes ressources ? Probablement que tu ne vas pas réussir à faire mieux avec Autopilot. Tu laisses traîner des pods à tout bout de champ et tu ne nettoies jamais tes vieux services ? Autopilot ne va pas le faire pour toi. Tu n’as plus personne pour s’occuper de ton cluster ? Guess what, tu ne feras plus les mises à jour de cluster, mais ça ne te dispense pas de gérer tes pods quand même.

Par contre, si tu as des charges de travail élastiques mais prévisibles (comme des batchs planifiés, par exemple) et que tu ne veux plus passer du temps à gérer des tailles de node pool ou à faire du capacity planning, ça vaut le coup de jeter un œil à Autopilot.

À titre personnel, je continue de penser que Kubernetes est complexe et amène avec lui la fâcheuse tendance d’augmenter la consommation de ressources et les coûts, mais Autopilot est un compromis intéressant pour qui veut réduire la complexité opérationnelle.

Et bien sûr, comme toujours, si tu amènes tes problèmes dans Autopilot, il ne va pas les régler magiquement à ta place.

Conteneur mal fichu ? Service zombie ? Helm crado ? Code lent ? Perte de l’être aimé ? Appelle plutôt un marabout, Autopilot ne va pas sauver ton empilement de mauvaises pratiques et ton cauchemar opérationnel.

Un cochon se roule dans la boue au milieu d’une prairie bucolique, entouré de cubes holographiques brillants affichant les logos de Kubernetes et Helm.