LP R&T - TP Docker === # Objectif: Pour ce premier TP, nous allons aborder la virtualisation par isolation (aussi appelée virtualisation légère) au travers de la solution leader du marché : **Docker** https://www.docker.com. Nous aborderons les concepts de base de Docker : - les images / conteneurs - les dockerfiles - les réseaux - les volumes - docker-compose # Organisation du TP : Pour ce TP, nous allons utiliser un hôte Docker sur un serveur linux CentOS 7.x mis à votre disposition. - adresses des serveurs : `docker-01.univ-rouen.fr` à `docker-15.univ-rouen.fr` Les serveurs sont accessibles en SSH avec le compte : - user: `formation` - password: `dsi2018` Vous disposez également d'un registre privé permettant de stocker et de distribuer vos propres images Docker. :::warning Certains exemples de commandes utilisent mon identifiant. Vous devez bien évidemment le substituer par le votre... ::: # Prérequis : - Poste de travail Windows - Mozilla Firefox : version 56 et + - Client SSH pour windows : Putty ( https://www.putty.org ) # Présentation : Docker est un projet open source (Apache 2.0) écrit en GO et hébergé sur GitHub: https://github.com/docker. Initialement porté par la startup DotCloud (renommée depuis Docker) fondée par deux français anciens de l'Epitech. Docker est composé de trois éléments : - le daemon Docker qui s'exécute en arrière-plan et qui s'occupe de gérer les conteneurs (Containerd avec runC) - une API de type REST qui permet de communiquer avec le daemon - Le client en CLI (command line interface) : commande `docker` Par défaut, le client communique avec le daemon Docker via un socket Unix (/var/run/docker.sock) mais il est possible d'utiliser un socket TCP. Docker c'est aussi un dépôt d'images (aussi appelé registry) : https://store.docker.com Il contient les images officielles maintenues par Docker mais aussi celles mises à disposition par d'autres contributeurs. Quelques concepts: - une image est un ensemble de fichiers inertes en read-only. - Un conteneur est une instance une active (started) ou inactive (stopped) d'une image. L'execution d'un conteneur n'altère jamais une image. # Lexique - **Conteneur** : Image exécutable d’un environnement complet incluant code, librairies, outils et configuration - **Image** : template de conteneur en read-only contenant un systeme de base et une application. - **Docker HUB** : Dépôt public d'images mises à disposition par Docker [DockerHub](https://store.docker.com) - **Dockerfile** : fichier texte de description d'une image - **Docker Compose** : fichier texte (yaml) de description d'un ensemble de conteneurs - **Docker Machine** : Outil de déploiement des hôtes Docker sur différentes plateformes (Mac, Windows) : https://docs.docker.com/machine/overview/ - **Orchestrateur** : gère un pool de ressources serveurs ( Swarm, Kubernetes, Mesos, Rancher...) - **Registry** : Dépôt privé d'images Docker # Installation de Docker [Méthode d'installation officielle](https://docs.docker.com/install/linux/docker-ce/centos/) #### Installer les prérequis (Centos 7.x amd64) ``` $ sudo yum install -y yum-utils device-mapper-persistent-data lvm2 ``` #### Configurer le dépôt officiel et installer Docker en version CE (Community Edition) ``` sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install docker-ce ``` #### Activer et démarrer le service ``` $ sudo systemctl enable docker $ sudo systemctl start docker ``` #### Vérifier l'installation ``` $ sudo docker run hello-world Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/ ``` :::info Details des opérations réalisées par cette commande: 1: le client docker se connecte au daemon docker via le socket Unix 2: l'image "hello-world" n'etant pas présente localement, le daemon Docker la télécharge depuis la registery Docker Hub 3: le daemon Docker crée un nouveau conteneur depuis cette image dont la finalité est de produire le message ci-dessus. 4: le daemon Docker renvoi le message au client Docker pour afficher le résultat dans le terminal ::: #### Vérifier la version ``` $ sudo docker version Client: Version: 18.06.1-ce API version: 1.38 Go version: go1.10.3 Git commit: e68fc7a Built: Tue Aug 21 17:23:03 2018 OS/Arch: linux/amd64 Experimental: false Server: Engine: Version: 18.06.1-ce API version: 1.38 (minimum version 1.12) Go version: go1.10.3 Git commit: e68fc7a Built: Tue Aug 21 17:25:29 2018 OS/Arch: linux/amd64 Experimental: false ``` #### Obtenir des infos sur la configuration Docker ``` $ sudo docker info Containers: 1 Running: 0 Paused: 0 Stopped: 1 Images: 1 Server Version: 18.06.1-ce Storage Driver: overlay2 Backing Filesystem: extfs Supports d_type: true Native Overlay Diff: false Logging Driver: json-file Cgroup Driver: cgroupfs Plugins: Volume: local Network: bridge host macvlan null overlay Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog Swarm: inactive Runtimes: runc Default Runtime: runc Init Binary: docker-init containerd version: 468a545b9edcd5932818eb9de8e72413e616e86e runc version: 69663f0bd4b60df09991c08812a60108003fa340 init version: fec3683 Security Options: seccomp Profile: default Kernel Version: 3.10.0-514.2.2.el7.x86_64 Operating System: CentOS Linux 7 (Core) OSType: linux Architecture: x86_64 CPUs: 1 Total Memory: 1.781GiB Name: tp-docker.univ-rouen.fr ID: GUWR:VBEF:77JQ:LZDZ:X2JG:E74O:3WJC:CRJN:VZOQ:XA5V:VNYF:UN5S Docker Root Dir: /var/lib/docker Debug Mode (client): false Debug Mode (server): false Registry: https://index.docker.io/v1/ Labels: Experimental: false Insecure Registries: 127.0.0.0/8 Live Restore Enabled: false ``` :::info Cette commande retourne plusieurs informations intéressantes: 1: le driver de stockage est overlayFS: `Storage Driver: overlay2` 2: le dossier dans le quel Docker va stocker les images et les volumes: `Docker Root Dir: /var/lib/docker` 3: l'adresse du registre pour télécharger les images: `Registry: https://index.docker.io/v1/` ::: #### Utiliser Docker sans les droits "root" Pour utiliser Docker sans les droits `root`, l'utilisateur doit appartenir appartenir au groupe `docker` ``` $ sudo groupadd docker $ sudo usermod -aG docker $USER ``` :::info Il faut se déconnecter / reconnecter pour appliquer les modifications. ::: #### Tester sans sudo: ``` $ docker run hello-world ``` ## Gestion des images Il existe 3 méthodes pour obtenir une image : - Télécharger depuis Docker Hub ou un registre privé : `docker pull` - Construire l'image depuis un dockerfile : `docker build` - Lancer un conteneur (l'image est téléchargée si elle n'est pas présente localement) : `docker run` #### Rechercher une image sur le Docker Hub : `docker search` ``` $ docker search debian NAME DESCRIPTION STARS OFFICIAL AUTOMATED ubuntu Ubuntu is a Debian-based Linux operating s... 8753 [OK] debian Debian is a Linux distribution that's comp... 2870 [OK] neurodebian NeuroDebian provides neuroscience research... 55 [OK] arm32v7/debian Debian is a Linux distribution that's comp... 43 itscaro/debian-ssh debian:jessie 24 ``` `ubuntu` et `debian` sont des images officielles maintenues par Docker (les autres sont de la forme user/nom_image). La colonne STARS donne une indication sur la popularité de l’image (mise en favoris) #### Télécharger une image: `docker pull` ``` $ docker pull debian:latest Using default tag: latest latest: Pulling from library/debian 3e17c6eae66c: Pull complete Digest: sha256:26b2647845d66e20eeadf73d1c302a4ffd2cc9a74c39a52f2aced4f823484328 Status: Downloaded newer image for debian:latest ``` :::info Les images hébergées sur le Docker Hub sont souvent générées depuis un dockerfile lui-même hébergé sur GitHub (ex : https://hub.docker.com/_/debian/ ). ::: #### Pour obtenir la liste des images : `docker image ls` ``` $ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE debian latest 4879790bd60d 3 days ago 101MB ``` Une même image peut avoir plusieurs TAG : ``` $ docker pull debian:buster $ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE debian latest 4879790bd60d 3 days ago 101MB debian stretch 4879790bd60d 3 days ago 101MB ``` Ici, les deux images ont le même `IMAGE ID`. ### Dockerfile Les images Docker sont créées à partir d'un fichier de configuration nommé `dockerfile` ```dockerfile= FROM docker/whalesay MAINTAINER bruno.levasseur@univ-rouen.fr RUN apt-get -y update && apt-get install -y figlet CMD figlet "Hy DSI Team" | cowsay -n ``` :::info Instructions de base : - **FROM** permet de définir l'image de base - **LABEL maintainer** permet de définir l'auteur de l'image - **RUN** permet de lancer une commande - **ADD** permet de copier un fichier depuis la machine hôte ou depuis une URL - **EXPOSE** permet d'exposer un port du container vers l'extérieur - **CMD** détermine la commande qui sera exécutée lorsque le container démarrera - **ENTRYPOINT** permet d'ajouter une commande qui sera exécutée par défaut - **WORKDIR** permet de définir le dossier de travail pour toutes les autres commandes (par exemple RUN, CMD, ENTRYPOINT et ADD) - **ENV** permet de définir des variables d'environnement qui pourront ensuite être modifiées grâce aux paramètres de la commande `run --env <key>=<value>` - **VOLUMES** permet de créer un point de montage qui permettra de rendre les données persistantes ::: #### Pour construire l'image : `docker build` `docker build -t <user>/<image>:<tag> /path/` ```bash $ docker build -t levasbr1/cowsay:latest . Sending build context to Docker daemon 10.33MB Step 1/4 : FROM docker/whalesay ---> 6b362a9f73eb Step 2/4 : MAINTAINER bruno.levasseur@univ-rouen.fr ---> Using cache ---> da73160d617d Step 3/4 : RUN apt-get -y update && apt-get install -y figlet ---> Using cache ---> e51bf10c9e9f Step 4/4 : CMD figlet "Hy DSI Team" | cowsay -n ---> Using cache ---> d6a52e303d4a Successfully built d6a52e303d4a Successfully tagged user_name/cowsay:latest ``` #### L'opération génère une nouvelle image : ``` $ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE debian latest 4879790bd60d 3 days ago 101MB debian stretch 4879790bd60d 3 days ago 101MB levasbr1/cowsay latest d6a52e303d4a About an hour ago 253MB ``` #### Pour lancer un conteneur depuis cette image : `docker run` ``` $ docker run levasbr1/cowsay ``` #### Déposer l'image sur le Docker Hub ou un registre privé : `docker push` Il faut modifier le repository pour indiquer l'adresse du registre privé : `docker tag <user>/<image>:<tag> registry.domain.tld:<port>/<user>/<image>:<tag>` ``` $ docker tag levasbr1/cowsay:latest registry.univ-rouen.fr:5000/levasbr1/cowsay:latest $ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE levasbr1/cowsay latest d6a52e303d4a About an hour ago 253MB registry.univ-rouen.fr:5000/levasbr1/cowsay latest d6a52e303d4a About an hour ago 253MB ``` On peut maintenant déposer l'image sur le registry : ``` $ docker push registry.univ-rouen.fr:5000/levasbr1/cowsay:latest The push refers to repository [registry.univ-rouen.fr:5000/levasbr1/cowsay] 205abcc4f9aa: Pushed 5f70bf18a086: Pushed d061ee1340ec: Pushed 528c8710fd95: Pushed 1154ba695078: Pushed latest: digest: sha256:e8c8e5372f2570ad30322e910b6b9ea29a492eb362a2b230ceb31b1bf7324e5c size: 2614 ``` :::info La démarche est identique pour pousser une images sur le Docker Hub : ``` $ docker login -u <user-docker-hub> $ docker push <user-docker-hub>/<image>:<tag> ``` ::: #### Supprimer une image : `docker rmi <image_ID>` Pour supprimer une image, on peut utiliser soit le repository soit l'image ID (abrégé). ``` $ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE levasbr1/cowsay latest d6a52e303d4a About an hour ago 253MB registry.univ-rouen.fr:5000/levasbr1/cowsay latest d6a52e303d4a About an hour ago 253MB $ docker rmi registry.univ-rouen.fr:5000/levasbr1/cowsay $ docker rmi d6a Error response from daemon: conflict: unable to delete d6a52e303d4a (must be forced) - image is being used by stopped container b46113092ad3 ``` :::warning Attention : on ne peut pas supprimer une image dont dépend un conteneur. ::: #### Afficher l'historique d'une image : `docker history` ``` $ docker history d6a docker history c70 IMAGE CREATED CREATED BY SIZE COMMENT c702e015d221 34 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "figl… 0B ce7dcf9568cc 34 minutes ago /bin/sh -c apt-get -y update && apt-get inst… 26.6MB 0a3e0d2202d5 36 minutes ago /bin/sh -c #(nop) MAINTAINER bruno.levasseu… 0B 6b362a9f73eb 3 years ago /bin/sh -c #(nop) ENV PATH=/usr/local/bin:/u… 0B <missing> 3 years ago /bin/sh -c sh install.sh 30.4kB <missing> 3 years ago /bin/sh -c git reset --hard origin/master 43.3kB <missing> 3 years ago /bin/sh -c #(nop) WORKDIR /cowsay 0B <missing> 3 years ago /bin/sh -c git clone https://github.com/moxi… 89.9kB <missing> 3 years ago /bin/sh -c apt-get -y update && apt-get inst… 58.6MB <missing> 3 years ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B <missing> 3 years ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$… 1.9kB <missing> 3 years ago /bin/sh -c echo '#!/bin/sh' > /usr/sbin/poli… 195kB <missing> 3 years ago /bin/sh -c #(nop) ADD file:f4d7b4b3402b5c53f… 188MB ``` ## Gestion des conteneurs #### Lancer un conteneur à partir d'une image : `docker run` ``` $ docker run debian:buster cat /etc/issue Debian GNU/Linux 10 \n \l ``` Dans cet exemple, le conteneur : 1. démarre 2. exécute la commande passée en argument 3. affiche la sortie stdout de la commande 4. s'arrête #### Lister les conteneurs en cours d'exécution : `docker ps` La commande `docker ps` ne retourne rien puisque le conteneur est arrété : ``` $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ``` Pour obtenir la liste complète des conteneurs (quelque soit leur état) il faut utiliser l'option `docker ps -a` : ``` $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 0ddb3befebe7 debian:stretch "cat /etc/issue" 3 hours ago Exited (0) 3 hours ago angry_nightingale b46113092ad3 c702e015d221 "/bin/sh -c 'figlet …" 4 hours ago Exited (0) 4 hours ago epic_borg ``` Le conteneur possède un identifiant unique, Container ID (CID), et un nom généré aléatoirement (angry_nightingale). #### Nommer un conteneur (option : `--name` ou `-n`) On peut utiliser l'option `--name` pour nommer un conteneur de manière plus explicite : ``` $ docker run --name cmd_cat debian:latest cat /etc/issue Debian GNU/Linux 10 \n \ ``` Cette commande a créé un nouveau conteneur : ``` $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 517729d68220 debian:stretch "cat /etc/issue" 12 seconds ago Exited (0) 11 seconds ago cat_cmd 0ddb3befebe7 debian:stretch "cat /etc/issue" 3 hours ago Exited (0) 3 hours ago angry_nightingale b46113092ad3 c702e015d221 "/bin/sh -c 'figlet …" 4 hours ago Exited (0) 4 hours ago epic_borg ``` #### Obtenir une session intéractive (option : `-it`) On peut obtenir une session intéractive (option `-i`) en se branchant sur l'entrée standard du conteneur et en connectant un pseudo terminal TTY (option `-t`) : ``` $ docker run -it debian:latest /bin/bash root@eae2cce2669d:/# ``` Le prompt reprend le "conteneur ID" (CID) du conteneur (utiliser la commande `exit` pour quitter le conteneur). #### Lancer un conteneur en mode daemon (option : `-d`) On peut lancer un conteneur en mode daemon pour qu'il tourne en tâche de fond. ``` $ docker run -d --name test_daemon nginx 4d81f9903afe1b777de6389954c762122b5aeea847f5b4f8953ad308bbc5203d $ docker ps CONTAINER ID IMAGE COMMAND CREATED 4d81f9903afe nginx "nginx -g 'daemon ..." 51 seconds ago $ docker stop test_daemon ``` :::info ### Cycle de vie des conteneurs ##### Obtenir la configuration détaillée d'un conteneur `$ docker inspect <nom_conteneur> ou <CID>` ##### Récupérer la sortie standard d'un conteneur `$ docker logs <nom_conteneur> ou <CID>` ##### Afficher les processus en cours dans un conteneur `$ docker top <nom_conteneur> ou <CID>` ##### Suspendre (freezer) et réactiver un conteneur `$ docker pause / unpause <nom_conteneur> ou <CID>` ##### Arrêter / démarrer / tuer / redémarrer un conteneur `$ docker stop / start / kill / restart <nom_conteneur> ou <CID>` ##### Exporter l'ensemble du système de fichier d'un conteneur dans une archive TAR `$ docker export <nom_conteneur> ou <CID> > archive.tar` ##### Afficher les ports réseaux exposés par un conteneur `$ docker port <nom_conteneur> ou <CID>` ##### Faire un diff entre les fichiers d'un conteneur et les fichiers de son image `$ docker diff <nom_conteneur> ou <CID>` ##### Obtenir des stats CPU / RAM / Disk / Net des conteneurs `$ docker stats` ##### Commiter un conteneur pour en faire une nouvelle image `$ docker commit <nom_conteneur> ou <CID> <user>/<image>:<tag>` ::: #### Exécuter une commande dans un conteneur démarré : `docker exec` Dans l'exemple, on lance un conteneur nginx en mode daemon et on utilise la commande `docker exec` pour s'y connecter : ``` $ docker run -d --name test_exec nginx $ docker exec -it test_exec /bin/bash root@331e1e904e1e:/# ls bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var root@331e1e904e1e:/# exit exit ``` #### S'attacher à un conteneur pour interagir avec le processus racine : `docker attach` Dans l'exemple, on lance un conteneur nginx en mode daemon et on utilise la commande attach pour visualiser la sortie standard du processus ngnix (il faut ouvrir un navigateur avec l'URL : `http://docker-xx.univ-rouen.fr:8000` pour générer des logs). :::info L'option `-p 8000:80` qui permet d'exposer le port 80 du conteneur et de le joindre sur le port 8000 de l'hôte est abordé dans le § sur les réseaux Docker. ::: ``` $ docker run -d -p 8000:80 --name test_attach nginx 5d7d6f3108532971fef27434fc3504ef5b42c741d7923913e556b9629de6c30c $ docker attach test_attach --sig-proxy=false 10.197.1.22 - - [18/Dec/2017:15:37:28 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/604.3.5 (KHTML, like Gecko) Version/11.0.1 Safari/604.3.5" "-" 10.197.1.22 - - [18/Dec/2017:15:37:28 +0000] "GET /favicon.ico HTTP/1.1" 404 169 "http://docker-deb.univ-rouen.fr:8000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/604.3.5 (KHTML, like Gecko) Version/11.0.1 Safari/604.3.5" "-" 2017/12/18 15:37:28 [error] 5#5: *2 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 10.197.1.22, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "docker-deb.univ-rouen.fr:8000", referrer: "http://docker-deb.univ-rouen.fr:8000/" ``` :::info L'option `--sig-proxy=false` permet de se détacher du conteneur avec la séquence `CTRL-c` sans tuer le processsus racine. ::: #### Supprimer un conteneur arrêté : `docker rm` ``` $ docker ps -a docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5d7d6f310853 nginx "nginx -g 'daemon ..." 29 minutes ago Exited (0) 24 minutes ago test_attach 331e1e904e1e nginx "nginx -g 'daemon ..." About an hour ago Exited (0) 9 minutes ago test_exec c7d328b087eb debian "/bin/bash" About an hour ago Exited (0) About an hour ago test_diff ``` Supprimer un conteneur par son nom ou CID abrégé : ``` $ docker rm test_attach $ docker rm 331 c7d ``` #### Copier des fichiers depuis ou à destination d'un conteneur Dans cet exemple, on récupère un fichier depuis un conteneur, puis on le ré-importe après modification. ``` $ docker run -d -p 8001:80 --name test_cp nginx ``` On copie le fichier index.html depuis le conteneur sur la machine hôte. ::: warning Attention au "." en fin de ligne qui représente le répertoire courant ::: ``` $ docker cp test_cp:/usr/share/nginx/html/index.html . ``` On remplace le contenu du fichier : ``` $ echo "Hello World" > index.html ``` On copie le fichier modifié depuis l'hôte vers le conteneur : ``` $ docker cp index.html test_cp:/usr/share/nginx/html/ ``` On teste depuis un navigateur avec l'URL `http://docker-xx.univ-rouen.fr:8001`. ## les volumes Docker Les volumes sont utilisés pour rendre les données persistantes. #### Les volumes nommés : L'utilisation la plus simple est d'initialiser un volume nommé à la création du conteneur : ``` $ docker run -d -v myvol:/var/log --name test_volume debian:latest /bin/sleep infinity ``` Ici l'option `-v myvol:/var/log` permet d'initialiser un volume (myvol) indépendant du conteneur, qui contiendra les logs du conteneur. La commande `inspect` permet de localiser le volume sur l'arborescence locale de l'hôte `Source` et le point de montage dans le conteneur `Destination` : ``` $ docker inspect test_volume "Mounts": [ { "Type": "volume", "Name": "myvol", "Source": "/var/lib/docker/volumes/myvol/_data", "Destination": "/var/log", "Driver": "local", "Mode": "z", "RW": true, "Propagation": "" } ``` On peut arrêter et supprimer le conteneur pour contrôler que les données sont toujours présentes sur l'hôte. ``` $ docker stop test_volume && docker rm test_volume $ sudo ls /var/lib/docker/volumes/myvol/_data apt btmp faillog lastlog wtmp ``` :::info On peut créer un volume directement avec la commande `docker volume create --name <volume name>` et lister tous les volumes créés avec la commande `docker volume ls`. ::: #### Partager un dossier entre l'hôte et un conteneur (bind mounts) L'autre utilisation des volumes est de partager un dossier entre le conteneur et le système hôte. ``` $ mkdir projet-web $ docker run -d --name test_volume2 -p 8004:80 -v ~/projet-web:/var/www/html newdeveloper/apache-php ``` Ici nous disposons d'un volume monté sur l'arborescence `/var/www/html` du conteneur et correspondant au dossier `/home/<user>/projet-web` de l'hôte. ``` $ echo "<?php phpinfo(); ?>" > ~/projet-web/phpinfo.php ``` On crée un fichier phpinfo.php directement sur l'arborescence du système hôte. On teste depuis un navigateur avec l'URL `http://docker-xx.univ-rouen.fr:8004/phpinfo.php` que le fichier est bien accédé par le conteneur. ``` $ docker inspect test_volume2 "Mounts": [ { "Type": "bind", "Source": "/home/formation/projet-web", "Destination": "/var/www/html", "Mode": "", "RW": true, "Propagation": "rprivate" ``` La commande `inspect` permet de visualiser cette configuration du conteneur. "Source" correspond à l'arborescence de l'hôte et "Destination" au point de montage dans le conteneur. :::danger Dans cette méthode, le conteneur dispose d'un accès direct au file-system de l'hôte. Elle est à proscrire en production pour des raisons de sécurité. ::: # Les réseaux Docker L'installation de Docker crée par défaut un réseau nommé `bridge` (interface Docker0). Il est utilisé par les conteneurs si aucun réseau n'est spécifié au lancement (option `--network=mon_reseau`). ``` $ docker network ls NETWORK ID NAME DRIVER SCOPE 8dd02d7ad16f bridge bridge local 72244137afae host host local f7ab4027acf8 none null local $ docker network inspect bridge { "Name": "bridge", "Id": "8dd02d7ad16f9e47a774f0ee5a652a606ecc23bcc15c126d3e6fbf6fd1c3465c", "Created": "2017-12-05T16:44:54.821533922+01:00", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "172.17.0.0/16", "Gateway": "172.17.0.1" } ``` Les conteneurs obtiennent une adresse IP fournie par le daemon Docker (réseau privé : 172.17.0.0/16, gateway : 172.17.0.1). Sous linux, Docker utilise netfilter (iptables) pour implémenter : - du NAT, pour permettre aux conteneurs de sortir sur le réseau de l'hôte - du port forwarding, pour accéder aux conteneurs depuis l'adresse IP de l'hôte :::info Il est possible d'utiliser un réseau Overlay (vxlan) pour permettre aux conteneurs de communiquer entre eux même si ils sont exécutés sur des hôtes différents. ::: ``` $ docker run -d -p 8006:80 --name test_port nginx ``` Ici l'option `-p` permet d'exposer le port 80 du conteneur et de joindre le conteneur sur le port 8006 et l'adresse IP de l'hôte. ### Réseaux définis par l’utilisateur On utilise aujourd'hui les `user-defined network`, qui apportent une meilleure isolation entre les conteneurs et un mécanisme interne de résolution DNS. On peut créer autant de réseaux qu'il est nécéssaire et connecter / déconnecter les conteneurs à la volée. Dans l'exemple, nous allons créer un réseau dédié pour y connecter un conteneur mysql et un frontal phpMyAdmin. On commence par déclarer un nouveau réseau en utilisant le driver de type bridge (on peut utiliser également un driver MACVLAN ou OVERLAY). ``` $ docker network create -d bridge mynet ``` On contrôle le nouveau réseau `mnynet` : ``` $ docker network inspect mynet [ { "Name": "mynet", "Id": "f5d8b3b0ef90654c06662afecbcee70b9dad4fb8dec54e4d3d2fccd8013ddc4e", "Created": "2018-11-11T17:45:17.124793619+01:00", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.18.0.0/16", "Gateway": "172.18.0.1" } ] }, ``` On affiche la liste des réseaux : ``` docker network ls NETWORK ID NAME DRIVER SCOPE 87adfb041868 bridge bridge local f946a0bd82f7 host host local b17e5a6fc39a mynet bridge local 4ade9b530900 none null local ``` On démarre le conteneur avec une database MySQL dans ce réseau : ``` $ docker run --name db --network mynet -e MYSQL_ROOT_PASSWORD=bonjour -d mysql:5.7 ``` :::info L'option `-e` permet de passer des variables d'environnement au conteneur (ici on définit le password root de la base de données). ::: On démarre ensuite un conteneur avec l'interface PHPmyadmin : ``` $ docker run --name phpmyadmin --network mynet -d -p 8080:80 phpmyadmin/phpmyadmin ``` On teste la connexion entre les 2 conteneurs : http://docker-xx.univ-rouen.fr:8080 On peut également tester la résolution DNS `db` sur le réseau `mynet` ``` docker run --network=mynet --name=test-dig sequenceiq/alpine-dig dig db ;; ANSWER SECTION: db. 600 IN A 172.18.0.2 ``` L'alias `db` est résolu en `172.18.0.2`. Pour vérifier qu'il s'agit bien de l'adresse IP du conteneur mysql : ``` $ docker inspect db | grep IPAddress "SecondaryIPAddresses": null, "IPAddress": "", "IPAddress": "172.18.0.2", ``` :::warning L'option `--lynk` qui permet de peupler dynamiquement le fichier `hosts` pour relier des conteneurs est considéré comme obsolète (deprecated) ::: # Docker-Compose Docker-compose est un outil pour simplifier le déploiement d'applications complexes (avec de nombreux conteneurs) en utilisant un simple fichier texte de description au format yaml. Il reprend l'ensemble des options qui seraient normalement à fournir à un `docker run`. Dans l'exemple retenu, nous allons deployer la solution d'édition collaborative CodiMD (utilisée pour ce support). Vous allez créer un fichier `docker-compose.yml` dans votre répertoire personnel : ```dockerfile= version: '3' services: database: image: postgres:9.6-alpine environment: - POSTGRES_USER=hackmd - POSTGRES_PASSWORD=hackmdpass - POSTGRES_DB=hackmd volumes: - database:/var/lib/postgresql/data networks: backend: restart: always app: image: hackmdio/hackmd:1.2.1 environment: - CMD_DB_URL=postgres://hackmd:hackmdpass@database:5432/hackmd ports: - "3000:3000" networks: backend: restart: always depends_on: - database networks: backend: volumes: database: ``` :::warning Attention, le yaml est très sensible à l'indentation... ::: Dans l'exemple proposé, docker-compose va lancer 2 conteneurs automatiquement : - **app**, qui contient le serveur d'application - **database**, qui contient un serveur PostgreSQL Pour lancer les conteneurs, on utilise la commande : `docker-compose up -d` ``` $ docker-compose up -d Creating network "formation_backend" with the default driver Creating volume "formation_database" with default driver Pulling database (postgres:9.6-alpine)... 9.6-alpine: Pulling from library/postgres 4fe2ade4980c: Pull complete 08cf8c12f47e: Pull complete a3e76355a10e: Pull complete a8839f4153ad: Pull complete Pulling app (hackmdio/hackmd:1.2.1)... 1.2.1: Pulling from hackmdio/hackmd f189db1b88b3: Pull complete 3d06cf2f1b5e: Pull complete 687ebdda822c: Pull complete 3dd088865e63: Pull complete 108825449826: Pull complete Creating formation_database_1_798dfc64f458 ... done Creating formation_app_1_5fe390eb2e7e ... done ``` On teste le bon fonctionnement de l'application avec l'URL `http://docker-xx.univ-rouen.fr:3000` Pour stopper les conteneurs, on utilise la commande (toujours dans le répertoire contenant le fichier docker-compose.yml) :`docker-compose stop` et pour détruire les conteneurs: `docker-compose down` # Exercice Utiliser une interface web d'administration Docker : Portainer. # Quelques astuces: - Pour stopper tous les conteneurs : `docker stop $(docker ps -q -a)` - Pour supprimer tous les conteneurs : `docker rm $(docker ps -q -a)` - Pour supprimer toutes les images : `docker rmi -f $(docker images -q)` - Pour purger les volumes orphelins : `docker volume rm $(docker volume ls -qf dangling=true)` - Pour avoir une interface web pour Docker : https://github.com/portainer/portainer - Pour avoir un reverse proxy automatique pour Docker : - https://github.com/jwilder/nginx-proxy - https://traefik.io - Pour disposer d'un bac à sable Docker en ligne : https://labs.play-with-docker.com/ - Quelques one-liners : http://www.commandlinefu.com/commands/matching/docker/ZG9ja2Vy - Formation officielle en ligne Docker : https://docs.docker.com/get-started/ - Purger Docker : `docker system prune -a` et `docker volume prune`. Vérification avec : `docker system df`