La containerisation est réalisée par l'intermédiaire d'un processus qui isole les éléments de la plateforme matérielle et qui offre un moyen d'exécuter des programmes ou environnements complets en parfaite isolation les uns des autres. Il se base sur un daemon (sous linux) utilisant les namespaces et les cgroups de linux pour réaliser cette isolation et offrir le maximum de performances à l'ensemble des containers installés sur la machine. Les containers sont disponibles par défaut sur le hub public https://hub.docker.com avec un choix important sur l'ensemble des problématiques informatiques : serveurs web, serveurs linux, serveurs de bases de données et bien d'autres encore.
Le lancement d'un container par la commande
$ docker container run -ti <nom du conteneur>
va provoquer son téléchargement et son installation s'il n'est pas déjà présent dans le repository local du serveur
qui gère le host Docker.
Exemples d'usage des containers :
Pour démarrer le conteneur de python : docker container run -ti python:3
Cela va démarrer un container avec le tag 3 de Python à savoir donc la version 3 du langage
Pour démarrer un container Ruby : docker container run -ti ruby:2.5.1
Même chose que précédemment avec cette fois un interpréteur Ruby en version 2.5.1
Pour démarrer un container NodeJS : docker container run -ti node:8.12-alpine
Pour Node, la subtilité au niveau du tag indique également la distribution alpine linux pour cette gestion
de conteneur Node car Alpine Linux est très impliqué dans la mise en place de Node sur Docker et reste le meilleur
support pour NodeJS.
Pour démarrer une base MongoDB : docker container run -p 27017:27017 -d mongo:4.0
Cela démarre un conteneur qui écoute sur le port 27017 et qui route le port externe sur le port interne 27017.
Pour utiliser Mongo, le plus simple est de faire appel à Compass qui offre une interface Web d'administration.
L'utilisation du flag -d pour détacher le processus demande à Docker de nous afficher clairement l'ID du processus.
C'est une suite de chiffres et lettres qui identifie de manière unique le processus en écoute (pour le killer par
exemple).
Pour démarrer un container Redmine : docker container run -p 3000:3000 redmine:3
L'accès à Redmine s'effectue via le port 3000 et sur la version 3 (tag :3)
Connexion par défaut par admin/admin.s
Rappel des flags :
Sous Windows Pro, Docker requiert au minimum Windows 10 build 18362 et supérieur, ainsi que de la technologie VT-x activée dans le BIOS et d'Hyper-V ou WSL (Windows Subsystem for Linux). Docker fonctionnera sur les deux systèmes Hyper-V ou WSL, même si la préférence va à WSL.
Consultez la documentation officielle Docker pour l'installation des éléments pour Windows. https://docs.docker.com/desktop/install/windows-install
ATTENTION : Docker Desktop ne fonctionne pas sur les versions Serveur de Windows.
Avec Windows Serveur, pour exécuter des conteneurs Docker, il faut passer par un runtime supporté par la machine. Il en existe actuellement 3 :
Pour installer Docker, nous allons utiliser Docker CE qui fournit un environnement standard pour les conteneurs avec une API et une gestion par ligne de commande. Un script de mise en place existe sur le github de la communauté Moby :
PS> Invoke-WebRequest -UseBasicParsing "https://raw.githubusercontent.com/microsoft/Windows-Containers/Main/helpful_tools/Install-DockerCE/install-docker-ce.ps1" -OutFile install-docker-ce.ps1
PS> .\install-docker-ce.ps1
ATTENTION à la fin du script le serveur sera automatiquement redémarré !
Les conteneurs sont créés à partir d'images de conteneurs. Une série de conteneurs de base sont disponibles à l'installation et fournis par Microsoft, sur la base de 4 versions de Windows Serveur :
PS> docker pull mcr.microsoft.com/windows/nanoserver:ltsc2022
Vérifiez ensuite que l'image a bien été chargée par PS> docker images
Cela va afficher le repository ainsi que le tag et le reste des informations de
l'image téléchargée.
Maintenant que nous disposons d'une image, utilisons-la pour exécuter un simple 'hello world' en mode console.
PS> docker run -it mcr.microsoft.com/windows/nanoserver:ltsc2022 cmd.exe
Puisque l'image existe déjà dans le conteneur d'images, celle-ci ne sera pas retéléchargée
mais simplement exécutée en mode interactif (-it) et va lancer une invite de commande (cmd.exe).
C'est une véritable machine Windows qui s'exécute, toutes les commandes usuelles du terminal Windows
sont disponibles. Vous pouvez faire echo "Hello World" > c:\hello\hello.txt
pour obtenir l'écho dans un fichier sur le serveur. Pour sortir du mode interactif, tapez simplement exit
.
Vous pouvez obtenir l'état des machines conteneurisées par la commande
PS> docker ps -a
pour obtenir
le status de l'image utilisée.
Maintenant que nous avons une image modifiée (suite à l'ajout d'un fichier) nous allons procéder à une duplication de cette image par
PS> docker commit <container_id> helloworld
et ainsi obtenir un nouveau coneneur dont l'id sera helloworld. Il faut bien prendre le container_id
obtenu depuis la commande docker ps -a car sinon vous obtiendrez une erreur
signalant qu'il n'y a pas de container du nom souhaité.
Nous pouvons maintenant exécuter ce nouveau conteneur et afficher le contenu du fichier précédemment
créé dans l'autre conteneur.
PS> docker run --rm helloworld cmd.exe /s /c type c:\hello\hello.txt
Le comportement d'un conteneur Docker est généralement lié à l'usage d'un fichier de configuration que l'on nomme le Dockerfile. Ce Dockerfile est à considérer comme un makefile (pour ceux qui viennent du monde C) pour définir comment sera construite l'image.
Pour prendre un exemple, obtenu depuis Microsoft Learn, nous allons étudier la génération de code .Net à travers un Dockerfile afin que ce soit Docker qui compile le code. Voici le contenu du Dockerfile :
# Déclare à partir de quelle image nous allons construire le conteneur.
# Si le système local ne dispose pas de l'image, alors celle-ci sera récupérée depuis le
# lien donné dans le Docker Hub.
# Le tag 2.1 signale que le sdk 2.1 est déjà installé dans l'image.
# Le WORKDIR /app provoque le changement du dossier courant vers /app dans le conteneur
FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build-env
WORKDIR /app
# Les instructions suivantes copient les fichiers *.csproj dans le dossier /app
du container.
# La commande dotnet va générer les dépendances requises pour le projet.
COPY *.csproj ./
RUN dotnet restore
# Copie des fichiers sources dans le container.
COPY . ./
# On informe .NET afin qu'il publie notre application en mode Release
RUN dotnet publish -c Release -o out
# L'application construite est basée sur .Net. Nous téléchargeons donc une image
# incluant par défaut le runtime.
FROM mcr.microsoft.com/dotnet/core/aspnet:2.1
WORKDIR /app
# Copie de tous les fichiers depuis le container temporaire dans le conteneur final.
COPY --from=build-env /epp/out .
ENTRYPOINT ["dotnet", "asp-net-getting-started.dll"]
# Installation de Docker
PS> Install-Module -Name DockerMsftProvider -Repository PSGallery -Force
PS> Install-Package -Name Docker -ProviderName DockerMsftProvider -Verbose
PS> Restart-Computer -Force
# Affiche l'état du service Docker
PS> Get-Service Docker
# Si le service est noté comme Stopped, vous pouvez le démarrer en faisant
PS> Start-Service Docker
PS> docker version
Cela nous affiche la version des différents modules client et serveur de docker.
Si nous obtenons une réponse cela veut bien dire que nous communiquons avec notre service docker.
Le site https://docs.docker.com/engine/reference/commandline/dockerd/ nous donne une
multitude d'informations sur le service docker et son démon dockerd. Nous pouvons y trouver
bon nombre de paramètres pour contrôler son fonctionnement, et notamment la façon dont le
service docker va démarrer ou bien la gestion de ses cartes réseau (bridge, ...)
Le fichier de configuration par défaut de docker se situe dans c:\programdata\docker\config\daemon.json.
Ce fichier n'existe pas. Il faut le créer par
PS> New-item -ItemType file c:\programdata\docker\config\daemon.json
Il faut ensuite préciser à docker que son port d'écoute sera le 2375 (par exemple) en ajoutant
les informations suivantes
PS> Add-Content 'c:\programdata\docker\config\daemon.json' '{"hosts":["tcp://0.0.0.0:2375","npipe://"]}'
Redémarrez le service docker
PS> Restart-service docker
Un petit netstat -aon|findstr "2375" pour être sûr que le port a bien été généré.
N'oublions pas de configurer le port du firewall pour la communication sur le nouveau port
PS> netsh advfirewall firewall add rule name="Docker daemon" dir=in action=allow protocol=TCP localport=2375
Le container le plus simple à exécuter est hello-world. Il s'exécute comme suit :
PS> docker run hello-world
Le container va se télécharger pour s'exécuter et simplement écrire "hello from Docker!"
A la fin le container s'arrête et se décharge de la mémoire du serveur. Nous pouvons le constater
en faisant docker ps pour afficher les conteneurs en cours de fonctionnement.
Si on lance la commande
PS> docker run -it hello-world powershell
cela va exécuter le conteneur hello-world en mode interactif et lancer un powershell dans ce
conteneur. Il est ainsi possible de faire cat ./hello.txt pour afficher le message que nous
avions reçu lorsque nous avons fait docker run hello-world.
Nous allons procéder à l'installation de docker sur poste de travail en téléchargeant la version qui correspond à celle du serveur en cours d'exécution (la 17.06 actuellement).
PS> $package = “https://download.docker.com/win/static/stable/x86_64/docker-17.06.0-ce.zip";
PS> Invoke-WebRequest $package -OutFile “$env:Temp\docker-17.06.0-ce.zip” -UseBasicParsing
PS> Expand-Archive -Path “$env:TEMP\docker-17.06.0-ce.zip” -DestinationPath $env:ProgramFiles
Pour s'assurer que tout a été désarchivé au bon endroit :
PS> ii $env:ProgramFiles
Cela devrait afficher un explorateur Windows montrant un dossier c:\ProgramFiles\Docker
A l'intérieur devraient se trouver deux fichiers : docker et dockerd.
A présent il faut ajouter le chemin de ce dossier à notre variable d'environnement
PS> [Environment]::SetEnvironmentVariable(“Path”, $env:Path + “;$($env:ProgramFiles)\Docker”, [EnvironmentVariableTarget]::Machine)
Fermez et ouvrez à nouveau l'interpréteur PowerShell et lancez la commande
PS> docker -H tcp://192.168.1.51:2375 version
en supposant que le serveur hébergeant le service docker soit sur l'IP 192.168.1.51 et que
le port sur lequel écoute docker soit le 2375 (comme nous l'avons paramétré dans les lignes précédentes)
La commande version devrait retourner les mêmes informations que si vous l'aviez exécutée directement
depuis le serveur, à savoir les version du client et du serveur docker (17.06 dans notre exemple).
Client:
Version: 17.06.0-ce
API version: 1.30
Go version: go1.8.3
Git commit: 02c1d87
Built: Fri Jun 23 21:30:30 2017
OS/Arch: windows/amd64
Server:
Version: 17.06.2-ee-17
API version: 1.30 (minimum version 1.24)
Go version: go1.8.7
Git commit: 66834de
Built: Thu Oct 25 12:25:12 2018
OS/Arch: windows/amd64
Experimental: false
Un container est un processus qui tourne sur le système isolé des autres processus. Il dispose de sa propre vision du système sur lequel il tourne (NameSpace) Il est limité dans les ressources qu'il peut utiliser avec les Control Groups (cgroups) Partage le kernel de la machine hôte avec les autres containers. Types de namespaces : pid : isolation de l'espace des processus net : donne une stack de réseau privé mount : système de fichiers privé uts : nom du host ipc : isole les communications inter processus user : mapping UID/GID entre hôte et containers Les control groups permettent de limiter les ressources utilisées par un processus : RAM, IO, Network, CPU
Présentation de docker ====================== Le client docker (écrit en Go) communique avec le daemon dockerd via une API REST. Le daemon Dockerd est responsable de la gestion des images docker, du réseau, des volumes, du cluster... Il délègue la gestion des containers à containerd Il écoute par défaut sur la socket locale /var/run/docker.sock mais peut être configuré pour écouter sur une socket TCP. Le client Docker communique en local par défaut avec la socket du daemon sur /var/run/docker.sock mais peut être configuré pour utiliser une connexion TCP en modifiant la valeur de la variable d'environnement DOCKER_HOST. Le gros avantage de Docker est de masquer la complexité de l'usage des containers Linux. Les images Docker incorporent l'intégralité des éléments requis pour le fonctionnement de Docker et sont paramétrées par un fichier dockerfile. Par défaut le contrôleur de Docker communique avec le hub docker https://hub.docker.com Un cluster SWARM peut être utilisé pour faire communiquer plusieurs docker hosts entre-eux. Il reste plus simple que Kubernetes pour la gestion multi hosts. Installer Docker sous Windows ============================= Utiliser Docker Community Edition, qui est gratuite, et propose une release stable produite chaque semestre La version Docker Entreprise (EE) permettent de s'intégrer avec un environnement AD Windows. Sur Windows 10 Pro ou MacOS>10.12, utiliser Docker Desktop. Pour les versions plus anciennes, utiliser Docker Toolbox Il est possible d'installer Docker sur Linux (https://docs.docker.com/install/linux/docker-ce/ubuntu/) et AWS / Azure. Pour télécharger le kit d'installation : https://hub.docker.com. Cliquer sur Explore pour récupérer les bonnes versions d'OS que vous utilisez. Tester avec Docker : Play With Docker (https://labs.play-with-docker.com/) Permet, depuis un navigateur, d'avoir accès à un environnement Docker. Chaque session dure 4 heures et permet d'avoir DockerInDocker (DinD) Pour Linux, le demon docker est lancé par Systemd et est en écoute sur la socket locale /var/run/docker.sock. Il est toutefois possible d'accéder aux informations docker depuis un serveur en simple ssh par la commande > docker -H ssh://user_ubuntu@ip_serveur_ubuntu info Cela permet d'ouvrir une connexion en SSH sur le serveur spécifié avec l'utilisateur user_ubuntu. Il faudra bien évidemment saisir le mot de passe de connexion. Comme la commande est un peu compliquée, il est possible d'utiliser une variable d'environnement qui sera automatiquement lue par la commande docker locale : > export DOCKER_HOST="ssh://user_ubuntu@ip_serveur_ubuntu" La commande finale sera simplement > docker info Pour ne pas avoir à saisir à chaque fois le mot de passe de connexion SSH, il est possible de copier la clé SSH sur le serveur distant par > ssh-copy-id -i ~/.ssh/id_rsa.pub user_ubuntu@ip_serveur_ubuntu Cela va automatiquement copier les bonnes clés sur le profil de l'utilisateur user_ubuntu sur le serveur ip_serveur_ubuntu.
Le choix a été fait ici de déployer docker sur une solution de type Ubuntu et Debian, qui sont très proches et relativement courantes.
Consultez le site officiel Docker pour une documentation approfondie sur le stockage et le réseau dans Docker.
Docker s’installe sur un serveur de base Ubuntu. Nous commençons donc par installer Ubuntu en version Serveur, sans GUI, pour le faire le plus léger possible. Une fois installé, assurez-vous que le serveur soit bien à jour :
$ sudo apt-get update
$ sudo apt-get upgrade
Pour mettre en place Docker, suivre ce lien Docker sur Ubuntu.
En voici les différentes étapes :
Mise en place du repository Docker pour Ubuntu
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Add the repository to Apt sources:
echo \
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
Installation des packages Docker
$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Test avec l’image hello-world récupérée automatiquement depuis le Docker hub
$ sudo docker run hello-world
Commencer par installer une machine Debian en version serveur car nous n’aurons pas besoin de GUI ou de programmes divers.
Rappel : les commandes root sont à taper en root. Contrairement à
Ubuntu, il n’y a pas de sudo par défaut existant. Il faut commuter par
$ su -
Mettre à jour le serveur par $ apt update && apt upgrade -y
Pour la mise en place de Docker sur Debian, suivre ce lien Docker sur Debian
Voici les étapes si vous ne voulez pas changer de page
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Add the repository to Apt sources:
echo \
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
Installation de la dernière version de docker pour Debian
$ apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Vérifier si l’installation a bien fonctionné en exécutant le conteneur hello-world depuis l’image du même nom que docker devrait aller télécharger sur le docker hub
$ docker run hello-world
Par défaut, docker requiert un niveau d’exécution privilégié et il est courant de :
$ adduser docker
$ sudo usermod -aG docker docker
$ sudo usermod -aG sudo docker
Afin que Docker redémarre automatiquement avec le reboot du serveur
$ sudo systemctl enable --now docker
Commencez par créer un volume pour que Portainer puisse stocker ses informations
$ docker volume create portainer_data
Pour installer Portainer dans Docker il suffit de faire la commande suivante
docker run -d \
--name portainer \
-p 8000:8000 \
-p 9443:9443 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
--restart=unless-stopped \
portainer/portainer-ce:latest
Se connecter ensuite à Portainer par l'URL https://@ip:9443. Le navigateur va afficher un message d'avertissement concernant le manque de certificat SSL pour cette connexion. Accepter cette alerte pour accéder à l'interface de connexion de Portainer.
Comme c'est la première connexion, il est obligatoire de créer un compte d'administration en mettant le username et le mot de passe que vous souhaitez. Il est bien évidemment recommandé de ne pas laisser admin comme nom d'utilisateur et de définir un mot de passe complexe pour des raisons évidentes de sécurité.
Supposons que nous installations Node.JS dans un containeur Docker.
> docker run -it ubuntu
Cela change le prompt montrant que nous sommes bien dans le container, et en mode root (donc pas besoin
de sudo non plus)
id-conteneur:/# apt update
id-conteneur:/# apt install nodejs
id-conteneur:/# node -v
Cela devrait afficher le numéro de version de Node
Une fois les éléments mis en place, il faut faire un commit sur votre repository local
> docker commit -m "modifications apport" -a "Author Name" container_id repository/new_image_name
L'option -m indique les modifications apportées à l'image (simple texte)
L'option -a indique l'auteur des modifications
Le container_id est celui qui s'affiche lorsque le containeur est créé
Le repository correspond à votre nom d'utilisateur dans Docker Hub.
Pour lister les images locales
> docker images
Pour pousser une image dans un référentiel Docker il faut avoir un compte sur le site.
Ensuite connectez-vous par
> docker login -u docker-registry-username
Attention : si le nom ayant servi pour générer l'image n'est pas celui utilisé pour la connexion
au service Docker Hub, il faudra tagger votre image avec votre nom d'utilisateur du registre
> docker tag
Nous allons créer une simple application Flask qui sera utilisée comme serveur web pour afficher la page d'index tout simplement. Il faudra disposer d'une arborescence comportant la structure suivante :
DOCKER_APP
|- app
|- templates
|- index.html
|- main.py
|- Dockerfile
|- requirements.txt
On commence par créer un fichier requirements.txt qui va contenir toutes les bibliothèques requises dans l'application. Puisqu'ici nous n'allons faire que du Flask, nous ne mettons donc que la ligne flask dans ce fichier. Il se trouvera dans la racine de l'application (donc dans le dossier app).
En fin de mise en place, nous créeons un fichier Dockerfile (avec la majuscule) qui va définir le comportement à adopter pour mettre en place cette application en format de container Docker. Il se trouvera dans la racine de l'application (donc dans le dossier app).
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
EXPOSE 6874
CMD ["python", "app/main.py"]
On part de l'installation de python 3.11 en précisant que notre dossier est /app. Nous copions le fichier requirements.txt dans le container sous le même nom. Ensuite nous exécutons l'installation des dépendances définies dans le fichier requirements.txt que nous venons de copier. On copie ensuite tout le reste du dossier dans le container. Nous exposons le port 6874 et lançons la commande python app/main.py.
Maintenant que tout est prêt il nous faut créer ce container par
$ docker build -t mon_container .
Une fois l'opération terminée, si vous faites $ docker image ls
voytre image nommée mon_container
devrait apparaître dans le listing.
Pour lancer le nouveau container, il faut faire
$ docker run -p 6874:6874 mon_container
Ensuite, comme c'est un site web, il suffit de se connecter à l'URL http://@ip:6874 et de laisser la magie opérer...