Introduction à Docker

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 :

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:

Lexique

Installation de Docker

Méthode d’installation officielle

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/

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

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

Il faut se déconnecter / reconnecter pour appliquer les modifications.

Tester sans sudo:

$ docker run hello-world

Les Images Docker

$ docker search debian
NAME              DESCRIPTION                                     STARS      OFFICIAL    AUTOMATED
ubuntu            Ubuntu is a Debian-based Linux operating s...   6898       [OK]                
debian            Debian is a Linux distribution that's comp...   2356       [OK]                
google/debian                                                     52                     [OK]
armhf/debian      Debian is a Linux distribution that's comp...   29                                  

Ici “ubuntu” et “debian” sont des images officielles (les autres sont de la forme user/nom_image). Elles sont maintenues par l’équipe docker et considérées comme plus “fiables”.

La colonne STARS donne une indication sur la popularité de l’image (mise en favoris).

Télécharger une image (commande : docker pull)

$ docker pull debian
Using default tag: latest
latest: Pulling from library/debian
3e17c6eae66c: Pull complete
Digest: sha256:26b2647845d66e20eeadf73d1c302a4ffd2cc9a74c39a52f2aced4f823484328
Status: Downloaded newer image for debian:latest

Lancer un conteneur (commande : docker run)

La commande docker run qui permet de lancer un conteneur peut également télécharger l’image si elle n’est pas disponible localement

$ docker run debian:stretch
docker run debian:stretch
Unable to find image 'debian:stretch' locally
stretch: Pulling from library/debian
Digest: sha256:26b2647845d66e20eeadf73d1c302a4ffd2cc9a74c39a52f2aced4f823484328
Status: Downloaded newer image for debian:stretch

Connaitre l’historique de création de l’image (commande : docker history)

$ docker history debian
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
6d83de432e98        4 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B                  
<missing>           4 weeks ago         /bin/sh -c #(nop) ADD file:a71e077a42995a6...   100MB         

Les TAGS

Lister les images présentent localement (commande : docker images ou docker image ls)

docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              f2a91732366c        2 weeks ago         1.85kB
debian              latest              6d83de432e98        4 weeks ago         100MB
debian              stretch             6d83de432e98        4 weeks ago         100MB

Ici les images debian ont le même ID (6d83de432e98) : c’est la même image mais avec un TAG différent

Exemple d’images et de TAGS proposés sur le dépôt officiel Debian : https://hub.docker.com/r/library/debian/tags/

Ajouter un tag à une image (commande : docker image tag)

$ docker image tag debian:stretch debian:levasbr1
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
debian              levasbr1            6d83de432e98        4 weeks ago         100MB
debian              stretch             6d83de432e98        4 weeks ago         100MB

Supprimer une image (commande : docker rmi)

Cette commande permet de supprimer une image à condition de ne pas avoir de conteneur lié.

$ docker rmi debian:latest
Untagged: debian:latest
Untagged: debian@sha256:26b2647845d66e20eeadf73d1c302a4ffd2cc9a74c39a52f2aced4f823484328
Deleted: sha256:6d83de432e98210aa25bcc5556a641d60ec621b67786a2862cfeec5d7258a4d0
Deleted: sha256:a75caa09eb1f7d732568c5d54de42819973958589702d415202469a550ffd0ea

$ docker rmi f2a
Untagged: hello-world:latest
Untagged: hello-world@sha256:be0cd392e45be79ffeffa6b05338b98ebb16c87b255f48e297ec7f98e123905c
Deleted: sha256:f2a91732366c0332ccd7afd2a5c4ff2b9af81f549370f7a19acd460f87686bc7
Deleted: sha256:f999ae22f308fea973e5a25b57699b5daf6b0f1150ac2a5c2ea9d7fecee50fdf

On peut aussi utiliser l’ID abrégé (ex: f2A) pour désigner une image (en l’absence d’ambiguité)

Les conteneurs Docker

Lancer un conteneur à partir d’une image (commande : docker run )

$ docker run debian:latest cat /etc/issue
Debian GNU/Linux 9 \n \l

L’état d’un conteneur dépend de la commande qui est lancée. Ici, le conteneur execute la commande cat et s’arrête dès quelle est terminée.

