3 votes

Comment gérer correctement la mise à l'échelle automatique des applications Java Spring dans Kubernetes?

Je suis en train de mettre en place un autoscaling dans Kubernetes (hébergé dans Google Kubernetes Engine) pour mon application Java Spring. J'ai rencontré deux problèmes :

  1. L'application Spring utilise beaucoup de CPU au démarrage (à peu près 250mCPU*, parfois même 500mCPU), ce qui casse vraiment l'autoscaling, car certaines instances de cette application, après plus ou moins 1 minute (démarrage du contexte Spring, etc.), n'utilisent que 50mCPU. Comme dans certains environnements cette application utilise une faible quantité de mCPU (quasiment toujours la nuit), j'aimerais définir cpu=200mCPU demandé max (= limite 80% cpu) (voire moins !). Ainsi, l'autoscaling aurait beaucoup plus de sens. Mais je ne peux pas vraiment le faire à cause de ce démarrage intensif de Spring, qui ne se terminera pas si je lui attribue trop peu de CPU.

  2. Lorsque l'application commence à recevoir du trafic (lorsqu'un nouveau pod est créé en raison d'un événement d'autoscaling), son utilisation du CPU peut sauter à quelque chose comme 200% de l'utilisation standard, puis revenir à 100% - cela ne semble pas être dû au fait que trop de requêtes sont envoyées à ce nouveau pod, cela ressemble plutôt à un démarrage plus lent de la JVM et à une réception de trop de trafic au début. On dirait que la JVM aurait besoin d'un genre de préchauffage (pour ne pas pousser 1/n du trafic vers le nouveau pod soudainement, mais rediriger le trafic vers ce nouveau pod plus lentement). Grâce à ce comportement, l'autoscaling devient parfois fou - lorsqu'il a vraiment besoin d'un seul pod de plus, il peut en monter beaucoup, pour ensuite en descendre...

* dans GKE, 1000mCPU = 1 cœur

Sur les images téléchargées, nous pouvons voir les graphiques du CPU. Sur la première, nous pouvons constater que l'utilisation du CPU après le démarrage est bien inférieure à celle du début. Sur la deuxième, nous pouvons repérer les deux problèmes : une utilisation élevée du CPU au démarrage, puis une période d'attente (le délai initial de la sonde de préparation* n'est pas encore terminé), et ensuite un pic élevé au début de la réception du trafic.

* J'ai défini un délai initial de la sonde de préparation pour être plus long que le chargement du contexte.

Graphique 1 Graphique 2

La seule solution que j'ai trouvée sur internet est d'ajouter un conteneur à ce pod, qui ne fera rien d'autre que "sleep x", puis mourra. Et définir pour ce conteneur un mCPU demandé à une quantité qui sera utilisée au démarrage de l'application Spring (ensuite je devrais augmenter la limite de CPU pour ce conteneur d'application Spring, mais cela ne devrait pas nuire, car l'autoscaling devrait empêcher l'application Spring de priver les autres applications du nœud).

Toute aide serait grandement appréciée.

0voto

Urosh T. Points 618

Il est vrai que les applications Spring (ou en réalité Java) ne sont pas la chose la plus conviviale pour les contenants, mais il y a quelques choses que vous pouvez essayer:

  1. Au démarrage, Spring effectue le câblage automatique des beans et l'injection de dépendances, crée des objets en mémoire, etc. Toutes ces opérations sont intensives en CPU. Si vous attribuez moins de CPU à votre pod, le temps de démarrage augmentera logiquement. Ce que vous pouvez faire ici:

    • Utilisez une startupProbe et laissez le temps à votre application de démarrer. C'est expliqué assez bien ici sur comment calculer les délais et les seuils

    • Ajustez le maxSurge et le maxUnavailable dans votre stratégie de déploiement en fonction de votre cas (par exemple, peut-être avez-vous 10 répliques et un max surge /max unavailable de 10% afin que vos pods soient déployés lentement, un par un). Cela aidera à réduire les pics de trafic sur les répliques globales de l'application (la documentation est ici).

    • Si votre cas d'utilisation le permet, vous pouvez envisager de charger paresseusement votre application Spring, ce qui signifie qu'elle ne créera pas tous les objets au démarrage, mais attendra qu'ils soient utilisés. Cela peut être quelque peu dangereux en raison du risque de ne pas être en mesure de détecter certains problèmes au démarrage dans certains cas.

  2. Si vous avez activé HPA + défini une valeur de replicas dans le déploiement, vous pourriez rencontrer des problèmes lors du déploiement. Je ne trouve pas le problème correspondant sur GH pour le moment, mais vous voudrez peut-être effectuer quelques tests pour voir comment cela se comporte (mise à l'échelle plus que nécessaire, etc). Ce que vous pouvez faire ici:

    • Ajustez les seuils et les délais d'auto-évolutivité (3 minutes par défaut, à ma connaissance) pour permettre à vos déploiements de se dérouler en douceur sans déclencher l'auto-évolutivité.

    • Écrivez une métrique d'auto-évolutivité personnalisée au lieu de l'évolutivité en fonction du CPU. Cela nécessite un certain travail mais pourrait résoudre vos problèmes d'échelle pour de bon (documentation pertinente).

Enfin, ce que vous suggérez avec un sidecar semble être une astuce :) Je ne l'ai pas essayé donc je ne peux pas vraiment dire les pour et les contre.

Malheureusement, il n'y a pas de solution miracle pour Spring Boot (ou Java) + K8s, mais les choses s'améliorent par rapport à il y a quelques années. Si je trouve des ressources utiles, je reviendrai ici pour les lier.

J'espère que ce qui précède vous aidera.

Santé

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X