Faire tourner une application Symfony avec Docker et docker-compose

Pourquoi s'embêter à démarrer une machine virtuelle complète lorsque l'on peut lancer uniquement les conteneurs Docker dont nous avons besoin afin de développer nos applications Symfony ? C'est une des questions que s'est posée Jeremy Derusse dans sa conférence Docker dans le développement l'intégration continue du Symfony Live Paris 2015. Ces slides sont très intéressantes, je vous invite à aller y jeter un oeil, elle montrent la puissance de Docker et docker-compose mais demandent de la pratique derrière pour la bonne compréhension. C'est donc ce que j'ai fais en ce week-end pluvieux !

De quels conteneurs avons-nous besoin pour une application Symfony ?

Schéma des conteneurs

  • Nginx: Un serveur web,
  • PHP-FPM: Une instance PHP-FPM qui sera utilisée par Nginx,
  • MySQL: Une base de données, qui sera utilisée par PHP-FPM,
  • Application Symfony: Notre code applicatif, qui sera lu par PHP-FPM et Nginx.

Si nous effectuons ces liens entre conteneurs, voici donc ce à quoi va ressembler notre fichier docker-compose.yml :

application:
    image: symfony/code
    volumes:
        - symfony:/var/www/symfony
        - logs/symfony:/var/www/symfony/app/logs
    tty: true
db:
    image: mysql
    ports:
        - 3306:3306
    environment:
        MYSQL_ROOT_PASSWORD: root
        MYSQL_DATABASE: symfony
        MYSQL_USER: root
        MYSQL_PASSWORD: root
php:
    image: symfony/php-fpm
    expose:
        - 9000:9000
    volumes_from:
        - application
    links:
        - db
nginx:
    image: symfony/nginx
    ports:
        - 80:80
    links:
        - php
    volumes_from:
        - application
    volumes:
        - logs/nginx/:/var/log/nginx

Comme vous pouvez le voir, chacun de ces conteneurs utilise une image particulière (symfony/code, symfony/php-fpm et symfony/nginx). Il nous faut donc créer ces images.

Certaines de ces images importent des fichiers de configuration, j'ai tout mis à disposition sur le repository Github suivant : https://github.com/eko/docker-symfony.

Définition de l'image symfony/code

Commençons par la plus simple, cette image n'est qu'un conteneur destiné à embarquer notre code applicatif afin de le rendre disponible par les autres conteneurs. Le Dockerfile de ce conteneurs est donc très simple :

FROM debian:jessie

VOLUME /var/www/symfony

Définition de l'image symfony/php-fpm

Nous allons installer les packages PHP nécessaires (cli & fpm) à la bonne exécution de notre application Symfony et spécifier les fichiers de configuration à utiliser. Nous définissons également l'identifiant utilisateur 1000 à l'utilisateur "www-data" afin qu'il puisse avoir les droits d'écriture. Enfin, nous lançons notre instance PHP-FPM et exposons le port 9000.

FROM debian:jessie

RUN apt-get update && apt-get install -y php5-common php5-cli php5-fpm php5-mcrypt php5-mysql php5-apcu php5-gd php5-imagick php5-curl php5-intl

ADD symfony.ini /etc/php5/fpm/conf.d/
ADD symfony.ini /etc/php5/cli/conf.d/

ADD symfony.pool.conf /etc/php5/fpm/pool.d/

RUN usermod -u 1000 www-data

CMD ["php5-fpm", "-F"]

EXPOSE 9000

Définition de l'image symfony/nginx

Ici, nous installons Nginx, définissons sa configuration ainsi que la configuration de notre projet Symfony. Nous lançons le serveur web et exposons ses ports 80 et 443.

FROM debian:jessie

RUN apt-get update && apt-get install -y nginx

ADD nginx.conf /etc/nginx/
ADD symfony.conf /etc/nginx/sites-available/

RUN ln -s /etc/nginx/sites-available/symfony.conf /etc/nginx/sites-enabled/symfony
RUN rm /etc/nginx/sites-enabled/default

RUN echo "upstream php-upstream { server php:9000; }" > /etc/nginx/conf.d/upstream.conf

RUN usermod -u 1000 www-data

CMD ["nginx"]

EXPOSE 80
EXPOSE 443

Construisons les conteneurs

Maintenant que nos définitions de conteneurs sont prêtes, nous pouvons les construires en lançant les commandes suivantes :

$ docker build -t symfony/code code
$ docker build -t symfony/php-fpm php-fpm
$ docker build -t symfony/nginx nginx

Vous devriez donc maintenant avoir toutes ces images :

$ docker images

REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
symfony/nginx       latest              84b3420ddc51        7 hours ago         196.4 MB
symfony/php-fpm     latest              cc73fd15858f        7 hours ago         341.1 MB
symfony/code        latest              29780cb0ca24        8 hours ago         125.1 MB
debian              jessie              61e9c91c4f08        4 days ago          125.1 MB

Voilà, vos conteneurs sont prêts à être exécutés ! Il ne vous reste plus qu'à lancer l'orchestration mise en place via docker-compose en tapant :

$ docker-compose up

Après quoi, votre infrastructure tourne et votre application est accessible sur le port 80.

Quelques commandes utiles

Il vous est possible de voir la quantité de mémoire et CPU utilisés par tout vos conteneurs via la commande suivante :

$ docker stats $(docker ps -q)

CONTAINER           CPU %               MEM USAGE/LIMIT       MEM %               NET I/O
0c338e365f18        0.00%               516 KiB/1.961 GiB     0.03%               2.795 KiB/648 B
f3990e53e003        0.00%               12.74 MiB/1.961 GiB   0.63%               1.715 KiB/648 B
f7c8dd163afb        0.00%               455.9 MiB/1.961 GiB   22.70%              2.455 KiB/648 B
fdd84bfe972f        0.00%               5.109 MiB/1.961 GiB   0.25%               1.043 KiB/648 B

Vous pouvez également supprimer tous les conteneurs et/ou toutes les images d'un coup :

$ docker rm $(docker ps -a -q)
$ docker rmi $(docker images -q)