📘 Publication de ports réseau d’un conteneur

1. Problématique

Nous avons vu qu’un conteneur avait pour caractéristique d’isoler un logiciel dans un environnement minimal adapté Cependant, comme vu dans le chapitre précédent, il arrive couramment qu'un processus logiciel nécessite de communiquer avec un autre processus, pour échanger des messages et/ou accomplir une tâche.

Or, la conteneurisation peut nous amener à mettre en place un conteneur par logiciel déployé, ce qui signifie que chaque processus logiciel déployé ainsi se retrouve dans un micro-environnement isolé de tous les autres processus avec lesquels il pourrait souhaiter communiquer.

Comment permettre à un processus exécuté dans un conteneur de communiquer avec d’autres processus situés à l’extérieur du conteneur ? Dans ce (bref) chapitre nous allons étudier un premier méchanisme permettant de permettre certaines communications : la publication de ports réseaux, pour relier un conteneur au réseau local (ou Internet) de l’environnement d’accueil où il s’exécute.

Nous verrons dans un futur chapitre comment mettre en réseau plusieurs conteneurs entre eux, afin de permettre à leurs processus de communiquer.

2. Principe

Dans un environnement donné, lorsqu’un processus logiciel dit serveur souhaite pouvoir être contacté par d’autres processus logiciels, il est nécessaire que ce processus serveur écoute sur un port réseau choisi d’une interface réseau de son environnement. Une fois cette écoute en place sur le port choisi, d’autres processus dits clients peuvent se connecter sur ce port et démarrer une communication avec le processus serveur.

Cependant, lorsque le processus serveur est exécuté au sein d’un conteneur, à savoir un mini-environnement lui-même à l’intérieur d’un environnement d’accueil, un problème se pose : si le processus serveur écoute sur un port réseau, ce sera via une interface réseau de son conteneur, et non une interface réseau de son environnement d’accueil. Ainsi, malheureusement, un processus client situé sur le réseau local de l’environnement d’accueil ne sera pas en mesure de rentrer en communication avec ce processus serveur, car le processus client ne pourra que contacter une interface réseau de l’environnement d’accueil du processus serveur.

Exemple 1. Problème de communication depuis l’extérieur vers Simplestblog conteneurisé

Pour rappel, Simplestblog est un logiciel qui s’exécute sous la forme d’un serveur web qui s’attend à recevoir des connexions de la part de_clients web communément appelés des navigateurs webs.

Supposons que :

  • Simplestblog s’exécute au sein d’un conteneur lui-même hébergé dans un environnement d’accueil.

  • Une personne utilise un poste de travail dénommé environnement utilisateur afin d’exécuter le navigateur web Firefox, et cherche à se connecter au serveur web Simplestblog.

  • Chacun de ces environnements est connecté à un réseau local ethernet via leur interfaces réseau ethernet respectives.

On aurait alors la situation suivante :

simplestblog port problem.drawio
Pour des raisons de lisibilité, dans ce schéma et dans les prochains schémas de ce chapitre, nous simplifions la représentation car nous ne représentons pas explicitement les couches "Matériel" et "Système d’exploitation" des différents environnements. Nous omettons également la représentation des dépendances de chaque logiciel.

Problème : si le serveur web conteneurisé se met à écouter sur un port réseau, ce sera forcément un port de l’interface réseau virtuelle du conteneur dans lequel il s’exécute, et non un port réseau de l’interface réseau ethernet de l’environnement d’accueil. Or, comme l’interface réseau virtuelle du conteneur n’est pas connectée au réseau local ethernet, il sera impossible pour le client web de se connecter au port d’écoute.

Pour remédier à cela, il est possible pour un conteneur de réaliser une publication de port réseau. Une publication de port consiste à relier un port réseau X d’une interface réseau du conteneur, à un port réseau d’une interface réseau de l’environnement d’accueil. Une fois cette liaison établie, toute connexion depuis l’extérieur sur le port Y sera systématiquement transmise au port X du conteneur.

port publication.drawio
Figure 1. Publication de ports de plusieurs conteneurs dans un environnement d’accueil

