A l’ère du cloud, du big data et de la virtualisation, une technologie sort son epingle du jeu et prend de plus en plus d’ampleur dans le monde de l’opensource.

Jusqu’à aujourd’hui, nous trouvions des applications prêtes à l’emploi sous forme d’images (ISO, VMDK, OVF…) embarquant toute la couche d’applications et le système d’exploitation compatible grâce à plusieurs technologies telles que VMWARE, CITRIX XEN, QEMU ou encore VirtualBox.

Docker est une petite révolution dans le monde de la virtualisation qui change aujourd’hui les règles.
C’est cette nouvelle technologie que je vais vous présenter dans cet article.

 

Nous y voilà ! Passons maintenant à la pratique !! 😉

Cette partie sera décomposée en deux étapes :

  1. Construction des images nécessaires qui vont nous permettre de dockeriser notre application Symfony
  2. Construction, isolation de notre application dans des containers à partir des images créées au préalable

Nous intégrerons également dans cette partie l’intégration de docker-registry et l’utilisation de docker-compose.

Mais qu’est-ce que docker-registry ?

docker-registry est le serveur qui va nous permettre de stocker nos images et de les versionner, un peu comme le fait GIT et SVN pour le code source.
Nous le verrons par la suite mais une image docker est un fichier à la base.

Pourquoi docker registry ?

Parce que c’est gratuit ! Il existe l’équivalent payant si vous voulez vous abstraire de l’administration du docker registry qui se nomme dockerhub similaire à github.
Ainsi vous pourrez récupérer vos containers de n’importe où via une simple commande « docker pull »

Ce que vous devez retenir :

  • docker-registry est le serveur qui stocke nos images
  • Une image est à la base un fichier nommé  « dockerfile »

Installation de Docker

A l’heure où j’écris cet article, Docker  est disponible en version 1.9.

La notice d’installation sur d’autres OS que Debian est décrite en suivant ce lien : https://docs.docker.com/engine/installation/[SP5]

Pour installer Docker 1.9 sur un système Debian 8 (Jessie)

Mettre à jour les informations des packages et veiller à ceux que APT puisse utiliser le HTTPS
apt-get update
apt-get install apt-transport-https ca-certificates
Ajoutez la clé GPG, à noter sur la “keyserver” est susceptible de changer. Vous pouvez alors la trouver dans le lien ci-dessus sur la documentation officielle de docker.
apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
Ajoutez le repo docker
echo deb https://apt.dockerproject.org/repo debian-jessie main > /etc/apt/sources.list.d/docker.list
Mettre à jour l’index des packages APT
apt-get update
Installer docker
apt-get install docker-engine
Démarrez docker
service docker start

docker est désormais maintenant installé sur notre machine.

Utilisation basique de Docker

Voici quelques commandes basiques qui vont permettront d’utiliser docker de façon simple

Notez que le MAN de docker est assez complet, pour le lire il vous suffit de lancer « man docker » sur votre machine.

PULL : Récupérer une image docker en local

docker pull debian

Cette commande vous permet de récupérer une image docker à partir d’un repository public. Cette image sera ensuite stockée en local sur votre machine.

Pour afficher la liste des images sur votre machine, vous pouvez tapez « docker images »
Pour chercher une image vous pouvez utiliser la commande « docker search »

RUN : Lancer un container

docker run -i -t debian /bin/bash

Décomposons un peu cette commande :

  • docker : c’est le nom de notre binaire, toute commande « docker » commencera par docker
  • run : c’est la commande qui permet de lancer un container
  • -i : garde STDIN ouvert
  • -t : alloue un terminal au container – pseudo TTY
  • debian : c’est le nom de notre image Debian
  • /bin/bash : création d’un bash shell interactif

Au lancement de cette commande, Docker peut maintenant faire deux choses :

  • Il récupère l’image debian depuis le repository officiel si l’image n’existe pas sur votre machine et il lance le container debian
  • Il lance directement le container debian à partir de l’image debian si l’image existe déjà sur votre machine en quelques secondes

Lancez la commande, vous devriez avoir quelque chose comme cela :

docker-run-step1

Félicitations, vous venez de lancer votre premier container docker.

