Assembler Docker, Traefik, mkcert et WordPress

Je continue mon apprentissage de Docker en ajoutant Traefik. Dans mon précédent article, j’installais WordPress avec Docker Compose, mais comme je le précisais l’installation était basique. Il restait des choses à améliorer, comme l’accès en HTTPS. De plus, la communication avec d’autres outils semblait ne pas fonctionner. Pour que l’utilisation d’un conteneur soit vraiment avantageux, il fallait améliorer ça. Comment s’y prendre ?

Résumons le projet

Dans le précédent article, nous avons vu comment installer WordPress avec Docker et docker-compose. Cependant, en procédant ainsi, nous ne pouvions pas accéder au site en HTTPS. Pour développer des thèmes et plugins, il serait également intéressant de pouvoir utiliser Gulp et BrowserSync. Je souhaite ici avoir une installation locale de WordPress pour faire du développement.

Je ne vais pas revenir en détail sur l’installation de WordPress et des conteneurs annexes (PHPMyAdmin, MySQL) ; consultez plutôt le précédent article si vous le souhaitez. Ici, je vais avant tout m’attarder sur la configuration de notre conteneur, Traefik, associé à mkcert pour du développement local. Évidemment, je reviendrais sur la configuration globale pour vous expliquer les changements entre le précédent article et celui-ci.

Au final, nous accéderons à :

  • WordPress avec l’adresse www.docker-wordpress.test
  • PHPMyAdmin avec l’adresse pma.docker-wordpress.test
  • Traefik avec l’adresse traefik.docker-wordpress.test

Vous pouvez choisir un autre nom de domaine ; pensez simplement à le modifier dans les prochaines étapes également. Sauf erreur, il s’agit uniquement de la création et de la déclaration des certificats ainsi que le domaine établi dans le fichier .env et la configuration de BrowserSync.

Étape 1 : générer un certificat avec mkcert

Dans un précédent article, je vous expliquais la création d’un virtualhost sur Manjaro donc je ne vais pas revenir dessus. Par contre, pensez à ajouter également les sous-domaines dans le fichier /etc/hosts.

Pour obtenir un certificat local avec mkcert, procédez comme ceci :

// Installons mkcert sur Manjaro
sudo pacman -S mkcert

// Créons une autorité de confiance local
mkcert -install

// Créons un répertoire qui contiendra nos certificats locaux (le nom est arbitraire)
mkdir ./certs/

// Générons le certificat pour docker-wordpress.test (un nom de domaine local)
mkcert -cert-file ./certs/docker-wordpress.test.pem -key-file ./certs/docker-wordpress.test-key.pem docker-wordpress.test "*.docker-wordpress.test"

Maintenant que nos certificats sont prêts, passons à l’étape suivante.

Étape 2 : configurer Traefik avec Docker

Qu’est-ce que Traefik ?

Traefik est un proxy inverse (« reverse proxy » en anglais) et un équilibreur de charge (« load balancer » en anglais) écrit en Golang, créé par Émile Vauge et distribué par Traefik Labs. Il va intercepter et acheminer les demandes entrantes vers notre site. Il détecte immédiatement le service déployé et met à jour les règles de routage en temps réel.

Traefik Proxy

Traefik utilise deux fichiers de configuration différents. Le premier définit la configuration statique qui est lue au démarrage du processus ; il faut redémarrer le service à chaque changement. Le second définit la configuration dynamique ; elle indique comment traiter les requêtes et les changements se font dynamiquement.

Comment le configurer ?

Nous allons avoir besoin de trois choses : deux fichiers de configuration pour Traefik et ajouter le service dans docker-compose.yml. Nous placerons les fichiers de configuration de Traefik dans un dossier du même nom pour plus de clarté : mkdir traefik.

Le fichier de configuration statique

Dans sa documentation, Traefik propose un exemple de fichier de configuration pour l’utiliser avec Docker. Je vais donc me baser sur celui-ci et lui apporter quelques modifications. Concernant le format, vous avez le choix entre YAML et TOML, c’est selon votre préférence ; ici j’utiliserai un fichier YAML : touch traefik/traefik.yml. Voici son contenu :

global:
  checkNewVersion: true
  sendAnonymousUsage: false

log:
  level: INFO

api:
  insecure: false
  dashboard: true

entryPoints:
  http:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: https
          scheme: https
  https:
    address: ":443"

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    watch: true
    network: dwp-proxy
    exposedByDefault: false
  file:
    filename: /etc/traefik/ssl.yml
    watch: true

