Table des matières

, , , ,

Monter un serveur LAMP grâce à Docker

Cet tutoriel est âgé (pour ce type de technologie) et mériterait au minimum une bonne mise à jour.

Voir ce message sur le forum.

Docker permet d'installer les logiciels de son choix, dans les versions de son choix quelle que soit notre version de Linux. Pour cela il isole les logiciels qu'on souhaite utiliser les uns des autres avec chacun leurs dépendances dans des "conteneurs". Mais il permet aussi d'éviter les inconvénients de la virtualisation (fichiers lourds, ressources machines divisées, lenteurs, etc.).
C'est donc une solution extrêmement pertinente lorsqu'il s'agit de déployer une plateforme de développement (afin de reproduire n'importe quel environnement de production), ou d'une manière générale de déployer des applications ou des versions de logiciels non supportées par le système courant. De plus cela confère une portabilité très aisée et une grande souplesse à sa configuration.

Nous traiterons ici de la mise en place d'un serveur LAMP (Linux Apache MySQL PHP) au moyen de Docker.
Pour cet exemple nous allons installer PHP 5.6 qui n'est pas disponible sur Xenial.

Nous allons considérer 2 méthodes : une simple, l'autre plus avancée. La méthode simple consiste à installer un seul conteneur avec toute la pile LAMP.
Avec la méthode avancée on installera le serveur web (Apache et PHP) séparément du serveur de base de données, dans 2 conteneurs différents. C'est la méthode conseillée par Docker, qui recommande de séparer chaque processus / service dans un conteneur différent. Cela rend effectivement notre environnement de travail extrêmement modulaire et permet par ex. de switcher de PHP 5.6 à PHP 7 ou de MySQL à MariaDB en un claquement de doigt !

De nombreuses images crées par la communauté sont disponibles sur le Docker Hub et d'autres plus officielles sur le Docker Store.

Installation de Docker

Pour installer Docker sous Ubuntu, cliquez sur docker.io ou en ligne de commande :

sudo apt install docker.io

Ajoutez votre utilisateur au groupe docker afin de pouvoir manipuler les conteneurs :

Cette méthode pose un problème de sécurité. Il devient, en effet, possible d'élever les privilèges de l'utilisateur sans entrer à nouveau de mot de passe. Si vous êtes sur un serveur en production n'entrez pas cette commande et utilisez la commande sudo docker au lieu de docker pour la suite des opérations.
sudo usermod -aG docker $USER

Finalement redémarrez votre ordinateur.

Mise en place des répertoires de travail

Par défaut les modifications apportées aux fichiers d'un conteneur ne sont pas persistantes (tout est réinitialisé à chaque lancement du conteneur). L'intérêt de l'option -v (volume) de Docker est de créer une sorte de lien symbolique entre le conteneur et le système hôte, ainsi les fichiers modifiés par le conteneur persisterons sur le système.

Commençons donc par créer des répertoires pour le contenu que l'on souhaite modifier et conserver, en l'occurrence les fichiers du site et les bases de donnés :

mkdir -p ~/.docker/www ~/.docker/mysql

Rendons les lisibles et modifiables par Docker :

chmod 777 ~/.docker/www ~/.docker/mysql

Pour la suite, à vous de choisir entre les deux méthodes en fonction de vos besoins.

Méthode simple

Pour cette méthode, choisissons une image contenant la pile LAMP complète. Celle-ci est basée sur Debian Jessie, PHP 5, Apache 2, et MySQL : https://hub.docker.com/r/lioshi/lamp/

Lançons donc un conteneur avec cette image :

docker run --mount type=volume,src=web_data,dst=/var/www/html,volume-driver=local,volume-opt=device=${HOME}/.docker/www,volume-opt=o=bind,volume-opt=type=none -v ~/.docker/mysql:/var/lib/mysql -p 80:80 -p 3306:3306 --restart=always lioshi/lamp:php5

Les options --mount et -v (volume) relient les répertoires locaux ~/.docker/www et ~/.docker/mysql aux répertoires /var/www/html et /var/lib/mysql de l'image Debian dans le conteneur. L'option -p (port) relie les ports qui nous intéressent du conteneur aux ports de notre machine locale. Ici le port 80 (HTTP) et le port 3306 (MySQL). L'option --restart=always permet de relancer le conteneur à chaque démarrage de Docker (donc au démarrage de l'ordinateur).

