Aprés avoir vu les conteneneurs Dockers, nous allons maintenant explorer l’isolation d conteneur Docker. Tout d’abord, nous commencerons par voir un avantage de la conteneurisation : l’isolation. En effet, la capacité à isoler les applications grâce aux conteneurs individuels résout les conflits de dépendances entre applications. Par exemple, lorsqu’une application requiert une version spécifique d’une bibliothèque, cela peut créer des conflits avec une autre application. Ainsi, en utilisant des conteneurs, chaque application est emballée avec ses propres dépendances, évitant ainsi les perturbations mutuelles. De ce fait, vous pouvez exécuter plusieurs applications sur la même machine sans conflits.
Contexte de la mise en pratique de l’isolation d’un conteneur Docker
Pour illustrer le concept de l’isolation de conteneur Docker, prenons un exemple concret. Supposons que vous ayez une application web relativement ancienne qui exige PHP7. En outre, sur cette machine, vous souhaitez également installer une autre application plus récente qui nécessite la version PHP8. Dans une installation traditionnelle, vous auriez besoin de deux machines virtuelles distinctes (ou deux machines physiques). Cependant, en les intégrant dans un conteneur Docker, nous pouvons exécuter les deux applications sur la même machine en s’appuyant sur des conteneurs Docker. Ainsi, cela évitera la complexité et les coûts supplémentaires associés à la gestion de plusieurs machines. C’est ce concept de la conteneurisation que nous allons couvrir dans un premier temps.
Création d’un conteneur Docker exécutant php7
Nous illustrerons cet exemple par une mise en pratique. Précédemment, nous avons récupérer une image Ubuntu, nous allons nous en servir pour créer un conteneur Docker.
Nous créerons ce conteneur Docker que nous nommerons « apache_php7 » soit la commande suivante :
[root@docker ~]# docker run -it --name apache_php7 ubuntu
Après avois créé notre conteneur Docker, nous mettrons à jours ce dernier.
root@94331d07f35a:/# apt update -y
root@94331d07f35a:/# apt upgrade -y
Installation serveur web Apache
PHP a besoin d’un serveur web pour fonctionner. Apache est l’un des serveurs web les plus couramment utilisés pour exécuter des applications PHP. Par conséquent nous installerons « Apache httpd »
root@94331d07f35a:/# apt install apache2 -y
Installation de PHP 7.4
La version 22.04 d’Ubuntu inclut des dépôts qui fournissent PHP dans sa version 8 par défaut. Si nous souhaitons installer la version 7 de PHP, nous devrons recourir à un PPA (Personal Package Archive). Dans le PPA de ondrej nous y retrouverons les packages requis pour installer PHP 7. Pour pouvoir ajouter un PPA, nous allons avoir besoin de l’utilitaire « add-apt-repository » présent dans le package « software-properties-common ».
root@94331d07f35a:/# apt install software-properties-common -y
root@94331d07f35a:/# add-apt-repository ppa:ondrej/php -y
root@94331d07f35a:/# apt install php7.4 -y
Lors de l’installation, il nous sera demandé le choix de zone géographique pour configurer le fuseau horaire de php (php.ini).
Vérifions que la version de PHP est effectivement bien la version 7 à l’aide de la commande suivante : .
root@94331d07f35a:/# php --version
Nous allons générer un fichier qui exécute la fonction phpinfo(), ce qui nous permettra d’afficher la configuration de PHP. Ce fichier sera placé dans le répertoire par défaut d’Apache, qui est /var/www/html.
La configuration est terminé . Nous sortirons de notre conteneur à l’aide de la combinaison de touche [ctrl]+[p], [ctrl]+[q].
Nous listerons nos conteneurs pour s’assurer que celui que nous venons de créer est bien présent et actif.
[root@docker ~]# docker ps
Mappage de port
Pour accéder à ce conteneur Docker, nous devons associer le port d’écoute du conteneur au port de la machine hôte. Dans notre exemple, nous avons conteneurisé un serveur web Apache. Le serveur apache par défaut écoute sur le port 80. En mappant le port 80 du conteneur au port 80 de la machine hôte, il sera accessible via son IP. L’adresse de la machine utilise l’adresse 10.0.0.3, cette sur cette adresse que le serveur web sera accessible
Nous n’avons pas précisé d’écoute à la création de notre container. Par conséquent, nous pouvons constater que le conteneur n’écoute sur aucun port.
Dans la partie suivante, nous verrons comment convertir ce conteneur Docker en une image. En effet, pour exposer le port 80 de notre conteneur Docker, nous devons le préciser au lancement de l’image. Dans notre cas, si nous ne convertissons pas le conteneur Docker en une image Docker, nous devrions tout refaire. Le fait de convertir ce conteneur Docker en image Docker, nous permettra de repartir de là où on en était.
Convertir un conteneur Docker en image Docker
Configurez le mappage de port lors de la création du conteneur Docker. Ensuite, pour éviter de répéter toutes les commandes utilisées pour créer ce conteneur, transformez-le en une image. Appelez cette image srv_web_php7 et ajoutez-y un tag.
[root@docker ~]# docker commit apache_php7 srv_web_php7:latest
Nous vérifierons que l’image Docker a bien été ajouter sur la machine hôte.
[root@docker ~]# docker images
Supprimer un conteneur
Nous répliquons la configuration de cette image chaque fois que nous l’utilisons pour créer un conteneur Docker. En résumé, nous pouvons supprimer le conteneur Docker utilisé pour créer cette image. Pour cela, nous commençons par arrêter le conteneur Docker, puis nous le supprimons. Enfin, nous vérifions que le conteneur Docker a bien été supprimé.
[root@docker ~]# docker stop apache_php7
[root@docker ~]# docker rm apache_php7
[root@docker ~]# docker ps -a
Supprimer plusieurs conteneurs
Pour rappel, un conteneur est conçu pour être léger et jetable. Donc, dans une utilisation typique, lorsqu’un conteneur est éteint, il n’a généralement plus de raison d’être. Nous supprimerons donc les conteneurs stockés sur la machine hôte après avoir arrêté ceux qui sont en statut « UP ».
[root@docker ~]# docker stop 7c85ed7d6261
[root@docker ~]# docker rm -f $(docker ps -aq)
Explication de la commande :
- docker ps -aq : liste tous les conteneurs, en incluant ceux qui ne sont pas en cours d’exécution
- docker rm -f supprime tous les conteneurs dont les IDs sont renvoyés par la commande précédente.
Nous vérifierons qu’il n’y a plus aucun conteneur.
[root@docker ~]# docker ps -a
Exposition d’un port d’un conteneur Docker
Maintenant nous avons un environnement sans aucun conteneur préexistant.Nous testerons notre image précédemment créé. Cette fois nous mapperons le port 80 du conteneur et le port 80 de la machine. Étant donné que le service httpd d’Apach n’est pas démarrer par défaut, nous allons le lancer manuellement.
[root@docker ~]# docker run -it -p 80:80 --name php7 srv_web_php7
root@81610801a595:/# apachectl start
L’adresse IP de la machine est 10.0.0.3. Par conséquent,le serveur Web de notre conteneur sera accessible à partir de cette adresse. Pour permettre à la machine hôte de recevoir des requêtes sur le port 80. Il nous faudra donc configurer le pare-feu de la machine hôte pour autoriser le trafic entrant sur ce port.
root@81610801a595:/# [ctrl]+[P] & [ctrl]+[Q]
[root@docker ~]# firewall-cmd --add-service=http --permanent
[root@docker ~]# firewall-cmd –reload
En lançant un navigateur depuis une machine connectée au même réseau nous nous connecterons à l’URL http://10.0.0.3/. Nous devrions avoir accès à notre serveur WEB
Nous nous rendrons sur http://10.0.0.3/phpinfo.php, pour vérifier que nous utilisons PHP version 7.
Création d’un conteneur en exécutant php8
La création de notre conteneur Docker qui exécutera PHP 8 suivra la même logique que conteneur exécutant PHP 7. Nous commencerons à partir de notre image Ubuntu de base. Nous installerons à la fois le service httpd d’Apache Foundation et PHP. Il est important de noter que la version 8 de PHP est disponible par défaut dans les dépôts d’Ubuntu 22.04. Par conséquent nous n’aurons pas besoin de recourir à l’utilisation de PPA (Personal Package Archive) pour obtenir cette version spécifique.
Le port 80 de la machine hôte est déjà occupé par le conteneur qui exécute PHP version 7. Nous ne pouvons pas l’utiliser pour ce conteneur. Nous utiliserons dons le port 81 de la machine hôte.
Configuration le conteneur docker PHP 8
Nous commencerons par créer notre conteneur avec la commande suivante :
[root@docker ~]# docker run -it -p 81:80 --name php8 ubuntu
Dans le conteneur que nous venons de créer, Aprés avoir mis à jour le systeme nous y installerons :
- le serveur web Apache
- php 8
root@fb9b37059606:/# apt update -y && apt upgrade -y
root@fb9b37059606:/# apt install apache2 -y
root@fb9b37059606:/# apt install php -y
root@fb9b37059606:/# apachectl restart
Une fois PHP installé, nous pouvons vérifier qu’il s’agit bien de la version 8 qui est en cours d’exécution.
root@fb9b37059606:/# php -v
Nous créerons également un fichier exécutant la fonction phpinfo() à la racine du répertoire « html » présent dans « /va/www/ »
root@fb9b37059606:/# echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php
Après être sorti de notre conteneur, nous autoriserons la machine hôte à recevoir des requêtes sur le port 81.
root@fb9b37059606:/#
[root@docker ~]# firewall-cmd --add-port=81/tcp --permanent
[root@docker ~]# firewall-cmd --reload
Nous pouvons maintenant nous rendre sur le port 81 de notre machine hôte pour accéder à notre conteneur. Nous constaterons que le conteneur exécutant PHP8 est bien fonctionnel.
Docker référencie bien ses deux conteneurs, un qui est mapper sur le port 80 et un autre mapper sur le port 81.
[root@docker ~]# docker ps
Accéder au conteneur derrière un reverse proxy
Dans un environnement de production, nous configurons un serveur Web pour qu’il soit accessible sur son port par défaut, correspondant aux protocoles « http » ou « https » : le port 80 pour http et le port 443 pour https. Pour atteindre cet objectif, nous utilisons un reverse proxy qui reçoit les requêtes entrantes et les redirige vers le conteneur Docker approprié. Pour faciliter l’identification de ces conteneurs Docker par les utilisateurs, nous employons des noms de domaine pleinement qualifiés (FQDN) en mettant en place un système de résolution de noms. Nous allons maintenant découvrir comment réaliser cette configuration.
Création d’u conteneur de l’image PHP8
Pour démarrer ce nouveau projet, nous capitalisons sur notre travail antérieur. Cependant, avant de poursuivre, nous créons en priorité l’image Docker pour le conteneur Docker exécutant PHP 8. Autrement dit, nous élaborons cette nouvelle image de la même manière que nous l’avons fait pour l’image du conteneur Docker srv_web_php7.
[root@docker ~]# docker commit fb9b37059606 srv_web_php8:latest
Nous vérifierons que l’image a bien été créée.
[root@docker ~]# docker images
Nous créerons l’architecture suivante :
Dans cette configuration de base, nous disposons déjà de l’image « srv_web_php7 » pour créer le conteneur « PHP7 », ainsi que de l’image « src_web_php8 » pour créer le conteneur « PHP8 ». Notre prochaine étape consiste à créer l’image qui exécutera le reverse proxy, lequel sera configuré sur un serveur nginx. Cependant, avant de faire cela, nous commencerons avec un environnement Docker propre, sans aucun conteneur. Supprimons donc ceux que nous avons précédemment créer.
[root@docker ~]# docker rm -f $(docker ps -aq)
Nous commencerons par créer notre conteneur PHP7. Nous allons également avoir besoin d’outils « ip » pour configurer la partie réseau et de l’outils ping pour tester la communication entre nos conteneurs. L’outils « ip » est présent dans le paquet « iproute2 » et l’outil « ping » sera disponible en installant le paquet « iptutils-ping ». Ce conteneur sera ensuite converti en images, par conséquent, pour le moment, nous le mapperons sur aucun port de la machine physique.
[root@docker ~]# docker run -it --name PHP7 srv_web_php7
root@413ddd8824ab:/# apt update -y && apt upgrade -y
root@413ddd8824ab:/# apt install iproute2 iputils-ping -y
Nous vérifierons que nous avons bien la possibilité d’utiliser la commande « ip », par exemple, nous utiliserons la commande « ip » pour connaître l’adresse IP de notre passerelle.
root@413ddd8824ab:/# ip route show
Nous testerons également la commande « ping » en tenant de communiquer avec la passerelle configurée sur le conteneur.
root@413ddd8824ab:/# ping 172.17.0.1 -c 4
Création du conteneur PHP8
Pour la création du conteneur PHP8, nous procéderons de la même façon après avoir détacher notre terminal de l’invite de commande de notre nouveau conteneur en utilisant la combinaison de touche [ctrl]+[P] & [ctrl]+[Q].
Ensuite nous mettrons à jours le système et nous installerons les paquets « iproute2 » et « iputils-ping ».
root@212bbec8e72c:/# apt update -y && apt upgrade -y
root@212bbec8e72c:/# apt install iproute2 iputils-ping -y
Nous effectuerons les mêmes tests que ceux que nous avons fait sur le conteneur que nous avons précédemment créé.
root@212bbec8e72c:/# ip route show
root@212bbec8e72c:/# ping 172.17.0.1 -c4
Nous constaterons que les conteneurs partagent la même adresse de passerelle, ce qui indique clairement que les deux conteneurs sont sur le même réseau à savoir 172.17.0.0/16. Si l’adresse de ce conteneur est 172.17.0.3, l’adresse du précédent conteneur est 172.17.0.2. Cette information est donnée lors de la commande « ip route show » à la fin de la seconde ligne.
Ce conteneur étant configurer nous pouvons détacher notre terminal de l’invite de commande du conteneur.
Création du conteneur NGINX
Pour le conteneur docker qui exécutera nginx, nous devons repartir d’une image vierge. Nous repartirons sur l’image Ubuntu vu qu’elle est présente sur notre machine.
[root@docker ~]# docker run -ti --name reverse_proxy ubuntu
Dans ce conteneur, nous installerons le paquet « nginx » qui sera configuré en reverse proxy et nous y ajouterons les outils « iproute2 » et « iputils-ping » après avoir mis à jour le système d’exploitation.
root@e4cee4f4deb3:/# apt update -y && apt upgrade -y
root@e4cee4f4deb3:/# apt install nginx iproute2 iputils-ping -y
Rappelle fonctionnement Reverse Proxy
Le reverse proxy fonctionne en se basant sur l’URL de la requête. Une seule machine peut héberger plusieurs sites ou applications. Pour différencier ces sites, il est essentiel d’utiliser des noms de domaine pleinement qualifiés (FQDN) uniques. Lorsqu’une requête HTTP est reçue, le reverse proxy examine l’entête HTTP, en particulier l’URL et le nom d’hôte, pour déterminer où rediriger la requête. Cela nécessite que le reverse proxy puisse résoudre le nom d’hôte (FQDN) en une adresse IP pour identifier la destination correcte et transmettre la requête au bon conteneur ou serveur.
Configuration de la résolution de nom
Pour éviter d’avoir à déployer un DNS, nous utiliserons le fichier /etc/hosts présent dans le conteneur « reverse_proxy ». Nous rappellerons ici qu’un conteneur n’a pas d’éditeur de texte par défaut, nous utiliserons donc la commande « echo » pour modifier le fichier /etc/hosts. Une ligne étant une information, dans notre contexte, nous avons à renseigner la résolution de nom de deux conteneurs, nous aurons donc un saut de ligne, il nous faudra donc utiliser le commutateur « -e » pour la prise en charge du saut de ligne ( \n ) dans la commande echo
Etant donnée que le conteneur PHP7 a pour adresse IP 172.17.0.2, nous lui attribuerons le nom pleinement qualifier « php7.elkhrissi.lab » et pour le conteneur PHP8 disponible à l’adresse IP 172.17.0.3 avec le FQDN « php8.elkhrissi.lab ».
Nous ajouterons donc ses informations dans le fichier hosts de notre conteneur « reverse_proxy ».
root@e4cee4f4deb3:/# echo -e "172.17.0.2 php7 php7.elkhrissi.lab \n172.17.0.3 php8 php8.elkhrissi.lab" >> /etc/hosts
Nous vérifierons que la résolution de nom est bien effective à l’aide de la commande « ping ».
root@e4cee4f4deb3:/# ping php7.elkhrissi.lab -c1
root@e4cee4f4deb3:/# ping php8.elkhrissi.lab -c1
Ce conteneur doit pouvoir communiquer avec les deux autres conteneurs PHP7 et PHP8, nous exécuterons une commande ping pour vérifier que le conteneur nginx peut joindre le conteneur PHP7 et PHP8.
Configuration du reverse proxy
Nous mettrons en place une configuration dans nginx qui permettra de rediriger les requêtes adressées au nom de domaine pleinement qualifié (FQDN) « php7.elkhrissi.lab » vers le conteneur PHP7, et les requêtes adressées à « php8.elkhrissi.lab » vers le conteneur PHP8.
Reverse proxy pour PHP7
Pour la configuration des requêtes à destination du conteneur PHP7, nous créerons un fichier de configuration php7.conf qui devra être stocké /etc/nginx/conf.d/. Le contenu de se fichier sera le suivant :
server {
listen 80;
server_name php7.elkrhissi.lab;
location / {
proxy_pass http://php7.elkhrissi.lab;
}
- server { … } : Cela définit un bloc de configuration pour un serveur virtuel. Cela signifie que les instructions à l’intérieur de ce bloc s’appliqueront aux requêtes entrantes qui correspondent au nom du serveur spécifié (server_name) et au port spécifié (listen).
- listen 80; : Cette ligne indique au serveur nginx d’écouter les requêtes entrantes sur le port 80. Le port 80 est le port par défaut pour les requêtes HTTP non chiffrées.
- server_name php7.elkrhissi.lab; : Ici, nous spécifions le nom de domaine pleinement qualifié (FQDN) auquel le reverse proxy répondra. Dans ce cas, il s’agit de php7.elkrhissi.lab. Cela signifie que ce serveur nginx traitera les requêtes entrantes pour ce FQDN spécifique.
- location / { … } : Cela configure une directive location qui spécifie comment le serveur nginx doit gérer les requêtes pour les chemins correspondant à la racine (« / »). Cela signifie que toutes les requêtes entrantes seront gérées par cette directive.
- proxy_pass http://php7.elkhrissi.lab; : C’est la partie la plus importante de la configuration. Cette directive indique à nginx de rediriger toutes les requêtes entrantes à cette location vers le conteneur Docker nommé « php7.elkhrissi.lab » en utilisant le protocole HTTP. En d’autres termes, lorsque le serveur nginx reçoit une requête pour php7.elkrhissi.lab, il transmettra cette requête au conteneur Docker qui exécute PHP7, permettant ainsi au conteneur de traiter la requête et de renvoyer la réponse.
Pour créer ce fichier, nous utiliserons l’outil « echo »
root@e4cee4f4deb3:/#echo 'server {
listen 80;
server_name php7.elkhrissi.lab;
location / {
proxy_pass http://php7.elkhrissi.lab;
}
}' | tee /etc/nginx/conf.d/php7.conf
Reverse proxy pour PHP8
Pour les requêtes à destination du conteneur exécutant PHP8, nous créerons un fichier de configuration en gardant la même logique que le fichier que nous avons créé précédemment.
root@e4cee4f4deb3:/#echo 'server {
listen 80;
server_name php8.elkhrissi.lab;
location / {
proxy_pass http://php8.elkhrissi.lab;
}
}' | tee /etc/nginx/conf.d/php8.conf
Conversion conteneur docker en image docker
Nous convertirons ses conteneurs en une image, pour ce faire, nous devrons détacher notre terminal de l’invite de commande de notre conteneur à l’aide des combinaisons de touche [ctrl]+[P] & [ctrl]+[Q]
root@e4cee4f4deb3:/# [ctrl]+[P] & [ctrl]+[Q]
[root@docker ~]# docker commit e4cee4f4deb3 reverse-proxy
Nous avons notre image prête à l’emploi, nous allons pouvoir l’exécuter et mapper le port 80 du conteneur avec le port 80 de la machine hôte. Le conteneur s’appellera nginx-rp.
[root@docker ~]# docker run -ti -p 80:80 --name nginx-rp reverse-proxy
En lançant le service nginx dans notre conteneur, nous sommes confrontés à une erreur. Cette erreur survient en raison de l’absence de persistance des données stockées dans notre conteneur précédent, qui a servi de base pour créer cette image.
Cette caractéristique essentielle des conteneurs, à savoir leur nature éphémère, signifie qu’ils ne conservent pas les données au-delà de leur cycle de vie, du moins pas celles qui ne sont pas présentes dans leur système de fichiers principal. Par défaut, lors de l’installation d’une nouvelle application, les fichiers de configuration sont généralement stockés dans les répertoires du service, c’est-à-dire dans le système de fichiers principal du conteneur. Ainsi, nous retrouverons bien nos fichiers de configuration.
root@1c6760241900:/# ls /etc/nginx/conf.d/
En revanche, les modifications que nous avons apportées au fichier /etc/hosts n’ont pas été conservé.
Par conséquent, lors du démarrage du service nginx, nous avons configuré des fichiers de configuration dans lesquels nous avons spécifié les noms de domaine vers lesquels il doit rediriger les requêtes. Cependant, le service nginx ne peut pas les contacter car le système ne peut pas résoudre ces noms de domaine en adresses IP. En conséquence, le service nginx génère une erreur. Nous ajouterons nos deux entrées dans le fichier /etc/hosts puis nous serons en mesure de démarrer le service nginx.
root@1c6760241900:/# echo -e "172.17.0.2 php7 php7.elkhrissi.lab \n172.17.0.3 php8 php8.elkhrissi.lab" >> /etc/hosts
root@1c6760241900:/# service nginx start
Verification des accès aux serveurs Web
À partir d’une machine cliente Windows connectée au même réseau que notre machine hôte, nous vérifierons la fonctionnalité de notre configuration. Avant de procéder aux vérifications, nous ajusterons le fichier hosts de la machine cliente afin qu’elle puisse résoudre les noms de domaine de nos deux conteneurs. Nous ouvrions « notepad » en tant qu’administrateur, nous ouvrions le fichier hosts disponible au chemin C:\Windows\System32\drivers\etc. En sélectionnant « Tout les fichiers » en bas à doite nous devrions voir le fichier hosts. Nous le sélectionnerons et cliquerons sur le bouton « Ouvrir »
L’adresse IP que nous renseignerons sera l’adresse IP de notre machine hôte qui, dans le cadre de cette documentation est 10.0.0.3 qui cela la résolution de nom de « php7.elkhrissi.lab » et « php8.elkhrissi.lab »
Une fois que la modification a été enregistré, nous allons nous rendre aux URLs après nous êtes assurer que les services apache sur nos conteneurs est bien démarré :
Après avoir enregistré la modification, nous allons accéder aux URLs suivant une fois que nous nous sommes assurés que les services Apache dans nos conteneurs Docker sont bien démarrés :
- http://php7.elkhrissi.lab/phpinfo.php
- http://php8.elkhrissi.lab/phpinfo.php
Nous vérifierons que les deux conteneurs ne communiquent pas ensemble. Pour ce faire, nous allons utiliser l’outils ping qui, par défaut, n’est pas présent. Nous l’installerons dans le conteneur app-php7, app-php8
[root@docker ~]# docker exec -it app-php7 apt-get install -y iputils-ping
[root@docker ~]# docker exec -it app-php8 apt-get install -y iputils-ping
Pour rappel, l’adresse IP du conteneur Docker de « app-php7 » est 192.168.1.2 et l’adresse IP de « app-php8 » est 192.168.2.2
Nous testerons que la communication entre app-php7 et app-php8 ne peux pas se faire.
[root@docker ~]# docker exec -it app-php7 ping -c 4 192.168.2.2
En toute logique, le conteneur app-php8 ne sera pas en mesure de communiquer avec app-php7.
[root@docker ~]# docker exec -it app-php7 ping -c 4 192.168.2.2
Dans ce chapitre, nous avons vu le cloisonnement sans passer par des solutions UTM. Si nous voulions durcir le cloisonnement la solution docker ne serait pas adaptée, d’autres solutions existent.