Introduction à Docker

 

Cet article est extrait de la présentation que j'ai fait pour l'association code2be le 24 Novembre à la Loco Numérique. C'est surtout un rappel des commandes de base vues ensemble et une base de départ pour aller plus loin avec Docker.

Premiers pas

Installation sous Linux

Il est possible d'installer Docker de différentes façons, suivant la distribution, la version installée peut-être plus ou moins à jour (version actuelle 1.9).

Je travaille sous Debian et j'ai choisi de faire l'installation en récupérant le script fourni par Docker :

# wget -O dockerinstall.sh https://get.docker.com
# sh ./dockerinstall.sh

L'installation est alors opérationnelle.

Il est vivement conseillé de ne pas utiliser Docker en root, il faut donc ajouter votre utilisateur au groupe docker.

 

Docker Hello World !

Un petit classique du genre

$ docker run hello-world

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b901d36b6f2f: Pull complete
0a6ba66e537a: Pull complete
Digest: sha256:8be990ef2aeb16dbcb9271ddfe2610fa6658d13f6dfb8bc72074cc1ca36966a7
Status: Downloaded newer image for hello-world:latest

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.
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 Hub account:
https://hub.docker.com

For more examples and ideas, visit:
https://docs.docker.com/userguide/

 

Analyse de ce qui c'est passé

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
64743392997d        hello-world         "/hello"            50 seconds ago      Exited (0) 48 seconds ago                       romantic_curie

 

La commande "docker ps" permet de lister les conteneurs qui fonctionnent L

La commande "docker ps -a" de lister tout les conteneurs même ceux éteints.

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
hello-world         latest              0a6ba66e537a        5 weeks ago         960 B

Cette commande permet de lister les images.

 

Docker Hub

Le docker hub est un dépôt d'images pour déployer des conteneurs, il est possible de l'explorer en mode Web :

Docker hub

 

Ou en ligne de commande :

$ docker search

 

Exemple :

$ docker search wordpress
NAME                                        DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
wordpress                                   The WordPress rich content management syst...   609       [OK]
tlongren/docker-wordpress-nginx-ssh         Latest WordPress with Nginx, PHP-APC, PHP-...   18                   [OK]
appcontainers/wordpress                     CentOS 6.7 based Customizible WordPress Co...   15                   [OK]
centurylink/wordpress                       WordPress image with MySQL removed.             9                    [OK]
kaihofstetter/wordpress-cli                 Installs a configured and ready to use Wor...   5                    [OK]
jbfink/docker-wordpress                     Same as jbfink/wordpress, just a trusted b...   5                    [OK]
edenservers/wordpress                       Ready to use WordPress docker image, mysql...   3                    [OK]
apsl/wordpress                              WordPress on LAMP managed with circus. Wor...   1                    [OK]
groupeforum/wordpress                       A WordPress Docker image.                       1                    [OK]
linuxconfig/wordpress                       WordPress blogging tool and a content mana...   1                    [OK]
cburki/wordpress                            WordPress container                             1                    [OK]
scjalliance/wordpress                       WordPress with GD and FreeType                  1                    [OK]
jprjr/wordpress                                                                             0                    [OK]
microwebapps/wordpress-atomicapp            WordPress Atomic App based on Nulecule, of...   0                    [OK]
indiehosters/wordpress                      WordPress application for IndieHosters net...   0                    [OK]
ppschweiz/wordpress                                                                         0                    [OK]
patrickaroo/wordpress                       WordPress with caching and email services.      0                    [OK]
amontaigu/wordpress                         A wordpress image with php-fpm + nginx.         0                    [OK]
microwebapps/wordpress                                                                      0                    [OK]
m0ikz/wordpress                                                                             0                    [OK]
projectatomic/wordpress-centos7-atomicapp   WordPress Atomic App                            0                    [OK]
bitnami/wordpress                           Bitnami Docker Image for WordPress              0                    [OK]
akroh/wordpress                             WordPress 4.1 installed in Apache 2.2 on C...   0                    [OK]
saphi/wordpress                             Build Docker image wordpress                    0                    [OK]
cloyne/wordpress                                                                            0                    [OK]

 

