Wordpress en mode Docker

Ou comment éclater un blog Wordpress en quelques conteneurs.

J’ai eu à installer un blog Wordpress il y a quelques temps pour une connaissance. N’étant pas emballé par la traditionnelle installation, je me suis dit qu’il serait bon de voir comment avait évolué Docker et ce que je serais capable d’en faire plus d’un an après mes premiers essais.

Installation de Docker

Je procède à l’installation de Docker sur une Ubuntu 14.04 pour ne pas changer, l’essentiel étant d’avoir un noyau d’une version supérieure à la 3.10. La procédure d’installation elle-même est très simple et la documentation complète. Rien de plus à ajouter !

Dockerisation de Wordpress

La façon la plus simple d’obtenir un Wordpress fonctionnel et dockerisé est certainement de partir d’une image officielle disponible sur le Hub Docker.

Malheureusement, aucune de ces images ne me convient. La plupart d’entre elles embarquent allégrement toute la pile (Wordpress, MySQL, PHP) dans un seul conteneur, ce qui est contraire à l’esprit Docker où il est prévu qu’un conteneur ne doit faire tourner qu’un seul processus.

De plus, l’image officielle de Wordpress, qui se rapproche le plus de ce que je veux, ne comprend pas les extensions PHP nécessaires à l’installation que j’ai en tête.

Voulant respecter le principe d’un processus par conteneur et voulant quelques options non disponibles dans les images officielles, je me suis donc tourner vers Docker Compose, qui permet de définir une application par l’ensemble des conteneurs la composant.

Docker Compose fait partie avec Swarm et Machine entre autres de qui s’appelle désormais la Docker Toolbox. Mais ne nous écartons pas de notre sujet principal !

Wordpress avec Docker Compose

Essayons de rentrer dans la logique qui va permettre de composer mon application en décrivant ce dont j’ai besoin pour motoriser un blog Wordpress.

Les options dépendent des goûts de chacun mais en ce qui me concerne, mon setup de référence pour Wordpress reste :

  • Un serveur MySQL
  • Un serveur Memcache
  • Un serveur PHP-FPM, qui contient les sources de Wordpress
  • Un serveur Nginx

soit quatre processus et donc quatre conteneurs !

Installation de Docker Compose

L’installation de Docker Compose est simple

curl -L https://github.com/docker/compose/releases/download/VERSION_NUM/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

il est temps de traduire en instructions, directives Docker Compose la description applicative faite préalablement.

Docker Compose utilise un fichier au format YAML nommé par défaut docker-compose.yml. Il contient la déclaration des briques composant l’application à dockeriser. Nous allons construire progressivement ce fichier en commentant chacune des directives.

Un serveur MySQL

Chez moi, MySQL c’est MariaDB ! Il fonctionne sans souci avec les dernières versions de Wordpress.

mysql:
  image: mariadb
  restart: always
  volumes:
    - ./var/mysql:/var/lib/mysql
  environment:
    MYSQL_DATABASE: wordpress
    MYSQL_USER: wordpress
    MYSQL_PASSWORD: password
    MYSQL_ROOT_PASSWORD: password
  • mysql est le nom du service fourni à l’application.
  • image permet de spécifier l’image qui va être utilisé pour construire le conteneur.
  • restart permet comme son nom l’indique de démarrer automatiquement le conteneur lors d’un reboot du serveur Docker par exemple.
  • volumes permet de définir un point de montage dans le conteneur qui pointe vers un dossier du serveur Docker.
  • environment permet de passer n’importe quelle variable en variable d’environnement au moment de l’exécution du conteneur.

Pas de quoi se faire mal à la tête il me semble, tout est plutôt explicite en fait ! Et vous pouvez même utiliser un éditeur en ligne pour vous aider dans l’écriture de ce fichier.

Un serveur Memcache

Rien de bien compliqué non plus côté Memcache

memcached:
  image: memcached
  restart: always

Ben oui, c’est tout !

Un serveur PHP

L’image officielle Wordpress contient plusieurs variantes qui permettent au choix une utilisation avec Apache ou PHP-FPM et qui permettent de spécifier une version particulière de PHP. Mais aucune ne contient les extensions PHP dont j’ai besoin comme par exemple memcache. Il faut donc construire une image spécifique à partir de celle qui est la plus proche de celle souhaitée.