Regardez le prompt, le terme « 50b39bd788d5 » situé après root@ est l’ID de votre container où « vm-docker » est le nom de votre serveur hôte.

Vous pouvez maintenant lancer toutes les commandes que vous voulez dans le container, ces commandes n’affecteront pas votre hôte. Un container ne contient que le strict minimum, il est alors possible que vous ne trouviez pas votre éditeur de texte favori vi par exemple.

Quittez maintenant votre container avec la commande « CTRL+D »

docker-run-step2

« Je viens de quitter mon container, est-ce qu’il tourne encore ? »

La réponse est non. Lorsque vous quittez un container lancé de cette façon, il s’arrête automatiquement.

« docker ps » est la commande qui va vous permettre de voir l’état de vos containers.

Lancez la commande docker ps

docker-ps-step1

Comme vous le voyez, nous n’avons pas de container en cours d’exécution.

Il faut aussi savoir que toutes modifications faites dans votre container seront effacées à l’arrêt du container.

« Est-il possible de lancer un container en processus de fond ? »

La réponse est oui, il suffit pour cela d’ajouter l’option « -d » pour daemon

Lancez de nouveau un container en daemon

docker-run-step3

Vérifiez que le container tourne avec la commande « docker ps »

docker-ps-step2

Vous avez remarqué à cette étape que le container est lancé, mais que vous n’êtes pas connecté dans le container mais toujours sur votre machine hôte. Le prompt de votre machine hôte vous donne cet indication « root@vm-docker », « root » est l’utilisateur avec lequel vous êtes connecté et « vm-docker » est le nom de votre machine hôte.

Vous remarquez aussi qu’un nom par défaut (ecstatic_kare) lui est donné dans la colonne NAMES

Lancez de nouveau le container en daemon avec le nom debian

docker-run-step4

Vérifiez que le container s’est lancé avec le nom « debian »

docker-ps-step3

Nous venons de lancer deux containers Debian en quelques secondes, vous commencez à comprendre la puissance de Docker ?

Vous pouvez maintenant arrêter ou redémarrer le container debian si besoin :

docker stop debian
docker restart debian

EXEC : Se connecter dans un container

C’est cette commande qui va vous permettre de vous connecter dans votre container ou bien d’exécuter des commandes à l’intérieur d’un container en cours d’exécution.

Connectez-vous à votre container :

docker exec –it debian /bin/bash
  • debian : c’est le nom de notre container
  • /bin/bash : c’est le shell que vous voulez utiliser

Attention, à l’arrêt du container même lancé en daemon, les modifications ne seront toujours pas enregistrées. Nous verrons par la suite la notion de VOLUME qui vous permettra de rendre les données persistantes au sein d’un container.

Créez un fichier dans un container en cours d’exécution :

docker exec debian touch /tmp/test

Nous avons créé le fichier « /tmp/test » dans le container debian

L’utilisation basique de container docker peut nous permettre de faire des tests divers si nous n’avons pas besoin de rendre persistant un environnement.[SP12]
De plus, on remarque avec la commande « docker ps » que le container debian n’a pas de port ouvert, le container est donc uniquement accessible depuis la machine hôte via son ID ou son IP interne.
Dans le cas d’une application web, il faudra alors faire en sorte que notre container écoute et ouvre le port HTTP au minimum.

A retenir :

  • Les données ne sont pas persistantes dans un container basique
  • De base, le container est uniquement accessible d’un point de vue réseau par l’hôte et les containers exécutés sur l’hôte
  • Vous pouvez lancer un container avec la commande « docker run »
  • Vous pouvez voir l’état de vos containers avec la commande « docker ps»
  • Vous pouvez vous connecter dans un container en cours d’exécution ou bien lancer des commandes directement dans le container avec la commande « docker exec »
  • Vous pouvez arrêter un container en cours d’exécution avec la commande « docker stop »

Utilisation avancée de Docker

Vous savez désormais comment lancer un container, comment lancer des commandes dans ce container et comment vous connecter dans un container en cours d’exécution.

Je vais maintenant vous montrer comment faire persister les données et vous présenter brièvement la gestion du réseau de Docker.

