Aller au contenu

Les reverses proxy avec Docker

Prêt à faire de la mise en prod avec du HTTPS ? 😄

Avant de commencer dans le vif du sujet, je voulais vous remercier pour l’engouement autour des tutoriels, je ne m’y attendais pas du tout ! 🤩🤩🤩

Donc aujourd’hui nous allons apprendre les reverse proxy ! Une notion très importante dans la conteneurisation et aussi pour vos futures tâches cloud ! Ça va vous apporter pas mal de notion.

Jusqu’a présent, avec nos docker compose dans les précédents tutoriel, nous ouvrions différents port sur notre machine hôte pour accéder au différents services. Sympa mais pas pratique pour une mise en prod.

Dans le plus beau des mondes, nous aurions juste le port 443/80 d’ouvert pour tout nos sites et chaque site pourrais avoir un nom de domaine différents, et bah du coup, bonjour le reverses proxy !

Petit explication en schéma :

Untitled

Comme vous l’avez compris, le reverse proxy va nous permettre de rediriger les requêtes de différents noms de domaines vers les bon conteneurs ! Il y a d’autre fonctionnalité que nous allons découvrir petit à petit !

Maintenant que vous avez compris le concept, voici une liste de solution remplissant ce rôles :

  1. NGINX: C’est un serveur web très populaire qui peut également être utilisé comme reverse proxy, load balancer, et cache HTTP. Il est connu pour sa performance et sa faible utilisation des ressources.
  2. Caddy: C’est un serveur web moderne qui inclut le support automatique de HTTPS. Il est apprécié pour sa simplicité et sa configuration automatique.
  3. Traefik: C’est un reverse proxy et un load balancer spécialement conçu pour les conteneurs. Il se distingue par sa facilité d’intégration avec les plateformes d’orchestration de conteneurs comme Docker et Kubernetes.
  4. NGINX Proxy Manager: Il s’agit d’une interface graphique pour gérer facilement vos proxy NGINX. Elle est souvent utilisée pour simplifier la configuration de NGINX pour ceux qui préfèrent une interface utilisateur à une ligne de commande.
  5. Apache HTTP Server (Apache2): Bien qu’Apache ne soit pas principalement reconnu comme un reverse proxy, il est capable de fonctionner en tant que tel grâce à ses modules mod_proxy et mod_ssl.

Chaque solutions à des documentations complètes, pour ma part je travaille principalement avec Traefik. Pour ce tutoriel je vais vous montrer un exemple avec Nginx Proxy Manager, avec une interface d’administration assez sympathique. Puis en second partie, on verra Traefik.

Nginx Proxy Manager

Alors, je vais partir d’un docker compose sans nginx, ici j’intègre jenkins en plus qui est une solution de CI/CD pour nos amis développeur !

