Planifier pour l'évolutivité


Cette page décrit les bonnes pratiques générales concernant la conception de clusters GKE évolutifs. Vous pouvez appliquer ces recommandations à tous les clusters et charges de travail pour obtenir des performances optimales. Ces recommandations sont particulièrement importantes pour les clusters que vous prévoyez de faire évoluer à grande échelle. Ces bonnes pratiques sont destinées aux administrateurs en charge du provisionnement de l'infrastructure et aux développeurs qui préparent les composants et les charges de travail Kubernetes.

Qu'est-ce que l'évolutivité ?

Dans un cluster Kubernetes, l'évolutivité fait référence à la capacité du cluster à se développer tout en respectant ses objectifs de niveau de service (SLO). Kubernetes possède également son propre ensemble de SLO.

Kubernetes est un système complexe. Sa capacité d'évolution est déterminée par plusieurs facteurs, dont les suivants : le type et le nombre de nœuds d'un pool de nœuds, le type et le nombre de pools de nœuds, le nombre de pods disponibles, la méthode d'attribution des ressources aux pods et le nombre de services ou de backends derrière un service.

Bonnes pratiques pour la disponibilité

Choisir un plan de contrôle régional ou zonal

En raison de différences architecturales, les clusters régionaux conviennent mieux à la haute disponibilité. Les clusters régionaux comportent plusieurs plans de contrôle sur plusieurs zones de calcul d'une même région, tandis que les clusters zonaux possèdent un plan de contrôle dans une seule zone de calcul.

Si un cluster zonal est mis à niveau, la VM du plan de contrôle subit un temps d'arrêt pendant lequel l'API Kubernetes est indisponible jusqu'à la fin de l'opération.

Au sein des clusters régionaux, le plan de contrôle reste disponible lors des opérations de maintenance du cluster, telles que le renouvellement des adresses IP, la mise à niveau des VM du plan de contrôle, ou le redimensionnement des clusters ou des pools de nœuds. Lors de la mise à niveau d'un cluster régional, deux des trois VM du plan de contrôle restent en cours d'exécution pendant la mise à niveau progressive. Ainsi, l'API Kubernetes est toujours disponible. De même, l'interruption d'une seule zone n'entraîne pas de temps d'arrêt sur le plan de contrôle régional.

Toutefois, les clusters régionaux à la disponibilité la plus élevée s'accompagnent des compromis suivants :

  • Les modifications apportées à la configuration du cluster prennent davantage de temps à être appliquées, car celles-ci doivent se propager à tous les plans de contrôle d'un cluster régional alors que les clusters zonaux sont dotés d'un plan de contrôle unique.

  • Vous ne pourrez peut-être pas créer, ni mettre à niveau les clusters régionaux aussi souvent que les clusters zonaux. Si les VM ne peuvent pas être créées dans l'une des zones, que ce soit par manque de capacité ou à cause d'un autre problème temporaire, les clusters ne peuvent pas être créés, ni mis à niveau.

En raison de ces compromis, les clusters régionaux et zonaux sont soumis à des cas d'utilisation différents :

  • Utilisez des clusters zonaux pour créer ou mettre à niveau des clusters rapidement lorsque la disponibilité est un facteur moins préoccupant.
  • Utilisez des clusters régionaux lorsque la disponibilité est un facteur plus important que la flexibilité.

Réfléchissez bien avant de sélectionner le type de cluster à créer, car vous ne pouvez pas le modifier une fois l'opération terminée. À la place, vous devez créer un autre cluster, puis transférer le trafic vers celui-ci. Il est possible de migrer le trafic de production entre les clusters, mais cette opération devient difficile à réaliser à grande échelle.

Choisir des pools de nœuds multizones ou à zone unique

Pour atteindre une haute disponibilité, le plan de contrôle Kubernetes et ses nœuds doivent être répartis sur différentes zones. GKE propose deux types de pools de nœuds : à zone unique et multizones.

Pour déployer une application à haute disponibilité, répartissez votre charge de travail sur plusieurs zones de calcul d'une région en utilisant des pools de nœuds multizones, qui permettent de distribuer les nœuds uniformément entre les zones.

