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.

 

Vous savez donc maintenant utiliser docker, vous avez installé docker-compose et docker-registry.
Vous avez donc tous les outils pour construire votre première application sous forme de container.
Comment construire une image docker ? La façon la plus simple de construire une image est d’utiliser un fichier dockerfile.
Un dockerfile contient une suite d’instructions qui vont permettre de construire nos images.

La commande qui permet de construire notre image est la suivante :
docker build –t nom_de_l_image .

Décomposons un peu cette commande :

  • docker : c’est notre binaire Docker
  • build :c’est la commande qui nous permet de build notre image
  • -t : spécifie le nom de l’image que l’on veut lui donner
  • . (point) : docker cherche le fichier nommé dockerfile dans le répertoire courant

Voici les instructions les plus utilisées :

  • FROM : c’est l’image sur laquelle on se base pour construire notre image
  • MAINTAINER : c’est la personne qui a écrit le dockerfile
  • RUN : permet de lancer une commande
  • ADD : permet d’ajouter un fichier dans le container pendant sa construction
  • EXPOSE : permet d’exposer un port particulier
  • CMD : c’est la commande qui va être lancé au démarrage du container

La liste complète des commandes est disponible sur le site officiel de docker.
La grande force de docker réside dans les images qu’il génère, une image qui se base par exemple sur l’image  debian ne contiendra que la différence entre l’instruction FROM et l’ensemble des instructions contenus dans le Dockerfile.

Voici un exemple de Dockerfile pour un serveur web Apache 2.4 et PHP 5.6 :

FROM debian:jessie
MAINTAINER Stephane PHAM, srs@webnet.fr
## Installation des utilitaires de base
RUN apt-get update
RUN apt-get install wget -y
# Installation d'Apache 2 et de PHP 5.6
RUN echo deb http://packages.dotdeb.org jessie all >> /etc/apt/sources.list
RUN wget https://www.dotdeb.org/dotdeb.gpg && apt-key add dotdeb.gpg
## Installation d'Apache 2
RUN apt-get update && apt-get install -y apache2 apache2-doc apache2-mpm-prefork apache2-utils libexpat1 ssl-cert
## Installation de PHP 5.6
RUN apt-get install -y libapache2-mod-php5 php5 php5-common php5-curl php5-dev php5-gd php5-idn php-pear php5-imagick php5-mcrypt php5-mysql curl
## Installation des utilitaires
RUN apt-get install -y vim nano
## Installation de composer
RUN curl -sS https://getcomposer.org/installer | php
RUN mv composer.phar /usr/local/bin/composer
## Création du repertoire pour stocker les fichiers de configuration
RUN mkdir -p /config/php5/apache2 /config/php5/cli /config/apache2
## Ajout du fichier php.ini
ADD ./config/php5/apache2 /config/php5/apache2
ADD ./config/php5/cli /config/php5/cli
## Suppression du vhost par défaut
RUN rm /etc/apache2/sites-enabled/000-default.conf
## Ajout des fichiers virtualhost
ADD ./config/apache2/virtualhost.conf /config/apache2
## Activation des modules SSL et REWRITE
RUN a2enmod ssl
RUN a2enmod rewrite
RUN a2enmod headers
## On expose le port 80 et 443
EXPOSE 80
EXPOSE 443
## On déplace les fichiers de configuration s'il n'existe pas et on démarre apache
ADD ./tools/startup.sh /
RUN chmod +x /startup.sh
CMD ["/startup.sh"]

Cette image une fois construite ne contiendra qu’Apache, PHP et les différents utilitaires installés (wget, curl…).

Construction de nos images

Construisons nos premières images pour l’application Symfony.

Vous pouvez revenir plus haut à la partie « architecture » afin de vous remémorez les différents composants qui vont faire partie de notre application.

Je le répète mais la construction de notre application Symfony nécessite la construction de 3 images qui deviendront ensuite 3 containers indépendant.

Image application Symfony

Créez un fichier « dockerfile » et insérez-y le code suivant :
FROM debian:jessie
VOLUME /var/www/symfony

Oui, c’est tout 🙂
On se base sur l’image déjà existant debian:jessie « notez que l’ajout de :jessie correspond à un tag sur l’image, nous pouvons évidemment utiliser d’autre tags s’ils sont disponibles pour une image donnée. Pour cette image nous pouvons imaginer que :wheezy ou :squeezy fonctionnent ».
On crée un volume à l’intérieur de l’image qui nous servira à accueillir les sources
Construisons notre première image
docker build –t registry.webnet.fr:5000/application_symfony .

  • registry.webnet.fr:5000 : c’est notre serveur registry que nous avons construit précédemment
  • application_symfony : c’est le nom que nous donnons à notre image