Je passe donc par un Dockerfile qui va contenir l’ensemble des commandes qui dérivent de l’image officielle.

FROM wordpress:fpm

RUN apt-get update && apt-get install -y libmemcached-dev tidy csstidy

RUN curl -o memcached.tgz -SL http://pecl.php.net/get/memcached-2.2.0.tgz \
        && tar -xf memcached.tgz -C /usr/src/php/ext/ \
        && rm memcached.tgz \
        && mv /usr/src/php/ext/memcached-2.2.0 /usr/src/php/ext/memcached
RUN curl -o memcache.tgz -SL http://pecl.php.net/get/memcache-3.0.8.tgz \
        && tar -xf memcache.tgz -C /usr/src/php/ext/ \
        && rm memcache.tgz \
        && mv /usr/src/php/ext/memcache-3.0.8 /usr/src/php/ext/memcache
RUN curl -o zip.tgz -SL http://pecl.php.net/get/zip-1.13.1.tgz \
        && tar -xf zip.tgz -C /usr/src/php/ext/ \
        && rm zip.tgz \
        && mv /usr/src/php/ext/zip-1.13.1 /usr/src/php/ext/zip

RUN docker-php-ext-install memcached
RUN docker-php-ext-install memcache
RUN docker-php-ext-install zip

Rien de bien méchant, je me contente d’ajouter quelques modules PHP dont j’aurais besoin avec le plugin de cache que j’utilise avec Wordpress. C’est fait à la sauce Docker avec l’instruction docker-php-ext-install.

Ensuite, dans mon service, je remplace l’instruction image par build et je pointe vers le répertoire qui contient le Dockerfile, ici wordpress.

wordpress:
  build: wordpress
  restart: always
  links:
    - mysql
    - memcached
  environment:
    WORDPRESS_DB_USER: wordpress
    WORDPRESS_DB_PASSWORD: password
    WORDPRESS_DB_NAME: wordpress

Une nouvelle instruction est apparue et elle est d’une puissance redoutable.

links permet en effet de raccorder deux conteneurs entre eux. Ainsi, le service wordpress va donc être lié au service mysql et memcached. plus besoin de connaître l’adresse IP du serveur Memcache ou MySQL, et ça tombe bien puisque ces adresses sont assignées dynamiquement par Docker au démarrage d’un conteneur.

Un serveur Nginx

Rien de bien nouveau avec le serveur Nginx si ce n’est l’instruction ports, qui comme son nom l’indique permet d’exposer un ou plusieurs ports d’un conteneur vers le vaste monde. Sans ce type d’instruction, le serveur Nginx ne serait pas joignable depuis Internet.

nginx:
  image: nginx
  restart: always
  ports:
    - 80:80
    - 443:443
  links:
    - wordpress
  volumes_from:
    - wordpress:ro
  volumes:
    - ./etc/nginx/conf.d:/etc/nginx/conf.d:ro
    - ./var/log/nginx:/var/log/nginx

Et le monitoring alors ?

Docker apporte son lot de changements au niveau supervision. Il devient difficile d’embarquer un agent dans un conteneur puisqu’il faudra du coup dériver toutes les images depuis celles du Hub pour leur ajouter au niveau Dockerfile les instructions nécessaires au provisionnement de l’agent dans le conteneur. Et cela fera un processus de trop !

Heureusement, quelques projets sortent pour permettre de faire ça proprement et notamment Cadvisor de Google.

Le plus beau, c’est que nous allons faire tourner Cadvisor dans un conteneur et qu’il va permettre la supervision de l’ensemble de ceux-ci.

Il faut donc ajouter un nouveau service au fichier docker-compose.yml

cadvisor:
  image: google/cadvisor
  ports:
    - 8080:8080
  restart: always
  volumes:
    - /:/rootfs:ro
    - /var/run:/var/run:rw
    - /sys:/sys:ro
    - /var/lib/docker/:/var/lib/docker:ro

Le résultat est immédiat et plutôt flatteur.

Vue imprenable sur les conteneurs
Vue imprenable sur les conteneurs