Lister les conteneurs en cours d’exécution (commande : docker ps)

La commande docker ps qui permet de lister les conteneurs en cours d’executions ne retourne effectivement rien :

$ docker ps
CONTAINER ID    IMAGE   COMMAND   CREATED   STATUS    PORTS   NAMES

Pour obtenir la liste complete des conteneurs, il faut utiliser l’option docker ps -a :

$ docker ps -a
CONTAINER ID        IMAGE           COMMAND             CREATED     STATUS     PORTS   NAMES
396767b854a9        debian:latest   "cat /etc/issue"    44 minutes  Exited (0)         quizzical_easley   

Le conteneur possède un identifiant unique (96767b854a9) et un nom généré aléatoirement (quizzical_easley).

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 9 \n \

Cette commande a créé un nouveau conteneur :

docker ps -a
CONTAINER ID    IMAGE           COMMAND             CREATED              STATUS             PORTS     NAMES
2770f96e4a3d    debian:latest   "cat /etc/issue"    About a minute ago   Exited 1 minute              cmd_cat
ef6b6f1c64a1    debian:latest   "cat /etc/issue"    4 minutes ago        Exited 4 minutes             quizzical_easley
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 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 (le mode intéractif tourne au premier plan).

$ docker run -d --name test_daemon nginx
4d81f9903afe1b777de6389954c762122b5aeea847f5b4f8953ad308bbc5203d
// on affiche la liste des conteneurs en cours d'execution :
$ docker ps
CONTAINER ID        IMAGE                                 COMMAND                  CREATED
4d81f9903afe        nginx                                 "nginx -g 'daemon ..."   51 seconds ago
// on stoppe ce conteneur
$ docker stop test_daemon

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>

Afficher les changements effectués sur les fichiers d’un conteneur (A=Ajout, D=Delete, C=Modif )
$ docker run -it --name test_diff debian /bin/bash

root@c7d328b087eb:/# apt update && apt -y upgrade
Ign:1 http://deb.debian.org/debian stretch InRelease
Get:2 http://deb.debian.org/debian stretch-updates InRelease [91.0 kB]
Get:3 http://deb.debian.org/debian stretch Release [118 kB]                            
Get:4 http://deb.debian.org/debian stretch Release.gpg [2434 B]                               
Get:5 http://security.debian.org stretch/updates InRelease [63.0 kB]
Get:6 http://deb.debian.org/debian stretch-updates/main amd64 Packages [6499 B]

$ docker diff test_diff
C /root
A /root/.bash_history
C /tmp
C /var/lib/apt/lists
A /var/lib/apt/lists/deb.debian.org_debian_dists_stretch-updates_InRelease
A /var/lib/apt/lists/deb.debian.org_debian_dists_stretch-updates_main_binary-amd64_Packages.lz4
A /var/lib/apt/lists/deb.debian.org_debian_dists_stretch_Release
A /var/lib/apt/lists/deb.debian.org_debian_dists_stretch_Release.gpg
A /var/lib/apt/lists/deb.debian.org_debian_dists_stretch_main_binary-amd64_Packages.lz4
A /var/lib/apt/lists/lock
Commiter un conteneur pour obtenir une nouvelle image

Une solution pour “persister” les données ou les configurations consiste à commiter le conteneur pour créer une nouvelle image (clairement pas une bonne pratique).
Dans cet exemple, l’image est associée à l’utilisateur “levasbr1” correspondant à un compte sur le docker-hub pour faciliter la publication des images sur la plate-forme public.

$ docker commit test_diff  <user>/<image>:<tag>
$ docker commit test_diff levasbr1/debian:latest
Pousser une image locale sur une registry privée

Pour pouvoir utiliser une registry non sécurisée en https, il faut ajouter une exception dans la configuration de Docker.
Ajouter le fichier: /etc/docker/daemon.json

{
  "insecure-registries" : ["registry.univ-rouen.fr:5000"]
}

et relancer de daemon docker pour prendre en compte les modifications: sudo systemctl restart docker

On commence par “tagger” l’image locale pour la registry privée. Dans l’exemple le serveur qui heberge la registry se nomme “registry”

