Kubestr et CNPG Bench pour tester vos volumes persistants

Sommaire

"C'est lent", "Ça marche mieux sur mon poste local", des remarques que j'ai eues il y a quelque temps sur des bases de données hébergées sur Kubernetes, avec des Volumes Persistants. Souhaitant avoir des données objectives avant de râler contre ceux qui se plaignent, j'ai trouvé 2 outils sympa pour tester les performances des PV (Persistant Volumes) sur mes clusters. Voici un aperçu.

Note : Je ne vais pas expliquer ici comment tuner vos disques, ni tuner les applications de test pour optimiser les perfs.

Kubestr

Kubestr est une collection d'outils pour "découvrir, valider et évaluer les options de stockages sur Kubernetes". C'est un outil fournit par Kasten, un des leaders des solutions de protection des données en environnement Kubernetes.

Parmi les outils proposés par kubestr, on retrouve le fameux fio, que les "vieux" sysadmin comme moi connaissent. Pour faire simple, fio va faire des tests de lecture / écriture avec plusieurs paramètres, et en sortir des stats.

Ainsi, kubestr va nous permettre assez simplement de :

  • Créer un volume de taille définie à partir d'une classe de stockage choisie
  • Lancer un test de performances avec FIO
  • Nous donner les résultats.

Un schéma repris du blogpost de Kasten expliquant le process :

Benchmarking and Evaluating Your Kubernetes Storage with Kubestr

Installation

Kubestr est un binaire qui se télécharge directement depuis la page de releases GitHub. À la date d'écriture de l'article, nous sommes à la version v0.4.41. On récupère l'archive, on la décompresse et on lui donne les droits d'exécution :

1$ curl -LJO https://github.com/kastenhq/kubestr/releases/download/v0.4.41/kubestr_0.4.41_Linux_amd64.tar.gz 
2$ tar zxvf kubestr_0.4.41_Linux_amd64.tar.gz kubestr
3$ chmod +x kubestr
4$ ./kubestr --help
5kubestr is a tool that will scan your k8s cluster
6                and validate that the storage systems in place as well as run
7                performance tests.

Utilisation

Il existe plusieurs options d'utilisation de l'outil, ce qui nous intéresse ici est le module "FIO". Plusieurs options de lancement sont possibles, notamment :

  • -f, --fiofile : un fichier de configuration fio. On pourra trouver des exemples ici
  • -z, --size : taille du volume qui sera créé. Selon les providers, les I/O dépendent de la taille du volume (exemple avec le High Speed Gen2 de chez OVHCloud)
  • -s, --storageclass : type de StorageClass du volume persistant utilisé pour le test

Pour l'exemple, on va tester les 2 types de classe de stockage par défaut d'un cluster OVHCloud, avec un volume de 25Gb.

 1$ ./kubestr fio -z 25Gi -s csi-cinder-high-speed
 2PVC created kubestr-fio-pvc-wn4f6
 3Pod created kubestr-fio-pod-hxfvs
 4Running FIO test (default-fio) on StorageClass (csi-cinder-high-speed) with a PVC of Size (25Gi)
 5Elapsed time- 1m20.178437269s
 6FIO test results:
 7  
 8FIO version - fio-3.34
 9Global options - ioengine=libaio verify=0 direct=1 gtod_reduce=1
10
11JobName: read_iops
12  blocksize=4K filesize=2G iodepth=64 rw=randread
13read:
14  IOPS=1412.357544 BW(KiB/s)=5666
15  iops: min=1220 max=1724 avg=1427.666626
16  bw(KiB/s): min=4880 max=6896 avg=5711.066895
17
18JobName: write_iops
19  blocksize=4K filesize=2G iodepth=64 rw=randwrite
20write:
21  IOPS=825.023193 BW(KiB/s)=3316
22  iops: min=462 max=1292 avg=827.566650
23  bw(KiB/s): min=1848 max=5170 avg=3310.566650
24
25JobName: read_bw
26  blocksize=128K filesize=2G iodepth=64 rw=randread
27read:
28  IOPS=1657.303345 BW(KiB/s)=212667
29  iops: min=1490 max=1858 avg=1671.933350
30  bw(KiB/s): min=190720 max=237824 avg=214017.406250
31
32JobName: write_bw
33  blocksize=128k filesize=2G iodepth=64 rw=randwrite
34write:
35  IOPS=862.746399 BW(KiB/s)=110966
36  iops: min=692 max=1298 avg=865.000000
37  bw(KiB/s): min=88576 max=166144 avg=110755.734375
38
39Disk stats (read/write):
40  sdb: ios=51692/28087 merge=445/670 ticks=2147425/2151581 in_queue=4303451, util=99.532051%
41  -  OK