La première fois qu'on lance le conteneur, Docker télécharge toutes les librairies nécessaires, ce qui prend un peu de temps.

Et c'est tout ! À partir de là notre serveur tourne.

Les bases de données seront sauvegardées dans notre répertoire ~/.docker/mysql. PhpMyAdmin est accessible sur http://localhost/phpmyadmin
Avec cette image Docker l'utilisateur par défaut pour les bases de données sera admin avec le mot de passe admin (hôte localhost).

On peut mettre les fichiers de son site dans notre répertoire ~/.docker/www. On pourra ensuite y accéder sur http://localhost

Méthode avancée

Pour cette méthode nous avons besoin d'une image pour Apache avec PHP, une image pour MySQL et une image pour phpMyAdmin.

Pour la suite nous allons nous servir d'un outil bien pratique pour lancer plusieurs conteneurs en évitant les lignes de commandes à rallonge : docker-compose. Il faut tout d'abord l'installer :

sudo apt install docker-compose

Nous allons ensuite créer un fichier docker-compose.yml dans lequel nous allons définir notre environnement :

version: '2'

services:
    web:
        image: lavoweb/php-5.6
        ports:
            - "80:80"
        volumes:
            - ~/.docker/www:/var/www/html
        links:
            - db:db
    db:
        image: mysql:5.5
        volumes:
            - ~/.docker/mysql:/var/lib/mysql
        ports:
            - "3306:3306"
        environment:
            - MYSQL_ROOT_PASSWORD=root
    myadmin:
        image: phpmyadmin/phpmyadmin
        ports:
            - "8080:80"
        links:
            - db:db

Les services sont des conteneurs. web, db et myadmin sont les noms qu'on décide de leur donner. Ces noms sont utilisés pour créer des liens - links - entre les différents conteneurs. Par ex. db:db signifie que notre conteneur db (du nom de notre conteneur MySQL) correspondra à l'hôte db dans notre conteneur web. Pour se connecter au serveur MySQL il faudra donc entrer db comme nom d'hôte.
On peut également passer des variables d'environnements à nos conteneurs. Ici nous définissons le mot de passe de l'utilisateur MySQL root comme étant root.
De la même manière que l'options -v de la ligne de commande, le paramètre volumes relie les répertoires locaux ~/.docker/www et ~/.docker/mysql aux répertoires /var/www/html de l'image Apache/PHP et /var/lib/mysql de l'image MySQL dans nos conteneurs. Et le paramètre ports de la même manière que l'options -p, relie les ports qui nous intéressent de nos conteneurs aux ports de notre machine locale. Ici le port 80 (HTTP) et le port 3306 (MySQL), ce dernier peut-être et même doit-être supprimé si vous avez déjà un LAMP sur votre machine locale (hôte).

Une fois ce fichier mis en place on peut lancer tous nos conteneurs avec un simple

docker-compose up

Il faut attendre un peu que les images soient téléchargées, et c'est tout ! Notre serveur est en route.

De la même manière qu'avec la méthode simple les bases de données seront sauvegardées dans notre répertoire ~/.docker/mysql. PhpMyAdmin est cette fois accessible sur le port 8080 : http://localhost:8080
Et cette fois l'utilisateur par défaut pour les bases de données sera root avec le mot de passe root (hôte db).

On peut mettre les fichiers de notre site dans notre répertoire ~/.docker/www. On pourra ensuite y accéder sur http://localhost.

Pour passer de MySQL 5.5 à MySQL 5.7 par ex. il suffit de remplacer image: mysql:5.5 par image: mysql:5.7 et de relancer docker-compose up.

Aller plus loin

Configuration de PHP, d'Apache et de MySQL

Pour ajuster les configurations des différents services, le mieux est aussi d'utiliser la fonctionnalité volume (qu'on peut utiliser comme des liens symboliques entre les conteneurs et l'exterieur, aussi bien pour des répertoires que pour des fichiers). Ainsi on peut facilement éditer ou modifier les fichiers de config sur notre système. C'est très facile, à condition de savoir où doivent se trouver ces fichiers de config sur nos images. Pour stocker nos configs personnalisées on peut créer un répertoire :

mkdir ~/config

Puis on créé notre fichier ~/config/php.ini qui contient seulement les paramètres que l'on souhaite modifier, par ex. :

short_open_tag = On

error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
display_errors = On

max_execution_time = 600
memory_limit = 1024M
post_max_size = 1024M
upload_max_filesize = 5G
max_file_uploads = 200