$ docker tag  <user>/debian:latest registry.domain.tld:<port/<user>/debian:latest
$ docker tag levasbr1/debian:latest registry.univ-rouen.fr:5000/levasbr1/debian:latest
$ docker images
REPOSITORY                     TAG                 IMAGE ID            CREATED             SIZE
registry.univ-rouen.fr/levasbr1/debian   latest              55f2b1db8911        6 hours ago         120MB

Pousser l’image dans la registry :

$ docker push registry.univ-rouen.fr:5000/levasbr1/debian:latest
The push refers to repository [registry.univ-rouen.fr:5000/levasbr1/debian]
6737a6f8cc10: Pushed 
e1df5dc88d2c: Pushed 
latest: digest: sha256:56bbd19006e1d1166d56715e01771cf22fb2ca67335009b79ead16767fb63e98 size: 741

Pour créer une registry, vous pouvez tout simplement utiliser un conteneur Docker:
$ docker run -d -p 5000:5000 --restart=always --name registry registry:2

Pour récuperer l’image depuis la registry privée:

on commence par supprimer l’image locale:

$ docker rmi registry.univ-rouen.fr:5000/levasbr1/debian:latest

ensuite on récupère l’image sur la registry privée:

$ docker pull registry.univ-rouen.fr:5000/levasbr1/debian:latest

La démarche est identique pour pousser une images sur le DockerHub avec les commandes docker login -u identifiant-docker-hub et docker push nom_image (il faut disposer d’un compte sur docker-hub)

Exécuter une commande dans un conteneur démarré

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 démarré pour visualiser ou interagir avec le procesus racine

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://<ip-server-docker>.univ-rouen.fr:8000 pour générer des logs).

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 § les links 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/"

L’option --sig-proxy=false permet de se detacher du conteneur avec la sequence CTRL-c sans killer le processsus racine.

Supprimer un conteneur (il doit être arrêté…)
$ 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

$ docker rm test_attach

Supprimer plusieurs conteneurs en utilisant les CID abrégés

$ docker rm 331 c7d
Copier des fichiers depuis ou à destination d’un conteneur

Dans cet exemple, on récupere 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

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 avec un navigateur avec l’URL : http://<ip-server-docker>.univ-rouen.fr:8001

Afficher des informations sur les conteneurs exécutés par Docker (équivalent à un top sous Linux)
$ docker stats
CONTAINER           CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
776035a48a44        0.00%               1.766MiB / 1.958GiB   0.09%               578B / 0B           0B / 0B             2
Afficher les événements qui se produisent sur le bus du moteur Docker (équivalent à un tailf sur un fichier de log)

Pour tester cette commande, il faut générer quelques actions sur un conteneur et visualiser en même temps les évènements sur un autre terminal :

$ docker run -d --name test_events nginx
$ docker pause test_events
$ docker unpause test_events
$ docker stop test_events

Observer les événements depuis un autre terminal :

$ docker events
2017-12-18T16:54:15.866668426+01:00 container pause 331e1e904e1e21e963ac729df121cabe782a5a937ed157aec284929c2d6ca9e1 (image=nginx, maintainer=NGINX )
2017-12-18T16:54:24.678680900+01:00 container unpause 331e1e904e1e21e963ac729df121cabe782a5a937ed157aec284929c2d6ca9e1 (image=nginx, maintainer=NGINX )
2017-12-18T16:54:29.942202432+01:00 container kill 331e1e904e1e21e963ac729df121cabe782a5a937ed157aec284929c2d6ca9e1 (image=nginx, maintainer=NGINX Docker)

Les DockerFiles

Un dockerfile est un fichier texte (donc versionable) de description qui permet de générer une image. Il est basé sur une image standard auquel on ajoute les éléments propres à l’application que l’on veut déployer.

Instructions de bases :

Nous allons créer les 2 fichiers suivants ( dans votre homedir ):

$ cat dockerfile
#
# Simple example of a Dockerfile
# Build Debian / Nginx with demo file
#
FROM debian:latest
LABEL maintainer="prenom.nom@domain.tld"

RUN apt-get update
RUN apt-get install -y nginx

ADD demo.html /var/www/html/demo.html
CMD ["nginx", "-g", "daemon off;"]
EXPOSE 80
STOPSIGNAL SIGTERM