La même commande sur une StorageClass "classic" :

 1$ ./kubestr fio -z 25Gi -s csi-cinder-classic
 2PVC created kubestr-fio-pvc-8pnmw
 3Pod created kubestr-fio-pod-z9bnn
 4Running FIO test (default-fio) on StorageClass (csi-cinder-classic) with a PVC of Size (25Gi)
 5Elapsed time- 41.494978608s
 6FIO test results:
 7  
 8FIO version - fio-3.34
 9Global options - ioengine=libaio verify=0 direct=1 gtod_reduce=1
10
11JobName: read_iops
12  blocksize=4K filesize=2G iodepth=64 rw=randread
13read:
14  IOPS=120.751854 BW(KiB/s)=498
15  iops: min=44 max=184 avg=123.064514
16  bw(KiB/s): min=176 max=736 avg=492.354828
17
18[...]
19
20Disk stats (read/write):
21  sdb: ios=4468/2655 merge=8/151 ticks=2224172/1996479 in_queue=4222117, util=99.475037%
22  -  OK

L'application a créé à chaque fois un PVC avec la bonne taille, dans la bonne StorageClass, créé un pod, monté le volume persistant, fait les tests FIO et nous a sorti les résultats. Easy !

Résultats

Les résultats nous arrivent par défaut sur la console, et on peut voir que plusieurs tests ont été faits :

  • read_iops
  • write_iops
  • read_bw
  • write_bw

Je ne vais pas commenter les performances des volumes persistants ici, mais on voit bien que le "high-speed" est plus rapide que le "classic" ;)

Exemple sur les tests en lecture :

 1# High-Speed
 2JobName: read_iops
 3  blocksize=4K filesize=2G iodepth=64 rw=randread
 4read:
 5  IOPS=1412.357544 BW(KiB/s)=5666
 6  iops: min=1220 max=1724 avg=1427.666626
 7  bw(KiB/s): min=4880 max=6896 avg=5711.066895
 8
 9# Classic
10JobName: read_iops
11  blocksize=4K filesize=2G iodepth=64 rw=randread
12read:
13  IOPS=120.751854 BW(KiB/s)=498
14  iops: min=44 max=184 avg=123.064514
15  bw(KiB/s): min=176 max=736 avg=492.354828

Il est possible de sortir les résultats au format JSON si besoin.

CloudNativePG Bench

En cherchant d'autres outils, je suis tombé sur l'opérateur CloudNativePG qui permet de gérer des bases de données PSQL. Au premier abord aucun lien avec les tests de performances des disques. Sauf que le projet propose un plugin kubectl, avec la possibilité de benchmarker les disques sur lesquels pourront tourner les bases psql !

Le plugin cnpg utilise comme l'outil précédent fio, plus d'infos sur les options du plugin ici.

Installation

Quand on parle de plugin kubectl, le meilleur moyen d'installer est d'utiliser krew. On va donc utiliser cette méthode :

 1$ kubectl krew install cnpg 
 2Updated the local copy of plugin index.
 3Installing plugin: cnpg
 4Installed plugin: cnpg
 5\
 6 | Use this plugin:
 7 |      kubectl cnpg
 8 | Documentation:
 9 |      https://github.com/cloudnative-pg/cloudnative-pg
10/
11
12$ kubectl cnpg version  
13Build: {Version:1.21.0 Commit:9bc5b9b2 Date:2023-10-12}
14
15$ kubectl cnpg fio --help
16Creates a fio deployment that will execute a fio job on the specified pvc.

Utilisation

On retrouve sensiblement les mêmes options que kubestr pour tester les volumes :

  • --pvcSize : taille du PVC
  • --storageClass : type de classe de stockage
  • Nom du job à fournir

Ici pas de possibilité de fournir un fichier de configuration FIO. Par contre, l'option --dry-run permet de générer les manifests YAML qui seront utilisés pour le déploiement. On remarquera la création d'une ConfigMap qui contient les paramètres suivants :

 1apiVersion: v1
 2kind: ConfigMap
 3metadata:
 4  name: fio-job
 5data:
 6  job: |-
 7    [read]
 8        direct=1
 9        bs=8k
10        size=1G
11        time_based=1
12        runtime=60
13        ioengine=libaio
14        iodepth=32
15        end_fsync=1
16        log_avg_msec=1000
17        directory=/data
18        rw=read
19        write_bw_log=read
20        write_lat_log=read
21        write_iops_log=read    