Volume
Un volume de données dans docker est un espace physique sur la machine hôte qui est dédié à un ou plusieurs containers.
Comme vous l’avez vu, les données dans un container docker ne sont pas persistantes et sont perdues à l’extinction du container.

Les volumes de données docker nous permettent de faire persister ces données, les volumes docker fonctionnent par partage et par point de montage, le but étant de partager un répertoire de votre machine locale et de le monter dans le container docker sur un point de montage.

Rien de mieux qu’un exemple pour comprendre.
Lancez un container debian en daemon :

docker run –it –d debian /bin/bash

Créez le fichier “/tmp/test.txt” dans votre container :

docker exec debian touch /tmp/test.txt

Eteignez le container, relancez-le :

docker stop debian
docker rm debian
docker run -d -i -t --name debian debian /bin/bash

Listez le répertoire /tmp/ de votre container debian.
Le fichier a disparu !

ls-1

Eteignez à nouveau votre container, nous allons relancer le container en mappant notre répertoire /tmp du serveur hôte avec celui du container

docker stop debian
docker rm debian
docker run –d –it -–name debian –v /tmp:/tmp debian /bin/bash
  • -v : c’est l’option qui va nous permettre de monter un répertoire local dans le container
  • Le premier répertoire avant les « : » est le répertoire physique du serveur hôte, celui après les « : » sera le point de montage dans votre container

Créez un fichier sur votre machine locale et affichez le contenu du répertoire « /tmp » dans le container debian

root@vm-docker $ touch /tmp/test.txt
root@vm-docker $ docker exec debian ls /tmp

ls-2

Vous pouvez maintenant éteindre et rallumer votre container, le fichier « /tmp/test.txt » sera toujours présent dans votre container.

A retenir :

  • L’option « -v » vous permet de monter des répertoires physiques sur la machine hôte dans le container Docker
  • Il est possible de monter plusieurs volumes en utilisant l’option « -v » pour chaque VOLUME à monter (-v /tmp:/tmp –v /root:/root)

Docker et le réseau

Docker possède son propre réseau interne, il permet aux containers de communiquer entre eux.
Chaque container possède une IP interne.

La plage IP interne de docker est 172.17.0.1/16 par défaut.
Il est possible de changer cette plage et d’en ajouter d’autres.

Lors du démarrage de docker, une interface virtuelle docker0 est montée sur notre machine hôte avec l’IP 172.17.0.1, c’est cette interface virtuelle qui va permettre à notre machine hôte de communiquer avec vos containers et d’ouvrir les containers sur le monde extérieur ou entre eux. Notre machine hôte sera en fin de compte la passerelle des containers docker.

Pour connaitre l’IP d’un container en cours d’exécution, il suffit de taper la commande suivante :


docker inspect --format '{{ .NetworkSettings.IPAddress }}' 0c0bb33937d5
  • 0c0bb33937d5 = id de notre container que nous pouvons trouver grâce à la commande « docker ps »
  • 0c0bb33937d5 peut être remplacé par le nom de notre container, dans notre cas debian

network-1

L’IP de notre container est 172.17.0.2

OK, testons la communication entre nos containers.

Nous allons lancer deux containers debian en mode daemon, un avec le nom debian1 et l’autre avec le nom debian2

Lancez les deux containers :

network-2

On va maintenant récupérer l’IP de chaque container avec « docker inspect » :

network-3

  • ID du container debian1 : 172.17.0.2
  • ID du container debian2 : 172.17.0.4

Entrez dans le container debian1 et effectuez un ping sur le container debian2

network-4

Notre container debian1 communique bien avec le container debian2

A retenir :

  • L’adressage réseau privé de Docker par défaut est 172.17.0.1/16
  • Votre machine hôte est la passerelle entre tous les containers et a pour IP 172.17.0.1 sur l’interface Docker0
  • De base, les containers ne sont pas accessibles sur l’extérieur mais uniquement entre eux et depuis la machine hôte

Docker sur l’extérieur

Comme nous l’avons vu, Docker possède son propre réseau interne, cependant ce réseau n’est pas accessible depuis l’extérieur.
Par défaut, les containers sont complétement isolés du monde extérieur.