version: '3.7'
services:
# MySQL Service
mysql:
image: mysql:latest # Utilise l'image MySQL version 8
container_name: mysql-server
volumes:
- ./mysql_data:/var/lib/mysql # Persiste les données de MySQL dans un volume nommé
environment:
MYSQL_ROOT_PASSWORD: rootpassroot # Définit le mot de passe root (à changer pour un mot de passe sécurisé)
MYSQL_DATABASE: wordpress # Crée une base de données WordPress automatiquement
MYSQL_USER: wordpressuser # Définit l'utilisateur de la base de données
MYSQL_PASSWORD: wordpresspass # Définit le mot de passe pour l'utilisateur de la base de données (à changer pour un mot de passe sécurisé)
restart: always # Redémarre toujours le service en cas d'arrêt
networks:
- wordpress-network # Utilise le réseau wordpress-network
- monitor-network # Utilise le réseau monitor-network
# WordPress Service
wordpress:
depends_on:
- mysql # Dépend du service MySQL pour fonctionner
image: wordpress:latest # Utilise la dernière version de WordPress
container_name: wordpress-server
ports:
- "8000:80" # Expose WordPress sur le port 8000 de la machine hôte
environment:
WORDPRESS_DB_HOST: mysql-server # Utilise le service MySQL pour la base de données
WORDPRESS_DB_USER: wordpressuser # Utilisateur de la base de données
WORDPRESS_DB_PASSWORD: wordpresspass # Mot de passe de la base de données
WORDPRESS_DB_NAME: wordpress # Nom de la base de données
restart: always # Redémarre toujours le service en cas d'arrêt
networks:
- wordpress-network # Utilise le réseau wordpress-network
- monitor-network # Utilise le réseau monitor-network
# Jenkins Service
jenkins:
image: jenkins/jenkins:lts # Utilise l'image Jenkins Long-Term Support (LTS)
container_name: jenkins-server
ports:
- "8080:8080" # Expose Jenkins sur le port 8080 de la machine hôte
volumes:
- ./jenkins_data:/var/jenkins_home # Persiste les données de Jenkins dans un volume nommé
restart: always # Redémarre toujours le service en cas d'arrêt
networks:
- jenkins-network # Utilise le réseau jenkins-network
- monitor-network # Utilise le réseau monitor-network
# Uptime Kuma Service
uptimekuma:
image: louislam/uptime-kuma:1 # Utilise l'image Uptime Kuma
container_name: uptimekuma-server
ports:
- "3001:3001" # Expose Uptime Kuma sur le port 3001 de la machine hôte
volumes:
- ./uptimekuma_data:/app/data # Persiste les données de Uptime Kuma dans un volume nommé
restart: always # Redémarre toujours le service en cas d'arrêt
networks:
- uptimekuma-network # Utilise le réseau uptimekuma-network
- monitor-network # Utilise le réseau monitor-network
networks:
# Réseau pour WordPress et MySQL
wordpress-network:
driver: bridge
# Réseau pour Jenkins
jenkins-network:
driver: bridge
# Réseau pour Uptime Kuma
uptimekuma-network:
driver: bridge
# Réseau pour les services de monitoring
monitor-network:
driver: bridge

Voici le docker compose par défaut que je vais déjà exécuter pour vérifier le fonctionnement. Tout roule !

Untitled

Ok maintenant passons à l’intégration du reverse proxy nginx, à partir de ce moment je vais passer sur un vps ovh à quelques euros, vous pouvez toujours ouvrir les ports 443 de votre box mais je ne vous conseille pas pour votre sécurité 🙂

Allez hop petit vps connexion par clé ssh, fail2ban d’activé, on est bien là !

Untitled

Je vous laisse faire l’installation de docker, voici un petit script qui peut vous aider :

#!/bin/bash
sudo apt install -y ca-certificates curl gnupg lsb-release
sudo mkdir -m 0755 -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update -y
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Maintenant, je vais rajouter le reverse proxy au docker compose, direction la doc ! 😝

https://nginxproxymanager.com/guide/#quick-setup

Voici le bout de code que je vais rajouter, tout en supprimant les ports des autres conteneurs :

proxymanager:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
- '80:80'
- '81:81'
- '443:443'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt

Voici le nouveau docker-compose :