L’image est maintenant construite et nous pouvons la retrouver en tapant la commande « docker images » sur notre serveur hôte.

1

Attention, l’image est construite mais elle n’est disponible que sur la machine locale.

Nous allons maintenant l’envoyer sur notre serveur registry avec la commande « docker push »

Envoyons notre image dans le registry
docker push registry.webnet.fr:5000/application_symfony

Voilà c’est fait, notre première image est sur le registry. Nous pouvons la récupérer si nous voulons sur une autre machine ayant accès au serveur registry.
Vérifions que l’image est physiquement sur notre serveur registry
ls /docker-registry/data/docker/registry/v2/repositories/

Vous devriez voir le répertoire « application_symfony »

Image apache 2.4 et php5.6

Créez un fichier « dockerfile » et insérez-y le code suivant :
FROM debian:jessie
MAINTAINER Stephane PHAM, srs@webnet.fr
## Installation des utilitaires de base
RUN apt-get update
RUN apt-get install wget -y
# Installation d'Apache 2 et de PHP 5.6
RUN echo deb http://packages.dotdeb.org jessie all >> /etc/apt/sources.list
RUN wget https://www.dotdeb.org/dotdeb.gpg && apt-key add dotdeb.gpg
## Installation d'Apache 2
RUN apt-get update && apt-get install -y apache2 apache2-doc apache2-mpm-prefork apache2-utils libexpat1 ssl-cert
## Installation de PHP 5.6
RUN apt-get install -y libapache2-mod-php5 php5 php5-common php5-curl php5-dev php5-gd php5-idn php-pear php5-imagick php5-mcrypt php5-mysql curl
## Installation des utilitaires
RUN apt-get install -y vim nano
## Installation de composer
RUN curl -sS https://getcomposer.org/installer | php
RUN mv composer.phar /usr/local/bin/composer
## Création du repertoire pour stocker les fichiers de configuration
RUN mkdir -p /config/php5/apache2 /config/php5/cli /config/apache2
## Ajout du fichier php.ini
ADD ./config/php5/apache2 /config/php5/apache2
ADD ./config/php5/cli /config/php5/cli
## Suppression du vhost par défaut
RUN rm /etc/apache2/sites-enabled/000-default.conf
## Ajout des fichiers virtualhost
ADD ./config/apache2/virtualhost.conf /config/apache2
## Activation des modules SSL et REWRITE
RUN a2enmod ssl
RUN a2enmod rewrite
RUN a2enmod headers
## On expose le port 80 et 443
EXPOSE 80
EXPOSE 443
## On déplace les fichiers de configuration s'il n'existe pas et on démarre apache
ADD ./tools/startup.sh /
RUN chmod +x /startup.sh
CMD ["/startup.sh"]

Comme vous pouvez le voir, ce dockerfile est un petit peu plus fourni que le précèdent. Décomposons le dockerfile :
FROM debian:jessie
MAINTAINER Stephane PHAM, srs@webnet.fr

  • La première ligne spécifie l’image sur laquelle nous allons nous baser
  • La deuxième ligne spécifie la personne qui maintient ce Dockerfile, ce n’est qu’à titre informatif
## Installation des utilitaires de base
RUN apt-get update
RUN apt-get install wget -y
# Installation d'Apache 2 et de PHP 5.6
RUN echo deb http://packages.dotdeb.org jessie all >> /etc/apt/sources.list
RUN wget https://www.dotdeb.org/dotdeb.gpg && apt-key add dotdeb.gpg
## Installation d'Apache 2
RUN apt-get update && apt-get install -y apache2 apache2-doc apache2-mpm-prefork apache2-utils libexpat1 ssl-cert
## Installation de PHP 5.6
RUN apt-get install -y libapache2-mod-php5 php5 php5-common php5-curl php5-dev php5-gd php5-idn php-pear php5-imagick php5-mcrypt php5-mysql curl
## Installation des utilitaires
RUN apt-get install -y vim nano

  • On installe tous les packages Apache et PHP nécessaires via la directive RUN
  • On installe également quelques utilitaires comme vim, nano et curl
  • Notez l’utilisation de l’option « -y» pour la commande « apt-get », cela permet de répondre directement « yes » au gestionnaire de paquet APT. SI on oublie de mettre cette option, le build échoue.
