Profiler une application PHP avec xhgui et xhprof sous docker

Introduction

Dans le monde des applications PHP, il existe plusieurs outils qui permettent d’analyser le temps d’exécution du code et de cibler quelles instructions ralentissent le rendu des pages HTML. Le plus connue est sans doute Blackfire par Sensio que j’ai eu l’occasion d’essayer et de proposer à mon équipe alors que je travaillais à CommonLedger.

Même si Blackfire est relativement facile à mettre en place, il ne répondait pas à toutes nos attentes étant donné que nous ne travaillions pas sur des projets open-source. En effet si Blackfire reste gratuit pour les projets open-source, il est en revanche payant pour les projets propriétaires. Si mes souvenirs sont bons, le fait que les données analysées soient envoyés sur un serveur distant a également provoqué quelques grimaces…

Apres quelques recherches il s’est avéré que Xhprof consitutait une alternative qui ne posait pas ces problèmes. Étant donné que notre environnement de développement fonctionnait intégralement sur docker, je me suis attelé à dockerizer Xhprof, ainsi que l’interface web Xhgui.

Voici un schéma représentant l’architecture que ce tutoriel va mettre en place.

Schéma architecture docker

Si vous êtes pressé, tous les fichiers créés dans ce tuto sont disponibles sur ce repository github avec des instructions pour tout installer en accéléré.

Mise à jour: Une configuration docker-compose officielle a vu le jour depuis que cet article a été rédigé. Jetez-y un coup d’œil : https://github.com/perftools/xhgui/blob/master/docker-compose.yml

Installer l’application PHP avec docker

Un prérequis à ce tutoriel est d’avoir une application PHP servie par un conteneur.

Il est tout à fait envisageable d’avoir certains composants conteneurisés et d’autres non. Pour simplifier ce tutoriel au niveau réseau, tous les composants fonctionneront sous docker.

J’ai déjà écris un article avec des fichiers initiaux identiques. Plutôt que de faire un copié-collé suivez la première partie de ce tutoriel puis revenez sur cette page. Si vous avez déjà une application PHP fonctionnant sous docker c’est encore mieux! Il vous suffira d’adapter ce qui suit à votre environnement.

À partir d’ici, vous devriez avoir un dossier contenant les 5 fichiers comme suit:

  • docker-compose.yml
  • Dockerfile-php-app
  • php.ini
  • app.conf
  • index.php

Installation des conteneurs xhgui et xhprof

Mettons à jour le fichier docker-compose.yml en ajoutant 3 nouveaux services.

services:

  ...
  php_xhgui:
    build:
      context: .
      dockerfile: Dockerfile-php-xhgui

  nginx_xhgui:
    build:
      context: .
      dockerfile: Dockerfile-nginx-xhgui
    ports:
      - 8081:80
    volumes:
      - ./xhgui.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - php_xhgui

  mongo:
    image: mongo:3.7.9

Comme vous vous en doutez, il nous faut créér les 3 nouveaux fichiers référencés dans le fichier docker-compose.yml.

  • Dockerfile-php-xhgui

Le Dockerfile de l’image PHP xhgui s’occupe d’installer l’extension mongo pour récupérer et stocker les données profilées, ainsi que l’application PHP xhgui.

FROM php:7.1-fpm-alpine

RUN apk add --no-cache git

RUN apk add --no-cache autoconf alpine-sdk zlib-dev
RUN pecl install mongodb && docker-php-ext-enable mongodb
RUN docker-php-ext-install zip # useful for composer

