Virt/Docker
Théorie
Docker est donc une plateforme de virtualisation par conteneur qui, contrairement à la virtualisation sur hyperviseur où de nouvelles machines complètes doivent être créée, va permettre de créer des conteneurs qui vont uniquement contenir une ou plusieurs applications. Empaquetées sous forme de conteneurs, ces applications pourront ainsi être facilement déployées sur tout hôte exécutant Docker, chaque conteneur restant parfaitement indépendant !
Le nom et le vocabulaire de cette technologie n’ont pas été choisis au hasard : tout comme les conteneurs en métal, toujours fabriqués selon les mêmes standards, peuvent être pris en charge par n’importe quel transporteur (et peu importe ce qu’il y a dedans), une application « Dockerisée » doit pouvoir être exécutée sur n’importe quel hôte faisant tourner Docker, peu importe son contenu !
La virtualisation par hyperviseur
Avec la virtualisation par hyperviseur de type 2 (VMware Workstation, VirtualBox…), chaque hôte invité virtualise un environnement complet, ce qui permet notamment de pouvoir exécuter un système virtualisé différent du système hôte (typiquement du Windows sur un GNU/Linux par exemple).
La virtualisation par conteneur
Dans ce cas, conteneur virtualisé et système hôte sont fortement liés et c’est pour cela que cette technologie, qui existe pourtant depuis longtemps (OpenVZ a déjà presque 10 ans !), était peu répandue jusqu’à maintenant. Certains appellent d’ailleurs ce type de virtualisation la paravirtualisation...
Principe d'utilisation
Docker simplifie l’utilisation d’outils existants, et c’est sa principale force. S’il est possible d’isoler des conteneurs : processus, interfaces réseau, points de montage, c’est grâce à l’utilisation des namespaces Linux. Un processus qui s’exécute dans un espace de nom correspondant à un conteneur ne sera visible que par ce conteneur et le système hôte exécutant ce conteneur. En comparant les deux précédents schémas, on remarque que la virtualisation par conteneur est plus rapide que la virtualisation par hyperviseur car les conteneurs ont directement accès aux ressources matérielles de l’hôte. La consommation de ces ressources pouvant être gérées par l’hôte en question grâce aux control groups (cgroups) du noyau Linux.
Tout ceci fait donc de Docker un chroot dopé aux stéroïdes.
À ses débuts Docker était une couche d’abstraction à LXC car il permettait de s’affranchir de la complexité de ce dernier en gérant pour nous un certain nombre d’opérations. Aujourd’hui Docker va beaucoup plus loin que LXC puisqu’il dispose de sa propre plate-forme : Libcontainer (bien que LXC soit toujours proposé sous forme de plugin).
Et ce qui démarque Docker des autres, c’est la rapidité de la mise en place des flux de travail : tout est fait pour réduire le temps entre le développement, les tests, le déploiement et l’utilisation de votre application en production. Et pour cela, on trouve les images.
Une image est le composant de base de Docker. C’est un instantané d’un système d’exploitation (par exemple Debian). Ces images sont disponibles sur un dépôt public, géré par Docker, appelé le registre.
Les images
L’exemple ci-dessus se compose de deux conteneurs, parfaitement isolés (d’où les couleurs différentes). On trouve à la base les composants nécessaires à Docker, et qui sont fournis par le noyau Linux de l’hôte. On trouve ensuite deux images : Debian et BusyBox.
Une image ne peut être modifiée directement, elle reste toujours en lecture seule.
Remarquez qu’un environnement d’exécution peut nécessiter l’empilement de plusieurs images (ici mariadb, puis apache), rendu possible par les fonctionnalités de copy-on-write des modules de stockages utilisés par Docker (AuFS, Btrfs).
Enfin, on trouve la couche supérieure, accessible en écriture, qui contiendra toutes les modifications apportées à l’application.
On fait donc de sacrées économies de consommation puisque plusieurs conteneurs utilisant la même image de base utiliseront le même système de fichiers !
Le Hub
Le Hub Docker est un GitHub pour les images Docker.
Pour conserver les modifications apportées à l'application, Il faut enregistrer celles-ci dans une nouvelle image qui sera uniquement composée des différences apportées. Cette image sera identifiée par Docker grâce à un numéro unique, et pourra être pousser (push) vers le dépôt public Docker.
Le Docker Hub est aux images ce que git est au code. Les commandes sont les mêmes.
Installation
Installation du paquet RPM
Docker est supporté sur CentOS 6 et 7, avec de légères différences durant l'installation. Il est vivement recommandé de partir d'un hôte ayant les dernières mises à jour: yum update
- CentOS 6: paquet présent dans le dépôt EPEL
yum install docker-io
- CentOS 7: paquet présent dans le dépôt EPEL
yum install docker
Exécuter Docker sans les droits root
La documentation nous indique que le daemon Docker tourne toujours avec l’utilisateur root, et qu’il se connecte à une socket Unix en lieu et place d’un socket TCP. L’utilisateur root est par défaut propriétaire de ce socket Unix, et ne peut donc être accessible qu’avec la commande sudo.
Pour éviter d’utiliser sudo à tort et à travers, nous allons pouvoir créer un groupe Unix nommé docker pour nous y ajouter. Ainsi, les propriétés du daemon docker seront modifiées pour que celui-ci soit accessible en lecture / écriture pour les membres du groupe docker. Certes le daemon doit toujours fonctionner en tant que root, mais si nous utilisons le client docker en tant que membre du groupe docker, alors nous n’aurons plus besoin d’utiliser sudo.
# On ajoute le groupe docker s'il n'existe pas déjà.
groupadd docker
# On ajoute l'utilisateur courant (nous) "${USER}" au groupe.
# On peut y mettre n'importe quel utilisateur
# Il faudra se reconnecter pour que les modifications prennent effet.
gpasswd -a ${USER} docker
# On redémarre le daemon.
service docker restart
C’est du bonus hein… Cela permet aussi de gérer la surface d’attaque de Docker, mais vous pouvez très bien sauter cette étape et continuer à utiliser Docker avec sudo.
Tests
docker version
Client version: 1.7.1 Client API version: 1.19 Go version (client): go1.4.2 Git commit (client): 786b29d/1.7.1 OS/Arch (client): linux/amd64 Server version: 1.7.1 Server API version: 1.19 Go version (server): go1.4.2 Git commit (server): 786b29d/1.7.1 OS/Arch (server): linux/amd64
docker info
Images: 7 Storage Driver: devicemapper Pool Name: docker-253:0-1183660-pool Pool Blocksize: 65.54 kB Backing Filesystem: extfs Data file: /dev/loop0 Metadata file: /dev/loop1 Data Space Used: 957.5 MB Data Space Total: 107.4 GB Data Space Available: 24.65 GB Metadata Space Used: 1.507 MB Metadata Space Total: 2.147 GB Metadata Space Available: 2.146 GB Udev Sync Supported: true Deferred Removal Enabled: false Data loop file: /var/lib/docker/devicemapper/devicemapper/data Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata Library Version: 1.02.95-RHEL6 (2015-09-08) Execution Driver: native-0.2 Logging Driver: json-file Kernel Version: 2.6.32-573.22.1.el6.x86_64 Operating System: <unknown> CPUs: 8 Total Memory: 7.685 GiB Name: webdev-test.b2pweb.com ID: TZXP:Z74H:7XCC:JMST:ZAKF:R5Y5:DSOR:JHGO:Z4GC:T3SZ:662F:3NR2
Les conteneurs
Pour récupérer une image, on va interroger le docker hub pour lister toutes les images du registre:
docker search centos
NAME DESCRIPTION STARS OFFICIAL AUTOMATED centos The official build of CentOS. 2147 [OK] jdeathe/centos-ssh CentOS-6 6.7 x86_64 / CentOS-7 7.2.1511 x8... 20 [OK] jdeathe/centos-ssh-apache-php CentOS-6 6.7 x86_64 / Apache / PHP / PHP M... 16 [OK] nimmis/java-centos This is docker images of CentOS 7 with dif... 8 [OK] torusware/speedus-centos Always updated official CentOS docker imag... 7 [OK] nickistre/centos-lamp LAMP on centos setup 3 [OK] centos/mariadb55-centos7 3 [OK] [...]
Un grand nombre de résultats sont proposés. On observe que le premier résultat indique une image CentOS officielle, ce qui nous assure qu’elle a été validée par l’équipe de Docker. Les images gérées par l’équipe de Docker se remarquent par le format de leur nom, qui est de la forme nom-image alors que les non-officielles sont plutôt de la forme utilisateur/nom-image.
On récupère l'image officielle CentOS 7 (latest)
docker pull centos
latest: Pulling from centos 47d44cb6f252: Pull complete 8aa7f4a1dd13: Pull complete e0f5867add13: Pull complete eeb3a076a0be: Pull complete Digest: sha256:1b9adf413b3ab95ce430c2039954bb0db0c8e2672c48182f2c5b3d30373d5b71 Status: Downloaded newer image for centos:latest
ainsi quer la version 6
docker pull centos:6
6: Pulling from centos 9868edceea88: Pull complete e0eab6e60b7e: Pull complete 61bf77ab8841: Pull complete 47d44cb6f252: Already exists Digest: sha256:57f0027b92985edbd0eb87818b73d5dd771dd5955e6b794d624c8a79a4bd4795 Status: Downloaded newer image for centos:6
On liste les images déjà téléchargées. La sortie de la commande suivante indique que nous avons récupéré les image portant l’ID 61bf77ab8841 et eeb3a076a0be dans le dépôt intitulé « centos ». Un ID permet d’identifier de manière unique la version d’une image. Ensuite, comme un ID n’est pas forcément explicite pour désigner une version, on lui affecte un ou plusieurs tags, ici « latest » et « 6 ». Plusieurs tags peuvent effectivement désigner la même image, mais chaque version d’image ne possède qu’un ID.
docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE centos 6 61bf77ab8841 2 weeks ago 228.9 MB centos latest eeb3a076a0be 2 weeks ago 196.7 MB
L'empilement peut être affiché avec l'option --all
docker images --all
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE centos 6 61bf77ab8841 2 weeks ago 228.9 MB <none> <none> e0eab6e60b7e 2 weeks ago 228.9 MB <none> <none> 9868edceea88 2 weeks ago 228.9 MB centos latest eeb3a076a0be 2 weeks ago 196.7 MB <none> <none> e0f5867add13 2 weeks ago 196.7 MB <none> <none> 8aa7f4a1dd13 2 weeks ago 196.7 MB <none> <none> 47d44cb6f252 7 months ago 0 B
Les images intermédiaires correspondent aux différentes commandes qui ont été utilisées pour générer l’image finale. Ces commandes peuvent être affichées avec la commande history.
docker history centos:6
IMAGE CREATED CREATED BY SIZE COMMENT 61bf77ab8841 2 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B e0eab6e60b7e 2 weeks ago /bin/sh -c #(nop) LABEL name=CentOS Base Imag 0 B 9868edceea88 2 weeks ago /bin/sh -c #(nop) ADD file:ae8f506cbd1f016c67 228.9 MB 47d44cb6f252 7 months ago /bin/sh -c #(nop) MAINTAINER The CentOS Proje 0 B
Premiers lancements
On lance notre premier conteneur. Évidemment l'intérêt est purement éducatif, car celui-ci ne fera qu'afficher Hello World! et se terminera.
docker run centos:latest /bin/echo 'Hello World!'
Hello World!
On lance maintenant l'interpréteur Bash à l'intérieur de notre conteneur. L'option i laisse l'entrée standard ouverte et l'option t alloue un pseudo-terminal à la commande.
docker run -t -i centos:6 /bin/bash
[root@a3bcca2ae195 /]# hostname a3bcca2ae195 [root@a3bcca2ae195 /]# exit exit
On liste nos conteneurs (l'option -a permet d'afficher tous les conteneurs car seul les actifs sont affiché par défaut).
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a3bcca2ae195 centos:6 "/bin/bash" 35 seconds ago Exited (0) 12 seconds ago angry_goodall f947fb083eaa centos:6 "/bin/echo 'Hello Wo About a minute ago Exited (0) About a minute ago ecstatic_kirch b6710cdfbcb1 centos:latest "/bin/echo 'Hello Wo About a minute ago Exited (0) About a minute ago lonely_feynman
On peut visualiser tous les détails d'une image
docker inspect centos:6
[
{
"Id": "61bf77ab884102c059826f37af32b766aec3a0145af35dbc296bc33754e74757",
"Parent": "e0eab6e60b7ea4336660dfcbc7ce837621f191d7d6ea587cdf225eecd2001dc6",
"Comment": "",
"Created": "2016-04-01T21:29:01.147650406Z",
"Container": "01a4934c6001e17bfa875876616d915ee5c7a2fdd094f63c4ff84c70a9da3c43",
"ContainerConfig": {
"Hostname": "4d7e41fcce2c",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"PortSpecs": null,
"ExposedPorts": null,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": null,
"Cmd": [
"/bin/sh",
"-c",
"#(nop) CMD [\"/bin/bash\"]"
],
"Image": "e0eab6e60b7ea4336660dfcbc7ce837621f191d7d6ea587cdf225eecd2001dc6",
"Volumes": null,
"VolumeDriver": "",
"WorkingDir": "",
"Entrypoint": null,
"NetworkDisabled": false,
"MacAddress": "",
"OnBuild": null,
"Labels": {
"build-date": "2016-03-31",
"license": "GPLv2",
"name": "CentOS Base Image",
"vendor": "CentOS"
}
},
"DockerVersion": "1.9.1",
"Author": "The CentOS Project \u003ccloud-ops@centos.org\u003e",
"Config": {
"Hostname": "4d7e41fcce2c",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"PortSpecs": null,
"ExposedPorts": null,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": null,
"Cmd": [
"/bin/bash"
],
"Image": "e0eab6e60b7ea4336660dfcbc7ce837621f191d7d6ea587cdf225eecd2001dc6",
"Volumes": null,
"VolumeDriver": "",
"WorkingDir": "",
"Entrypoint": null,
"NetworkDisabled": false,
"MacAddress": "",
"OnBuild": null,
"Labels": {
"build-date": "2016-03-31",
"license": "GPLv2",
"name": "CentOS Base Image",
"vendor": "CentOS"
}
},
"Architecture": "amd64",
"Os": "linux",
"Size": 0,
"VirtualSize": 228912764
}
]
Démonisation
On va daemoniser notre conteneur avec l'option d. Cette fois la sortie est l'ID du conteneur.
docker run -t -i -d centos:6 /bin/bash
713ed98359e25eebd6316831d192c9094d5ec7b5bb8f1907ad444db8eabbb298
Afin de se simplifier la vie, on va obtenir son petit nom
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 713ed98359e2 centos:6 "/bin/bash" 2 minutes ago Up 2 minutes gloomy_jones
- Pour visualiser la sortie standard de notre conteneur daemonisé, on lance la commande
- avec l'ID
docker logs 713ed98359e25eebd6316831d192c9094d5ec7b5bb8f1907ad444db8eabbb298
- avec son nom
docker logs gloomy_jones
- avec l'ID
- On peut s'attacher à ce conteneur
docker attach gloomy_jones
- Le mettre en pause
docker pause gloomy_jones
- Le sortir de sa léthargie imposée
docker unpause gloomy_jones
- L'arrêter
docker stop gloomy_jones
- Le renommer
docker rename gloomy_jones centos6
- Le supprimer
docker rm centos6
