Déployer un site wordpress

Oui c'est un sujet qui court les rues mais j'ai été obligé de suivre 25 tutoriels différents pour arriver à le faire fonctionner. Pourquoi ? Et bien tout simplement car je ne souhaite pas utiliser un hébergement OVH gratuit ou entrée de gamme, je veux la liberté de gestion et de sécurisation et surtout pouvoir facilement absorber les pics de charges.

Wordpress et Docker

Oui, nous allons utiliser Docker dans cette histoire. Cela va permettre de séparer les services, d'avoir une architecture applicative cohérente et de permettre des montées en charges si nécessaire !

Première chose à faire

Installer Docker.
Petit rappel, on part d'un VPS OVH ! Pour installer Docker sur un VPS, je vous renvoi à ce post ou tout y est décrit en détails !

Une fois Docker installé, il faut installer Docker-compose qui permet de créer des applications multi-containers qui se géreront avec une ligne de commande unique (pratique pour éviter de lancer 20 docker container run). Pour cela rien de plus simple :

  • il faut passer en mode superutilisateur : sudo -i
  • un coup de mise à jour apt-get update
  • un coup de apt-get install curl (accepter les modifications qui seront ajoutées à l'OS)
  • et enfin la commande suivante :
curl -L https://github.com/docker/compose/releases/download/1.13.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose

Pour finir, il faut mettre les bons attributs : sudo chmod +x /usr/local/bin/docker-compose

Et voilà, Docker-compose est installée :

root@serveur-2:~# docker-compose -v
docker-compose version 1.13.0, build 1719ceb

Bien entendu, il faut que les ports 80 et 443 soient accessible de l'extérieur !!

Maintenant que c'est fait, parlons un peu de ce que nous voulons faire !

Architecture applicative


Vous avez ci-dessus, le schéma de ce que je souhaite avoir.
Mon VPS avec sa distribution Débian, le système de container Docker puis 3 containers (pour commancer) :

  • un container pour la base de données (MariaDB)
  • un container pour l'application (wordpress) avec le moteur php-fpm
  • un container pour gérer les accès clients (Nginx)

Les fichiers seront sur des volumes locaux au VPS (pour le moment aussi).

Seul le container Nginx sera accessible de l'extérieur. Les communications MariaDB-Wordpress et Wordpress-Nginx se feront via un réseau interne mis en place par Docker.

Préparation

Pour arriver à cette architecture, il vaut mieux passer par plusieurs étapes pour bien comprendre ce qu'il se passe.
1ère étape : monter un couple de container Wordpress/MariaDB. Pour cela rien de plus simple un docker-compose.yml comme celui-ci fait l'affaire :

version: '2'
services:
  mariadb:
    image: mariadb:latest
    environment:
     - MYSQL_ROOT_PASSWORD=votre-mot-de-passe
     - MYSQL_DATABASE=wordpress

  wordpress:
    image: wordpress:latest
    links:
     - mariadb:mysql
    environment:
     - WORDPRESS_DB_PASSWORD=votre-mot-de-passe
    ports:
     - 80:80

Et vous n'avez plus qu'à lancer ces containers avec docker-compose up -d vous obtenez alors les lignes suivantes lors d'un docker ps :

CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS                                      NAMES
9a1e0fef2489        wordpress:latest             "docker-entrypoint..."   23 seconds ago      Up 22 seconds       0.0.0.0:80->80/tcp                       demo_wordpress_1
ce8d51b52ddf        mariadb:latest               "docker-entrypoint..."   42 seconds ago      Up 41 seconds       3306/tcp                                   demo_mariadb_1

Vous noterez que les containers prennent un nom particulier : le nom du répertoire, suivi du nom du service défini dans le docker-compose.yml et un numéro (séparés par "_"). On remarque que le container demo_wordpress_1 expose le port 80 sur toutes les IP ! On obtient donc cela lorsque l'on accède à l'ip publique de notre VPS :

Nous n'avons pas spécifier de volumes sur nos containers donc les données ne seront pas persisté (à chaque arret via docker-compose down et docker-compose up il faudra réinstaller toutes les données de Wordpress).

Mode de fonctionnement

Le container wordpress (qui ici intègre le serveur web apache avec php 5.6 et wordpress 4.7.4) accède via le réseau interne au container mariadb sur le port 3306. Cette communication n'est pas accessible par un autre moyen qu'un container lié via la commande links du fichier docker-compose.yml. Les variables d'environnements permettent d'échanger le mot de passe de la base de données. Si on veut mettre à jour php ou wordpress, ou même mariaDB, il suffit de faire :

  • docker-compose down puis docker-compose up -d mais cela génère une coupure
  • docker-compose pull puis docker-compose up -d et il n'y a pas de coupure !!

Génial donc, reste que les données ne sont pas persistées (enfin tant que l'on ne supprime pas les containers créés, via la commande docker-compose rm wordpress ou docker-compose rm mariadb).

Mise en conformité (données persistente)

Pour un environnement de production autant avoir nos données qui persistent sur le serveur ! C'est mieux !
Pour cela, nous modifions notre fichier docker-compose.yml avec les instructions suivantes :

- volumes:
  - ./wp:/var/www/html 

Pour le container wordpress et :

- volumes:
  - ./mariadb:/var/lib/mysql

Pour le container mariadb !

Ainsi nous aurons dans le répertoire courant deux répertoires (à créer au préalable) qui acceuilleront nos données. Nous pourrons y accéder hors des containers (cf. schéma en début de post).

Ajout de Nginx

Pour Nginx, c'est un peu plus compliqué car je souhaite que mon site soit en HTTPS. On ne badine pas avec la sécurité surtout en ce moment !! On commence par créer un répertoire etc dans le répertoire courant pour accueillir nos certificats let's encrypt et on lance la commande suivante :

sudo docker run -it --rm -p 443:443 -p 80:80 --name letsencrypt \
            -v "./etc/letsencrypt:/etc/letsencrypt" \
            -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
            quay.io/letsencrypt/letsencrypt:latest auth

Cela va executer un container qui va générer nos certificats. Il est nécessaire d'entrer une adresse mail (après avoir faire le choix 2 pour avoir un serveur web temporaire - pour la vérification du domaine faite par let's encrypt), le nom du domaine et voilà. Nos certificats sont générés.

Pour aller plus loin dans la sécurité, nous allons générés les paramètres Diffie-Hellman via la commande sudo openssl dhparam -out ./nginx/certs/dhparam.pem 4096. Cela permet d'avoir une sécurité SSL forte.

C'est assez long, suivant la puissance CPU de votre VPS.

Pour ce qui est de la configuration de Nginx, je vous conseille la configuration trouvée sur le blog https://blog.ouvrard.it qui est sur le guist suivant : ici !
Elle est complète et il n'y a qu'à modifier les votre-domaine par votre nom de domaine !

Maintenant, nous allons changer un peu notre container wordpress, pour utiliser non pas apache, car les fichiers statiques seront gérés par Nginx, mais php-fpm qui est un serveur dédier aux fichiers php et bien plus rapide qu'apache pour cette tâche.

Ne vous en faites pas, grâce à docker c'est assez simple, il suffit de modifier la ligne suivante de notre fichier docker-compose.yml :

image: wordpress:lastest

par

image: wordpress:4.7.4-php7.0-fpm

Attention aussi à la directive fastcgi_pass wp_db:9000; du fichier nginx.conf qui doit être changée pour le nom de votre service utilisant l'image wordpress-fpm. Elle devient dans notre cas fastcgi_pass wordpress:9000;.

Cela nous donne donc le fichier docker-compose.yml suivant :

version: '2'

services:

  wordpress:
    image: wordpress:4.7.4-php7.0-fpm
    links:
      - mysql
    volumes:
      - ./wp:/var/www/html
    environment:
      WORDPRESS_DB_NAME: wordpress_db
      WORDPRESS_DB_PASSWORD: password

  mysql:
    image: mariadb
    restart: always
    volumes:
     - /var/mysql:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: password

  wp_web:
   image: nginx:latest
   restart: always
   ports:
     - 80:80
     - 443:443
   links:
     - wordpress
   volumes:
     - ./wp:/var/www/html
     - ./etc/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
     - ./var/log/nginx:/var/log/nginx
     - ./etc/letsencrypt:/etc/letsencrypt
     - ./etc/nginx/certs/dhparam.pem:/etc/nginx/certs/dhparam.pem:ro

Voilà nous avons terminé, il ne reste plus qu'à faire un docker-compose up -d (le -d permet de lancer les containers en arrière plan). Si vous lancez les containers sans cette option, vous aurez l'affichage des logs en permanent sur le terminal. Autant éviter cela pour passer d'autres commandes en cas de maintenance !

Avec le docker container ps nous avons donc les containers suivants :

debian@serveur-1:~/demo$ docker ps
CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS                                      NAMES
3b7dc6e62b75        nginx:latest                 "nginx -g 'daemon ..."   2 hours ago         Up 2 hours          0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   debian_wp_web_1
6c4b1b78220f        wordpress:4.7.4-php7.0-fpm   "docker-entrypoint..."   2 hours ago         Up 2 hours          9000/tcp                                   debian_wordpress_1
98bc586eb4fb        mariadb                      "docker-entrypoint..."   2 hours ago         Up 2 hours          3306/tcp                                   debian_mysql_1

A noter que si un se coupe (mariadb par exemple), il sera automatiquement redémarré, idem pour nginx grâce aux directives restart: always.

Aller plus loin

Pour aller plus loin, un plugin wordpress est à installer : all in one WP migration qui permet de faire une sauvegarde de toutes les données de votre site. Avec ce plugin et votre docker-compose.yml votre site peut redémarrer de n'importe ou en quelques secondes (modulo le temps d'upload de la sauvegarde).