## Installation de composer
RUN curl -sS https://getcomposer.org/installer | php
RUN mv composer.phar /usr/local/bin/composer
  • C’est une application Symfony, on installe le fameux composer afin de pouvoir récupérer les dépendances de notre projet.
  • J’ai installé CURL précédemment pour pouvoir récupérer composer, curl n’étant pas inclus de base dans l’image debian dont nous nous sommes basés
## Création du répertoire pour stocker les fichiers de configuration
RUN mkdir -p /config/php5/apache2 /config/php5/cli /config/apache2
## Ajout du fichier php.ini
ADD ./config/php5/apache2 /config/php5/apache2
ADD ./config/php5/cli /config/php5/cli
## Suppression du vhost par défaut
RUN rm /etc/apache2/sites-enabled/000-default.conf
## Ajout des fichiers virtualhost
ADD ./config/apache2/virtualhost.conf /config/apache2

Attention à cette partie de notre dockerfile qui va être très importante pour la suite.

Nous voulons pouvoir modifier par exemple le virtualhost d’Apache ou une configuration PHP dans le php.ini

Vous allez me dire « Facile, je n’ai qu’à me connecter au container et à changer ce que je veux ».

Effectivement vous pouvez faire ça, mais rappelez-vous que les données ne persistent pas dans un container docker !

Là encore, docker a prévu une fonctionnalité que j’ai nommé VOLUME, nous allons simplement injecter les fichiers de configuration que nous voulons modifier comme le php.ini de PHP ou le fichier de configuration virtualhost d’Apache avec la directive ADD, ces fichiers seront ensuite exposés sur notre machine local au travers des VOLUME.

  • On crée un dossier racine « /config » qui va contenir tous les fichiers de configuration que nous voulons exposer
  • On injecte dans notre image les fichiers de configuration voulu avec ADD
## Activation des modules SSL et REWRITE
RUN a2enmod ssl
RUN a2enmod rewrite
RUN a2enmod headers
## On expose le port 80 et 443
EXPOSE 80
EXPOSE 443
  • On active les mod ssl, rewrite et headers
  • On expose notre container sur les ports 80 pour le http et 443 pour le https
## On déplace les fichiers de configuration s'il n'existe pas et on démarre apache
ADD ./tools/startup.sh /
RUN chmod +x /startup.sh
CMD ["/startup.sh"]

Les fichiers de configuration sont ajoutés comme nous l’avons vu dans le dossier racine « /config » de notre container.

Le script startup.sh va vérifier que les fichiers de configurations sont présents, dans le cas contraire, il prendra les fichiers de configurations dans « /config » pour les placer au bon endroit (/etc/apache2/sites-enabled par exemple pour le fichier virtualhost.conf).

A la fin il démarre aussi apache à chaque démarrage du container.

«  Pourquoi on ne place pas directement les fichiers de configuration au bon endroit ?»

Très bonne question, nous verrons par la suite pourquoi.

Créez les répertoires et fichiers nécessaires à la construction de notre build
Comme vous pouvez le voir, ce dockerfile fait appel à de nombreux fichiers et dossiers.

Voici l’arborescence cible pour construire correctement notre image

  • config/ : contient les fichiers de configuration à injecter
  • dockerfile : c’est notre fichier de construction
  • tools : contient le script qui va se lancer à chaque démarrage du container
    • Le script contient le code suivant
