Comment résoudre automatiquement un vhost dans un réseau docker

Temps de lecture estimé : 4 minutes

Un serveur virtuel (ou vhost) est une fonctionnalité offerte par les logiciels de serveur Web permettant de répartir sur un seul serveur applicatif une ou plusieurs applications. C’est peu ou prou le socle d’un reverse proxy : offrir un point d’entrée unique pour protéger l’accès des vraies applications.

La configuration d’un tel vhost n’oppose aucune difficulté, mais cette configuration ne suffit pas pour le routage. Comme le dit si bien la documentation d’Apache :

« La configuration de serveurs virtuels […] ne provoque pas leur apparition magique dans la configuration du DNS. Il faut que leurs noms soient définis dans le DNS, et qu’ils y soient résolus sur l’adresse IP du serveur, faute de quoi personne ne pourra visiter votre site Web. »

Dans un contexte conteneurisé, comment cette résolution DNS peut-elle se faire ? Par construction, un conteneur docker est volatile : que ce soit par arrêt « propre » ou par échec, il peut s’arrêter à tout moment et quand il redémarre son IP a changé, alors comment un conteneur pourrait-il résoudre l’accès à l’un de ses congénères ?

Le réseau docker

Avant de répondre à cette question, attardons-nous un instant sur le fonctionnement du réseau sous docker. Le type de réseau principal de docker, bridge, est une surcouche au réseau hôte et grâce à elle, nous pouvons créer la topologie de réseau que l’on souhaite : via des réseaux isolés, X peut contacter Y, mais Z ne le peut pas. Comme son nom l’indique, il crée un pont entre les conteneurs d’un même réseau docker par l’entremise du système hôte (et interdit toute connexion non légitime grâce au pare-feu système).

Dans un conteneur, ce pont réseau se constate facilement :

root@2de60bd47fdb:/# nmap -sP 172.20.0.0/16
Starting Nmap 7.80 ( https://nmap.org ) at 2023-04-02 16:04 UTC
Nmap scan report for redwatchio (172.20.0.1)
Host is up (0.000024s latency).
Nmap scan report for host1.mynetwork (172.20.0.2)
Host is up (0.000043s latency).
Nmap scan report for host2.mynetwork (172.20.0.3)
Host is up (0.000055s latency).

La réponse de nmap nous indique que le conteneur peut contacter n’importe quel conteneur de ce même réseau via [host].mynetwork (et plus simplement [host]), mais aussi l’hôte système lui-même. Cette résolution ne se base pas sur un /etc/hosts statique qui ne résoudrait en aucun cas la volatilité d’un conteneur, mais au contraire sur un solveur DNS interne à Docker, dynamique donc.

Ainsi, quels que soient les événements qui surviennent dans la vie du réseau, host1 pourra toujours contacter host2.

Le virtual host

À présent que nous avons un réseau stable en dépit de la volatilité de ses composants, nous pouvons contacter tout hôte distant. Mais qu’arrive-t-il si le serveur applicatif est restreint à un vhost précis ? Imaginons le serveur Node suivant :

var connect = require('connect')
var vhost = require('vhost')
var app = connect()

app.use(vhost('redwatch.io', function handle (req, res, next) {
  res.setHeader('Content-Type', 'text/plain')
  res.end('Hello world!')
}))

app.listen(3000)

Est-on condamné à ajouter l’en-tête “Host” dans toutes nos requêtes ? Heureusement non. Docker fournit en effet une option pour légèrement manipuler le solveur DNS, en ajoutant un alias à une résolution d’hôte.

Pour un conteneur unique, cette option se passe au démarrage…

docker run -d --name mycontainer --network-alias=redwatch.io --network=mynetwork myimage

… Et pour une pile Docker compose ou Swarm, cette option se passe réseau par réseau :

services:
  web:
    image: myimage
    container_name: mycontainer
    networks:
      mynetwork:
        aliases:
          - redwatch.io

Pour vérifier que cela fonctionne bien, rien de plus simple ! Il suffit de faire un curl avec le nom DNS :

> curl mycontainer
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot GET /</pre>
</body>
</html>

> curl redwatch.io
Hello world!

Le vhost se résout correctement !

Bonus : le cas du système hôte

Le système hôte est particulier : n’étant pas un conteneur, il n’y a pas de définition où mettre un alias. On va donc retourner la configuration et ce sera l’appelant qui résoudra le nom avec l’option --add-host=[nom:ip].

Mais quelle IP mettre ? Ici encore, Docker aide à la configuration et propose un alias host-gateway pour ne pas se préoccuper du réseau interne.

L’option se manipule ainsi :

docker run -d --name mycontainer --add-host=redwatch.io:host-gateway --network=mynetwork myimage

Attention : dans le détail, add-host altère le fichier /etc/hosts du conteneur, c’est donc une configuration statique. C’est un mode dégradé à réserver que pour des cas spécifiques.

Et voilà ! Vous savez à présent comment router tous vos vhosts, internes comme externes, en suivant automatiquement les évolutions de votre topologie réseau.

Comme d’habitude, vous pouvez retrouver le code pour essayer par vous-même sur le dépôt d’exemple.


Source :


    Ce billet vous a plu ? Partagez-le sur les réseaux…


    … Ou inscrivez-vous à la newsletter pour ne manquer aucun article (Si vous ne voyez pas le formulaire, désactivez temporairement uBlock).

    Voir aussi