Si tous vos nœuds se trouvent dans la même zone, vous ne pourrez pas planifier de pods si cette zone devient inaccessible. L'utilisation de pools de nœuds multizones présente les inconvénients suivants :

  • Les GPU ne sont disponibles que dans des zones spécifiques. Vous ne pourrez peut-être pas les obtenir dans toutes les zones de la région.

  • La latence aller-retour entre les zones d'une même région peut être supérieure à celle entre les ressources d'une même zone. La différence doit être immatériel pour la plupart des charges de travail.

  • Le prix du trafic de sortie entre les zones d'une même région est disponible sur la page Tarifs de Compute Engine.

Bonnes pratiques pour l'évolutivité

Infrastructure de base

Les charges de travail Kubernetes nécessitent des opérations de mise en réseau, de calcul et de stockage. Vous devez fournir une quantité suffisante de processeurs et de mémoire pour pouvoir exécuter les pods. Toutefois, il existe d'autres paramètres d'infrastructure sous-jacente qui peuvent influencer les performances et l'évolutivité d'un cluster GKE.

Mise en réseau des clusters

L'utilisation d'un cluster de VPC natif constitue la mise en réseau par défaut et le choix recommandé pour configurer de nouveaux clusters GKE. Les clusters de VPC natif offrent des charges de travail plus importantes, un nombre de nœuds plus élevé et d'autres avantages.

Dans ce mode, le réseau VPC dispose d'une plage secondaire pour toutes les adresses IP de pods. Chaque nœud se voit alors attribuer une tranche de cette plage secondaire pour ses propres adresses IP de pods. Cela permet au réseau VPC de comprendre de manière native comment acheminer le trafic vers les pods sans passer par des routages personnalisés. Un seul réseau VPC peut accepter jusqu'à 15 000 VM.

Une autre approche, qui est obsolète et ne permet pas plus de 1 500 nœuds, consiste à utiliser un cluster basé sur le routage. Un cluster basé sur le routage n'est pas adapté aux charges de travail volumineuses. Il consomme le quota de routes VPC, sans présenter les autres avantages du réseau VPC natif. Son principe est d'ajouter une route personnalisée à la table de routage du réseau VPC pour chaque nouveau nœud.

Clusters privés

Dans les clusters GKE standards, tous les nœuds possèdent des adresses IP publiques. Dans les clusters privés, les nœuds ne disposent que d'adresses IP internes, ce qui permet de les isoler vis-à-vis des connexions entrantes et sortantes depuis/vers Internet. GKE utilise l'appairage de réseaux VPC pour connecter les VM exécutant le serveur d'API Kubernetes avec le reste du cluster. Cela permet un débit plus élevé entre les plans de contrôle GKE et les nœuds, car le trafic est acheminé à l'aide d'adresses IP privées.

L'utilisation de clusters privés présente l'avantage d'une sécurité supplémentaire dans la mesure où les nœuds ne sont pas exposés à Internet.

Équilibrage de charge au niveau du cluster

Les objets GKE Ingress et Cloud Load Balancing configurent et déploient les équilibreurs de charge pour exposer les charges de travail Kubernetes en dehors du cluster et sur le réseau Internet public. Les contrôleurs d'objets GKE Ingress et Service déploient des objets tels que règles de transfert, mappages d'URL, services de backend, groupes de points de terminaison réseau, etc. pour le compte des charges de travail GKE. Chacune de ces ressources comporte ses propres quotas et limites qui s'appliquent également à GKE. Lorsqu'une ressource Cloud Load Balancing spécifique a atteint son quota, elle empêche le déploiement correct d'un objet Ingress ou Service donné et des erreurs apparaissent dans les événements de la ressource.

Le tableau suivant décrit les limites de scaling lors de l'utilisation de GKE Ingress et des services :

Équilibreur de charge Limite de nœuds par cluster
Équilibreur de charge réseau passthrough interne
Équilibreur de charge réseau passthrough externe 1 000 nœuds par zone
Équilibreur de charge d'application externe
Équilibreur de charge d'application interne Aucune limite de nœuds

Si vous devez effectuer un scaling à la hausse, contactez votre équipe commerciale Google Cloud pour demander une augmentation de cette limite.

DNS

La détection de services dans GKE est assurée via kube-dns, une ressource centralisée permettant de fournir une résolution DNS aux pods s'exécutant dans le cluster. Cela peut devenir un goulot d'étranglement sur des clusters très volumineux ou pour des charges de travail qui présentent une charge de requêtes élevée. GKE effectue un scaling automatique de kube-dns en fonction de la taille du cluster afin d'augmenter sa capacité. Lorsque cette capacité n'est pas suffisante, GKE propose une résolution locale distribuée des requêtes DNS sur chaque nœud à l'aide de NodeLocal DNSCache. Cela permet de fournir un cache DNS local sur chaque nœud GKE qui répond aux requêtes localement, de répartir la charge et d'accélérer les temps de réponse.