CMD ["nginx", "-g", "daemon off;"]
# Then run the following command for build new image
# $ docker build -t identifiant_docker_hub/nginx:latest .
# $ docker run -d -p 8002:80 identifiant_docker_hub/nginx:latest

et

$ cat demo.html
<html>
  <header><title>Hello world</title></header>
  <body>
    Hello world
  </body>
</html>

Générer une image à partir d’un dockerfile (commande : docker build)

Attention au “.” en fin de première ligne qui représente le répertoire courant

$ docker build -t levasbr1/nginx:latest .
Sending build context to Docker daemon  6.144kB
Step 1/9 : FROM debian:latest
 ---> da653cee0545
Step 2/9 : MAINTAINER "prenom.nom@domaine.tld"
 ---> Running in 5680fbaa6dce
 ---> 7139f3b99829
Removing intermediate container 5680fbaa6dce
Step 3/9 : RUN apt-get update
 ---> Running in c849b83991b7
---------8<-------------

On peut ensuite lancer un conteneur depuis cette nouvelle image

$ docker run -d -p 8002:80 --name test_build  levasbr1/nginx:latest

On teste avec un navigateur avec l’URL : http://<ip-server-docker>:8002/demo.html

les volumes Docker

Par essence, les conteneurs Docker sont éphémères. Ils doivent être conçus pour pouvoir être supprimés sans perdre les données. Pour assurer cette persistance, on utilise les volumes Docker.

Initialiser un volume (option : -v)

L’utilisation la plus simple est d’initialiser un volume à la création du conteneur :

$ docker run -d -v /var/log --name test_volume debian:latest  /bin/sleep infinity

Ici l’option -v /var/log permet d’initialiser un volume indépendant du conteneur qui contiendra les logs du conteneur :