allow_url_fopen = On

date.timezone = "Europe/Paris"

Finalement on relie ce fichier avec le paramètre volume, par ex. pour l'image de notre méthode simple :

-v ~/config/php.ini:/etc/php5/apache2/conf.d/90-custom.ini

Ainsi on garde la configuration par défaut de l'image mais on ajoute notre config customisée en dernier, afin d'écraser les valeurs prédéfinies (conf.d/90-custom.ini, ces fichiers sont chargés par ordre alphabétique).
Si on préfère on peut aussi écraser complètement le fichier /etc/php5/apache2/php.ini.

Pour Apache on peut si on le souhaite définir une liste de VirtualHosts (chacun dans un fichier .conf dans le répertoire ~/config/virtualhosts) :

-v ~/config/virtualhosts:/etc/apache2/sites-enabled

Création de Dockerfiles personnalisés

Le Docker Hub et le Docker Store fournissent une quantité impressionnante d'images toutes faites. Mais parfois il est nécessaire de modifier ces images, par ex. pour installer une extension exotique à PHP.

La plupart des images disponibles sur ces hubs fournissent un fichier Dockerfile. exemple. Ce fichier est une liste d'instructions qui permettent de construire une image. Il définit une image de base (par ex. Debian:telle_version) et contient ensuite une série de commandes telles que apt install …. On peut également créer ce fichier sur notre machine pour créer une image Docker qui corresponde exactement à nos besoin (pour plus de facilité on peut évidemment partir d'un Dockerfile existant).

Voici un site qui explique plus en détail comment s'y prendre : https://putaindecode.io/fr/articles/docker/dockerfile/.

Une fois notre Dockerfile créé, par ex. à l'emplacement ~/webserver/Dockerfile, on peut construire notre image avec la commande :

docker build -t apache-php5.6 ~/webserver/

après quoi on peut lancer notre image nommée apache-php5.6 dans un conteneur :

docker run -v ~/www:/var/www/html -p 80:80 apache-php5.6

Commandes utiles

Certaines commandes sont très utiles pour manipuler nos images et nos conteneurs :

docker ps -a

Lister les conteneurs. Pour chaque conteneur on récupère ainsi un id comme fc2ff39a9270

docker rm fc2

Supprimer un conteneur. les 3 premiers caractères de l'id suffisent à l'identifier.

docker images

Lister les images installées. On récupère aussi leurs ids.

docker rmi e15

Supprimer une image dont l'id commence par e15.

docker exec -ti fc2 bash

Se connecter au terminal d'un conteneur dont l'id commence par fc2 pour en explorer les entrailles. Souvenez-vous que les modifications disparaîtront au prochain démarrage du conteneur.

Problèmes connus

Soucis de fuseaux horaire en PHP

Certains logiciels PHP n'auront pas de mal à fonctionner tel quel, par exemple SMI(Services Maintenance Interventions) mais d'autres s'en plaindront sans cesse avec ce genre de message, dans cet exemple il s'agit de Dolibarr :

Warning: getdate(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone. in /var/www/html/dolibarr/htdocs/core/lib/functions.lib.php on line 1702

Ceci est dû au fait que le conteneur Docker ne donne pas accès par défaut a la configuration de fuseaux horaires définie dans le système hôte, ce problème peut facilement être résolu en deux étapes simples.

Le principe est de créer un fichier timezone.ini dans le même répertoire que le fichier docker-compose.yml contenant la ligne de commande de la timezone et d'en inclure la déclaration dans le fichier docker-compose.yml.

Pour cela créer un fichier timezone.ini et y mettre juste ceci :

date.timezone=Europe/Paris

Insérer cette ligne dans le fichier docker-compose.yml pour déclarer la présence du fichier .ini dans la section web:

volumes:
            - ~/www:/var/www/html            
            - "./timezone.ini:/usr/local/etc/php/conf.d/timezone.ini"

Ne pas oublier d'arrêter le conteneur simplement par la combinaison de touche CTRL+C dans la fenêtre de terminal ayant lancé Docker et le relancer et ça fonctionne !

Merci à Emilyshepherd du forum Docker pour son excellente solution.

Voir aussi

Pour des éventuelles questions, suggestions, retour, n'hésitez pas à participer à ce sujet sur le forum.


Contributeur principal : krodelabestiole , ajout d'informations : yoritomo