Gérer les adresses IP dans les clusters de VPC natif

Un cluster de VPC natif utilise trois plages d'adresses IP :

  • Plage principale pour le sous-réseau de nœud : par défaut, /20 (4 092 adresses IP).
  • Plage secondaire pour le sous-réseau de pod : par défaut, /14 (262 144 adresses IP). Vous pouvez toutefois configurer le sous-réseau de pod.
  • Plage secondaire pour le sous-réseau de l'objet Service : par défaut, /20 (4 096 adresses). Toutefois, vous ne pouvez pas modifier cette plage après avoir créé ce sous-réseau de l'objet Service.

Pour en savoir plus, consultez la section Plages d'adresses IP pour les clusters de VPC natif.

Limites et recommandations concernant les adresses IP :

  • Limite de nœuds : la limite de nœuds est déterminée par les adresses IP principales et les adresses IP des pods par nœud. Il doit y avoir suffisamment d'adresses dans les plages d'adresses IP des nœuds et des pods pour provisionner un nouveau nœud. Par défaut, vous ne pouvez créer que 1 024 nœuds en raison des limites d'adresse IP des pods.
  • Limite de pods par nœud : par défaut, la limite des pods par nœud est de 110 pods. Cependant, vous pouvez configurer des CIDR de pods plus petits pour une utilisation efficace avec moins de pods par nœud.
  • Scaling au-delà de la norme RFC 1918 : si vous avez besoin de plus d'adresses IP que disponibles dans l'espace privé défini par la norme RFC 1918, nous vous recommandons d'utiliser des adresses IP privées non RFC 1918 ou des adresses PUPI pour plus de flexibilité.
  • Plage d'adresses IP secondaire pour l'objet Service et les pods : par défaut, vous pouvez configurer 4 096 objets Service. Vous pouvez toutefois configurer d'autres objets Service en choisissant la plage de sous-réseau Service. Vous ne pouvez pas modifier les plages secondaires après la création. Lorsque vous créez un cluster, veillez à choisir des plages suffisamment grandes pour faire face à la croissance prévue. Toutefois, vous pouvez ajouter d'autres adresses IP pour les pods ultérieurement à l'aide d'un CIDR multipods non contigu. Pour plus d'informations, consultez la section Pas assez d'espace d'adresses IP libre pour les pods.

Pour en savoir plus, consultez les sections Plages de limites de nœud et Planifier des adresses IP lors de la migration vers GKE.

Configurer les nœuds pour obtenir de meilleures performances

Les nœuds GKE sont des machines virtuelles Google Cloud classiques. Certains de leurs paramètres, tels que le nombre de cœurs ou la taille du disque, peuvent avoir une incidence sur les performances des clusters GKE.

Réduire les délais d'initialisation des pods

Vous pouvez utiliser le streaming d'images pour diffuser des données à partir d'images de conteneurs éligibles à mesure que vos charges de travail les demandent, ce qui réduit les délais d'initialisation.

Trafic de sortie

Dans Google Cloud, le type de machine et le nombre de cœurs alloués à l'instance déterminent sa capacité réseau. La bande passante de sortie maximale varie de 1 à 32 Gbit/s, tandis que la bande passante de sortie maximale des machines e2-medium-2 par défaut est de 2 Gbit/s. Pour en savoir plus sur les limites de bande passante, consultez la section Types de machines à cœur partagé.

IOPS et débit des disques

Dans Google Cloud, la taille des disques persistants détermine les IOPS et le débit des disques. En règle générale, GKE utilise des disques persistants en tant que disques de démarrage. Il s'en sert également pour soutenir les volumes persistants de Kubernetes. L'augmentation de la taille des disques augmente à la fois les IOPS et le débit, jusqu'à certaines limites.

Chaque opération d'écriture sur le disque persistant se cumule et rapproche votre instance de machine virtuelle de son plafond de sortie réseau. Ainsi, les performances des disques en termes d'IOPS, en particulier des disques SSD, dépendent également du nombre de processeurs virtuels dans l'instance, en plus de la taille du disque. Les machines virtuelles comportant moins de cœurs ont des limites d'IOPS en écriture inférieures en raison des limitations du débit en écriture à la sortie réseau.

