Environnement de développement basé sur Docker

Voir la vidéo

Dans le tutoriel précédent on a vu ce qu'était docker. Maintenant je vous propose de passer à un cas concret en mettant en place en environnement de développement basé sur des conteneurs docker.

Boot2Docker

Si comme moi vous êtes sur Windows (ou sur Mac), il va falloir commencer par installer Boot2docker. Cet outil va se charger d'installer les composants nécessaires afin de faire fonctionner Docker sur votre environnement. En réalité, il va installer une machine virtuelle qui, elle, possédera docker et fournira une interface en ligne de commande pour interagir avec cette dernière. Globalement, il n'y a pas des tonnes de choses à retenir

boot2docker up  # Permet de démarrer la machine contenant docker
boot2docker stop # Permet de l'arrêter
boot2docker ssh  # Permet de se connecter en ssh 
boot2docker ip # Permet d'obtenir l'ip de la machine

Attention cependant cette machine n'est pas entièrement persistée, comprendre par là que mis à part les dossiers /var/lib/docker et /var/lib/boot2docker tous les autres dossiers seront réinitialisés à chaque redémarrage.

Enfin, par défaut boot2docker monte votre dossier utilisateur sur la machine virtuelle. Si vous souhaitez partager un autre dossier vous pouvez créer un dossier partagé via Virtualbox et ensuite le monter sur la machine en ajoutant la commande suivante dans le fichier /var/lib/boot2docker/bootlocal.sh.

mount -t vboxsf www /var/www

Le fichier bootlocal.sh vous permet de placer une logique que vous souhaitez voir lancée au démarrage de la machine boot2docker.

Docker-compose

Pour orchestrer nos conteneurs nous allons utiliser un outil fournit par docker : docker compose. Cet outil permet de configurer vos conteneurs via un fichier yml. Ce qui permet d'éviter d'avoir à taper des lignes de commandes très longues, mais aussi un partage plus aisé d'une configuration basée sur plusieurs conteneurs.

Pour l'installer, rien de plus simple, il vous suffit de suivre les instructions présentes sur la documentation (je ne copie pas les lignes ici, car elles changent assez souvent).

En revanche, si vous êtes sur boot2docker le dossier dans lequel on vous demande de placer compose n'est pas persisté. Pour remédier au problème j'ai tout simplement placé docker-compose dans le dossier /var/lib/boot2docker et je le copie à chaque démarrage de ma machine grace au fichier bootlocal.sh

cp /var/lib/boot2docker/docker-compose /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

Apache + PHP

On va maintenant commencer à construire notre stack avec apache et le module PHP. Pour cette configuration, on va se baser sur l'image tutum/aphache-php. Donc dans notre fichier docker-compose.yml :

web:
    image: tutum/apache-php
    ports:
        - "80:80"
    volumes:
        - /var/www:/app
    environment:
        - ALLOW_OVERRIDE=true

Tel quelle la configuration d'apache possède quelques défauts.

  • Les erreurs PHP ne sont pas affichée (ce qui peut être un peu chiant pendant le dev)
  • On ne peut pas personnaliser les VirtualHost
  • Il nous manque l'extension bcrypt nécessaire à l'utilisation de certains frameworks PHP

Pour les erreurs et les alias, la solution est plutôt simple. On va créer la configuration spéciale sur notre machine hôte et on fera ensuite des liens. En revanche l'activation de mcrypt va se faire via un fichier Dockerfile. On va donc se créer une "recette" pour créer notre propre image d'apache. Pour cela il suffit de créer un dossier au même niveau que notre fichier docker-compose.yml et créer un fichier Dockerfile.

FROM  tutum/apache-php

RUN apt-get update && apt-get -yq install php5-mcrypt && php5enmod mcrypt

EXPOSE 80

CMD ["/run.sh"]

Et on modifie donc notre yml pour lui demander d'utiliser ce Dockerfile plutôt que l'image en ligne

web:
    build: apache
    ports:
        - "80:80"
    volumes:
        - /var/www:/var/www/local.dev
        - /var/www/docker/php.ini:/etc/php5/apache2/conf.d/30-custom.ini
        - /var/www/docker/sites:/etc/apache2/sites-enabled
    environment:
        - ALLOW_OVERRIDE=true

Maintenant on peut build notre image à partir de notre fichier yml.

docker-compose build
docker-compose up 

Pour le dossier sites-enabled, on peut aussi faire des liaisons fichier par fichier, mais vu que je travaille sur beaucoup de sites en même temps je me retrouve rapidement avec de nombreux virtualhost donc je préfère lier tout le dossier

Mysql

Pour travailler sur nos sites, on va avoir besoin d'utiliser une ou plusieurs bases de données. On va donc partir de l'image officielle de docker. Ce conteneur sera lié à un dossier de notre host afin de conserver les bases à chaque redémarrage.