Je pense que la configuration globale et celle des logs parlent d’elles-mêmes. Concernant l’api :

  • insecure: true permet d’exposer l’API sur le point d’entrée traefik (ou sur le port 8080 s’il n’existe pas). Ici, nous ne voulons pas utiliser ce port mais un sous-domaine, donc il faut remplacer true par false.
  • dashboard: true permet d’activer le tableau de bord

Ensuite, passons à la configuration des points d’entrée :

  • nous définissons deux points d’entrée (HTTP et HTTPS)
  • nous appliquons une redirection de http vers https
  • pour chaque point d’entrée, nous définissons une adresse ; elle correspond au numéro de port utilisé (80 pour HTTP / 443 pour HTTPS)

Enfin, concernant les fournisseurs (providers), nous définissons :

  • Docker avec :
    • watch qui permet d’indiquer à Traefik de surveiller ce fournisseurs.
    • network qui permet de spécifier le réseau Docker à utiliser. Ici, nous utiliserons un réseau spécifique (dwp-proxy) pour les conteneurs communiquant avec Traefik.
    • exposedByDefault: false qui indique à Traefik d’ignorer tous les conteneurs sauf ceux qui ont un label l’autorisant.
  • notre fichier de configuration dynamique avec watch pour le surveiller et filename pour spécifier son emplacement.

Le fichier de configuration dynamique

Nous créons un fichier de configuration dans le dossier traefik : touch traefik/ssl.yml. Voici son contenu :

tls:
  certificates:
    - certFile: "/etc/certs/docker-wordpress.test.pem"
      keyFile: "/etc/certs/docker-wordpress.test-key.pem"

Évidemment, si vous avez utilisé un autre nom de domaine pour générer les certificats, il faut penser à le changer. Il n’est pas possible d’utiliser de variables d’environnement ici…

De même, il n’est pas possible de les déclarer dans notre fichier docker-compose.yml comme le précise la documentation :

In the above example, we’ve used the file provider to handle these definitions. It is the only available method to configure the certificates (as well as the options and the stores).

Traefik : https://doc.traefik.io/traefik/https/tls/

L’ajout du service Traefik dans Docker Compose

Maintenant que nos fichiers de configuration sont prêts, il nous reste à configurer le service dans notre fichier docker-compose.yml :

  traefik:
    image: traefik:${DWP_TRAEFIK_VERSION:-2.3.1}
    container_name: dwp_traefik
    restart: on-failure
    ports:
      - "127.0.0.1:80:80"
      - "127.0.0.1:443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./traefik/traefik.yml:/etc/traefik/traefik.yml
      - ./traefik/ssl.yml:/etc/traefik/ssl.yml
      - ./certs:/etc/traefik/certs
    networks:
      - dwp-proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.entrypoints=https"
      - "traefik.http.routers.traefik.rule=Host(`traefik.${DWP_DOMAIN_NAME}`)"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.routers.traefik.middlewares=auth"
      - "traefik.http.middlewares.auth.basicauth.users=${DWP_TRAEFIK_USER:-admin}:${DWP_TRAEFIK_PASSWORD:-admin}"
      - "traefik.http.routers.traefik.tls=true"

Dans le précédent article, j’expliquais le rôle d’image, restart, ports et volume. Je ne vais pas revenir dessus. Cependant, pour volume, voici les différentes choses à monter :

  • le service Docker
  • le fichier de configuration statique
  • le fichier de configuration dynamique
  • les certificats

Lors de la création du fichier de configuration statique, je vous ai parlé du réseau Docker. Nous attachons le service traefik à ce réseau (dwp-proxy).

Viens la partie plus délicate, la configuration de Traefik au travers des labels. Il est possible de faire la même chose (avec une autre syntaxe) dans le fichier de configuration dynamique, mais ce dernier ne prend pas en compte les environnements de variable. J’ai donc préféré définir la configuration ici :

  • avec traefik.enable, nous autorisons Traefik à surveiller ce conteneur
  • ensuite, nous définissons le point d’entrée (https)
  • puis, nous définissons l’adresse de Traefik
  • il faut également ajouter le service api@internal pour le tableau de bord de Traefik
  • puis, nous indiquons que l’accès au tableau de bord nécessite des identifiants
  • nous spécifions les identifiants à utiliser
  • finalement, nous indiquons à Traefik que le routeur est uniquement dédié aux requêtes HTTPS

Pour générer les identifiants, choisissez un nom d’utilisateur et créez votre mot de passe avec la commande htpasswd :