version: '3.7'
services:
# MySQL Service
mysql:
image: mysql:latest # Utilise l'image MySQL version 8
container_name: mysql-server
volumes:
- ./mysql_data:/var/lib/mysql # Persiste les données de MySQL dans un volume nommé
environment:
MYSQL_ROOT_PASSWORD: rootpassroot # Définit le mot de passe root (à changer pour un mot de passe sécurisé)
MYSQL_DATABASE: wordpress # Crée une base de données WordPress automatiquement
MYSQL_USER: wordpressuser # Définit l'utilisateur de la base de données
MYSQL_PASSWORD: wordpresspass # Définit le mot de passe pour l'utilisateur de la base de données (à changer pour un mot de passe sécurisé)
restart: always # Redémarre toujours le service en cas d'arrêt
networks:
- wordpress-network # Utilise le réseau wordpress-network
- monitor-network # Utilise le réseau monitor-network
# WordPress Service
wordpress:
depends_on:
- mysql # Dépend du service MySQL pour fonctionner
image: wordpress:latest # Utilise la dernière version de WordPress
container_name: wordpress-server
ports:
- "8000:80" # Expose WordPress sur le port 8000 de la machine hôte
environment:
WORDPRESS_DB_HOST: mysql-server # Utilise le service MySQL pour la base de données
WORDPRESS_DB_USER: wordpressuser # Utilisateur de la base de données
WORDPRESS_DB_PASSWORD: wordpresspass # Mot de passe de la base de données
WORDPRESS_DB_NAME: wordpress # Nom de la base de données
restart: always # Redémarre toujours le service en cas d'arrêt
networks:
- wordpress-network # Utilise le réseau wordpress-network
- monitor-network # Utilise le réseau monitor-network
- frontend # Utilise le réseau frontend
proxymanager:
image: 'jc21/nginx-proxy-manager:latest' # Utilise l'image Nginx Proxy Manager
container_name: nginx-proxy-manager # Nomme le conteneur nginx-proxy-manager
restart: unless-stopped # Redémarre le service en cas d'arrêt
ports:
# Expose les ports 80, 81 et 443 de la machine hôte
- '80:80'
- '81:81'
- '443:443'
volumes:
# Persiste les données de Nginx Proxy Manager dans des volumes nommés
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
- monitor-network # Utilise le réseau monitor-network
- frontend # Utilise le réseau frontend
# Jenkins Service
jenkins:
image: jenkins/jenkins:lts # Utilise l'image Jenkins Long-Term Support (LTS)
container_name: jenkins-server
user: root
ports:
- "8080:8080" # Expose Jenkins sur le port 8080 de la machine hôte
volumes:
- ./jenkins_data:/var/jenkins_home # Persiste les données de Jenkins dans un volume nommé
restart: always # Redémarre toujours le service en cas d'arrêt
networks:
- jenkins-network # Utilise le réseau jenkins-network
- monitor-network # Utilise le réseau monitor-network
- frontend # Utilise le réseau frontend
# Uptime Kuma Service
uptimekuma:
image: louislam/uptime-kuma:1 # Utilise l'image Uptime Kuma
container_name: uptimekuma-server
ports:
- "3001:3001" # Expose Uptime Kuma sur le port 3001 de la machine hôte
volumes:
- ./uptimekuma_data:/app/data # Persiste les données de Uptime Kuma dans un volume nommé
restart: always # Redémarre toujours le service en cas d'arrêt
networks:
- uptimekuma-network # Utilise le réseau uptimekuma-network
- monitor-network # Utilise le réseau monitor-network
- frontend # Utilise le réseau frontend
networks:
# Réseau pour WordPress et MySQL
wordpress-network:
driver: bridge
# Réseau pour Jenkins
jenkins-network:
driver: bridge
# Réseau pour Uptime Kuma
uptimekuma-network:
driver: bridge
# Réseau pour les services de monitoring
monitor-network:
driver: bridge
# Réseau pour le reverse proxy
frontend:
driver: bridge

Et là ce qui est trop cool, comme nous n’avons pas encore de data persistante et que nos services sont dans un fichier, on va pouvoir copier coller le code dans notre vps sans problème. Je vais donc faire un nano docker-compose.yaml et copier le code.

Untitled

C’est parti !

Untitled

Une fois le déploiement fini, rendez-vous sur http://votre_ip_public:81

Untitled

Untitled

Rendez-vous sur http://ip_public:81 Identifiant par défaut : email: admin@example.com mot de passe: changeme

Au démarrage vous serez invité à changer le mail ainsi que le mot de passe, je vous conseilles de mettre des identifiants fort même si plus tard nous fermerons le port 81.

Untitled

Super ! Avant de découvrir quelques fonctionnalités, je vais allez faire 3 entrées de type A de mon nom de domaine sur l’ip publique du VPS OVH.

Untitled

Untitled

Maintenant que c’est fait nous allons créer des certificats SSL pour sécuriser les connexions à nos sites.

Nous allons utiliser le challenge HTTP01

En gros du gros, Let’s encrypt va demander de mettre à disposition une url, nginx va s’en occuper et ensuite signer avec clé un fichier pour que let’s encrypt délivre le certificat.

(https://letsencrypt.org/fr/how-it-works/)

Untitled

Maintenant que vous avez un aperçu du mécanisme on peut retourner dans notre console de nginx proxy manager.

Dans la partie SSL certificates, nous allons créer 3 certificats.

Untitled

Untitled

Et voila nos 3 certificats !

Untitled

Maintenant que nous avons nos certificats de stockés, on va allez faire les redirection vers les conteneurs. Allez dans la partie Hosts et Proxy Hosts

Untitled

Dans domain name, nous indiquons le nom de domaine qui sera affilié au conteneur, ensuite on choisis sur quelle conteneur on redirige et sur quel port.

Untitled

Ensuite dans la partie SSL on va choisir notre certificat.

Untitled

Une fois les redirection configurer, on test ? 😋

Untitled

Sur Jenkins :

Untitled

Petit problème sur uptime, on va regarder si on a une option websocket dans Nginx manager ?

Untitled

On modifie notre configuration :

Untitled

Et voilà le résultat :

Untitled

C’est simple non ? Vous pouvez bien sur allez plus loins, je vous laisse découvrir par vous mêmes.

Untitled

Si vous souhaitez héberger un WordPress, un vps chez ovh, un nom de domaine et pour 7€ par mois vous avez votre site en ligne. Personnellement, je trouve ça raisonnable mais pour les personnes qui font juste des sites vitrines, regardez cotée serverless 👀

Bon, on va passer à une partie qui me hype un peu plus je vous avoue, Traefik.

Untitled

Traefik peut être un peu plus compliqué, mais il est très très puissant, de plus si vous avez besoin pendant votre phase de développement, vous pouvez générer un certificat ssl en local, incroyable.

Ici je vais bien sûr vous montrer comment générer nos certificats avec Let’s Encrypt mais si vous êtes intéressé pour avoir un certificat en local → https://yoandev.co/du-https-en-local-avec-docker-traefik-traefik.me-et-lets-encrypt/

Avec Traefik nous allons ajouter des routes, pour accéder au conteneur, nous pouvons le configurer via des fichier YAML, TOML ou en cli directement dans le docker compose.

https://doc.traefik.io/traefik/assets/img/providers/docker.png

Personnellement j’utilise beaucoup la méthode cli, car je trouve ça rapide et tout est dans mon docker compose.

La petite documentation qu’on aime bien : https://doc.traefik.io/traefik/providers/docker/

Fini le bla-bla on va créer notre conteneur Traefik, j’enlève donc nginx proxy manager dans le docker compose.

Voici un exemple de conteneur simple Traefik :

traefik:
restart: unless-stopped
image: traefik:v2.10.4
command:
- --providers.docker=true
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
ports:
- "80:80"
- "443:443"
volumes:
- "./letsencrypt:/letsencrypt"
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- frontend

Ici nous indiquons les points d’entrée à utiliser, le port 80 va servir à let’s encrypt. L’accès au service docker permet de vérifier les conteneurs en fonctionnement.

Ensuite, je vais ajouter un resolver pour résoudre les challenges let’s encrypt.

traefik:
restart: unless-stopped
image: traefik:v2.10.4
command:
- --providers.docker=true
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --certificatesresolvers.myresolver.acme.email=youremail@mail.com
- --certificatesresolvers.myresolver.acme.caserver=https://acme-v02.api.letsencrypt.org/directory
- --certificatesresolvers.myresolver.acme.keytype=RSA4096
- --certificatesresolvers.myresolver.acme.tlschallenge=true
- --certificatesresolvers.myresolver.acme.httpchallenge=true
- --certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web
- --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json
ports:
- "80:80"
- "443:443"
volumes:
- "./letsencrypt:/letsencrypt"
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- frontend

Maintenant pour les conteneurs en frontend je vais ajouter une route et utiliser le resolver pour avoir un certificat SSL.

Nous allons ajouter les indications de traefik avec une section label, ce qui corresponds à des étiquettes, voici pourquoi traefik doit avoir accès à docker.

uptimekuma:
image: louislam/uptime-kuma:1 # Utilise l'image Uptime Kuma
container_name: uptimekuma-server
labels:
- "traefik.enable=true"
- traefik.http.routers.uptime.rule=Host(`uptime.yourdomain.com`)
- traefik.http.routers.uptime.tls=true
- traefik.http.routers.uptime.tls.certresolver=myresolver
volumes:
- ./uptimekuma_data:/app/data # Persiste les données de Uptime Kuma dans un volume nommé
restart: always # Redémarre toujours le service en cas d'arrêt
networks:
- uptimekuma-network # Utilise le réseau uptimekuma-network
- monitor-network # Utilise le réseau monitor-network
- frontend # Utilise le réseau frontend

Bien, ça m’a l’air pas mal, je vais juste ajouter une ligne pour avoir tout les logs info et voir ce qui ce passe.

Voici le docker compose final :

version: '3.7'
services:
# MySQL Service
mysql:
image: mysql:latest # Utilise l'image MySQL version 8
container_name: mysql-server
volumes:
- ./mysql_data:/var/lib/mysql # Persiste les données de MySQL dans un volume nommé
environment:
MYSQL_ROOT_PASSWORD: rootpassroot # Définit le mot de passe root (à changer pour un mot de passe sécurisé)
MYSQL_DATABASE: wordpress # Crée une base de données WordPress automatiquement
MYSQL_USER: wordpressuser # Définit l'utilisateur de la base de données
MYSQL_PASSWORD: wordpresspass # Définit le mot de passe pour l'utilisateur de la base de données (à changer pour un mot de passe sécurisé)
restart: always # Redémarre toujours le service en cas d'arrêt
networks:
- wordpress-network # Utilise le réseau wordpress-network
- monitor-network # Utilise le réseau monitor-network
# WordPress Service
wordpress:
depends_on:
- mysql # Dépend du service MySQL pour fonctionner
image: wordpress:latest # Utilise la dernière version de WordPress
container_name: wordpress-server
labels:
# Définit les labels pour Traefik
- "traefik.enable=true" # Active Traefik pour ce service
- traefik.http.routers.tutopress.rule=Host(`tutopress.ninapepite.ovh`) # Définit le nom de domaine pour ce service
- traefik.http.routers.tutopress.tls=true # Active le TLS pour ce service
- traefik.http.routers.tutopress.tls.certresolver=myresolver # Définit le résolveur de certificats pour ce service
environment:
WORDPRESS_DB_HOST: mysql-server # Utilise le service MySQL pour la base de données
WORDPRESS_DB_USER: wordpressuser # Utilisateur de la base de données
WORDPRESS_DB_PASSWORD: wordpresspass # Mot de passe de la base de données
WORDPRESS_DB_NAME: wordpress # Nom de la base de données
restart: always # Redémarre toujours le service en cas d'arrêt
networks:
- wordpress-network # Utilise le réseau wordpress-network
- monitor-network # Utilise le réseau monitor-network
- frontend # Utilise le réseau frontend
traefik:
# Définit le service Traefik
restart: unless-stopped # Redémarre toujours le service en cas d'arrêt
image: traefik:v2.10.4 # Utilise l'image Traefik version 2.10.4
command:
# Définit les commandes pour Traefik
- --providers.docker=true # Active le provider Docker
- --entrypoints.web.address=:80 # Définit le port 80 pour le service web
- --entrypoints.websecure.address=:443 # Définit le port 443 pour le service websecure
- --log.level=INFO # (Default: error) DEBUG, INFO, WARN, ERROR, FATAL, PANIC
- --certificatesresolvers.myresolver.acme.email=youremail@mail.com # Définit l'adresse email pour les certificats
- --certificatesresolvers.myresolver.acme.caserver=https://acme-v02.api.letsencrypt.org/directory # Définit le serveur ACME pour les certificats
- --certificatesresolvers.myresolver.acme.keytype=RSA4096 # Définit le type de clé pour les certificats
- --certificatesresolvers.myresolver.acme.tlschallenge=true # Active le TLS challenge pour les certificats
- --certificatesresolvers.myresolver.acme.httpchallenge=true # Active le HTTP challenge pour les certificats
- --certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web # Définit le port 80 pour le HTTP challenge
- --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json # Définit le chemin pour le stockage des certificats
ports:
- "80:80" # Expose le port 80
- "443:443" # Expose le port 443
volumes:
- "./letsencrypt:/letsencrypt" # Persiste les données des certificats dans un volume persistant
- /var/run/docker.sock:/var/run/docker.sock:ro # Montre le socket Docker
networks:
# Définit les réseaux pour Traefik
- frontend # Utilise le réseau frontend
# Uptime Kuma Service
# Jenkins Service
jenkins:
image: jenkins/jenkins:lts # Utilise l'image Jenkins Long-Term Support (LTS)
container_name: jenkins-server
user: root
labels:
# Définit les labels pour Traefik
- "traefik.enable=true" # Active Traefik pour ce service
- traefik.http.routers.jenkins.rule=Host(`jenkins.ninapepite.ovh`) # Définit le nom de domaine pour ce service
- traefik.http.routers.jenkins.tls=true # Active le TLS pour ce service
- traefik.http.routers.jenkins.tls.certresolver=myresolver # Définit le résolveur de certificats pour ce service
volumes:
- ./jenkins_data:/var/jenkins_home:rw # Persiste les données de Jenkins dans un volume nommé
restart: always # Redémarre toujours le service en cas d'arrêt
networks:
- jenkins-network # Utilise le réseau jenkins-network
- monitor-network # Utilise le réseau monitor-network
- frontend # Utilise le réseau frontend
uptimekuma:
image: louislam/uptime-kuma:1 # Utilise l'image Uptime Kuma
container_name: uptimekuma-server
labels:
# Définit les labels pour Traefik
- "traefik.enable=true" # Active Traefik pour ce service
- traefik.http.routers.uptime.rule=Host(`uptime.ninapepite.ovh`) # Définit le nom de domaine pour ce service
- traefik.http.routers.uptime.tls=true # Active le TLS pour ce service
- traefik.http.routers.uptime.tls.certresolver=myresolver # Définit le résolveur de certificats pour ce service
volumes:
- ./uptimekuma_data:/app/data # Persiste les données de Uptime Kuma dans un volume nommé
restart: always # Redémarre toujours le service en cas d'arrêt
networks:
- uptimekuma-network # Utilise le réseau uptimekuma-network
- monitor-network # Utilise le réseau monitor-network
- frontend # Utilise le réseau frontend
networks:
# Réseau pour WordPress et MySQL
wordpress-network:
driver: bridge
# Réseau pour Jenkins
jenkins-network:
driver: bridge
# Réseau pour Uptime Kuma
uptimekuma-network:
driver: bridge
# Réseau pour les services de monitoring
monitor-network:
driver: bridge
# Réseau pour le reverse proxy
frontend:
driver: bridge

Go test !

Untitled

Petit docker logs sur le service traefik, on vois que le ACME est chargé pour les certificats SSL.

Untitled

On a sur chaque site pour vérifier.

(sur un site j’ai eu une error 504 Gateway, un restart du conteneur Traefik a suffit)

Tada !

Untitled

Et là pas de soucis de websocket avec Uptime ! 😀

Petit tips, avec Uptime Kuma, vous pouvez surveiller vos certificats et vérifier que le renouvellement se fait bien sans problèmes.

Untitled

Si vous avez compris les mécanismes et pratiquer en même temps, je pense que vous avez pas mal de connaissance pour commencer à mettre en ligne des projets dans des conteneurs. Si besoin voici le github avec les trois docker compose : https://github.com/Ninapepite/medium-reverse-proxy

Merci pour votre lecture !