Si votre instance de machine virtuelle est dotée d'un nombre insuffisant de processeurs, votre application ne pourra pas s'approcher de la limite IOPS. En règle générale, vous devez avoir un processeur disponible pour 2 000 à 2 500 IOPS de trafic attendu.

Les charges de travail qui nécessitent une capacité élevée ou un grand nombre de disques doivent tenir compte des limites concernant le nombre de disques persistants pouvant être associés à une VM unique. Pour les VM classiques, cette limite est de 128 disques pour une taille totale de 64 To, tandis que pour les VM à cœur partagé, cette limite est de 16 disques persistants pour une taille totale de 3 To. Google Cloud applique cette limite, contrairement à Kubernetes.

Surveiller les métriques du plan de contrôle

Utilisez les métriques de plan de contrôle disponibles pour configurer vos tableaux de bord de surveillance. Les métriques du plan de contrôle vous permettent d'analyser l'état du cluster, d'examiner les résultats des modifications apportées à la configuration du cluster (par exemple, lors du déploiement de charges de travail supplémentaires ou de composants tiers) ou de résoudre des problèmes.

L'une des métriques les plus importantes à surveiller est la latence de l'API Kubernetes. L'augmentation de la latence indique une surcharge du système. Gardez à l'esprit que les appels LIST permettant de transférer de grandes quantités de données sont supposés avoir une latence beaucoup plus élevée que les requêtes plus petites.

La latence accrue de l'API Kubernetes peut également être due à un manque de réactivité des webhooks d'admission tiers. Vous pouvez utiliser les métriques pour mesurer la latence des webhooks afin de détecter ce type de problèmes courants.

Bonnes pratiques pour les développeurs Kubernetes

Utiliser un modèle de liste et de surveillance au lieu d'une liste périodique

En tant que développeur Kubernetes, vous devrez peut-être créer un composant répondant aux exigences suivantes :

  • Votre composant doit régulièrement récupérer la liste de certains objets Kubernetes.
  • Votre composant doit s'exécuter dans plusieurs instances, et même dans chaque nœud dans le cas d'un DaemonSet.

Ce type de composant peut générer des pics de charge sur le serveur kube-apiserver, même si l'état des objets récupérés régulièrement ne change pas.

La méthode la plus simple consiste à utiliser des appels LIST périodiques. Cependant, cette approche est inefficace et coûteuse pour l'appelant et le serveur, car tous les objets doivent être chargés en mémoire, sérialisés et transférés à chaque fois. L'utilisation excessive de requêtes LIST peut surcharger le plan de contrôle ou générer une limitation importante de ces requêtes.

Vous pouvez améliorer votre composant en définissant le paramètre resourceVersion=0 dans les appels LIST. Cela permet à kube-apiserver d'utiliser le cache d'objets en mémoire et de réduire le nombre d'interactions internes entre kube-apiserver et la base de données etcd et le traitement associé.

Nous vous recommandons vivement d'éviter les appels LIST reproductibles et de les remplacer par le modèle de liste et de surveillance. Répertoriez les objets une fois, puis utilisez l'API Watch pour obtenir des modifications incrémentielles de l'état. Cette approche réduit le temps de traitement et minimise le trafic par rapport aux appels LIST périodiques. Lorsque les objets ne changent pas, aucune charge supplémentaire n'est générée.

Si vous utilisez le langage Go, consultez les pages SharedInformer et SharedInformerFactory pour obtenir les packages Go mettant en œuvre ce modèle.

Limiter le trafic inutile généré par les requêtes "watch" et "list"

Kubernetes utilise des événements de surveillance en interne pour envoyer des notifications concernant les mises à jour d'objets. Même si les événements de surveillance nécessitent beaucoup moins de ressources que les appels LIST périodiques, leur traitement dans de grands clusters peut nécessiter une part importante de ressources du cluster et affecter les performances de celui-ci. L'impact le plus négatif est généré par la création d'événements de surveillance qui observent des objets sujets à des modifications fréquentes à partir de plusieurs emplacements (par exemple, en observant les données de tous les pods à partir d'un composant s'exécutant sur tous les nœuds). Si vous installez des extensions ou du code tiers sur votre cluster, ils peuvent créer des événements de surveillance en arrière-plan.