$ htpasswd -n nomUtilisateur
New password:
Re-type new password:
nomUtilisateur:leMotDePasseEncrypté

Si vous n’utilisez pas les variables d’environnement, il faut dédoubler les $ du mot de passe pour échapper le caractère.

L’intégralité du fichier Docker Compose

Nous avons défini le service Traefik, mais les autres conteneurs doivent également être modifiés. Ainsi, nous supprimons les ports, nous ajoutons des labels et nous créons nos réseaux.

version: "3.8"

services:
  db:
    image: mysql:${DWP_MYSQL_VERSION:-5.7}
    container_name: dwp_db
    restart: on-failure
    environment:
      MYSQL_ROOT_PASSWORD: ${DWP_ROOT_PASSWORD:-root}
      MYSQL_DATABASE: ${DWP_DB_NAME:-wordpress}
      MYSQL_USER: ${DWP_DB_USER:-wordpress}
      MYSQL_PASSWORD: ${DWP_DB_PASSWORD:-wordpress}
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - dwp-web
  wordpress:
    depends_on:
      - db
    image: wordpress:${DWP_WORDPRESS_VERSION:-latest}
    container_name: dwp_wordpress
    restart: on-failure
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_NAME: ${DWP_DB_NAME:-wordpress}
      WORDPRESS_DB_USER: ${DWP_DB_USER:-wordpress}
      WORDPRESS_DB_PASSWORD: ${DWP_DB_PASSWORD:-wordpress}
      WORDPRESS_TABLE_PREFIX: ${DWP_TABLE_PREFIX:-wp_}
    working_dir: /var/www/html
    volumes:
      - ./wp-content:/var/www/html/wp-content
      - ./uploads.ini:/user/local/etc/php/conf.d/uploads.ini
    networks:
      - dwp-proxy
      - dwp-web
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.wordpress.entrypoints=http,https"
      - "traefik.http.routers.wordpress.rule=Host(`www.${DWP_DOMAIN_NAME}`) || Host(`${DWP_DOMAIN_NAME}`)"
      - "traefik.http.routers.wordpress.tls=true"
  pma:
    depends_on:
      - db
    image: phpmyadmin:${DWP_PMA_VERSION:-latest}
    container_name: dwp_pma
    restart: on-failure
    environment:
      PMA_HOST: db
      MYSQL_ROOT_PASSWORD: ${DWP_ROOT_PASSWORD:-root}
    networks:
      - dwp-proxy
      - dwp-web
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.pma.entrypoints=http,https"
      - "traefik.http.routers.pma.rule=Host(`pma.${DWP_DOMAIN_NAME}`)"
      - "traefik.http.routers.pma.tls=true"
  traefik:
    image: traefik:${DWP_TRAEFIK_VERSION:-2.3.1}
    container_name: dwp_traefik
    restart: on-failure
    ports:
      - "127.0.0.1:80:80"
      - "127.0.0.1:443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./traefik/traefik.yml:/etc/traefik/traefik.yml
      - ./traefik/ssl.yml:/etc/traefik/ssl.yml
      - ./certs:/etc/traefik/certs
    networks:
      - dwp-proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.entrypoints=https"
      - "traefik.http.routers.traefik.rule=Host(`traefik.${DWP_DOMAIN_NAME}`)"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.routers.traefik.middlewares=auth"
      - "traefik.http.middlewares.auth.basicauth.users=${DWP_TRAEFIK_USER:-admin}:${DWP_TRAEFIK_PASSWORD:-admin}"
      - "traefik.http.routers.traefik.tls=true"
volumes:
  db_data:
networks:
  dwp-proxy:
    name: dwp-proxy
  dwp-web:
    name: dwp-web

Voici ce qui change par rapport au précédent article :

  • Deux réseaux sont crées en fin de fichier : l’un sert à notre proxy inverse Traefik et les conteneurs qu’il surveille, l’autre isole notre base de données de Traefik tout en la reliant aux conteneurs avec lesquels elle doit communiquer.
  • J’ai ajouté un container_name pour plus de clarté.
  • Il faut ajouter des labels à nos conteneurs WordPress et PHPMyAdmin :
    • nous commençons par activer le suivi Traefik,
    • puis, nous définissons les points d’entrée (http ou https),
    • ensuite, nous définissons l’adresse qui permettra d’y accéder,
    • finalement, comme Traefik, nous indiquons que le routeur est uniquement dédié aux requêtes HTTPS.
  • J’utilise des variables d’environnement avec certaines valeurs par défaut.