On pourra modifier alors cela si besoin de lancer des jobs fio différents.

On lance les 2 tests avec les mêmes caractéristiques que précédemment (les 2 StoragesClass, avec disque de 25Gb) :

 1$ kubectl cnpg fio fio-job-high-speed --storageClass csi-cinder-high-speed --pvcSize 25Gi
 2Running this directly to the cluster may produce a disruption in the service, are you sure you want to proceed? (y/n)
 3y
 4PersistentVolumeClaim/fio-job-high-speed created
 5ConfigMap/fio-job-high-speed created
 6Deployment/fio-job-high-speed created
 7To remove this test you need to delete the Deployment, ConfigMap and PVC with the name fio-job-high-speed
 8
 9The most simple way to do this is to re-run the command that was runto generate the deployment with the --dry-run flag and pipe that output to kubectl delete, e.g.:
10
11kubectl cnpg fio <fio-job-name> --dry-run | kubectl delete -f 

Tiens ! Ici pas de résultats... Mais un déploiement qui se crée, fait sa vie, et ne sera pas arrêté à la fin du test. On devra supprimer le déploiement nous-même.

Lançons le 2ème test :

 1$ kubectl cnpg fio fio-job-classic --storageClass csi-cinder-classic --pvcSize 25Gi 
 2Running this directly to the cluster may produce a disruption in the service, are you sure you want to proceed? (y/n)
 3y
 4PersistentVolumeClaim/fio-job-classic created
 5ConfigMap/fio-job-classic created
 6Deployment/fio-job-classic created
 7To remove this test you need to delete the Deployment, ConfigMap and PVC with the name fio-job-classic
 8
 9The most simple way to do this is to re-run the command that was runto generate the deployment with the --dry-run flag and pipe that output to kubectl delete, e.g.:
10
11kubectl cnpg fio <fio-job-name> --dry-run | kubectl delete -f -

Bon, on a bien nos 2 déploiements, avec mes 2 PVCs, il a dû se passer des "trucs" ?

1$ kubectl get deploy,pvc
2NAME                                 READY   UP-TO-DATE   AVAILABLE   AGE
3deployment.apps/fio-job-classic      0/1     1            0           83s
4deployment.apps/fio-job-high-speed   1/1     1            1           5m17s
5
6NAME                                       STATUS   VOLUME                                                                   CAPACITY   ACCESS MODES   STORAGECLASS            AGE
7persistentvolumeclaim/fio-job-classic      Bound    ovh-managed-kubernetes-wqforj-pvc-fec4df30-b026-4f34-9dc2-6ad59dde805e   25Gi       RWO            csi-cinder-classic      83s
8persistentvolumeclaim/fio-job-high-speed   Bound    ovh-managed-kubernetes-wqforj-pvc-92f26269-6c0d-4953-9e5f-d4b4c88ab12e   25Gi       RWO            csi-cinder-high-speed   5m17s

Résultats

Pour voir les résultats, en fait le déploiement utilise fio-tools pour générer de "beaux" graphiques, et les données sont disponibles via un service web ! Il va falloir lancer un navigateur web pour voir cela, avec un coup de port-forwarding :

1$ kubectl port-forward deployment/fio-job-high-speed 8000
2Forwarding from 127.0.0.1:8000 -> 8000
3Forwarding from [::1]:8000 -> 8000

On va retrouver sur l'interface web :

  • les résultats du job fio au format texte
  • les données de chaque job, au format brut/texte (fichiers .log)
  • des graphes visuels montrant les perfs au format PNG

Voici, pour l'exemple, le résultat du précédent tests sur du disque "high-speed" :

Resultats FIO avec cnpg

Mes remarques

Avec ces tests, j'ai pu confirmer que les gens qui se plaignaient au début avaient raison, et j'ai pu adapter le type de PVC pour coller au besoin applicatif. Remarque pour celles & ceux travaillant sur des clusters OVHcloud, pensez à créer la StorageClass "high-speed-gen2" qui n'est pas activée par défaut ;)

Concernant les 2 outils, ma préférence pour les tests va au plugin cpng, qui avec la ConfigMap & les graphes est plus sympa d'utilisation. Juste pensez-bien à supprimer les déploiements après tests.

Pour kubestr, ne pas négliger les autres options de l'outil comme kubestr browse qui permet de créer un snapshot de volume et naviguer dedans, très pratique !