RUN git clone https://github.com/perftools/xhgui.git /var/xhgui
RUN cd /var/*xhgui && php install.php
COPY xhgui_config.php /var/xhgui/config/config.php
  • Dockerfile-nginx-xhgui

Le Dockerfile de l’image nginx xhgui copy simplement les fichiers de l’application xhgui. Cela permettra à nginx de servir les fichiers statiques (css, js, images, etc.).

FROM nginx:1.12.2

RUN apt-get update
RUN apt-get install -y git

RUN git clone https://github.com/perftools/xhgui.git /var/xhgui
RUN chmod -R 0777 /var/xhgui/cache
  • xhgui.conf

Voici le fichier de configuration nginx concernant xhgui. En ce qui concerne la configuration spécifique à xhgui:

  • Le répertoire de base de xhgui est /var/xhgui/webroot/.
  • fastcgi_pass correspond au nom de service docker-compose (php_xhgui). Ce nom de domaine sera résolu par le DNS interne de docker avec l’IP du conteneur xhgui php.

Le reste est standard dans le cas d’une application PHP.

server {
    listen 80 default_server;

    root /var/xhgui/webroot/;
    index  index.php;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_pass    php_xhgui:9000;
        fastcgi_index   index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

  • Vous avez sans doute remarqué un fichier nommé xhgui_config.php qui est copié dans le Dockerfile Dockerfile-php-xhgui.

Ce fichier est nécessaire pour configurer la connexion entre mongo et le conteneur PHP. Créez ce fichier avec le contenu suivant :

<?php

return array(
    'save.handler' => 'mongodb',
    'db.host' => 'mongodb://mongo:27017',
    'db.db' => 'xhprof',
    'db.options' => array(),
    'profiler.enable' => function() {
        return true;
    },
    'profiler.simple_url' => null,
    'profiler.options' => array(),
    'date.format' => 'M jS H:i:s',
    'detail.count' => 6,
    'page.limit' => 25,
);

Vous pouvez modifier cette configuration comme vous le souhaitez en vous référant à la documentation par défaut sur github.

Enfin nous devons mettre à jour l’image docker PHP pour inclure l’extension qui effectuera le profiling. Ajoutez ces lignes au fichier Dockerfile-php-app

# installe l'extension xhprof pour le profilage du code PHP
RUN curl "https://github.com/tideways/php-xhprof-extension/archive/v4.1.7.tar.gz" -fsL -o ./php-xhprof-extension.tar.gz && \
    tar xf ./php-xhprof-extension.tar.gz && \
    cd php-xhprof-extension-4.1.7 && \
    apk add --update --no-cache build-base autoconf && \
    phpize && \
    ./configure && \
    make && \
    make install
RUN rm -rf ./php-xhprof-extension.tar.gz ./php-xhprof-extension-4.1.7
RUN docker-php-ext-enable tideways

# installe l'extension mongodb. Le paquet xhgui-collector enverra les données xprof à mongo
RUN apk add --no-cache autoconf alpine-sdk
RUN pecl install mongodb && docker-php-ext-enable mongodb

# installw xhgui
# en théorie nous avons seulement besoin du paquet perftools/xhgui-collector mais l'installation est plus simple en récupérant le repository entier
RUN git clone https://github.com/perftools/xhgui.git /var/xhgui
RUN cd /var/xhgui && php install.php
COPY xhgui_config.php /var/xhgui/config/config.php

Ces instructions n’ont été testées que pour PHP 7

Exécutez la comande docker-compose build pour créer une nouvelle version des images PHP, suivi de docker-compose up -d. Vous pourrez accéder à l’interface xhgui à l’adresse http://localhost:8081/

Profilage des requêtes

Au démarrage de l’application PHP dont on souhaite mesurer les performances, ajouter le code suivant:

define('XHGUI_CONFIG_DIR', '/var/xhgui/config/');
require_once '/var/xhgui/vendor/perftools/xhgui-collector/external/header.php';

Dans notre exemple, il s’agit simplement de mettre à jour le fichier index.php.

<p>Let's do some math.</p>

<ul>
    <?php
        define('XHGUI_CONFIG_DIR', '/var/xhgui/config/');
        require_once '/var/xhgui/vendor/perftools/xhgui-collector/external/header.php';

        for ($i = 0; $i < 10; $i++) {
            echo sprintf("<li>%s * 5 = %s</li>", $i, $i*5);
        }
    ?>
</ul>

Allez sur la page http://localhost:8080/ puis rafraîchissez http://localhost:8081/. Vous devriez voir les premières mesures.

Profiled requests

Profile data

Si cet article vous a rendu service, n’hésitez pas à starrer ce repo en guise de remerciement ! ⭐

Bonne continuation!