L’illustration ci-dessus présente trois conteneurs dans le même environnement d’accueil. Le conteneur A exécute un logiciel qui écoute sur les ports 5000 et 21 au travers de l’interface réseau virtuelle du conteneur. On décide de publier le port 5000 en le reliant au port 80 de l’environnement d’accueil, et on publie également le port 21 en le reliant au port 21 de l’environnement d’accueil. On publie également le port 465 du conteneur B sur le même port de l’environnement d’accueil. Enfin, on publie les ports 465 et 22 et du conteneur C sur les ports 565 et 2222 de l’environnement d’accueil

Ainsi, de manière transparente :

  • Si on accède au port 80 de l’environnement d’accueil, on se retrouvera connecté au port 5000 du conteneur A.

  • Si on accède au port 21 de l’environnement d’accueil, on se retrouvera connecté au port 21 du conteneur A.

  • Si on accède au port 465 de l’environnement d’accueil, on sera connecté au port 465 du conteneur B.

  • Si on accède au port 565 de l’environnement d’accueil, on sera connecté au port 465 du conteneur C.

  • Si on accède au port 2222 de l’environnement d’accueil, on sera connecté au port 22 du conteneur C.

On remarque qu’un même numéro de port peut être utilisé à la fois chez un conteneur et chez l’environnement d’accueil, et même par plusieurs conteneurs simultanément.
Exemple 2. Publication de port pour Simplestblog conteneurisé

On repart de la situation précédente avec Simplestblog conteneurisé, mais cette fois-ci on décide de briser l’isolation du conteneur à l’aide d’une publication de port. Plus précidément, on choisit de publier le port 5000 du conteneur (sur lequel le processus Simplestblog écoute) vers le port 4000 de l’environnement d’accueil. On obtient donc :

simplestblog port publication.drawio

Ainsi, tout navigateur qui se connecte sur le port 4000 de l’environnement d’accueil sera de manière transparente connectée au port 5000 du conteneur, et donc au processus Simplestblog qui écoute sur ce port.

3. Mise en œuvre avec Podman

La publication d’un port du conteneur vers l’environnement d’accueil se fait lors de la création d’un conteneur (par exemple lors d’un appel à podman container run) à l’aide de l’option suivante :

  • --publish (version courte : -p ) : publie un port du conteneur vers l’environnement d’accueil, avec une syntaxe <port environnement d’accueil>:<port conteneur>

Par défaut, la publication de ports se fait sur toutes les interfaces réseau de l’environnement d’accueil, ce qui est symbolisé par l’adresse 0.0.0.0. Il est également possible de spécifier sur quelle interface réseau de l’environnement d’accueil un port doit être publié, avec la syntaxe <adresse ip environnement d’accueil>:<port environnement d’accueil>:<port conteneur>. Cela peut être nécessaire lorsque l’environnement d’accueil se trouve sur plusieurs réseaux (par exemple un réseau local d’une part, le réseau Internet d’autre part).

Pour ce cours, on pourra se contenter de garder le comportement par défaut, autrement dit la publication sur l’adresse 0.0.0.0, ce qui correspond à "toutes les interfaces réseau de l’environnement d’accueil".

Pour afficher la liste des publications de ports d’un conteneur, on également faire :

$ podman container port <nom du conteneur>
Exemple 3. Publication d’un port pour un serveur web nginx avec Podman

On souhaite déployer un petit serveur web nginx mais cette fois-ci en connectant ce serveur web à l’extérieur pour qu’on puisse s’y connecter à l’aide d’un navigateur. Par défaut, nginx est un logiciel qui écoute sur le port 80. On doit donc effectuer une publication du port 80 du conteneur vers un port de l’environnement d’accueil − pour cet exemple, on choisit le port 8200.

On exécute donc la commande suivante :

$ podman container run --name mon_serveur_web \
                       --detach \
                       --publish 8200:80 \
                       docker.io/nginx:latest

On vérifie que le port est bien publié comme souhaité :

$ podman container port mon_serveur_web

80/tcp -> 0.0.0.0:8200
0.0.0.0 signifie que la publication se fait sur toutes les interfaces réseau de l’environnement d’accueil.

À l’aide d’un navigateur web comme Firefox, on peut ensuite se connecter sur le port 8200 de l’environnement d’accueil, ce qui de manière transparent nous connecte au port 80 du conteneur mon_serveur_web, ce qui nous emmène sur le serveur web nginx qui présente la page par défaut suivante :

nginx default page