Si on test le fonctionnement on s'apppersoit que le serveur répond extrèmement vite (10000 requête avec une concurrence de 1000) :

Requests per second:    1929.76 [#/sec] (mean)
Time per request:       518.200 [ms] (mean)
Time per request:       0.518 [ms] (mean, across all concurrent requests)

contre

rien, apr_socket_recv: Connection reset by peer (104)
impossible de traiter autant de connexion, j'ai donc baisser à 1000 requêtes

Autant dire que j'ai fait un déni de service sur mon second site disposant d'une configuration standard (pour le moment) !!

Requests per second:    230.53 [#/sec] (mean)
Time per request:       433.792 [ms] (mean)
Time per request:       4.338 [ms] (mean, across all concurrent requests)

Pour un site avec une configuration standard (apache/php/mariadb) !

De même la qualité SSL est au RDV et le resultat de SSL Server Test :

Un sans fautes !!

Encore plus loin

L'autre avantage avec docker est la possiblité d'augmenter le nombre de serveur en fonction de la charge. Il sera maintenant facile de basculer à 2 serveurs wordpress pour gérer les requêtes plus rapidement. Pour cela, docker-compose scale wordpress=2et le tour est joué ! Bon il faut modifier la configuration nginx pour prendre en compte cela mais je prépare un prochain article sur l'augmentation des performances d'ici peu.

Nous verrons aussi comment gérer un cluster d'hôtes swam pour avoir plusieurs VPS gérant la charge.

Le monitoring

Avant de nous quitter, on va parler de monitoring. Il existe de nombreuses solutions mais la plus élégante à mon gout est portainer. Simple à installer, il suffit de lancer la ligne de commande suivante : docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portaineret d'ouvrir le port 9000. Vous arrivez sur une page vous demandant un mot de passe administrateur, puis choisissez le premier choix (gérer l'hôte sur lequel s'execute portainer) et vous avez accès à un dashboard complet de votre système de container :

Avec un monitoring au container (ici nginx lors du test de charge) :

Autant dire qu'il reste de la marge avant d'augmenter la resistance à la charge car 30% en pic pour gérer 10000 requêtes, c'est assez light !

Voilà, à très bientôt pour la version cluster swarm de ce déployement !

Je me suis basé sur les sites suivants pour élaborer ce post :

A+
Tony B.