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.