Faire évoluer Kubernetes pour utiliser des milliers de CRD
(blog.upbound.io)Crossplane, créé par Upbound, fournit un control plane cloud dans Kubernetes. Il dispose donc de centaines de CRD (Custom Resource Definition) pour représenter des ressources cloud comme AWS, Azure et GCP. Dans Crossplane, on les appelle des MR (Managed Resource).
Même les utilisateurs avancés de Kubernetes n’exploitent généralement qu’un nombre raisonnable de CR, de l’ordre de quelques dizaines, mais comme Crossplane nécessite l’utilisation de centaines de MR, ils ont commencé à examiner les limites de Kubernetes quant au nombre de CRD qu’il peut gérer.
On peut répartir cela entre problèmes côté client et problèmes côté serveur.
Problèmes côté client
- Côté client, c’est le processus de discovery qui pose problème.
- Lorsqu’un client comme kubectl cherche à savoir quelles API le serveur fournit, il effectue une discovery, ce qui impose de parcourir une fois tous les endpoints API.
- Les CR sont exposées comme des endpoints API.
- Pour consulter un MR comme
https://example.org/apis/rds.aws.upbound.io/v1/instances/cool-db, il faut trouver les groupes d’API pris en charge viahttps://example.org/apis/, puis les versions prises en charge viahttps://example.org/apis/rds.aws.upbound.io, puis les CR prises en charge viahttps://example.org/apis/rds.aws.upbound.io/v1. - Les MR Crossplane pour les fournisseurs cloud AWS, Azure et GCP représentent environ 2 000 ressources, réparties sur 300 groupes et versions d’API.
- Le client envoie donc 300 requêtes HTTP pour la discovery.
- Avec les réseaux actuels, ce n’est pas un gros problème, mais les points réellement observés concernaient la limitation de débit et le cache.
Limitation de débit côté client
- Une limitation de débit est appliquée à 5 requêtes par seconde en moyenne (avec un burst jusqu’à 100), et le cache de discovery est invalidé toutes les 10 minutes.
- Cela peut se résoudre en augmentant cette limite, et même si elle est toujours à 5 requêtes par seconde par défaut, elle peut désormais monter jusqu’à 300.
- Un ticket demandant de relever cette limite a été ouvert avec kubectl v1.22, et le cache de discovery a également été ajusté de 10 minutes à 5 heures ; Kubernetes v1.25 peut donc tirer parti de cette limite client augmentée.
Cache côté client
- Même en désactivant la limitation de débit pour les tests, interroger 300 groupes d’API prenait encore près de 20 secondes.
- Au départ, cela semblait être un problème réseau, mais il s’est avéré que cela venait des consultations de fichiers de cache.
- Le problème a été corrigé dans Kubernetes 1.25, avec un gain de vitesse de 25x sur macOS et de 2x sur Linux.
Améliorations futures côté client
- Il est raisonnable d’appliquer une limitation de débit côté client, mais en réalité cela ne protège pas correctement le serveur.
- API Priority and Fairness (AP&F), introduit dans Kubernetes 1.20, fournit côté serveur des files d’attente et du lissage de trafic pour protéger l’API server.
- Un unique endpoint HTTP agrégé pour la discovery a été approuvé dans une KEP et devrait être pris en charge en alpha dans la 1.26.
Problèmes côté serveur
Calcul du schéma OpenAPI
- Après avoir enregistré des centaines de CRD, ils ont constaté que les requêtes API devenaient très lentes pendant presque une heure.
- Le profiling a montré que ce problème venait de la logique de calcul du schéma OpenAPI v2.
- Lorsqu’un CRD est ajouté ou mis à jour, le contrôleur OpenAPI construit la spécification swagger de la CR, la fusionne avec celles de toutes les autres CR pour produire une grande spécification unique, puis la sérialise en JSON pour l’exposer sur
/openapi/v2. - Ils ont modifié ce fonctionnement pour calculer
/openapi/v2de manière différée, uniquement lorsqu’un endpoint lié à une CR réelle est demandé. - Cette correction a été intégrée dans la v1.24.0 et backportée vers 1.20.13, 1.21.7 et 1.22.4.
Client etcd
- C’est le nouveau goulot d’étranglement découvert après avoir résolu le problème OpenAPI.
- Ils ont constaté que l’API server utilisait 4 MiB de mémoire par CRD.
- C’est encore plus problématique sur des Kubernetes managés comme GKE et EKS, car le CPU et la mémoire de l’API server y sont limités. Si davantage de ressources sont nécessaires, l’API server est bien redimensionné automatiquement, mais malheureusement l’ajout de CRD n’est pas un facteur pris en compte pour déclencher cette mise à l’échelle. En conséquence, il n’est pas étendu tant qu’il n’est pas tué à répétition par OOM.
- Lors des tests sur GKE, AKE et EKS, l’auto-healing fonctionnait, mais l’API server pouvait rester indisponible de 5 secondes à 1 heure. Le cluster ne s’arrêtait pas complètement, mais toutes les réconciliations s’interrompaient.
- Le profiling a révélé que Zap, la bibliothèque de logging, consommait 20 % de la mémoire.
- L’API server crée un client etcd par version de CR, et chaque client etcd crée un logger Zap.
- En conséquence, ces loggers dupliqués augmentaient la mémoire utilisée et créaient aussi des connexions TCP inutiles entre l’API server et etcd.
- Les mainteneurs ont convenu qu’il serait préférable d’utiliser un seul client etcd pour tous les endpoints CR, mais comme la sortie de Kubernetes 1.25 était proche, il était difficile de tout corriger ; ils ont donc appliqué un changement plus limité pour faire partager un seul logger à tous les clients etcd.
-Cela devrait être inclus dans la 1.25 et backporté vers 1.22, 1.23 et 1.24. Cela réduira l’usage mémoire de 20 %.
Améliorations futures côté serveur
- Le plan est de ne plus créer un client etcd par version de CR, mais un par transport (c’est-à-dire par cluster etcd).
- Ils collaborent aussi avec les équipes d’ingénierie de GKE, EKS et AKE pour mieux prendre en charge l’installation d’un grand nombre de CRD Crossplane.
2 commentaires
Gratuité -> invalidation
claingent -> client