Un sujet qui revient souvent sur mes missions et que j’aime particulièrement : les tests de charges. (rien à voir avec l’activité qui consiste à tester diverses boissons houblonnées, le soir, jusqu’à plus soif 🤫)

my name is Earl, joy bourrée, tombe dessous la table

J’aime bien parce que c’est le seul moment dans la vie de l’infra, où c’est acceptable de casser des trucs de manière contrôlée, et ça donne surtout une idée des forces et faiblesses d’une infra. Il faut le faire de préférence sur une preprod le plus ISO possible de la prod. Il faut être bien certain de casser tout ça avant que vos clients s’en chargent gracieusement pour vous sur la prod. 🙄

Car on sait tous que le vrai test c’est l’utilisateur final qui vient faire des trucs de la vraie vie sur ton infra.🙈🙈 Des explorateurs du web, qui testent des trucs, des pentesters du dimanche (qui utilisent votre infra comme sujet d’étude), des robots. 🤖🤖 Et parfois juste TF1 qui parle de vous sans prévenir, et bien évidemment l’infra n’est pas prévue pour accueillir autant de monde d’un coup. 😂😂

Et si tu as juste une prod, bah, fais une preprod 😤😤😤 (ou charge ta prod, mais assume les conséquences ensuite. 😂😂)

Un outil que j’utilisais avant, k6.io. Il marche vraiment bien pour faire des scénarios de charges en rampe, des utilisateurs en parallèle et on peut directement convertir des HAR en fichier js k6 autogéré.

HAR est un format de fichier de type HTTP archive, qui est un enregistrement des requêtes HTTP d’un navigateur, ce qui est très pratique pour faire des scénarios multinavigateurs 😏 Défaut majeur, c’est du JavaScript. Qu’est-ce que je déteste ce langage ouloulou 😤😤😤 C’est pour mon référencement. N’hésite pas à dire pourquoi j’ai tort en bas de page de manière gratuite et agressive !

Chats lounge

chats posés sur un canapé

Voici les challenges auxquels je fais face le plus souvent :

  • maintenir et modifier du JavaScript pourri autogénéré
  • manque de puissance de la machine que j’utilise pour faire des tests
  • débit moisi même si la machine est puissante
  • ça fait des grosses machines coûteuses et spécialisées dans le cloud si on est confronté à une des deux conditions précédentes
  • ça fait une machine snowflake (une machine de dev chaussettes sales) qui traîne et qu’on utilise rarement
    • il faut s’arranger pour régulièrement mettre à jour les scripts comme si on traînait un serveur de prod qui tourne du code en définitive 🤷
  • on pourrait le lancer dans un Gitlab-ci, mais ça fait un Gitlab runner énorme qui tourne en permanence coincé longtemps pour les tests de charges
  • c’est dommage de consommer dans le cloud une machine de grande taille qui ne va pratiquement rien foutre le reste du temps 🤫

Du coup le challenge d’aujourd’hui ça va être de faire des tests de charges dans un Cloudrun, on va essayer de régler 3 problèmes d’un coup :

  • la machine snowflake mal gérée et difficilement reconstructible
    • on va faire des containers avec la dernière version du code autodeployé à chaque push sur gitlab
  • le runner coincé quand les tests tournent
    • gitlab ne portera pas la charge de travail du test de charge, on délégue la charge à cloudrun et on analyse les résultats en asynchrone
  • javaFuckingScript (marque déposée)
    • on va découvrir ensemble locust.io et voir s’ils ont fait aussi bien qu’avec k6 voir peut être mieux
    • au moins on utilisera du Python et on est un peu plus confiant sur la capacité à maintenir et faire évoluer le script
    • oui les rageux, vous pouvez dire que je n’aime pas le JavaScript parce que je ne sais pas faire, ça ne me pose aucun souci on assume les débats stériles