$ docker inspect test_volume
----------------8<--------------------
"Mounts": [
    {
        "Type": "volume",
        "Name": "e566163a0978a34dc1357cb4df7c9d86ddbb308de1c0ba26393e564223ec8c09",
        "Source": "/var/lib/docker/volumes/e566163a0978a34dc1357cb4df7c9d86ddbb308de1c0ba26393e564223ec8c09/_data",
        "Destination": "/var/log",
        "Driver": "local",
        "Mode": "",
        "RW": true,
        "Propagation": ""
----------------8<-------------

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”)

$ sudo ls /var/lib/docker/volumes/e566163a0978a34dc1357cb4df7c9d86ddbb308de1c0ba26393e564223ec8c09/_data
apt  btmp  faillog  lastlog  wtmp

On constate que les fichiers de logs du conteneur sont accessibles dans ce volume

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 systeme hôte ( exemple : pour qu’un développeur PHP puisse éditer directement les fichiers depuis la machine hôte).

$ mkdir projet-web
$ docker run -d --name test_volume2  -p 8004:80 -v ~/projet-web:/var/www/html eboraas/apache-php

Ici nous disposons d’un volume monté sur l’arborescence /var/www/html du conteneur et correspondant au dossier /home/user (~) 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://<ip-server-docker>:8004/phpinfo.php que le fichier est bien accédé par le conteneur

$ docker inspect test_volume2
---------------8<--------------------
"Mounts": [
    {
        "Type": "bind",
        "Source": "/home/formation/projet-web",
        "Destination": "/var/www/html",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
--------------8<--------------------

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

Dans cette méthode, le conteneur dispose d’un accès direct au filesysteme de l’hôte. Elle est à proscrire en production pour des raisons de sécurité.

Partager un volume de données entre plusieurs conteneurs (option : --volumes-from)

On démarre un conteneur qui “exporte” son arborescence /usr/share/nginx/html dans un volume

$ docker run -d -p 8005:80 -v /usr/share/nginx/html  --name test_volume3 nginx

On démarre ensuite un conteneur en mode intéractif qui partage le même volume de données

$ docker run -it --volumes-from test_volume3 debian:latest  /bin/bash

Depuis ce conteneur on crée un fichier de test (exit pour quitter le conteneur)

root@e232d8d9d527:/# echo bonjour > /usr/share/nginx/html/test.html

On teste depuis un navigateur avec l’URL http://docker-x.univ-rouen.fr:8005/test.html que le fichier est bien accessible depuis le conteneur test_volume3

Docker ne supprime pas les volumes à la suppression des conteneurs. Pour éviter l’accumulation de volumes orphelins, il faut utiliser la commande rm avec l’option -v docker rm -v nom_conteneur ou CID ou faire une purge régulière avec la commande docker volume rm $(docker volume ls -qf dangling=true)

Les links 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)

Par défaut, tous les conteneurs peuvent communiquer entre eux sur le réseau bridge. Pour plus de sécurité, il est possible de désactiver ce comportement (option DOCKER_OPTS="-icc=false" de la configuration de daemon).

$ 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 uilisent une adresse IP fournie par le daemon Docker (réseau privé non routable: 172.17.0.0/16, gateway: 172.17.0.1).

Sous linux, Docker utilise netfilter (iptables) pour implémenter les fonctions de NAT qui permettent aux conteneurs de communiquer avec l’extérieur en utilisant l’adresse IP de l’hôte et les fonctions de DNAT pour joindre les conteneurs depuis l’extérieur en utilisant un port particulier.

$ 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

On démarre un conteneur avec une database MySQL

$ docker run --name mysql -e MYSQL_ROOT_PASSWORD=bonjour -d mysql:5.7

On démarre ensuite un conteneur avec l’interface PHPmyadmin, qui permet d’administrer une base de données MySQL. L’option --link permet de renseigner le fichier host du conteneur phpmyadmin pour qu’il puisse se connecter à la base de données

$ docker run --name phpmyadmin  -d --link mysql:db -p 8080:80 phpmyadmin/phpmyadmin

On consulte le fichier hosts du conteneur phpmyadmin:

$ docker exec phpmyadmin cat /etc/hosts
# cat /etc/hosts
127.0.0.1	localhost
172.17.0.3	db d72489c44026 mysql
172.17.0.4	7d5ed10b026e

Ici on constate que les alias db et mysql pointent vers l’ip 172.17.0.3 du serveur MySQL

$ docker inspect mysql | grep IP
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.3",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
                    "IPAMConfig": null,
                    "IPAddress": "172.17.0.3",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,

La commande inspect permet de confirmer l’adresse IP du conteneur MySQL. On teste depuis un navigateur avec l’URL : http://<ip-server-docker>:8080 l’utilisation de phpmyadmin pour administrer la base

L’option -e (dans l’exemple ci-dessus -e MYSQL_ROOT_PASSWORD=bonjour) permet de passer des variables d’environnement au conteneur (ici on définit le password root de la base de données).

Docker-Compose

Docker-compose est un outil officiel Docker qui permet de 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

Installer

$ sudo pip install docker-compose
$ docker-compose -v
docker-compose version 1.22.0, build f46880f

Tester

Pour tester docker-compose, vous pouvez créer un fichier docker-compose.yml dans votre répertoire personnel

version: '2'

services:
   db:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: bonjour
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress

   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
volumes:
    db_data:

Dans l’exemple proposé, docker-compose va lancer 2 conteneurs automatiquement (mysql et wordpress).

Pour lancer les conteneurs, on utilise la commande docker-compose up ou docker-compose up -d pour lancer en arrière plan.
La commande doit être executée depuis le répertoire contenant le fichier yaml

$ docker-compose up
Creating network "levasbr1_default" with the default driver
Creating volume "levasbr1_db_data" with default driver
Pulling db (mysql:5.7)...
5.7: Pulling from library/mysql
Digest: sha256:1f95a2ba07ea2ee2800ec8ce3b5370ed4754b0a71d9d11c0c35c934e9708dcf1
Status: Downloaded newer image for mysql:5.7
Pulling wordpress (wordpress:latest)...
latest: Pulling from library/wordpress
e7bb522d92ff: Already exists
75651f247827: Pull complete
93b186f8edb2: Pull complete
77c007e2f556: Pull complete
bf4da9c43c0b: Pull complete
11843d906ebb: Pull complete
e03cc73ddbff: Pull complete

On teste le bon fonctionnement depuis un navigateur avec l’URL : http://<ip-server-docker>:8000

On utilise la sequence CTRL-c permet de stopper les conteneurs ou la commande docker-compose down si le docker-compose est lancé avec l’option -d

Exercice

Utiliser une interface web d’administration Docker : Portainer

Quelques snippets: