La réputation de Nginx comme serveur web n'est plus à faire. Il est également possible de l'utiliser comme reverse-proxy afin de gérer le flux entrant HTTP ou HTTPS de vos applications, et de façon transparente, sur votre serveur. Au cours de cet article, nous allons installer Nginx comme reverse-proxy dans un environnement conteneurisé !
Préambule
Certains aspects des applications Web, comme le chiffrement SSL, la mise en cache des demandes et la découverte de services, peuvent être gérés en dehors de l'application elle-même.
Un reverse-proxy, comme Nginx ou même Traefik, peuvent assumer certaines de ces tâches, cela permet notamment aux développeurs de ne pas se soucier de ces problématiques. Et tout cela peut être réalisé de façon simple avec Docker, docker-compose et dans le cadre de cet article : Nginx.
Pré-requis
Il sera bien sûr nécessaire d'avoir une installation de Docker avec docker-compose fonctionnelle. Vous pouvez suivre ces articles afin de réaliser cette installation :
Mais ne perdons pas plus de temps !
Nginx
Avant tout, une petite note pour les connaisseurs :
Il existe une image Docker, très bien réalisée, que j'ai moi même utilisée pendant plusieurs mois/années : https://hub.docker.com/r/jwilder/nginx-proxy/
Cette image permet de faire de l'auto-découverte de service nécessitant un reverse-proxy en utilisant les variables d'environnement et l'API Docker. Pourquoi ne pas l'utiliser dans cet article ?
Le but de mon article, reste de découvrir comment mettre en place Nginx en reverse proxy de façon basique. Et ainsi de comprendre le fonctionnement de cette mise en place.
Si vous appréhendez ces bases, alors utiliser cette image sera simple et accessible.
Je vais donc utiliser l'image Docker nginx officielle et utiliser un fichier de configuration que nous allons éditer manuellement. Mais commençons par créer un service qui va écouter sur le port 80 et nous afficher quelques informations !
Pour cela, je vais créer un fichier docker-compose.yaml
:
version: '3.7'
services:
whoami:
image: containous/whoami
container_name: whoami
ports:
- 80:80
On lance notre service :
docker-compose up -d
Et on vérifie le bon fonctionnement :
$ curl localhost:80
Hostname: 3ae1aebfa72b
IP: 127.0.0.1
IP: 172.28.0.2
RemoteAddr: 172.28.0.1:44648
GET / HTTP/1.1
Host: localhost
User-Agent: curl/7.64.0
Accept: */*
Notre application répond mais n'utilise pas de reverse-proxy. Nous allons arrêter nos services et modifier notre fichier afin d'ajouter un service Nginx.
docker-compose down
Et voici mon nouveau fichier docker-compose :
version: '3.7'
services:
reverse-proxy:
image: nginx
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
ports:
- 80:80
whoami:
image: containous/whoami
container_name: whoami
Ce n'est donc plus mon service whoami qui va gérer les requêtes sur le port 80 mais bien mon reverse-proxy.
Je vais donc utiliser un fichier de configuration que je vais monter dans mon conteneur. Voici le fichier de configuration :
events {
}
http {
server {
listen 80;
location / {
proxy_pass http://whoami:80;
}
}
}
On peut relancer nos services :
docker-compose up -d
et valider le fonctionnement :
$ curl localhost:80
Hostname: b5f0f543b844
IP: 127.0.0.1
IP: 172.28.0.3
RemoteAddr: 172.28.0.2:52562
GET / HTTP/1.1
Host: whoami
User-Agent: curl/7.64.0
Accept: */*
Connection: close
Et si nous ajoutions le SSL à notre application ? En utilisant par exemple let's encrypt.
SSL
Pour cet article, je vais utiliser certbot
en standalone pour me fournir un certificat Let's encrypt et l'utiliser avec mes services.
Cela nous permet de revoir les bases de l'utilisation de certbot, mais le downtime que va provoquer cette utilisation ne correspond pas à une utilisation en production.
Pour réaliser l'installation de certbot, vous trouverez plus d'informations ici :
Par exemple sur Debian, un simple apt-get install certbot
suffira.
Je vais créer un certificat SSL pour mon url : whoami.mydomain.com
certbot certonly --standalone -d whoami.mydomain.com
Les certificats vont être stockés dans /etc/letsencrypt par défaut. Je vais modifier mon service afin de lui fournir un volume avec les certificats :
...
reverse-proxy:
image: nginx
volumes:
- /etc/letsencrypt/:/etc/letsencrypt/
...
Enfin, je vais adapter la configuration de Nginx afin de prendre en compte les certificats, directives ssl_certificate
et ssl_certificate_key
, et ajouter un port d'écoute pour le HTTPS : le port 443.
...
server {
server_name whoami.mydomain.com;
...
listen 80;
listen 443 ssl;
...
ssl_certificate /etc/letsencrypt/live/whoami.mydomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/whoami.mydomain.com/privkey.pem;
}
...
J'ajoute également la directive server_name
afin que Nginx traite les requêtes HTTP(S) via mon nom de domaine.
Vous pouvez relancer votre stack et valider le fonctionnement :
$ curl https://whoami.mydomain.com
Hostname: b5f0f543b844
IP: 127.0.0.1
IP: 172.28.0.2
RemoteAddr: 172.28.0.3:45130
GET / HTTP/1.1
Host: whoami
User-Agent: curl/7.64.0
Accept: */*
Connection: close
Et le renouvellement de mon certificat dans tout ça ? Et oui il n'est pas automatique avec cette méthode ! Mais nous pouvons le mettre en œuvre facilement :
certbot renew --pre-hook "docker-compose -f path/to/docker-compose.yaml down" --post-hook "docker-compose -f path/to/docker-compose.yaml up -d"
Vous pouvez très bien mettre cette tâche dans un cron de façon quotidienne.
Nous avons donc pu mettre en place une application web, dans notre exemple whoami, derrière Nginx en mode reverse proxy.
Cette installation, certes minimaliste, nous permet de voir que cette solution est possible et n'est pas forcément très complexe. Toutefois elle demande d'écrire notre fichier de configuration Nginx pour chaque application que nous allons ajouter. Et ça, sans parler du certificat SSL qui nécessite un arrêt de nos services.
Nous pourrons au cours d'un prochain article, améliorer notre service afin de répondre à ces problématiques. Nous en profiterons pour ajouter quelques en-têtes de sécurité.
Vous arrive t-il d'utiliser Nginx comme reverse proxy pour vos conteneurs Docker ?
En tout cas n'hésitez pas à m'apporter des remarques ou des commentaires sur Twitter ! C'est toujours un plaisir d'avoir des retours ! 😇