À voir si on arrive à paralléliser aussi bien les utilisateurs, faire des rampes et faire des scénarios un peu sympas. 🤪

gif saut à ski

Contraintes et concepts

On sait que Cloudrun peut avoir un timeout max de 1 heure c’est bien plus qu’il nous faut pour lancer des tests de charges, mais c’est une composante importante à prendre en compte en cas de test beaucoup plus long.

On veut pouvoir stopper le test de charge si ça se passe mal, donc probablement qu’on va utiliser le mode job de Cloudrun plutôt que de wrapper un service Flask avec une route qui lance Locust. Je vais essayer un peu les deux, je pense, et voir où ça nous mène.🤷 (spoiler en fait non)

On part dans l’idée dans un premier temps qu’on se fiche totalement de l’interface web de Locust, on part sur un lancement de scripts en mode headless, et on va stocker les résultats sur GCS.

Ça devrait passer crème si on gère la rétention de GCS et qu’on ne garde pas une quantité trop importante de résultats de test de charges. Disons 30 jours de tests de charges, un par jour c’est déjà pas mal. Sauce magique beau gosse, si tu fais un déplacement vers une rétention longue si tu veux archiver long terme les résultats.

Si on lance ça souvent, ça permet aussi de détecter l’encrassement des bases de données, fuite de mémoires, explosion de cache et réagir en conséquence avant la catastrophe. Effet sympa, tu pourras tranquillement donner les droits GCS et partager ton historique à qui tu veux et bien rangé, fais pas le débile et ne mets pas d’accès public sur le bucket hein.

Sauf si tu veux montrer au monde entier comme ton site tient bien la charge.🤷

Le fait de charger régulièrement la preprod vous donne aussi un indicateur de performance, si vous avez des devs qui livrent comme un mauvais livreur Fedex. 🙂

livreur Fedex balance une télévision par-dessus une clôture

Implémentation

On va faire comme si on ne connaissait pas du tout l’infra qu’on test et on va utiliser un outil de conversion HAR vers Locust. Comme je fais avec k6, on va voir à quel point ça à une sale tronche en Python 😜

https://github.com/zalando-incubator/transformer

On va charger mon site, comme c’est un site statique avec un cache Cloudflare devant ça risque pas grand-chose 😏 Pour avoir un HAR c’est assez simple, je montre sur Chromium et sur les autres le principe est identique.

CTRL + SHIFT + I, onglet network, on n’oublie pas de cocher la case preserve log si on change de page et enfin le bouton export HAR.

capture d’écran de l’onglet Network de la console developer chromium, on y voit les requetes HTTP capturés et l’option de sauvegarde vers une archive HAR.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File automatically generated by Transformer v2.0.0:
# https://github.com/zalando-incubator/Transformer
import re
from distutils.version import LooseVersion
from locust import __version__
LOCUST_MAJOR_VERSION = LooseVersion(__version__).version[0]
if LOCUST_MAJOR_VERSION >= 1:
    from locust import HttpUser
    from locust import SequentialTaskSet
    from locust import TaskSet
    from locust import task
    HttpLocust = HttpUser
    TaskSequence = SequentialTaskSet
    def seq_task(_):
        return task
else:
    from locust import HttpLocust
    from locust import TaskSequence
    from locust import TaskSet
    from locust import seq_task
    from locust import task