db:
    image: mysql
    volumes:
        - /var/lib/boot2docker/mysql:/var/lib/mysql
    environment:
        - MYSQL_ROOT_PASSWORD=root

Maintenant, on se retrouve avec une problématique assez importante. Comment PHP et Mysql vont pouvoir communiquer vu qu'ils sont dans 2 conteneurs différents ?
Docker permet de lier plusieurs conteneurs entre eux en utilisant le réseau. Au niveau de la configuration web on va ajouter l'argument suivant :

    links:
        - db:db

Maitenant si on ping "db" dans le conteneur web on recevra alors une réponse du conteneur Mysql créé précédemment. De la même manière, le conteneur web aura accès aux différentes variables d'environnement de db. Ces dernières seront préfixées par le nom de la liaison établie : DB_.

Du coup avec cette configuration il faudra taper db comme nom d'hôte de base de donnée dans nos scripts PHP.

Maildev

Une autre brique importante de notre environnement de développement c'est la mise en place d'un faux serveur SMTP afin de pouvoir tester les emails sortant de notre application.

web:
    ........
    links:
        - db:db
        - maildev:maildev
maildev:
    image: djfarrelly/maildev
    ports:
        - "1080:80"

Et voila, nous aurons accès à l'interface de maildev sur le port 1080 de notre machine hôte et notre bloc web aura accès à maildev sur le nom de domaine maildev et sur le port 25.

Alternative Nginx

Parcequ'il n'y a pas qu’apache dans la vie je vous propose aussi de voir comment remplacer notre partie apache avec nginx. Personnellement, c'est une solution que je préfère, car il est plus simple de séparer nginx et PHP avec le système fastcgi. Nous allons donc commencer par installer PHP fpm, et comme tout à l'heure on va se créer un fichier Dockerfile afin de pouvoir personnaliser les modules présents.

FROM php:5.6-fpm

RUN apt-get update && apt-get install -y \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libmcrypt-dev \
        libpng12-dev \
        libsqlite3-dev \
        libssl-dev \
        libcurl3-dev \
        libxml2-dev \
        libzzip-dev \
    && docker-php-ext-install iconv json mcrypt mbstring mysql mysqli pdo_mysql pdo_sqlite phar curl ftp hash session simplexml tokenizer xml xmlrpc zip \
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install gd

WORKDIR /var/www

CMD ["php-fpm"]

Et maintenant dans mon Dockerfile

web:
    image: nginx
    ports:
        - "80:80"
    volumes:
        - /var/www/docker/nginx:/etc/nginx/conf.d
        - /var/www:/var/www/local.dev
    links:
        - php:php
PHP:
    build: php
    volumes:
        - /var/www:/var/www/local.dev
        - /var/www/docker/php.ini:/usr/local/etc/php/php.ini
    links:
        - db:db
        - maildev:maildev

Comme précédemment je lie la configuration de nginx à un dossier de l'hôte afin de pouvoir toucher la configuration facilement. Pour PHP je me base sur le Dockerfile pour construire l'image et je fais un lien vers mon fichier php.ini (Par défaut l'image docker de PHP ne contient pas de php.ini donc je n'ai pas de risque d'écraser la configuration initiale).

Voici un exemple de ma configuration default.conf de nginx

server {

    listen 80;
    server_name localhost;
    root /var/www/local.dev/Lab/Laravel/public;
    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~* \.PHP$ {
        fastcgi_index   index.php;
        fastcgi_pass   php:9000;
        include         fastcgi_params;
        fastcgi_param   SCRIPT_FILENAME    $document_root$fastcgi_script_name;
    }

}

Composer

Un des problèmes de cette architecture c'est que maintenant PHP fonctionne dans un conteneur du coup comment peut-on utiliser PHP pour installer nos différentes dépendances ? En utilisant un conteneur évidemment :

docker run -ti --rm -v $(pwd):/app composer/composer install

Évidemment cette commande est un peu chiante à taper à chaque fois que l'on veut installer une librairie pour notre application. On va donc se configurer un petit alias pour nous simplifier la tâche.

alias composer='docker run -ti --rm -v $(pwd):/app composer/composer'

Si vous êtes sur boot2docker et que vous souhaitez que cet alias reste en mémoire il faudra alors modifier le fichier bootlocal.sh

echo 'alias composer='\''docker run -ti --rm -v $(pwd):/app composer/composer'\''' >> /home/docker/.ashrc

Dorénavant dès que vous voudrez utiliser composer il vous suffira alors de faire un composer install comme avant.

Conclusion

J'ai mis tous les fichiers utilisés pour créer ma configuration sur github histoire de vous simplifier la tâche.

Publié
Technologies utilisées
Auteur :
Grafikart
Partager