Vous pouvez essayer de faire un ping 172.17.0.4 depuis une machine externe, cela ne fonctionnera pas.
C’est logique, notre réseau externe (192.168.51.0/24 qui est le réseau de la machine hôte) ne connait pas le réseau 172.17.0.1/16 et donc il ne connait pas l’adresse 172.17.0.4

Un petit schéma pour mieux comprendre 😉

network-5

On retrouve ici notre machine hôte avec deux interfaces, une communiquant avec son réseau LAN et une interface communiquant avec le réseau interne de docker.
Nous voyons également qu’une machine du LAN ne peut avoir accès qu’à la machine hôte et pas au réseau interne de docker.

Docker a prévu une fonctionnalité qui permet de rendre accessible un container sur l’extérieur, c’est le « port forwarding ».
Le port forwarding va permettre à une machine du LAN de contacter un container sur l’adresse IP de la machine hôte et sur un port déterminé.


docker run –it –d –p 24:22 debian /bin/bash

« Que fait cette commande ? »

Je vais me focaliser sur cette option « -p 24:22 ».

Cela veut tout simplement dire que tout ce qui arrive sur le port 24 de la machine hôte va être redirigé sur le port 22 du container.
On peut imaginer dans ce cas-là, qu’un service SSH écoute sur le port 22 dans notre container.
Nous pourrons alors depuis une machine externe tenter de nous connecter à notre container en SSH


ssh –P24 user@192.168.51.128
  • ssh : c’est la commande ssh qui permet de vous connecter à un serveur en SSH
  • -P24 : on spécifie le port sur lequel on veut se connecter
  • user@192.168.51.128 : user est l’utilisateur SSH, 192.168.51.128 est l’adresse IP de notre machine hôte

Ce n’est que grossièrement une redirection de port basique.
Un container expose un port et le moteur Docker fait le lien entre les requêtes entrantes sur la machine hôte vers les containers Docker en fonction du port appelé.

Dans le cas d’un serveur web, nous pouvons imaginer lancer le container comme cela :


docker run –it –d –p 80:80 debian /bin/bash
  • -p 80:80 : on redirige toutes les requêtes sur le port 80 arrivant sur la machine hôte vers le container sur son port 80

Pour bien comprendre, voici un schéma :

network-6

On peut tout à fait imaginer faire écouter le container Apache sur le port 8080 ou 8081, pour le client, la requête ne changera pas, il appellera toujours l’adresse IP 192.168.51.128 sur le port 80.

A retenir :

  • L’option « -p » permet d’exposer des ports sur la machine hôte et de faire de la redirection de ports dans les containers docker
  • Un container docker est accessible depuis l’extérieur par l’adresse IP de son hôte
  • Le réseau interne de docker « 172.17.0.1/16 » n’est pas disponible sur l’extérieur

Installation de docker compose

« Qu’est-ce que docker-compose ? »

docker-compose est un binaire qui va nous permettre d’administrer plus facilement nos applications sous forme de containers. docker compose nous permet également de créer des templates au format YAML de nos applications.

Ces templates pourront ensuite être versionnés et réutilisables par n’importe qui.

docker-compose nous permet facilement de lier nos containers, monter des volumes, utiliser des variables d’environnement ou encore gérer les ports de nos containers.

Le principal avantage d’utiliser docker compose plutôt que la commande native de Docker est sa simplicité d’utilisation surtout pour les architectures multi-containers.

Comparaison entre l’utilisation du binaire docker et docker-compose pour lancer nos applications

Pour lancer une application avec beaucoup d’options via le binaire de base docker, cela ressemblerait à ça :


docker run -d -p 5000:5000 --restart=always --name registry \
-v `pwd`/auth:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-v `pwd`/certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/www.webnet.fr.cer \
-e REGISTRY_HTTP_TLS_KEY=/certs/www.webnet.fr.key \
-v `pwd`/data:/var/lib/registry \
registry:2

Ce n’est pas très compliqué mais c’est difficilement maintenable car on le lance en ligne de commande.

Ici nous n’appelons qu’une seule image, cela peut devenir très vite une très longue ligne de commande lorsque l’on lie plusieurs containers et images avec plusieurs options.