Voilà une supervision temps réel de base des conteneurs à peu de frais. Je vois d’un seul coup d’œil la consommation CPU, mémoire, réseau, disque de chacun des conteneurs démarrés, le tout dans une interface plutôt agréable. De plus, il est possible d’envoyer l’ensemble des métriques dans InfluxDB. On se garde ça sous le coude pour une autre fois ?

Mais cela ne suffit pas. Ce n’est pas parce que tout roule côté serveur que le site est accessible. J’ajoute donc naturellement ce nouveau Wordpress dockerisé sur Check my Website, histoire de vérifier qu’il est bien disponible depuis l’extérieur.

Et parce que c’est une fonctionnalité dificile à avoir sur une solution de supervision classique et que l’application ainsi dockerisé le vaut bien, une topologie complète applicative et mise à jour en temps réel obtenue grâce à Weave Scope.

Topologie applicative avec Weave Scope
Topologie applicative avec Weave Scope

Le résultat final

le résultat final est ce fichier docker-compose.yml qui contient la déclaration de l’ensemble des briques nécessaires à Wordpress.

nginx:
  image: nginx
  restart: always
  ports:
    - 80:80
    - 443:443
  links:
    - wordpress
  volumes_from:
    - wordpress:ro
  volumes:
    - ./etc/nginx/conf.d:/etc/nginx/conf.d:ro
    - ./var/log/nginx:/var/log/nginx

wordpress:
  build: wordpress
  restart: always
  links:
    - mysql
    - memcached
  environment:
    WORDPRESS_DB_USER: wordpress
    WORDPRESS_DB_PASSWORD: password
    WORDPRESS_DB_NAME: wordpress

mysql:
  image: mariadb
  restart: always
  volumes:
    - ./var/mysql:/var/lib/mysql
  environment:
    MYSQL_DATABASE: wordpress
    MYSQL_USER: wordpress
    MYSQL_PASSWORD: password
    MYSQL_ROOT_PASSWORD: password

memcached:
  image: memcached
  restart: always

cadvisor:
  image: google/cadvisor
  ports:
    - 8080:8080
  restart: always
  volumes:
    - /:/rootfs:ro
    - /var/run:/var/run:rw
    - /sys:/sys:ro
    - /var/lib/docker/:/var/lib/docker:ro

Gestion du cycle de vie

Pour démarrer l’ensemble de la pile applicative ainsi déclarée, il suffit d’un

docker-compose up

ou

docker-compose start

pour le même résultat mais en arrière plan.

Vous êtes sûrs que je vous donne la commande pour arrêter l’application ? j’ai envie de dire stop à la facilité !

Mettre à jour tout ce petit monde est d’une simplicité redoutable et c’est certainement un des gros avantages de Docker.

docker-compose pull
docker-compose build
docker-compose up -d

Tout est mis à jour sans interruption de services, la classe !

Enfin, le jour ou vous en aurez marre de votre Wordpress, un simple

docker-compose rm

supprimera les conteneurs et avec l’option -v associée, supprimera les volumes associés.

Il est bien sûr toujours possible d’améliorer ce setup. L’intérêt dans le contexte docker est qu’on ne repart pas à zéro comme trop souvent. Ce qui est décrit permet de capitaliser pour la suite.

Par exemple, comme indiqué au niveau bonnes pratiques dans la documentation de Docker, il serait possible de créer un conteneur uniquement destiné au stockage des données Wordpress et qui seraient monté avec l’instruction volumes_from depuis les services en ayant besoin soit wordpress et nginx.

Sentiment final

L’éco-système Docker a beaucoup évolué en un et demi, c’est le moindre que l’on puisse dire. L’outil est désormais beaucoup plus mature et simple à prendre en main.

J’aime beaucoup le format du fichier docker-compose ou il y a finalement assez peu de choses à déclarer pour avoir une description plutôt complète d’une application.

Une fois que l’on a goûté au workflow proposé par Docker, c’est difficile de s’en passer !

Edit: 17/05/2016

Pour illustrer cet article nous avons mis à disposition l’ensemble de l’arborescence sur un Git.

Olivier Jan

À propos de l’auteur

| Cofondateur de Check my Website.

Check My Website a arrêté son activité en 2017, n'hésitez pas à vous tourner vers Dareboost.