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 :
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 ...