Cocktailand - Monitorer les performances d'un site web

Comment monitorer la performance d'un site web avec webpagetest.

Publié le 29/05/2018

Les outils

Monitorer les performances WEB d'un site web est vraiment indispensable, mais ce n'est pas simple de le faire gratuitement. Il existe des outils en ligne comme SpeedCurve mais c'est relativement cher.

L'idée ici est d'avoir un suivi, certes plus simple, mais de façon gratuite. Il se trouve que j'ai déjà une stack graphite/grafana qui tourne en production. Si ce n'est pas votre cas et que vous avez un serveur avec docker qui tourne dessus vous pouvez toujours démarrer un container avec l'image docker-grafana-graphite pour tester la suite de cet article. Une fois que votre graphite est en ligne on va pouvoir commencer à pousser des metrics dedans. Un outil est très connu pour obtenir des metrics de performance sur un site web. Webpagetest, dont j'ai déjà parlé dans un billet précédent sur la mise en place de varnish et des ESIs. Il se trouve que webpagetest fournie une api.

Pour obtenir une clef d'api il faut en faire la demande sur le site de WebPageTest.

Vous recevrez votre clef par email automatiquement sous quelques minutes.

La mise en place

Il ne reste plus qu'a faire le lien entre les retours de l'API de webpagetest et notre graphite. J'ai choisi de faire un petit script en node js, mais vous pouvez le faire avec ce que vous voulez.

Installer les dépendances

npm install statsd-client webpagetest --save

Le script

Très claireiment c'est pas un script optimisé ni prévu pour monitorer plusieurs pages d'un site. Maintenant si vous avez besoin d'étendre son scope ce ne sera pas trop difficile.

Il y a quelques placeholder dans le script à remplacer par vos valeurs:

  • NOM_DU_SITE_OU_DE_LA_PAGE: Le nom de votre site en snake case (ex: cocktailand_fr)
  • VOTRE_URL: L'url de la page à monitorer (ex https://cocktailand.fr)
  • VOTRE_HOST_GRAPHITE: L'ip ou le hostname de votre graphite
  • VOTRE_API_KEY: La clef d'api que WebPageTest vous a donnée

    const WebPageTest = require('webpagetest');
    const http = require('http');
    const SDC = require('statsd-client'),
        sdc = new SDC({
            prefix: '<NOM_DU_SITE_OU_DE_LA_PAGE>.webpagetest',
            host: '<VOTRE_HOST_GRAPHITE>',
            port: 8125
        });
    
    const wpt = new WebPageTest('www.webpagetest.org', '<VOTRE_API_KEY>');
    
    wpt.runTest('<VOTRE_URL>', {location: 'ec2-eu-west-1:Chrome'}, (err, data) => {
        if (err) {
            console.error(err);
            return;
        }
    
        let jsonUrl = data.data.jsonUrl;
        let iterationNumber = 0;
        let stop = setInterval(function () {
    
            // 20 mins max
            if (iterationNumber > 600) {
                clearInterval(stop);
    
                return;
            }
            iterationNumber ++;
    
            http.get(jsonUrl, (resp) => {
                let rawData = '';
    
                // A chunk of data has been recieved.
                resp.on('data', (chunk) => {
                    rawData += chunk;
                });
    
                // The whole response has been received. Print out the result.
                resp.on('end', () => {
                    let response = JSON.parse(rawData);
    
                    if (response.statusCode === 200) {
                        clearInterval(stop);
    
                        ['firstView', 'repeatView'].forEach((key) => {
                            sdc.gauge(key + '.first_paint', response.data.median[key].firstPaint);
                            sdc.gauge(key + '.fully_loaded', response.data.median[key].fullyLoaded);
                            sdc.gauge(key + '.bytes_out', response.data.median[key].bytesOut);
                            sdc.gauge(key + '.image_total', response.data.median[key].image_total);
                            sdc.gauge(key + '.load_time', response.data.median[key].loadTime);
                            sdc.gauge(key + '.speed_index', response.data.median[key].SpeedIndex);
                            sdc.gauge(key + '.cached', response.data.median[key].cached);
                            sdc.gauge(key + '.dom_interactive', response.data.median[key].domInteractive);
                            sdc.gauge(key + '.nb_domains', Object.keys(response.data.median[key].domains).length);
                            sdc.gauge(key + '.resources.html.size', response.data.median[key].breakdown.html.bytes);
                            sdc.gauge(key + '.resources.html.nb', response.data.median[key].breakdown.html.requests);
                            sdc.gauge(key + '.resources.css.size', response.data.median[key].breakdown.css.bytes);
                            sdc.gauge(key + '.resources.css.nb', response.data.median[key].breakdown.css.requests);
                            sdc.gauge(key + '.resources.js.size', response.data.median[key].breakdown.js.bytes);
                            sdc.gauge(key + '.resources.js.nb', response.data.median[key].breakdown.js.requests);
                            sdc.gauge(key + '.resources.image.size', response.data.median[key].breakdown.image.bytes);
                            sdc.gauge(key + '.resources.image.nb', response.data.median[key].breakdown.image.requests);
                        });
                    }
                });
    
            }).on("error", (err) => {
                console.log("Error: " + err.message);
            });
        }, 2000);
    });

Cronner le script

Je fait tourner le script toutes les heures.

0 * * * * node webPageTest.js

De cette manière je peux analyser le comportement du site sur une journée complète et éliminer les fausses alertes.

Il est en effet possible d'avoir des résultats qui varient en fonctions de plusieurs facteurs extérieur:

  • La saturation du réseau de webPageTest
  • Un agent de webpagetest qui est plus lent qu'un autre
  • Des publicités mal optimisées de la part d'un partenaire

Il ne faut pas s'inquiéter si un résultat est moins bon qu'un autre.

Le but ici est d'analyser une tendance.

Faire un beau dashboard

Maintenant que vous avez toutes vos informations dans votre graphite on peut commencer à grapher ce qui nous intéresse.

Voici par exemple mon dashboard:

Grafana cocktailand

Pour ma part je surveille principalement les temps de réponse et le cache avec mon dashboard, mais en fonction des objectifs que l'on se donne on peut axer les graphiques sur d'autres KPIs.

Conclusion

J'ai un suivi des performances. Si grafana est à jour (ce qui n'est pas mon cas) il est même possible d'envoyer des mails d'alertes quand on dépasse une limite fixée.

Après plusieurs semaines d'exploitation je me rends compte que le site en lui-même est stable et avec des performances prévisibles, mais que les grandes inconnues sont les publicités. Une mauvaise pub sur le site peut anéantir tout le travail fait en amont sur les performances.