Un peu plus loin dans la ligne de commande

Lancement interactif

$ docker run -i -t ubuntu
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
0a85502c06c9: Pull complete
0998bf8fb9e9: Pull complete
a6785352b25c: Pull complete
e9ae3c220b23: Pull complete
Digest: sha256:f91f9bab1fe6d0db0bfecc751d127a29d36e85483b1c68e69a246cf1df9b4251
Status: Downloaded newer image for ubuntu:latest
root@b9a8375de85f:/# cat /etc/issue
Ubuntu 14.04.3 LTS \n \l

On viens ici de démarrer un conteneur de type ubuntu en mode interactif (-i) et de se connecter en terminal avec un tty dessus (-t)

 

On peut vérifier qu'effectivement on est plus dans le terminal du serveur :

root@b9a8375de85f:/# hostname
b9a8375de85f

 

Pour sortir du conteneur sans l'arrêter :
CTRL P / CTRL Q

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS               NAMES
b9a8375de85f        ubuntu              "/bin/bash"         About a minute ago   Up About a minute                       desperate_mietner

 

Se reconnecter dans un conteneur

$ docker attach desperate_mietner

 

Faire des modifications dans le conteneur :

# rm -rf /home

 

Sortir (CTRL-P CTRL-Q)

 

Différence entre l'image et l'état du conteneur actuel :

$ docker diff b9a8375de85f
D /home

 

Lancer un second conteneur

$ docker run --name ubuntu01 --hostname ub01 -ti ubuntu
# hostname -f
ub01
# ls -lda /home
drwxr-xr-x 2 root root 4096 Apr 10  2014 /home

 

Sortir

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
15b95422a798        ubuntu              "/bin/bash"         54 seconds ago      Up 52 seconds                           ubuntu01
b9a8375de85f        ubuntu              "/bin/bash"         11 minutes ago      Up 11 minutes                           desperate_mietner

 

On remarque sur cette suite d'actions que si on fait une action sur le système de fichier d'un conteneur, ici la suppression de /home, cette modification n'est pas répliquée sur les autres conteurs qui seront générés à partir de la même image, ici ubuntu.

On remarque aussi que nous avons utilisé deux nouveaux paramètres sur la ligne de commande :
--name : permet de donner un nom à notre conteneur, sinon sont nom est généré aléatoirement comme desperate_mietner plus haut
--hostname : permet de changer le hostname du conteneur

 

Gérer les conteneurs et les images

Stopper un conteneur

$ docker stop ubuntu01
ubuntu01

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
b9a8375de85f        ubuntu              "/bin/bash"         12 minutes ago      Up 12 minutes                           desperate_mietner

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS                      PORTS               NAMES
15b95422a798        ubuntu              "/bin/bash"         About a minute ago   Exited (0) 15 seconds ago                       ubuntu01
b9a8375de85f        ubuntu              "/bin/bash"         12 minutes ago       Up 12 minutes                                   desperate_mietner
3bd75c71513c        hello-world         "/hello"            16 minutes ago       Exited (0) 16 minutes ago                       happy_lamarr
64743392997d        hello-world         "/hello"            17 minutes ago       Exited (0) 17 minutes ago                       romantic_curie

 

Supprimer un conteneur

On arrête le conteneur et après on le suprime ensuite

$ docker stop ubuntu01
ubuntu01

$ docker rm ubuntu01
ubuntu01

 

Supprimer une image

$ docker rmi hello-world
Error response from daemon: conflict: unable to remove repository reference "hello-world" (must force) - container 3bd75c71513c is using its referenced image 0a6ba66e537a
Error: failed to remove images: [hello-world]

 

On ne peut pas supprimer une image si des conteneurs instanciés avec celle-ci existent encore, même si ils sont éteints. Il faut donc supprimer les conteneurs instanciés à partir de l'image que nous voulons effacer.