#!/bin/bash
phpcli=/etc/php5/cli/
phpapache2=/etc/php5/apache2/
apache2=/etc/apache2/sites-enabled/
## Vérification du repertoire $phpcli
if [ $(find $phpcli -maxdepth 0 -type d -empty 2>/dev/null) ]; then
    echo "Répertoire vide"
    cp -rv /config/php5/cli/* /etc/php5/cli/
else
    echo "Le répertoire n'est pas vide phpcli"
fi
## Verification du répertoire $phpapache2
if [ $(find $phpapache2 -maxdepth 0 -type d -empty 2>/dev/null) ]; then
    echo "Répertoire vide"
    cp -rv /config/php5/apache2/* /etc/php5/apache2
else
    echo "Le répertoire n'est pas vide phpapache2"
fi
## Vérification du repertoire $apache2
if [ $(find $apache2 -maxdepth 0 -type d -empty 2>/dev/null) ]; then
    echo "Répertoire vide"
    cp -rv /config/apache2/virtualhost.conf /etc/apache2/sites-enabled
else
    echo "Le répertoire n'est pas vide apache2"
fi
## Démarrage d'Apache2
 /usr/sbin/apache2ctl -D FOREGROUND

Voici l’arborescence de notre répertoire de build apache2_php56 :

2
Passons maintenant au build de notre application

docker build –t registry.webnet.fr :5000/apache2_php56

Un petit listing des images ne fait pas de mal

docker images

3

Poussons notre image sur le registry docker

docker push registry.webnet.fr :5000/apache2_php56

 Vérifions dans le répertoire « /data » de notre serveur registry que l’image est bien présente

ls /docker-registry/data/docker/registry/v2/repositories/

Vous devriez voir le répertoire « apache2_php56»

Image MySQL 5.6

Créez un fichier « dockerfile» et insérez-y le code suivant :

FROM mysql:5.6
MAINTAINER Stephane PHAM, srs@webnet.fr

Oui, c’est tout J. Il existe déjà de nombreuses images déjà prêtes à l’emploi et très bien documentées.
Le but n’est pas de réinventer la roue, à vous de tester les images déjà existantes et de les adapter au besoin, la construction des dockerfile étant ouvert au public.

  • On se base ici sur l’image officiel mysql et sur le tag 5.6 (rappelez-vous nous avions utilisé les tags pour l’image debian avec le tag jessie)

Construisons notre image

docker build –t registry.webnet.fr:5000/mysql56

Vérifiez que l’image s’est construite et est disponible en local

docker images

Vérifiez que l’image s’est construite et est disponible en local

docker push registry.webnet.fr :5000/mysql56

C’est fini ! Nous avons 3 images qui sont prêtes à être utilisés pour notre application Symfony.
Passons maintenant à l’étape suivante, nous allons lier ces images ensemble.

Ce que vous devez retenir :

  • docker build : permet de construire l’image à partir d’un fichier Dockerfile
  • docker push : permet d’envoyer l’image sur un serveur Registry ou DockerHub
  • docker images : permet de lister l’ensemble des images connues par la machine hôte
  • Il existe des milliers d’images prêtes à l’emploi sur le repository public
  • Une image ne contient que la différence entre l’image de base FROM et le jeu d’instructions que l’image exécute

Préparation du projet

Il ne reste plus qu’à lier nos images ensemble grâce à docker compose.

Créons tout d’abord l’arborescence projet nécessaire au lancement de notre application

4

  • app1 : c’est notre répertoire racine de travail
    • config : c’est le répertoire qui va contenir nos fichiers de configuration
    • logs : c’est le répertoire qui va contenir les différents logs apache2 et Symfony par exemple
    • mysql : c’est le répertoire qui va contenir la future base de données de notre application
    • txt : une petite notice J
    • www : c’est le répertoire qui va contenir les sources de notre application Symfony
    • docker-compose.yml : c’est le fichier qui contient les instructions permettant de lancer notre application

L’arborescence est maintenant créée, il suffit d’ajouter le fichier docker-compose.yml suivant :

5

Découpons le fichier docker-compose.yml afin de mieux comprendre

Partie Apache et PHP

apache2_php56:
    image: registry.webnet.fr:5000/apache2_php56
    volumes_from:
       - application
    ports:
        - 80 :80
    volumes:
        - /logs/apache2:/var/log/apache2
        - ./config/apache2:/etc/apache2/sites-enabled
        - ./config/php5/apache2:/etc/php5/apache2
        - ./config/php5/cli:/etc/php5/cli
    environment:
        POST_MAX_SIZE: 10M
        UPLOAD_MAX_FILESIZE: 10M
        MEMORY_LIMIT: 128M
        MAX_EXECUTION_TIME: 30
        DOCUMENTROOT: /var/www
        SERVERNAME: dev-php56.webnet.fr
        SERVERALIAS:
        ERRORLOG: error.log
        ACCESSLOG: access.log
  • apache2_php56 : c’est le nom que va avoir notre container au lancement
  • image : on se base sur l’image précédemment créé
  • volumes_from : on monte le container « application » en tant que VOLUME dans le container apache2_php56, le volume application est celui qui contiendra les sources de notre application
  • ports : on expose le port 80 et redirigeons tout le trafic sur la machine hôte sur le port 80 vers le port 80 du container apache/php
  • volumes : ce sont les différents points de montage entre notre machine hôte et notre container
    • ./logs/apache2/:/var/log/apache2/ : on monte le répertoire local /logs/apache2/ dans le container, le répertoire sera monté dans /var/log/apache2/
    • Ainsi de suite pour les autres VOLUME
  • environment : ce sont les variables que nous allons utiliser dans notre fichier php.ini ou virtualhost.conf par exemple
    • SERVERNAME: dev-php56.webnet.fr

La variable « SERVERNAME » nous permet de modifier directement le ServerName dans notre virtualhost sans aller chercher dans la configuration.

Dans le virtualhost le champ ServerName sera complété avec la valeur ${SERVERNAME}

Rappelez-vous, ce fichier nous l’avions injecté à la création de l’image.

Le fichier virtualhost ressemblera à ceci :

6

Partie application

application:
    image: registry.webnet.fr:5000/application_symfony
    volumes:
        - ./www:/var/www/symfony
        - ./logs/symfony:/var/www/symfony/app/logs
    tty: true
  • application : c’est le nom que va avoir notre container
  • image : c’est l’image que l’on utilise
  • volumes : ce sont les différents points de montage entre notre machine hôte et notre container
    • ./www/:/var/www/symfony : on monte le répertoire local /www/ dans le container, le répertoire sera monté dans /var/www/symfony
    • On monte également le répertoire /logs/symfony dans le container sur /var/www/symfony/app/logs
      • Les logs Symfony se trouvent dans app/logs, le montage de ce volume nous permettra d’accéder aux logs de Symfony

Partie MySQL

db:
    image: registry.webnet.fr:5000/mysql_56
    ports:
        - 3306:3306
    volumes:
        - ./mysql:/var/lib/mysql
    environment:
        MYSQL_ROOT_PASSWORD: root
        MYSQL_DATABASE: db_name
        MYSQL_USER: user
        MYSQL_PASSWORD: pw
  • db : c’est le nom du container une fois lancé
  • image : c’est l’image que nous allons utiliser
  • ports : nous exposons le port 3306 et redirigeons tout le trafic sur la machine hôte vers le port 3306 du container MySQL
  • volumes: on monte le répertoire local ./mysql dans le container sur le répertoire /var/lib/mysql, cela nous permettra de faire des changements en bases de données de façon persistante
  • environment : permet de définir un nom de base de données, un password root…

Lancement de l’application

Tout est prêt, lançons maintenant nos containers !

Placez-vous dans votre répertoire racine (celui où se trouve le fichier docker-compose.yml) et lancez la commande

docker-compose up –d 

Regardons un peu si nos containers sont lancés correctement

docker-compose ps

7

Regardons un peu ce qu’il se passe dans notre répertoire de travail

8

9

Les fichiers natifs de MySQL se sont bien installés dans le répertoire ./mysql et les fichiers de configurations apache2 et php5 que nous avions injectés au préalable dans les images sont bien exposés dans le répertoire ./config

Il nous reste maintenant à injecter dans le répertoire ./www les sources du site et un dump de base de données, placez-vous dans le répertoire de travail racine « /data/app1/ »

touch www/index.html
echo « Hello World » > www/index.html

Nous venons d’ajouter un fichier index.html dans le répertoire ./www.

Pour vous connecter à la base MySQL, il suffira d’utiliser votre client préféré (SQLYog par exemple) et de spécifier le host/ip et le port :

  • host : IP/DNS de la machine hôte dans notre cas 192.168.51.128
  • port : 3306
  • user : user (défini dans le fichier docker-compose.yml)
  • password : pw (défini dans le fichier docker-compose.yml)

Lancez maintenant votre navigateur et entrez l’adresse http://192.168.51.128 (remplacez 192.168.51.128 par l’IP de votre machine hôte)

10

Félicitations, vous avez lancé votre première application reposant sur 3 containers.

Pour lancer l’application sur un autre serveur, il suffira de récupérer le fichier docker-compose.yml et de lancer la commande « docker-compose up –d ».

Supprimez maintenant le fichier index.html et injectez les sources Symfony et dans le répertoire « ./www »

Connectez-vous au container apache/php, vous pouvez y lancer des commandes comme composer install

docker exec –it app1_apache2_php56_1 /bin/bash

Pour aller encore plus loin !

Vous l’aurez compris, docker est un formidable allié lorsqu’il s’agit de déployer des environnements de façon rapide et homogène.

Nous venons de voir comment nous pouvons utiliser docker pour dockeriser une application Symfony simple et vous avez maintenant toutes les informations nécessaires pour monter vous-même vos propres applications.

La puissance de Docker nous permet d’imaginer pouvoir déployer des centaines d’applications en quelques secondes grâce à un seul fichier YAML sur des dizaines de serveurs différents.
Lors d’un prochain article, je parlerai du processus d’intégration de docker dans nos processus d’intégration continue (développement à la recette jusqu’à la production) couplé avec un outil de CI (Jenkins par exemple).

Il y a de nombreux projets en cours au sein de la communauté docker, swarm pour la haute disponibilité, ou encore kubernetes pour le déploiement de containers dans le cloud.