Avec docker compose, nous n’avons qu’un seul fichier docker-compose.yml qui fait exactement la même chose que le commande ci-dessus, le fichier YAML ressemblerait plutôt à ça :

registry:
  restart: always
  image: registry:2
  ports:
    - 443:5000
    
  environment:
    REGISTRY_HTTP_TLS_CERTIFICATE: /certs/www.webnet.fr.cer
    REGISTRY_HTTP_TLS_KEY: /certs/www.webnet.fr.key
    REGISTRY_AUTH: htpasswd
    REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
    REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
  
  volumes:
    - /path/data:/var/lib/registry
    - /path/certs:/certs
    - /path/auth:/auth

Tout ce code est contenu dans un unique fichier YAML, il vous sera alors plus facile de maintenir l’application.

Dans le fichier YAML, « volumes : » correspond à l’option « -v », « ports » à « -p » et « environment » à « -e ».

Pour lancer notre application, il suffira de nous placer dans le même répertoire que le fichier docker-compose.yml et de lancer la commande docker-compose up.

Pour lancer l’application en daemon avec docker compose, il faudra utiliser l’option « -d »

docker-compose up -d

Nous verrons plus tard comment utiliser docker compose.
Passons à son installation.

Actuellement docker compose est en version 1.6

Récupérez le binaire avec CURL
curl -L https://github.com/docker/compose/releases/download/1.6.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
Ajoutez les droits d’exécution sur le binaire
chmod +x /usr/local/bin/docker-compose
Vérifiez son installation
 docker-compose-1

A retenir :

  • Le binaire docker-compose fait les mêmes choses que docker, il permet juste une meilleure gestion de nos containers et donc de nos applications.
  • docker-compose a besoin du fichier docker-compose.yml pour construire le container
  • docker-compose up permet de lancer le container
  • L’option « -d » permet de lancer le container en daemon comme pour la commande docker run

Installation d’un serveur registry

Avant de créer nos images, nous allons créer le serveur qui va permettre de les accueillir et de les rendre accessibles au monde.

docker-registry est bien sûr disponible sous forme de container et il existe une image prête à l’utilisation, il va nous falloir la customiser un peu.
Je ne vais pas m’éterniser sur docker-registry car il existe de nombreuses documentations déjà très bien rédigées à ce sujet.

Notre registry sera disponible via le DNS « registry.webnet.fr » sur le port 5000, il sera sécurisé en SSL grâce à un certificat.
Nous allons gérer l’authentification au registry par méthode htpasswd.

Pour installer un serveur registry :

Préparez un répertoire qui va contenir votre serveur docker registry sur la machine hôte
mkdir /docker-registry
Créez l’arborescence nécessaire au lancement du docker registry
mkdir /docker-registry/auth /docker-registry/certs /docker-registry/data

  • Le répertoire auth contient le fichier d’authentification htpasswd à notre docker-registry
  • Le répertoire certs contient nos certificats SSL
  • Le répertoire data contient les images

 

Placez-vous dans le répertoire racine /docker-registry et lancez le container

docker run -d -p 5000:5000 --restart=always --name registry \
-v `pwd`/auth:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-v `pwd`/certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/www.webnet.fr.cer \
-e REGISTRY_HTTP_TLS_KEY=/certs/www.webnet.fr.key \
-v `pwd`/data:/var/lib/registry \
registry:2

Vérifiez que le container est lancé avec la commande « docker ps »

registry-1

Le serveur est bien lancé, nous allons créer le premier utilisateur avec la commande « htpasswd »

docker run --entrypoint htpasswd registry:2 -Bbn testuser testpassword > auth/htpasswd
  • L’option “entrypoint” permet de rendre notre container exécutable et de lui ajouter des paramètres
  • testuser = login utilisateur
  • testpassword = mot de passe de l’utilisateur

On peut maintenant s’authentifier avec l’utilisateur créé :

registry-2

A retenir :

  • docker-registry est une alternative gratuite à DockerHub
  • docker-registry permet de stocker nos images que nous allons créer
  • docker-registry est disponible sous forme de container

Suite de l’articleCréation des images Docker et lancement du projet Symfony2