Docker

Dans mon précédent article, je vous parlais de RabbitMQ. Aussi simple soit-il à installer, sachez qu'il est possible de l'installer encore plus simplement (oui oui, c'est possible !!). Mieux encore, cela concerne pas que RabbitMQ, mais aussi tout autre service que vous souhaitez installer dans votre projet : ElasticSearch, MySQL, etc…

Vous l'aurez compris, je parle bien entendu de Docker !

Kézako ?

Docker est une plateforme destinée aux développeurs et sysadmin, leur permettant d'installer facilement et rapidement n'importe quel service.

Concrètement, une fois Docker installé sur votre machine, vous pouvez virtualiser n'importe quel service grâce à 1 seule ligne de commande (si si, puisque je vous le dit !).

Par exemple, pour installer le service RabbitMQ :

docker run –d –P rabbitmq  

Rapide, simple, efficace, c'est quand même beau non ? ☺

Oh l'autre, il connaît même pas Vagrant !

En effet, Vagrant propose le même système. Seulement son architecture est différente, ses performances en sont donc plus faibles.

Vagrant propose une structure imposant 1 OS par application :

Vagrant

Docker quant à lui embarque déjà l'OS, laissant ainsi le soin à chaque application d'installer juste ce dont elle a besoin :

Docker

Vous l'aurez compris, les performances de Docker sont donc nettement supérieures à celles de Vagrant :

Vagrant Docker
Démarrage ~1 minute ~0,3 seconde
Mémoire ~256 Mo ~1 Mo
Espace disque ~1 Go ~100 Ko

Ouai bon, ok, t'as peut-être raison… Mais ça s'installe comment ton truc ?

Installation

Il vous suffit d'exécuter la commande suivante et Docker tourne sur votre Linux :

wget -qO- https://get.docker.com/ | sh  

Attention : cette commande est destinée à un environnement Linux.

Il existe des logiciels assurant la compatibilité sur OS X et Windows, le plus courant étant Boot2Docker.

Une alternative graphique est sortie récemment, basée sur Boot2docker : Kitematic

Usage

Vous allez être amené à utiliser souvent les mêmes commandes en Docker. En voici les principales (et les plus utiles) :

  • docker run : crée un container depuis une image
  • docker ps : liste les containers en cours d'exécution
  • docker images : liste les images locales
  • docker stop : arrête un container
  • docker rm : supprime un container
  • docker logs : affiche les logs d'un container
  • docker rmi : supprime une image
  • docker exec : exécute une commande dans un container en cours d'exécution

Oula, oula, attends : c'est quoi un container ? C'est quoi une image ?

Excellente question (1 bon point pour celui qui l'a posé !). Lorsque vous installez un service via Docker, vous travaillez dans un container. Celui-ci est basé sur une image.

Par exemple, si vous exécutez la commande docker run –d –P rabbitmq:3, une image rabbitmq sera téléchargée, et un container sera alors créé depuis cette image. Cela vous permet alors d'avoir plusieurs containers qui tournent, basés sur une même image ☺.

Et ce « 3 », c'est quoi ?

Il s'agit de la version. En effet, la plupart des services proposent différentes versions. Avec la précédente commande, nous avons créé un container basé sur l'image rabbitmq dans sa version 3.

Et les options « -d », « -P » ?

Docker propose plusieurs options, je ne les connais pas toutes. Celles que je viens d'introduire sont les plus utiles :

  • -d lance le container en tâche de fond (daemon)
  • -P permet une correspondance des ports, plutôt utile pour directement utiliser les ports du service que vous venez d'installer
  • --name permet de préciser le nom du container

Donc si je lance docker run –d –P elasticsearch, j'ai Elasticsearch qui tourne sur ma machine, utilisable et configuré ?

Oui ☺. Et même encore mieux : pour se connecter sur un container en cours d'exécution, voici la commande à exécuter :

docker exec –it nom_de_mon_container bash  

Cela va exécuter en mode non interactif le script bash dans le container demandé.

Un peu plus loin

OK, maintenant que vous maitrisez Docker (si si !), regardons comment ajouter un peu de souplesse dans nos service. Par exemple, si vous vous basez sur une image déjà existante (au hasard : mysql), et souhaitez installer certains paquets supplémentaires :

  • Première solution : vous téléchargez l'image mysql, vous lancez un container dans lequel vous vous connectez pour installer les paquets additionnels voulus.

    Le souci, c'est que vous allez devoir répéter cette action sur chaque machine ou sera installé votre service…

  • Meilleure solution : vous créez un fichier de configuration Dockerfile dans votre projet, dans lequel vous notez toutes les actions à effectuer :
FROM debian:jessie

RUN apt-get update && \  
    apt-get install -y \
    ruby \
    ruby-dev \
    build-essential \
    sqlite3 \
    libsqlite3-dev

RUN gem install mailcatcher --no-ri --no-rdoc

RUN apt-get remove --purge -y \  
    build-essential \
    ruby-dev \
    libsqlite3-dev && \
    apt-get autoclean && \
    apt-get clean

RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

EXPOSE 1025  
EXPOSE 1080

CMD ["mailcatcher", "-f", "--ip=0.0.0.0"]  

Depuis ce fichier de configuration, nous allons pouvoir créer une image sur laquelle se baseront les containers :

docker build –t nom_de_mon_image .  

N'oubliez pas le point après le nom de l'image, il permet de définir le chemin vers votre fichier Dockerfile !

Construisez maintenant votre premier container basé sur cette image :

docker run –d –name nom_de_mon_container nom_de_mon_image  

L'ordre est particulièrement important : le nom de l'image doit obligatoirement se trouver en dernier paramètre !

Mais dans ce Dockerfile, c'est quoi « RUN », « ADD », etc… ?

Le fichier Dockerfile est composé de plusieurs commandes :

  • RUN permet d'exécuter un script
  • ADD permet de monter un répertoire dans le container
  • WORKDIR définit le répertoire de travail, celui dans lequel sont exécutées les commandes par défaut
  • CMD permet d'exécuter une commande
  • ENTRYPOINT définit une commande d'entrée

Important : pour qu'un container tourne, il faut obligatoirement qu'une commande soit en cours d'exécution en premier plan !

Ainsi, si votre Dockerfile ne contient aucun CMD ni ENTRYPOINT, votre container se fermera automatiquement (pas très pratique tout ça…).

Encore plus loin

Bien, prenez l'exercice 34 à la page 68. Vous là-bas au fond, lisez-nous l'énoncé :
« Mathieu dispose d'un Dockerfile lui permettant de créer un container contenant un environnement PHP + Apache. Il souhaite utiliser l'image officielle mysql et mailcatcher pour sa base de données et pour gérer ses e-mails en dev. Comment peut-il mettre cette structure en place ? »

Vous avez 30mn.

Bon, plus sérieusement, cette problématique nous montre clairement cette possibilité de lier des containers entre eux. Dans cet exercice, nous disposons de 3 containers :

  • web : contenant PHP, Apache et le code du projet monté en volume
  • db : contenant MySQL
  • mailcatcher : contenant le service Mailcatcher

Avec Docker, il suffirait de faire une correspondance des ports respectifs à chaque service lors du lancement de chaque container (et ça, vous savez faire !).

Mais comme je suis une grosse feignasse qui a la flemme de taper 3 lignes de commandes, je préfère n'en lancer qu'une et aller prendre un café ! Et c'est là que je vous présente…(pause pour le suspens)… Docker Compose.

Peut-être certains d'entre vous le connaissez déjà sous le nom de FIG. FIG a été racheté par Docker au titre de Docker Compose. Vous n'avez plus d'excuse alors pour l'utiliser ☺ Et ne me dites pas que son installation est compliquée, elle tient en 1 commande :

curl -L https://github.com/docker/compose/releases/download/1.2.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose  

À l'exemple du Dockerfile, il vous suffit de créer un fichier docker-compose.yml avec la structure suivante :

web:  
    build: .
    environment:
        - NODE_ENV=production
    volumes:
        - content:/var/www
    links:
        - db:db
        - mailcatcher:mailcatcher
    ports:
        - 80:2368

db:  
    image: mysql
    ports:
        - 3306:3306

mailcatcher:  
    image: schickling/mailcatcher
    ports:
        - 1080:1080
        - 1025:1025

Ici nous mettons en place les 3 containers cités précédemment. Notez la section ports permettant une correspondance des ports entre le container et la machine hôte.

Une autre section très importante est links. Celle-ci permet de lier les containers entre eux. Ainsi, dans le container web, le container db sera accessible via l'alias db, même principe pour le container mailcatcher.

Si vous souhaitez qu'un container utilise une configuration définie dans un Dockerfile, utilisez la syntaxe build suivie du chemin vers le fichier Dockerfile correspondant. Mais pour utiliser une image déjà existante, vous pouvez utiliser la syntaxe image suivie du nom de l'image.

Usage

Une fois encore, tout passe par des commandes archi-ultra-méga simples :

  • docker-compose build : construit les images
  • docker-compose up : monte et démarre les containers
  • docker-compose start : démarre les containers précédemment montés
  • docker-compose stop : arrête les containers
  • docker-compose rm : supprime les containers
  • docker-compose run : exécute une commande dans un container

Note : les noms des containers suit la structure suivante : <nom_du_répertoire>_<nom_du_container>_1.

Par exemple, pour un répertoire erb, les containers précédemments cités seront nommés comme suit : erb_web_1, erb_db_1 et erb_mailcatcher_1.

À travers Docker, vous devez préciser le nom complet du container lors de son utilisation. Dans Docker Compose, vous devez juste préciser le nom de son container.

Par exemple, pour exécuter la commande php app/console ca :cl dans le container web :

docker-compose run web php app/console ca:cl  

Vous l'aurez remarqué, je n'ai précisé que web et non pas erbweb1. Docker Compose fait automatiquement la correspondance.

Pour conclure, voici le dépôt officiel Docker : https://registry.hub.docker.com. Vous y trouverez toutes les images officielles ou non, utiles ou non, de la grande communauté Docker en ligne ☺

Avantages

Docker est :

  • Performant (y'a pas photo)
  • Très facile à installer (peut-être même trop…)
  • Utilisable en simple ligne de commande ou à travers un/des fichier(s) de configuration (Dockerfile, docker-compose.yml)
  • Propose un dépôt public et privé pour vos images

Et vous savez quoi ? Docker propose même le code de son registry sur GitHub, si vous souhaitez mettre en place votre propre registry : https://github.com/docker/docker-registry !

Inconvénients

Après plusieurs semaines/mois d'utilisation, le seul inconvénient que j'ai trouvé se trouve dans Boot2docker. Pas toujours facile à mettre en place, il propose une surcouche à Docker qui peut s'avérer sensible voir difficile à manipuler au début. Mais une fois pris en main, on en fait vite abstraction et profitons à fond de Docker ! Il faut juste persévérer quelques jours au début afin de découvrir les particularités de Boot2docker, et c'est parti ☺

Oh au fait, vous aimerez peut-être ma présentation Slides.com introduisant Docker : http://slides.com/vincentchalamon/docker/fullscreen

Mise à jour du 8 juillet 2015

Je viens de publier mon premier Dockerfile vous permettant de mettre en place facilement un environnement Web pour Symfony : https://registry.hub.docker.com/u/vincentchalamon/symfony/