Étape 3 : Vérification de la configuration Docker-Traefik

Maintenant que Traefik est configuré, vérifions que tout fonctionne.

docker-compose up -d

En accédant à traefik.docker-wordpress.test, vous devriez voir ça (après authentification) :

Tableau de bord de Traefik

En accédant à pma.docker-wordpress.test, vous devriez voir le formulaire de connexion à PHPMyAdmin :

Connexion à PHPMyAdmin

En accédant à www.docker-wordpress.test, vous devriez voir le formulaire d’installation de WordPress. Une fois la procédure terminé, votre site devrait fonctionner :

Docker + Traefik + WordPress + mkcert

Grâce aux variables d’environnement, nous pouvons modifier quasiment toute notre configuration à partir du fichier .env. Malheureusement, dans le fichier de configuration dynamique ce n’est pas possible de les utiliser, il faudra donc penser à remplacer le nom des certificats. Voici à quoi ressemble mon fichier :

# Global
DWP_DOMAIN_NAME=docker-wordpress.test

# MySQL
DWP_MYSQL_VERSION=5.7
DWP_ROOT_PASSWORD=password

# WordPress
DWP_WORDPRESS_VERSION=latest
DWP_DB_NAME=wordpress
DWP_DB_USER=wordpress
DWP_DB_PASSWORD=password
DWP_TABLE_PREFIX=dwp_

# PHPMyAdmin
DWP_PMA_VERSION=latest

# Traefik
DWP_TRAEFIK_VERSION=2.3.1
DWP_TRAEFIK_USER=admin
DWP_TRAEFIK_PASSWORD=$apr1$9yJz5uLe$kmI2.2Jn46PuXVg7IB5JS0

Une installation WordPress opérationnelle !

Désormais, votre installation locale fonctionnera aussi bien avec HTTP (qui est redirigé) qu’HTTPS. Le certificat est correctement détecté et utilisé, sans erreur ou avertissement. Vous pouvez donc développer vos thèmes et plugins !

Si vous utilisez Gulp avec BrowserSync, ça devrait fonctionner sans avoir à ajouter Node (ou d’autres conteneurs). Il faut, par contre, configurer les options correctement. Voici ce que j’utilise (dans un fichier de configuration séparé, à vous de l’adapter) :

// BrowserSync options
bsConfig: {
	proxy: {
		target: 'https://www.docker-wordpress.test',
	},
	hostname: 'docker-wordpress.test',
	port: '8080',
	open: 'local',
	browserAutoOpen: false,
	injectChanges: true,
	ghostMode: {
		scroll: true,
		links: true,
		forms: true,
	},
	snippetOptions: {
		// Provide a custom Regex for inserting the snippet.
		rule: {
			match: /<\/body>/i,
			fn: function(snippet, match) {
				return snippet + match;
			},
		},
	},
	rewriteRules: [
		{
			match: /localhost:8080/g,
			fn: function(req, res, match) {
				return 'localhost:3000';
			},
		},
	],
},

Quelques problèmes avant d’arriver au résultat

Finalement, une fois opérationnel, ça ne paraît pas si compliqué. Pourtant, j’ai eu quelques soucis avant d’arriver à ce résultat. Mon premier problème vient tout simplement de ma méconnaissance de Traefik ; c’est un outil nouveau pour moi. Mon deuxième problème se rapproche d’avantage de l’étourderie.

Je voulais utiliser des variables d’environnement pour que la configuration soit facilement réutilisable. De plus, je voulais déclarer la configuration dynamique dans un fichier séparé plutôt que de l’inclure dans docker-compose.yml. Seulement, Traefik ne supporte pas les variables d’environnement dans la configuration dynamique… Il m’a fallu du temps pour comprendre que la plupart de mes problèmes venaient de là.

Enfin, j’ai longtemps cherché comment configurer l’accès aux sous-domaines, en vain. Puis, je me suis souvenu que mon fichier /etc/hosts contenait seulement www.docker-wordpress.test… Il fallait que je déclare également les sous-domaines.

Pour les deux articles, voici deux commandes qui m’ont été utiles pour résoudre mes problèmes :

// Vérifier que les volumes sont montés correctement :
docker-compose exec wordpress bash
// ou, pour Traefik :
docker-compose exec traefik /bin/sh

// Vérfier les logs de Traefik :
docker-compose logs traefik

Vous pouvez retrouver les fichiers sur mes dépôts Github et Gitlab accompagnés des étapes en anglais.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.