Nous vous recommandons de respecter les bonnes pratiques suivantes :

  • Limitez le traitement et le trafic inutiles générés par les événements de surveillance et les appels LIST.
  • Évitez de créer des événements de surveillance qui observent des objets sujets à des modifications fréquentes à partir de plusieurs emplacements (par exemple, des DaemonSets).
  • (Vivement recommandé) Créez un contrôleur central qui surveille et traite les données requises sur un nœud unique.
  • Observez uniquement un sous-ensemble d'objets. Par exemple, le kubelet n'observe que les pods programmés sur le même nœud.
  • Évitez de déployer des composants ou extensions tiers susceptibles d'affecter les performances du cluster en générant un grand nombre d'événements de surveillance ou d'appels LIST.

Limiter la taille des fichiers manifestes des objets Kubernetes

Si vous avez besoin d'opérations rapides nécessitant un débit de pod élevé, telles que le redimensionnement ou la mise à jour de charges de travail volumineuses, assurez-vous de conserver une taille minimale de fichier manifeste de pod, idéalement inférieure à 10 Kio.

Kubernetes stocke les fichiers manifestes des ressources dans etcd. L'ensemble du fichier manifeste est envoyé à chaque fois que la ressource est récupérée, y compris lorsque vous utilisez un modèle de liste et de surveillance.

La taille du fichier manifeste présente les limites suivantes :

  • Taille maximale de chaque objet dans etcd : environ 1,5 Mio.
  • Quota total pour tous les objets etcd du cluster : le quota préconfiguré est de 6 Gio. Cela inclut un journal des modifications avec toutes les mises à jour des objets au cours des 150 dernières secondes de l'historique du cluster.
  • Performances du plan de contrôle lors des pics de trafic : des fichiers manifestes de plus grande taille augmentent la charge sur le serveur d'API.

Pour les objets uniques rarement traités, la taille du fichier manifeste n'est généralement pas un problème tant qu'il est inférieur à 1,5 Mio. Toutefois, des tailles de fichiers manifestes supérieures à 10 Kio pour de nombreux objets fréquemment traités, tels que des pods dans des charges de travail très volumineuses, peuvent entraîner une augmentation de la latence des appels d'API et une baisse des performances globales. Les listes et les surveillances en particulier peuvent être affectées par des tailles de fichiers manifestes importantes. Vous pouvez également rencontrer des problèmes de quota etcd, car la quantité de révisions au cours des 150 dernières secondes peut s'accumuler rapidement pendant les pics de trafic du serveur d'API.

Pour réduire la taille du fichier manifeste d'un pod, vous pouvez exploiter Kubernetes ConfigMaps pour stocker une partie de la configuration, en particulier celle qui est partagée par plusieurs pods dans le cluster. Par exemple, les variables d'environnement sont souvent partagées par tous les pods d'une charge de travail.

Notez qu'il est également possible de rencontrer des problèmes similaires avec les objets ConfigMap si ceux-ci sont aussi nombreux, volumineux, et traités aussi souvent que les pods. L'extraction d'une partie de la configuration est particulièrement utile lorsqu'elle réduit le trafic global.

Désactiver l'installation automatique du compte de service par défaut

Si la logique exécutée dans vos pods n'a pas besoin d'accéder à l'API Kubernetes, vous devez désactiver l'installation automatique par défaut du compte de service pour éviter de créer des secrets et des événements de surveillance associés.

Lorsque vous créez un pod sans spécifier de compte de service, Kubernetes effectue automatiquement les actions suivantes :

  • Attribue le compte de service par défaut au pod.
  • Installe les identifiants du compte de service en tant que secret pour le pod.
  • Pour chaque secret installé, le kubelet crée un événements de surveillance afin d'observer les modifications apportées à ce secret sur chaque nœud.

Dans de grands clusters, ces actions représentent des milliers d'événements de surveillance, ce qui peut constituer une charge importante dans le kube-apiserver.

Utiliser des tampons de protocole au lieu de JSON pour les requêtes API

Utilisez des tampons de protocole lorsque vous implémentez des composants hautement évolutifs, comme décrit sur la page Concepts de l'API Kubernetes.

L'API REST Kubernetes est compatible avec JSON et les tampons de protocole en tant que format de sérialisation pour les objets. JSON est utilisé par défaut, mais les tampons de protocole sont plus efficaces pour des performances à grande échelle, car ils nécessitent un traitement moins gourmand en ressources de processeur et envoient moins de données sur le réseau. Les frais généraux liés au traitement de données JSON peuvent entraîner des délais d'inactivité lors de la création d'une liste de données de grande taille.

Étape suivante