$ docker rm 3bd75c71513c 64743392997d
3bd75c71513c
64743392997d

$ docker rmi hello-world
Untagged: hello-world:latest
Deleted: 0a6ba66e537a53a5ea94f7c6a99c534c6adb12e3ed09326d4bf3b38f7c3ba4e7
Deleted: b901d36b6f2fd759c362819c595301ca981622654e7ea8a1aac893fbd2740b4c

 

Modifications dans une image

Nous allons voir maintenant comment faire des modifications dans un conteneur et créer une image que nous redéployer à partir de celui-ci

$ docker run --name ub01 --hostname ub01 -ti ubuntu
# apt-get install apache2

 

Sortir

$ docker commit ub01 guidtz/ubuntu_apache2
fea7360a78d06cabf8295f3d23b67aabc24d7c31658d2477c1b871375fd5cc12

$ docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
guidtz/ubuntu_apache2   latest              fea7360a78d0        40 seconds ago      202.3 MB
ubuntu                  latest              e9ae3c220b23        2 weeks ago         187.9 MB

$ docker run --name ap1 --hostname srv01 -ti guidtz/ubuntu_apache2
root@srv01:/#

root@srv01:/# service apache2 start
* Starting web server apache2                                                                                                                    AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
*
root@srv01:/# netstat -tpnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp6       0      0 :::80                   :::*                    LISTEN      46/apache2

Voilà c'est pas plus compliqué que ça mais on peut faire d'une autre façon.

 

DockerFile

Le Dockerfile est en quelque sorte la recette de création d'une image Docker.

Voici un Dockerfile pour faire la même chose qu'au paragraphe précédent :

$ cat Dockerfile

# Base image ubuntu
FROM ubuntu:15.10

MAINTAINER Guillaume Chéramy <adresse@email>

# Install Apache2
RUN apt-get update && \
apt-get install -y apache2 && \
apt-get clean

# Set the log directory path
ENV APACHE_LOG_DIR /var/log/apache2

# Expose port 80
EXPOSE 80

# Launch Apache2
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

 

On se base sur une image ybuntu version 15.10 sur laquelle on installe apache2, on définit le répertoire de logs, on expose le port 80 et on précise qu'au démarrage du conteneur le script qui sera exécuté.

Pour construire notre image, il faut être dans le répertoire du Dockerfile :

$ docker build -t guidtz/apache2 .

$ docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
guidtz/apache2          latest              aeb4f100004a        39 seconds ago      251.1 MB


$ docker run --name ap01 -d --hostname ap01 guidtz/apache2
48a2f93c83fb707252f5a19fabd2f9155fe44be0291b7a3edcc157203d13913e

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
48a2f93c83fb        guidtz/apache2      "/usr/sbin/apache2ctl"   48 seconds ago      Up 47 seconds       80/tcp              ap01

 

Notre conteneur à cette fois-ci été démarré en mode daemon (-d). Pour savoir ce qu'il se passe dessus on va regarder les logs :

$ docker logs ap01
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message

 

On a une information importante c'est l'adresse IP qu'à récupéré notre conteneur. On peut aussi l'obtenir avec la commande suivante :

$ docker inspect ap01

 

Ou plus précisement :

$ docker inspect ap01 |grep IPAddress
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.2",
"IPAddress": "172.17.0.2",

 

On est sur une IP locale il faut donc utiliser un outils en local pour voir si notre serveur Apache réponds :

$ links http://172.17.0.2

On obtiens la page d'accueil d'Apache.

 

Les bases sur le réseau et les ports

Donc on a un service apache qui tourne sur notre container sur une ip privée.
Cette IP est obtenue via DHCP sur un réseau privé bridgé sur l'interface docker0 :

docker0   Link encap:Ethernet  HWaddr 02:42:00:f4:04:3f
inet adr:172.17.0.1  Bcast:0.0.0.0  Masque:255.255.0.0

 

Pour avoir un accès extérieur il va falloir binder le port du conteneur sur un port réseau de la machine

