···8181 .mode(Mode.Throughput)
8282 .timeUnit(TimeUnit.SECONDS)
8383 .forks(0)
8484+ .threads(2)
8485 .result(tempFile.getAbsolutePath())
8586 .resultFormat(ResultFormatType.TEXT)
8687 .build();
···403404GET localhost:8080/metrics
404405405406cpu_count 1.0
406406-process_cpu_load 1.4755859375
407407+process_cpu_load 0.18505859375
407408jvm_memory_max_mb 396.375
408409jvm_memory_used_mb 4.081321716308594
409410```
···448449449450```shell
450451cpu_count 1.0
451451-process_cpu_load 0.875
452452+process_cpu_load 0.99267578125
452453jvm_memory_max_mb 396.375
453454jvm_memory_used_mb 353.49063873291016
454455```
···481482```bash
482483& kubectl events --for pod/timbernetes-demo
483484LAST SEEN TYPE REASON OBJECT MESSAGE
484484-17m Normal NotTriggerScaleUp Pod/timbernetes-demo pod didn't trigger scale-up:
485485-17m Warning FailedScheduling Pod/timbernetes-demo no nodes available to schedule pods
486486-17m (x4 over 17m) Warning FailedScheduling Pod/timbernetes-demo no nodes available to schedule pods
48748516m Normal Scheduled Pod/timbernetes-demo Successfully assigned default/timbernetes-demo to scw-timbernetes-dem-timbernetes-demo-po-1b7caa
48848616m Normal Pulling Pod/timbernetes-demo Pulling image "rg.fr-par.scw.cloud/timbernetes-demo/java:latest"
48948715m Normal Pulled Pod/timbernetes-demo Successfully pulled image "rg.fr-par.scw.cloud/timbernetes-demo/java:latest" in 14.487s (27.916s including waiting). Image size: 109821985 bytes.
···493491112s Normal ResizeCompleted Pod/timbernetes-demo Pod resize completed: {"containers":[{"name":"timbernetes-demo","resources":{"limits":{"cpu":"2","memory":"1Gi"},"requests":{"cpu":"2","memory":"1Gi"}}}],"generation":2}
494492```
495493494494+Les deux dernières lignes font bien état de la modification.
495495+496496+Quand je requête à nouveau le endpoint `/metrics`, j'obtiens alors la réponse suivante :
497497+498498+```http request
499499+GET localhost:8080/metrics
500500+501501+cpu_count 2.0
502502+process_cpu_load 0.04296875
503503+jvm_memory_max_mb 396.375
504504+jvm_memory_used_mb 362.24312591552734
505505+```
506506+507507+On observe que le nombre de CPU visibles par le JVM a changé, c'est une bonne nouvelle.
508508+Par contre, comme on l'attendait, la Heap maximale que peut consommer la JVM n'a pas changé. La Heap est configurée au démarrage de la JVM et n'est donc pas redimensionnée à chaud, même si le pod a plus de RAM disponible.
509509+510510+Une fois le stress test lancé, on observe les métriques suivantes :
511511+512512+```http request
513513+GET localhost:8080/metrics
514514+515515+cpu_count 2.0
516516+process_cpu_load 1.90654296875
517517+jvm_memory_max_mb 396.375
518518+jvm_memory_used_mb 355.3716583251953
519519+```
520520+521521+Un `top` dans le container permet de confirmer ce qu'on voit avec la métrique, le process utilise 200% de CPU, les 2 coeurs sont bien exploités par la JVM.
522522+
523523+524524+### Redimensionnement et dernier tir
525525+526526+Pour compléter les tests, je redimensionne à nouveau le pod, cette fois-ci avec des valeurs à la baisse, pour revenir aux valeurs initiales :
527527+528528+```bash
529529+kubectl patch pod timbernetes-demo --subresource resize --patch \
530530+ '{"spec":{"containers":[{"name":"timbernetes-demo","resources":{"limits":{"cpu":"1","memory":"512Mi"},"requests":{"cpu":"1","memory":"512Mi"}}}]}}'
531531+532532+pod/timbernetes-demo patched
533533+```
534534+535535+Les évènements sur le pod affichent bien que le resizing a été exécuté :
536536+537537+```bash
538538+kubectl events --for pod/timbernetes-demo
539539+LAST SEEN TYPE REASON OBJECT MESSAGE
540540+53s Normal ResizeStarted Pod/timbernetes-demo Pod resize started: {"containers":[{"name":"timbernetes-demo","resources":{"limits":{"cpu":"1","memory":"512Mi"},"requests":{"cpu":"1","memory":"512Mi"}}}],"generation":3}
541541+52s Normal ResizeCompleted Pod/timbernetes-demo Pod resize completed: {"containers":[{"name":"timbernetes-demo","resources":{"limits":{"cpu":"1","memory":"512Mi"},"requests":{"cpu":"1","memory":"512Mi"}}}],"generation":3}
542542+```
543543+544544+La métrique affiche de nouveau 1 CPU disponible, aucun changement au niveau de la RAM comme attendu :
545545+546546+```http request
547547+GET localhost:8080/metrics
548548+549549+cpu_count 1.0
550550+process_cpu_load 0.03466796875
551551+jvm_memory_max_mb 396.375
552552+jvm_memory_used_mb 206.46312713623047
553553+```
554554+555555+Pas de surprise non plus sur ce redimensionnement qui est aussi effectué à chaud.
556556+557557+Enfin, pour observer ce qu'il se passerai avec un redimensionnement sur une RAM déjà consommé, j'opère un redimensionnement à une valeur de RAM inférieure à celle que consomme déjà le pod.
558558+Je dois m'attendre à un OOMKill, puis un redémarrage du pod, qui reprendra donc un taille de Heap à 80% de la RAM disponible, vu que ce dimensionnement est fait au démarrage de la JVM.
559559+560560+```bash
561561+$ kubectl patch pod timbernetes-demo --subresource resize --patch '{"spec":{"containers":[{"name":"timbernetes-demo","resources":{"limits":{"cpu":"1","memory":"128Mi"},"requests":{"cpu":"1","memory":"128Mi"}}}]}}'
562562+pod/timbernetes-demo patched
563563+```
564564+565565+Cette fois-ci, lorsque je regarde les évènements du pod, j'observe une erreur :
566566+567567+```bash
568568+$ kubectl events --for pod/timbernetes-demo
569569+32s Normal ResizeStarted Pod/timbernetes-demo Pod resize started: {"containers":[{"name":"timbernetes-demo","resources":{"limits":{"cpu":"1","memory":"128Mi"},"requests":{"cpu":"1","memory":"128Mi"}}}],"generation":5}
570570+32s Warning ResizeError Pod/timbernetes-demo Pod resize error: {"containers":[{"name":"timbernetes-demo","resources":{"limits":{"cpu":"1","memory":"128Mi"},"requests":{"cpu":"1","memory":"128Mi"}}}],"generation":5,"error":"cannot decrease memory limits: [attempting to set pod memory limit (134217728) below current usage (418054144), attempting to set container \"timbernetes-demo\" memory limit (134217728) below current usage (418054144)]"}
571571+```
572572+573573+Kubernetes refuse de redimensionner le pod à chaud, car la RAM consommée est supérieure à la nouvelle taille de RAM, ce qui est cohérent.
574574+575575+## Conclusion
576576+577577+Le redimensionnement des ressources à chaud fonctionne à merveille, et le comportement de la JVM est bien celui auquel on s'attendait : le nombre de CPU est détecté dynamiquement, et les threads schedulés par la JVM peuvent exploiter pleinement les coeurs ajoutés.
578578+579579+Concernant la RAM, étant donné que la JVM fix sa quantité de Heap au démarrage, et que cette valeur ne peut pas être ajustée au runtime, modifier la RAM allouée à un pod Java n'a aucun effet.
580580+581581+Des [drafts de JEP](https://openjdk.org/jeps/8359211) proposent que les différents _Garbage Collector_ (G1, ZGC et Serial) soient modifiés pour pouvoir ajuster à chaud la taille de la Heap en fonction de l'environnement dans lequel s'exécute la JVM. Ces évolutions permettraient donc à terme de pouvoir bénéficier pleinement de cette feature de Kubernetes.
582582+583583+Les VPA (_Vertical Pod Autoscaler_) ont également un nouveau mode appelé `InPlaceOrRecreate` qui permet de redimensionner les pods sans les redémarrer, et de forer une recréation si le redimensionnement n'est pas possible. Cette fonctionnalité rend maintenant l'utilisation des VPA pertinentes pour des applications Java. On peut imaginer qu'un pod verrai son nombre de CPU ajusté à chaud, plutôt que de faire de la scalabilité horizontale.
584584+585585+Il faut cependant limiter cet usage à au CPU, et utiliser un VPA est toujours incompatible avec un HPA, donc l'intérêt reste encore un peu limité.
586586+587587+Voici un exemple de VPA pour une application Java :
588588+589589+```yaml
590590+apiVersion: autoscaling.k8s.io/v1
591591+kind: VerticalPodAutoscaler
592592+metadata:
593593+ name: timbernetes-demo-vpa
594594+spec:
595595+ targetRef:
596596+ apiVersion: "apps/v1"
597597+ kind: Deployment
598598+ name: timbernetes-demo
599599+ updatePolicy:
600600+ updateMode: "InPlaceOrRecreate"
601601+ resourcePolicy:
602602+ containerPolicies:
603603+ - containerName: "timbernetes-demo"
604604+ minAllowed:
605605+ cpu: 100m
606606+ maxAllowed:
607607+ cpu: 2
608608+ controlledResources:
609609+ - cpu
610610+ controlledValues: RequestsAndLimits
611611+```
612612+496613## Liens et références
497614498615Kubernetes :
···506623* La documentation du CLI Scaleway : [Creating and managing a Kubernetes Kapsule with CLI (v2)](https://www.scaleway.com/en/docs/kubernetes/api-cli/creating-managing-kubernetes-lifecycle-cliv2/)
507624* [Scaleway Instances datasheet](https://www.scaleway.com/en/docs/instances/reference-content/instances-datasheet/)
508625JMH :
509509-* Le tuto de Baeldung [Microbenchmarking with Java](https://www.baeldung.com/java-microbenchmark-harness)626626+* Le tuto de Baeldung [Microbenchmarking with Java](https://www.baeldung.com/java-microbenchmark-harness)
627627+Java :
628628+* [JEP draft: Automatic Heap Sizing for G1](https://openjdk.org/jeps/8359211)
629629+* [JEP draft: Automatic Heap Sizing for ZGC](https://openjdk.org/jeps/8377305)
630630+* [JEP draft: Automatic Heap Sizing for the Serial Garbage Collector](https://openjdk.org/jeps/8350152)