Docker introduction pour débutants
En tant que développeur, il nous arrive de vouloir "essayer" telle ou telle librairie, faire un petit POC rapide ou utiliser l'une des dizaines d'images utilitaire de https://docs.linuxserver.io/ 🥰. Il y a quelques années était possible d'isoler des environnement de travail et de les rendre "temporaires" ou "jetable" grâce à des solutions de virtualisations (plus ou moins lourde ou onéreuses).
Docker est apparue comme une alternative "légère" à la Virtualisation classique car elle repose sur le système d'exploitation hôte (contrairement aux VM qui nécessite une install d'OS).
Attention: on lit souvent que docker permet de déployer "cross plateform", mais il faut noter que les images docker sont dépendantes de l'OS et de l'architecture CPU hôte. Il faudra donc utiliser une méthode nommé multistage build si votre environnement de dév est sous Linux sur un PC (x64) et que vous souhaitez déployer sur des Raspberry Pi (armV7). Voir également Docker Machine (anciennement Boot2docker)
Concepts de base
Docker nous permet de créer un environnement isolé capable de faire tourner des services/serveurs (web, smtp ou autres) ou de lancer des exécutables, sans polluer le système hôte, tous les fichiers/libs nécessaires sont empaquetés dans l'image docker.
Mots clé
Image
Une image docker c'est un package exécutable en lecture seule, qui comprend tout ce qui est nécessaire pour lancer une application (le code, runtime, bibliothèques, var d'environnement et fichiers de conf). Une image Docker peut être une application web complète, un serveur quelconque ou juste un exécutable. Il existe de nombreuses images facilement récupérables depuis un repo Docker, elles nous serviront de base pour construire notre propre image (contenant notre toute dernière SPA par ex).
Container
Un docker container c'est l'instance d'une image, c'est une image qui est "exécutée" et visible avec la commande docker ps
DockerFile
le DockerFile est un fichier de commandes qui va nous permet de créer une image personnalisée en se basant sur des images existantes puis la configuration et installation d'outils divers
Docker Engine
Le docker engine est le moteur de Docker coté serveur, il se compose du process (deamon) dockerd
, qui va permettre de gérer les containers et images.
Pour interagir avec ce docker engine nous disposons d'une API REST ainsi que d'un SDK.
Le SDK officiel supporte Python et Go mais il existe également de nombreuses bibliothèques non officiels pour différents languages de programmations :
- C# : Docker.DotNet
- Nodejs : dockernode
L'API REST se nomme le Docker Engine API, et comme toute API bien concue, elle est versionnée. Sélection la version de l'API selon les versions de Docker daemon et Docker client utilisés.
Docker Hub
Repository d'images dockers pour divers architectures matériels (et spécificquement pour ici Raspberry Pi)
Commandes de base
Gestion
docker images
: liste l'ensemble des images présente en local (machine faisant tourner le deamon docker)
docker ps -a
ou docker container ls -all
: liste l'ensemble des containers (même ceux qui sont exited)
docker diff e319dc35e14e
: affiche la différence entre le container (l'argument est le CONTAINER ID fournie par docker ps ) et l'image à partir de laquelle il a été lancé
docker commit e319dc35e14e debian-perso
: enregistre le container ayant l'ID spécifié dans une image nommée debian-perso
docker save debian-perso > ~/debian-perso.tar
: exporte l'image debian-perso sur dans le home de l'utilisateur courant sous forme de fichier tar
docker run --hostname=dockerized --name=toto -ti debian
: lance l'image debian en nommant le container "toto" et en spécifiant le nom d’hôte dokerized, en mode interactif
docker run --hostname=dockerized -v /tmp/local:/tmp/container -ti debian
: lance l'image debian en mappant un dossier de la machine hôte sur un dossier du container, cela permet de faire persister les fichiers
Run
La commande docker run est particulièrement riche en options mais voici
# lance le conteneur mais s'arrete
docker run alpine:latest
# lance le conteneur et donne la main puis supprime le conteneur une fois la session terminée (--rm)
docker run -ti --rm debian:latest
# lance le conteneur en arrière plan et le nomme (--name)
docker run -di -p 8080:80 --name mon_nginx nginx:latest
docker run -di -p 8080:80 -v ./local/folder:/usr/share/nginx/html/ nginx:latest # montage de volume
docker exec -ti mon_alpine sh # reprendre la main sur un conteneur lancé en deamon
docker ps -a # lister tous les conteneurs meme ceux arretés
docker inspect mon_nginx | jq -C | less # liste toutes les infos => good pour Debug
les conteneur disposent d'une adresse IP dynamique sur un réseau docker fourni par docker inspect
, dans l'exemple précédent le conteneur mon_nginx nous mets à dispo le service sur :
http://localhost:8080
http://172.17.0.3:80
Gestion par lots
Ces commandes permettent de nettoyer un système, donc attention à ces copier/coller ☠️ :
docker ps -aq # liste les ID des containers présent
docker stop `docker ps -aq` # arrête tous le containers
docker rm `docker ps -aq` # supprime tous le containers
docker rmi `docker images -q` # supprime toutes les images
Persistance des données
Par défaut un conteneur Docker écrit sur un système de fichier interne (writable container layer). Ces données sont donc physiquement liés au conteneur ce qui présente 3 inconvénients :
- il est difficile de déplacer/backup la data
- il est difficile d'y accéder depuis un autre conteneur ou système hote
- les données ne résisteront pas à la destruction/re-création du conteneur
Pour persister les données utilisés par un conteneur, il faut utiliser l'un des 2 mécanismes dédiés :
Docker has two options for containers to store files on the host machine, so that the files are persisted even after the container stops: volumes, and bind mounts.
Il y a donc les notions de volumes et les bind mounts qui vont permettre de stocker les fichiers sur le système hôte, qui peuvent etre montés grace à l'option -v
ou --volume
du docker run
.
Volumes
Volumes are the preferred way to persist data in Docker containers and services.
Les volumes sont stockés dans un dossier gérer par Docker (/var/lib/docker/volumes/
sur Linux) il sont donc en quelque sorte cachés, ce qui évite les boulettes, mais aussi que d'autres process viennent y toucher.
Il est possible de monter 1 meme volume sur plusieurs conteneur simultanément.
Commandes docker volume
Les commandes de gestion des volumes sont les suivantes :
Usage: docker volume COMMAND
Commands:
create Create a volume
inspect Display detailed information on one or more volumes
ls List volumes
prune Remove all unused local volumes
rm Remove one or more volumes
volume drivers
Il existe également la notion de volume drivers qui permet de stocker nos fichiers non plus sur le système hote, mais dans le cloud ou sur des support de stockage distant.
Bind mounts
Les Bind mounts sont le système de persistance historique de docker. Ils permettent simplement lorsqu'on docker run
d'attacher un dossier du système hote à un dossier à l'intérieur du conteneur.
docker run -di -p 80:80 -v $(pwd)/toto:/usr/share/nginx/html/
Pour éviter de perdre du tps 😉 il est à noter que les chemins spécifiés doivent être des chemins absolus, faute de quoi docker créera un volume du meme nom, ici le volume toto sera créé (visible via un docker volume ls
)
docker run -di -p 80:80 -v toto:/usr/share/nginx/html/
Sécurité des bind mounts
Attention, d'après la doc officielle, les bind mounts permettent d'interagir avec le système de fichier hote via des process tournant à l'intérieur du conteneur 😱
Bind mounts allow access to sensitive files
One side effect of using bind mounts, for better or for worse, is that you can change the host filesystem via processes running in a container, including creating, modifying, or deleting important system files or directories. This is a powerful ability which can have security implications, including impacting non-Docker processes on the host system.
A lire
- comprendre ces concepts lire la doc officielle Docker.
- ce très bon blog post à propos des Docker volumes vs. bind mounts (logrocket) avec des exemples
exemple avec coloration syntaxique
docker volume ls --format '{{json .}}'|jq
Réseau
Le réseau par défaut utilisé par Docker est le bridge
aussi libellé docker0
Les conteneur peuvent communiquer à travers ce réseau (ou un réseau custom) en se référant les uns aux autre par leurs noms de conteneur.
Petit piège, les IP sont flottantes, il ne faut donc pas utiliser des adresses IP pour ces communications inter conteneurs.
Tips
Installation
Il existe différentes méthodes d'installation en fonction de l'OS hote:
- Debian x64 utiliser apt
- Raspberry pi utiliser cette méthode qui va exploiter le script get-docker.sh
- pour les vieux PC (i386) sous Linux voici la démarche (récupérer et compiler les sources) ou utiliser ce script (pour Ubuntu i386)
mais la plus simple est d'exécuter le script d'installation:
curl -fsSL https://get.docker.com -o get-docker.sh
Post-installation
Si vous souhaitez utiliser docker sans sudo à chaque commande, il suffit d'ajouter les logins au group docker (a créer s'il n'existe pas):
- Créer le groupe
docker
.
sudo groupadd docker
- Ajouter l’utilisateur courant (ou un autre a spécifier) au groupe système
docker
.
sudo usermod -aG docker $USER
Tester l'installation locale de docker
Pour vérifier son installation, faire un simple docker -v
puis tenter de lancer une image (ici Nginx pour Raspberry Pi) en prenant soin de mettre un fichier index.html dans le dossier spécifié en paramètre :
docker run --name testwebsrv -v /dossier/local/contenant/du/html:/usr/share/nginx/html:ro -d -p 8080:80 arm32v7/nginx
❗ si on obtient une erreur : standard_init_linux.go:207: exec user process caused "exec format error"
c'est que l'image que l'on souhaite exécuter n'est pas adaptée à l'architecture CPU locale (ex on essaye de lancer une image X64 sur un ARMv7).
Désintallation
Si l'installation a été faite via le script Curl fourni par le site Docke sur une distribution type Debian (ex raspbian), la désinstallation se fait avec dpkg
sudo dpkg -l|grep docker # pour vérifier que le(s) package docker est bien présent
sudo dpkg --remove --force-remove-reinstreq docker-ce
sudo groupdel docker
Liens
- Concepts avancés
- Série de vidéos gratuite de XAVKI sur #youtube : Tutos et formation DOCKER fr : débuter la conteneurisation