class www_cyberpunkachien_fr_har_2380466750(TaskSequence):
    @seq_task(1)
    def POST_https_www_google_fr_613877015__gen_204_192610911_2309116882405801414(self):
        response = self.client.post(url='https://www.google.fr/gen_204?atyp=i&ei=IAYPY-W0JuOAur4P57WQmAU&ct=slh&v=t1&im=M&pv=0.33650410118666496&me=10:1661928998994,V,0,0,0,0:541,h,1,1,i:341,h,1,1,o:26602,h,1,1,i:472,h,1,1,o:2665,e,U&zx=1661929029615', name='https://www.google.fr/gen_204?atyp=i&ei=IAYPY-W0JuOAur4P57WQmAU&ct=slh&v=t1&im=M&pv=0.33650410118666496&me=10:1661928998994,V,0,0,0,0:541,h,1,1,i:341,h,1,1,o:26602,h,1,1,i:472,h,1,1,o:2665,e,U&zx=1661929029615', timeout=30, allow_redirects=False, headers={'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="104"', 'sec-ch-ua-full-version-list': '" Not A;Brand";v="99.0.0.0", "Chromium";v="104.0.5112.101"', 'sec-ch-ua-mobile': '?0', 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.101 Safari/537.36', 'sec-ch-ua-arch': '"x86"', 'sec-ch-ua-full-version': '"104.0.5112.101"', 'sec-ch-ua-platform-version': '"5.4.0"', 'Referer': 'https://www.google.fr/', 'sec-ch-ua-bitness': '"64"', 'sec-ch-ua-wow64': '?0', 'sec-ch-ua-model': '', 'sec-ch-ua-platform': '"Linux"'})
    @seq_task(2)
    def GET_https_cyberpunkachien_fr_1186203458___3145776_8742243703866417757(self):
        response = self.client.get(url='https://cyberpunkachien.fr/', name='https://cyberpunkachien.fr/', timeout=30, allow_redirects=False, headers={'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,**/**;q=0.8,application/signed-exchange;v=b3;q=0.9', 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7', 'if-modified-since': 'Mon, 13 Jun 2022 10:08:28 GMT', 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="104"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Linux"', 'sec-fetch-dest': 'document', 'sec-fetch-mode': 'navigate', 'sec-fetch-site': 'none', 'sec-fetch-user': '?1', 'upgrade-insecure-requests': '1', 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.101 Safari/537.36'})
    @seq_task(3)

[...]
class LocustForwww_cyberpunkachien_fr_har_2380466750(HttpLocust):
    if LOCUST_MAJOR_VERSION >= 1:
        tasks = [www_cyberpunkachien_fr_har_2380466750]
    else:
        task_set = www_cyberpunkachien_fr_har_2380466750
    weight = 1
    min_wait = 0
    max_wait = 10%   

Oui c’est moche, mais avec un coup de Black on y voit tout de suite plus clair ! Black c’est un linter Python, super cool, si tu ne connais pas je t’invite à faire un tour https://github.com/psf/black . Tu mets ça en git hook pour toute ton équipe et plus jamais tu te poses des questions de format en Python.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class www_cyberpunkachien_fr_har_2380466750(TaskSequence):
    @seq_task(1)
    def POST_https_www_google_fr_613877015__gen_204_192610911_2309116882405801414(self):
        response = self.client.post(
            url="https://www.google.fr/gen_204?atyp=i&ei=IAYPY-W0JuOAur4P57WQmAU&ct=slh&v=t1&im=M&pv=0.33650410118666496&me=10:1661928998994,V,0,0,0,0:541,h,1,1,i:341,h,1,1,o:26602,h,1,1,i:472,h,1,1,o:2665,e,U&zx=1661929029615",
            name="https://www.google.fr/gen_204?atyp=i&ei=IAYPY-W0JuOAur4P57WQmAU&ct=slh&v=t1&im=M&pv=0.33650410118666496&me=10:1661928998994,V,0,0,0,0:541,h,1,1,i:341,h,1,1,o:26602,h,1,1,i:472,h,1,1,o:2665,e,U&zx=1661929029615",
            timeout=30,
            allow_redirects=False,
            headers={
                "sec-ch-ua": '" Not A;Brand";v="99", "Chromium";v="104"',
                "sec-ch-ua-full-version-list": '" Not A;Brand";v="99.0.0.0", "Chromium";v="104.0.5112.101"',
                "sec-ch-ua-mobile": "?0",
                "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.101 Safari/537.36",
                "sec-ch-ua-arch": '"x86"',
                "sec-ch-ua-full-version": '"104.0.5112.101"',
                "sec-ch-ua-platform-version": '"5.4.0"',
                "Referer": "https://www.google.fr/",
                "sec-ch-ua-bitness": '"64"',
                "sec-ch-ua-wow64": "?0",
                "sec-ch-ua-model": "",
                "sec-ch-ua-platform": '"Linux"',
            },
        )

    @seq_task(2)
    def GET_https_cyberpunkachien_fr_1186203458___3145776_8742243703866417757(self):
        response = self.client.get(
            url="https://cyberpunkachien.fr/",
            name="https://cyberpunkachien.fr/",
            timeout=30,
            allow_redirects=False,
            headers={
                "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,**/**;q=0.8,application/signed-exchange;v=b3;q=0.9",
                "accept-encoding": "gzip, deflate, br",
                "accept-language": "fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7",
                "if-modified-since": "Mon, 13 Jun 2022 10:08:28 GMT",
                "sec-ch-ua": '" Not A;Brand";v="99", "Chromium";v="104"',
                "sec-ch-ua-mobile": "?0",
                "sec-ch-ua-platform": '"Linux"',
                "sec-fetch-dest": "document",
                "sec-fetch-mode": "navigate",
                "sec-fetch-site": "none",
                "sec-fetch-user": "?1",
                "upgrade-insecure-requests": "1",
                "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.101 Safari/537.36",
            },
        )

    @seq_task(3)
    def GET_https_www_cyberpunkachien_fr_1735067861__css_main_min_d97b8381a6ce6529d30c75bb07eb9fa303311027feb38f87334d584664382893_css_3870496476_7849068206355491087(
        self,
    ):
        response = self.client.get(
            url="https://www.cyberpunkachien.fr/css/main.min.d97b8381a6ce6529d30c75bb07eb9fa303311027feb38f87334d584664382893.css",
            name="https://www.cyberpunkachien.fr/css/main.min.d97b8381a6ce6529d30c75bb07eb9fa303311027feb38f87334d584664382893.css",
            timeout=30,
            allow_redirects=False,
        )
[...]

On n’échappe pas aux structures de donnée un peu profondes, mais chaque requête est découpée unitairement et je n’ai rien à faire pour avoir de suite un résultat et je ne vais pas m’en priver. Si on compare avec k6, plus de {} de , et de () et des requêtes chainées, plus il faut bricoler un peu plus pour avoir un résultat exploitable. Dis-moi dans les commentaires si je suis de mauvaise foi ou pas haha (oui je force un peu)

kaamelott, face à la mauvaise foi, y’a que le mépris

/!\ attention ce qui suit est un morceau de JavaScript généré pour k6, rien à voir avec le coolissime Locust.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// Creator: WebInspector 537.36

import { sleep, group } from 'k6'
import http from 'k6/http'

export const options = {}

export default function main() {
  let response

  response = http.post(
    'https://www.google.fr/gen_204?atyp=i&ei=IAYPY-W0JuOAur4P57WQmAU&ct=slh&v=t1&im=M&pv=0.33650410118666496&me=10:1661928998994,V,0,0,0,0:541,h,1,1,i:341,h,1,1,o:26602,h,1,1,i:472,h,1,1,o:2665,e,U&zx=1661929029615',
    null,
    {
      headers: {
        'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="104"',
        'sec-ch-ua-full-version-list': '" Not A;Brand";v="99.0.0.0", "Chromium";v="104.0.5112.101"',
        'sec-ch-ua-mobile': '?0',
        'User-Agent':
          'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.101 Safari/537.36',
        'sec-ch-ua-arch': '"x86"',
        'sec-ch-ua-full-version': '"104.0.5112.101"',
        'sec-ch-ua-platform-version': '"5.4.0"',
        Referer: 'https://www.google.fr/',
        'sec-ch-ua-bitness': '"64"',
        'sec-ch-ua-wow64': '?0',
        'sec-ch-ua-model': '',
        'sec-ch-ua-platform': '"Linux"',
      },
    }
  )

  group('page_1 - https://cyberpunkachien.fr/', function () {
    response = http.get('https://cyberpunkachien.fr/', {
      headers: {
        accept:
          'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,**/**;q=0.8,application/signed-exchange;v=b3;q=0.9',
        'accept-encoding': 'gzip, deflate, br',
        'accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7',
        'if-modified-since': 'Mon, 13 Jun 2022 10:08:28 GMT',
        'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="104"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Linux"',
        'sec-fetch-dest': 'document',
        'sec-fetch-mode': 'navigate',
        'sec-fetch-site': 'none',
        'sec-fetch-user': '?1',
        'upgrade-insecure-requests': '1',
        'user-agent':
          'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.101 Safari/537.36',
      },
    })
    response = http.get(
      'https://www.cyberpunkachien.fr/css/main.min.d97b8381a6ce6529d30c75bb07eb9fa303311027feb38f87334d584664382893.css'
    )
    response = http.get('https://cyberpunkachien.fr/img/logo_cyberpunk.png', {
      headers: {
        accept: 'image/avif,image/webp,image/apng,image/svg+xml,image/**,**/**;q=0.8',
        'accept-encoding': 'gzip, deflate, br',
        'accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7',
        referer: 'https://cyberpunkachien.fr/',
        'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="104"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Linux"',
        'sec-fetch-dest': 'image',
        'sec-fetch-mode': 'no-cors',
        'sec-fetch-site': 'same-origin',
        'user-agent':
          'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.101 Safari/537.36',
      },
    })

Donc on prend notre fichier Locust qu’on peut lancer soit en headless, soit en mode web. Le mode web est pas mal puisqu’on peut organiser des classes et changer avec un formulaire le nombre d’utilisateurs, mais nous on veut lancer les tests en mode batch et de façon répétée et automatique.

Donc on va faire comme dans Apollo 13, on va mettre un truc rond dans un truc carré. (je vous conseille le film du même nom, avec T.Hanks)

Pour ceux qui n’ont pas la ref, pendant le premier vol lunaire retour, un réservoir d’oxygène a explosé. Ils ont fait un bricolage dégueu, mais méthodique pour permettre de faire rentrer un tuyau rond dans un trou carré pour de nouveau pouvoir respirer.

image du film appolo 13, ou on brainstorm sur le trou carré et le tuyau rond.

J’arrête ici, parce que je dis graisse, et ça commence à sentir la friture, comme la suite de cet article d’ailleurs.

premier problème non anticipé, changer les restrictions kernel

Pour que nos tests de charges puissent bombarder, il faut pouvoir écrire des fichiers comme des neuneu sur le disque et parfois faire sauter les restrictions du nombre de connexions simultanées.

Par défaut sur Linux Mint (ma machine de dev, ouloulou ce hipster) c’est max 1024 fichiers, c’est loin d’être suffisant, sur cloudrun 4086 on devrait pouvoir faire des trucs 😏 sur une machine classique on fait comme ça, en container c’est plus compliqué puisque sysctl demande un accès privilégié et ulimit c’est non.

1
2
3
4
5
sudo sysctl -w fs.file-max=500000
fs.file-max = 500000

on oublie pas les softs limites
ulimit -Sn 500000

Du coup on va chercher des moyens de contournement pour quand même lancer les tests de charges en Cloudrun.

On va builder un docker qui va contenir le code de mes tests de charges et Locust, pour pouvoir livrer tout ça salement avec un git hook dans un premier temps. On va déjà essayer de lancer Locust en mode headless dans un Cloudrun job manuel, et on verra pour trouver une solution pour les restrictions kernel ou du moins les limites du système. Sinon on pivotera le projet, vers une industrialisation de Locust distribuée sur des gros computes à papa. J’espère sincèrement ne pas en arriver là 😜 sinon on resort Ansible du placard, ça ne va pas aider mon allergie au yaml cette histoire.

On fait un Dockerfile un peu pourri avec un script d'entrypoint pour wrapper la commande Locust. Les variables d’envs seront fournies au provisioning du job Cloudrun et seront pilotées ensuite depuis la ci/cd.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
FROM python:3.10.6-slim-bullseye
ENV PATH $PATH:/usr/local/gcloud/google-cloud-sdk/bin
RUN apt update && apt upgrade -y
RUN apt install gcc g++ curl -y
RUN pip3 install --upgrade pip
RUN pip3 install locust
RUN curl https://dl.google.com/dl/cloudsdk/release/google-cloud-sdk.tar.gz > /tmp/google-cloud-sdk.tar.gz
RUN mkdir -p /usr/local/gcloud \
  && tar -C /usr/local/gcloud -xvf /tmp/google-cloud-sdk.tar.gz \
  && /usr/local/gcloud/google-cloud-sdk/install.sh
COPY . .

On installe GCP cloud SDK pour nous permettre d’utiliser gsutil qui est le client google cloud storage. C’est lourd, on aurai pu wrapper tout ça dans du Python, mais le use case est tellement simple que ça serait plus verbeux et c’est un peu bête. L’idée c’est de lancer le test de charge et de l’exporter au format HTML pour avoir un rapport tout prêt téléchargeable directement dans un bucket GCS.

1
2
3
4
5
6
7
#!/bin/bash
set -x
html_report="locust_$(date +"%Y_%m_%d-%Hh%Mm%Ss").html"
locust -f locustfile.py --headless -u "${MAX_USER}" -r "${SPAW_RATE}" --run-time "${RUN_TIME}" --stop-timeout "${STOP_TIMEOUT}" --host "${HOST}" --html="${html_report}"
locust_return_code="$?"
gsutil cp "${html_report}" "${GCS_REPORT_BUCKET}"
exit "$locust_return_code"

On va bien sûr donner un service account avec les permissions d’écriture sur les buckets GCS, et on est tout bon pour avoir un concept sale, mais fonctionnel.

meme, hey, As long as it works.

deuxième problème, où est le module Terraform ?

L’idée de lancer Locust en mode Cloudrun job semble bien, mais comme le service est tout neuf et encore en beta à l’heure ou j’écris ces lignes, il n’existe pas de module Terraform pour provisionner des jobs Cloudrun. Qu’à cela ne tienne ! On va essayer de rentrer ça à coup de pioche en utilisant gcloud et en mettant de la glue chaude un peu partout 🙈

On fait dans le gluant et le suintant, après tout c’est le concept ici hein ? 😏

Ici mon git hook de l’enfer, le service account et les permissions faites à la main.

On reprend le même template que d’habitude et bien sûr on ne l’améliore pas d’un pouce. 🙈 Je laisse mes noms de projets et mes infos de bucket, de toutes façons les ressources et le projet seront détruits à la fin de cet article …

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/bash

function shameful_cicd_function(){

   gcloud config set project coucou-5e1ab
   gcloud auth configure-docker europe-west1-docker.pkg.dev
   export TF_VAR_cloudrun_image=europe-west1-docker.pkg.dev/coucou-5e1ab/coucou/locustcyberpunk:$(git rev-parse --short HEAD)
   gcloud builds submit --tag "$TF_VAR_cloudrun_image" --timeout="9000" 
   #gcloud beta run jobs create cyberpunkachien --image $TF_VAR_cloudrun_image --command=/bin/bash --args=entrypoint.sh  --region=europe-west1 --project=coucou-5e1ab --memory=8G --cpu=4 --set-env-vars='MAX_USER=2000,SPAW_RATE=100,RUN_TIME=10m,STOP_TIMEOUT=99,HOST=https://cyberpunkachien.fr/,GCS_REPORT_BUCKET=gs://locust_report/' --service-account=cloudrun-cyberpunkachienlocust@coucou-5e1ab.iam.gserviceaccount.com --task-timeout=1h
   gcloud beta run jobs update cyberpunkachien --image $TF_VAR_cloudrun_image --command=/bin/bash --args=entrypoint.sh  --region=europe-west1 --project=coucou-5e1ab --memory=8G --cpu=4 --set-env-vars='MAX_USER=2000,SPAW_RATE=100,RUN_TIME=10m,STOP_TIMEOUT=99,HOST=https://cyberpunkachien.fr/,GCS_REPORT_BUCKET=gs://locust_report/' --service-account=cloudrun-cyberpunkachienlocust@coucou-5e1ab.iam.gserviceaccount.com --task-timeout=1h
   gcloud beta run jobs execute cyberpunkachien --region=europe-west1 --project=coucou-5e1ab
}

if [ -z "$(git status --porcelain)" ]; then
  # Working directory clean
  shameful_cicd_function  & disown
  echo ""
fi

exit 0

La Première fois on fait create job, toutes les autres fois update, c’est moche, ça me plait! On utilise le gcloud beta, on bricole à la main, pas de ressource Terraform, pas d’optimisations, parfait ! Il fallait que je teste ce truc, et le moins qu’on puisse dire c’est qu’y’a du potentiel, mais faudra attendre un peu pour que ça soit industrialisable proprement.

J’aime bien le dashboard qui nous donne en un coup d’œil les tâches en cours, réussies et échouées.

capture d’écran du dashboard cloudrun jobs, on y voit les jobs en cours, échoué et réussi

C’est bien plus pratique que de fourrer un script en mode web service et déclencher une alerte sur les logs en erreur.

Bon pour le coup la peinture est encore fraîche sur ce service, mais on sait où on met les pieds, et c’est souvent dans la gueule !

Chuck Norris, je mets les pieds où je veux Little John. Et c’est souvent dans la gueule !

Conclusion et Délusion

Bon la restriction du nombre de fichiers c’est franchement pénible, mais on peut lancer dans les 2000 utilisateurs si on lance des tests simples et raisonnables. Je pense que le jeu en vaut la chandelle parce qu’en termes de coût et de praticité on est imbattable ! Plus jamais on fait une connexion ssh et des ssh cp, des git clone ou que sais-je encore ? Si on a vraiment besoin de charger, on peut lancer le même job, mais dans des containers concurrents, il suffit de faire ensuite une comparaison sur plusieurs résultats en matchant la date du fichier.

Je pense que plutôt que trainer un énorme compute ou pire encore un Windows serveur avec Jmeter (je parle ici d’une expérience passée, les vrais savent), en termes de coût on est imbattable et en termes de sécu aussi. Pour mettre à jour les scripts, Locust fait gagner un temps précieux et permet avec peu de travail d’avoir un truc maintenable et/ou jetable. Le fait de produire des containers docker jetable permet d’être un peu moins strict et de pouvoir expérimenter sans problème. On peut en foutre partout sans le risque d’avoir une machine chaussette sale qui traine quelque part et qui tourne pour rien.

On oublie l’idée d’utiliser ça en prod pour le moment, il faut vraiment attendre que GCP sorte ça en version stable, parce que sinon c’est vraiment bricole et casserole et ce n’est pas terrible.

Petit coup de cœur pour ce service, par rapport à un cron Gitlab ou un cron Kubernetes. J’aime l’idée que le truc peut tourner de manière indépendante et sans forcément faire des bricolages compliqués ou trainer la complexité de Kubernetes pour une tache simple. Ce n’est pas cher, c’est moche, mais ça rend service, un peu comme un pull de Noël en septembre pour la rentrée.

Bisous

pull de Noël moche