$ docker run --name ap01 -d --hostname ap01 -p 80:80 guidtz/apache2
e9fcb58aa7a4d37a8d6dcb1c76d5837db86b9ba69d03a02da14247df97721c22

 

On spécifie ici que l'on "bind" le port 80 de notre conteneur, sur le port 80 de notre serveur docker.

Si on a plusieurs IPs sur notre serveur on peut même préciser l'ip :

$ docker run --name ap01 -d --hostname ap01 -p IP:80:80 guidtz/apache2

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
e9fcb58aa7a4        guidtz/apache2      "/usr/sbin/apache2ctl"   16 seconds ago      Up 15 seconds       0.0.0.0:80->80/tcp   ap01

Pour vérifier que ça marche :

http://IP_SRV

La partie réseau mérite à elle seule une exploration assez complète qui viendra plus tard.

 

Monter un volume local

Comme nous l'avons vu chaque conteneur crée son propre système de fichier au dessus de celui de l'image. Il est cependant intéressant, voir essentiel de connecter des données du système de fichier (ou d'un autre conteneur) sur notre conteneur.
Cette pratique est même vivement conseillée pour séparer les données de la partie fonctionnelle du conteneur.

Pour faire un exemple simple nous allons monter un répertoire local contenant un site statique sur notre conteneur Apache.

$ docker run --name web01 -d --hostname ap01 -p 80:80 -v /home/sysadmin/html:/var/www/html guidtz/apache2
bffeef40e7669e76e2a12c0dd7a3946db0d1a3e888d94d6cb7415a96ca30f41d

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
bffeef40e766        guidtz/apache2      "/usr/sbin/apache2ctl"   17 seconds ago      Up 16 seconds       0.0.0.0:80->80/tcp   web01

$ docker inspect web01 |grep -A 5 Mounts
"Mounts": [
{
"Source": "/home/sysadmin/html",
"Destination": "/var/www/html",
"Mode": "",
"RW": true

 

Pour finir

Petite mise en pratique déployer deux conteneurs pour héberger un site WordPress.

Un premier conteneur pour mysql

$ docker run --name mysql01 -e MYSQL_ROOT_PASSWORD=secret -d mysql
Unable to find image 'mysql:latest' locally
latest: Pulling from library/mysql
2a1fefc8d587: Pull complete
f9519f46a2bf: Pull complete
35e21079caee: Pull complete
b08f3d26d732: Pull complete
ced60f6188d8: Pull complete
93148df18c31: Pull complete
8587beca5034: Pull complete
2593a05c664f: Pull complete
1483cfdf29e5: Pull complete
1366ffba5c3b: Pull complete
4e07f9c5fa5e: Pull complete
1c19ce71381b: Pull complete
d6a18d40a940: Pull complete
0f1a7d5b17f5: Pull complete
Digest: sha256:d5471349b65f83021ae382e59636408ba172e26a8a6c773fec0017a7a298b0f3
Status: Downloaded newer image for mysql:latest
4996ef00dcc9740d22277ae1027eaf2c09a6faf7272f8d41a910ca6c93482720


$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS               NAMES
4996ef00dcc9        mysql               "/entrypoint.sh mysql"   About a minute ago   Up About a minute   3306/tcp            mysql01

 

Un second pour Apache / PHP / WordPress

$ docker run --name wp01 --link mysql01:mysql -p 80:80 -d wordpress
417d6b47fda6c02b782407e68c0ed8626de3d2960b300f3b46b2ffb6f907f837


$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
417d6b47fda6        wordpress           "/entrypoint.sh apach"   42 seconds ago      Up 40 seconds       0.0.0.0:80->80/tcp   wp01
4996ef00dcc9        mysql               "/entrypoint.sh mysql"   4 minutes ago       Up 4 minutes        3306/tcp             mysql01

On verra plus tard qu'avec Docker Compose il est envisageable de composer des architectures avec plusieurs conteneurs, mais ce sera pour une prochaine fois.

 

Voilà pour cette première introduction à Docker. Je suis ouvert à tous vos commentaires ...