Docker
Héberger plusieurs sites dans des containers Docker
L’objectif est d’auto héberger plusieurs sites web sur un VPS, chacun dans leur container Docker, avec un Nginx Reverse Proxy lui même dans un Docker.
Nous allons voir comment héberger plusieurs sites web avec Docker. Notre architecture finale ressemblera à ça :

Prérequis
- Un VPS auquel on peut accéder en SSH avec les droits root.
- Un nom de domaine sur lequel on peut modifier le DNS.
Outils recommandés
lazydocker: un outil qui permet de monitorer Docker (les images, les networks, les containers en cours, …) et de faire des actions dessus de manière plus intuitive.
https://github.com/jesseduffield/lazydockermidnightcommander: un outil qui permet facilement de gérer ses fichier et qui a la particularité de pouvoir se connecter en SSH à un autre serveur. Pratique pour migrer des serveurs.
https://doc.ubuntu-fr.org/midnight_commander
Etape 1 : Installer Docker / Docker compose
Docker sera la couche de virtualisation qui permettra de lancer des containers.
Pour l’installer :
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
On créer un réseau spécifique qui servira à regrouper plusieurs images dans le même sous-réseau.
docker network create --subnet=172.18.0.0/16 webserver_network
Etapes 2 : Nging-reverse-proxy
Modification du DNS
Chez notre registrar, on fait met en place une entrée DNS de type A pour un domaine ou un sous-domaine qui pointe vers l’IP de notre serveur.
Par exemple cheatsheet.fr.
Image Docker Nginx
Notre reverse proxy Nginx aura besoin d’enregistrer des informations. Pour éviter qu’elles soient perdues entre chaque redémarrage, on va faire en sorte qu’elles soient stockées sur notre machine.
Dans un répertoire nginx-proxy-manager, on va créer un répertoire data et un répertoire letsencrypt qui seront liés au volume de l’image Docker.
Afin de lancer notre image Docker avec l’utilisateur www-data du groupe www-data, on va créer un fichier .env qui contiendra l’identifiant de l’utilisateur et du groupe qu’on souhaitera utiliser dans la configuration :
PUID=33
PGID=33
Généralement, l’utilisateur www-data est l’identifiant 33. Pour s’en assurer, on peut taper la commande :
less /etc/passwd
On crée un fichier docker-compose.yml avec le contenu suivant :
version: '3.8'
services:
app:
container_name: nginx-proxy-manager
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
- '80:80' # Public HTTP Port
- '443:443' # Public HTTPS Port
- '81:81' # Admin Web Port
environment:
PUID: ${PUID}
PGID: ${PGID}
# Uncomment this if you want to change the location of
# the SQLite DB file within the container
# DB_SQLITE_FILE: "/data/database.sqlite"
# Uncomment this if IPv6 is not enabled on your host
# DISABLE_IPV6: 'true'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
default:
name: webserver_network
external: true
Explications :
container_name: nginx-proxy-manager : le nom qu’on souhaite donner au conteneur, pour le retrouver plus facilement.
image: 'jc21/nginx-proxy-manager:latest' : l’image Docker sur laquelle on va se baser. https://github.com/NginxProxyManager/nginx-proxy-manager
restart: unless-stopped : si l’image s’arrête, il faut qu’elle redémarre automatiquement, sauf si on l’a arrêtée volontairement.
ports:: on fait un mapping des ports 80 (http), 443 (https) et 81 (interface web de gestion de Nginx).
environment: on déclare les 2 variables d’environnement qui servent à déterminer l’utilisateur et le groupe.
DB_SQLITE_FILE: "/data/database.sqlite" : nom du fichier dans lequel on enregistre la base de données qui stocke la configuration de Nginx.
DISABLE_IPV6: 'true' : à définir lorsque notre serveur ne gère pas l’IPv6.
volumes: : on fait pointer le répertoire local ./data vers le répertoire /data du container, et le répertoire local ./letsencrypt vers le répertoire /etc/letsencrypt du container.
networks: : permet de dire que par défaut on utilise le réseau webserver_network qui est un réseau externe (celui créé plus haut).
On devrait avoir la structure suivante :
Workspaces/
└── nginx-proxy-manager/
├── .env
├── data/
├── docker-compose.yml
├── letsencrypt/
Démarrage du serveur
On démarre le serveur avec la commande :
docker compose up -d --build
Configuration du reverse-proxy
On peut accéder à l’interface de Nginx par son IP et le port 81.
Etape 3 : Nouveau site
Structure et container
- On crée un répertoire
Workspaces/mon_sitequi aura la structure suivante :
Workspaces/
└── mon_site/
├── .env
├── docker-compose.yml
├── Dockerfile
└── src/
└── index.php
- Le fichier
.envcontiendra toutes les variables d’environnements dont on aura besoin. Par exemple :
PORT=8080
DB_LOGIN=Mon identifiant
PORT sera le port sur lequel l’instance Docker tournera.DB_LOGIN sera une variable d’environnement sur le serveur.
- Le fichier
Dockerfileaura le contenu suivant :
FROM php:8.2-apache as base
afin de lui dire que l’image de base est un container qui contient PHP 8.2 et Apache.
- Le fichier
docker-compose.ymlaura le contenu suivant :
version: "3.9"
services:
php:
container_name: mon_site_php
image: php
restart: always
#depends_on:
# - db
build:
context: .
dockerfile: Dockerfile
target: base
volumes:
- ./src:/var/www/html
environment:
- DB_LOGIN=${DB_LOGIN}
ports:
- "127.0.0.1:${PORT}:80"
networks:
- webserver_network
- internal_network
networks:
webserver_network:
name: webserver_network
external: true
internal_network:
name: mysite_network
driver: bridge
L’élément volume permet de faire le mapping sur le répertoire src local vers /var/www/html distant.
L’élément environment permet de créer des variables d’environnements sur l’environnement distant.
L’élément ports permet de dire que le port local pointe sur le port 80 distant.
L’élément networks permet de lier l’image à un réseau interne et un réseau externe.
Ajouter une base de données
Pour utiliser une base de données de type MySQL, on peut rajouter dans docker-compose.yml :
db:
container_name: database_db
image: mysql:8.3
restart: unless-stopped
user: "${UID}:${GID}"
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
#healthcheck:
# test: mysqladmin ping -h 127.0.0.1 -u $$MYSQL_USER --password=$$MYSQL_PASSWORD
# interval: 5s
# timeout: 10s
# retries: 10
# start_period: 5s
volumes:
- ./db_data:/var/lib/mysql
networks:
- internal_network
Pour utiliser une base de donnée de type PosgreSQL, on peut rajouter dans docker-compose.yml :
db:
container_name: mydatabase_db
image: postgres
restart: unless-stopped
# set shared memory limit when using docker-compose
shm_size: 128mb
user: "${UID}:${GID}"
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
#healthcheck:
# test: [ "CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}" ]
# interval: 5s
# timeout: 10s
# retries: 10
# start_period: 5s
volumes:
- ./db_data:/var/lib/postgresql/data
networks:
- internal_network
Pour utiliser une base de données de type MariaDB, on peut rajouter dans docker-compose.yml :
db:
container_name: database_db
image: mariadb:latest
restart: unless-stopped
environment:
PUID: ${UID}
PGID: ${GID}
MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
MARIADB_DATABASE: ${MARIADB_DATABASE}
MARIADB_USER: ${MARIADB_USER}
MARIADB_PASSWORD: ${MARIADB_PASSWORD}
volumes:
- ./db_data:/var/lib/mysql
networks:
- internal_network
Ajouter une interface d’administration pour la base de données
Si on veut un PHPMyAdmin :
phpmyadmin:
container_name: famille-parfaite_myadmin
depends_on:
- db
image: phpmyadmin:latest
ports:
- 8080:80
restart: unless-stopped
environment:
PMA_HOST: db
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
UPLOAD_LIMIT: 1024M
MAX_EXECUTION_TIME: 10800
networks:
- internal_network
Si on veut un Adminer :
adminer:
image: adminer
restart: unless-stopped
environment:
ADMINER_DEFAULT_SERVER: db
depends_on:
- db
ports:
- 8080:8080
networks:
- internal_network
Contenu du site
- Le fichier
index.phpcontient par exemple :
<?php
echo "Hello " . getenv("DB_LOGIN") . "!";
?>
- En se mettant dans le répertoire
mon_siteon démarre le container.
sudo docker compose up --build
Etape 4 : Redirections Nginx-reverse-proxy
Maintenant que les sites sont opérationnels, il faut faire en sorte que le nom de domaine redirige sur la bonne instance Docker.
Pour cela, on s’authentifie sur l’instance Nginx-reverse-proxy en y accédant par son IP et le port 81.
Par exemple : http://123.45.67.89:81
A la première connexion, il faut renseigner une adresse email et un mot de passe fort.
Ensuite, il faut ajouter un « host ». Comme on ne connait pas forcément l’IP de notre container Docker dans le réseau « websote_network », on peut utiliser à la place le « hostname » que l’on a défini dans le fichier docker-compose.yml.

Il est possible de générer un certificat SSL avec Let’sEncrypt dans l’onglet « SSL ».
Fichiers d’exemple
Voici les archives complètes de Nginx et des exemples de sites :





