C'est quoi Docker et comment l'utiliser pour un projet symfony ?
Vendredi 31 Mars 2023 08:30

C'est quoi Docker et comment l'utiliser pour un projet symfony ?

Docker, l’une des meilleures façons de travailler… Et pourtant je ne l’utilisais pas encore.
Il faut dire que la dernière fois que j’ai tenté, ma machine n’en a pas voulu (j’en reparlerai plus tard). Mais la j’ai tenté l’aventure et oui c’est quand même magique comme solution.

Qu'est-ce que Docker ?

Celà fait quelques années que l’on entend parler de Docker et à moins d’avoir tenté l’expérience, le concept vous échappe peut-être.

Bien souvent les non-initiés associent Docker à des VM mais lorsque l’on y regarde de plus près, le fonctionnement diffère énormément de par sa gestion des ressources partagées.
Je vais faire simple, une vm est une installation complète OS + application et donc chaque vm augmente la consommation cpu et disque car vous avez chaque fois un nouvel OS de déployé. Si vous avez besoin d’une db sur chaque machine, vous duppliquerez l’application et les ressources nécessaires sur chaque machine.
Docker lui règle ce problème, vous avez une distribution linux, une série d'applications unique disponible et finalement des environnements indépendants et isolés (container) qui vont piocher dans les ressources (logiciel et physique) disponibles.
De ce fait, vous limitez grandement le besoin en ressource.


https://www.docker.com/resources/what-container/

En bref, une économie de ressource mais pas que. L’autre grand avantage est l’assurance d’un environnement fidèle peut importe l’emplacement d’installation. Votre container se base sur un fichier de config listant les “dépendances” dont il a besoin et lors d’un déploiement, Docker buildera une image correspondante à la configuration demandée.
Vous pouvez donc sur la même machine avoir un container avec un apache+PHP7.1+MySQL (oui c’est pas recommandé mais parfois il faut déterrer de vieux projets) et un autre container avec nginx+php8.2+PostgreSQL. Et comme vos versions sont fixées dans la config, chaque déploiement sera identique.

Prérequis

Historiquement, Docker ne pouvait tourner que sur Linux. Et encore aujourd’hui celà reste bien plus simple et moins contraignant. Mais j’en ai déjà parlé dans d’autres articles, je n’ai pas envie de jouer avec plusieurs machines ou plusieurs partitions. Et heureusement aujourd’hui Docker peut fonctionner sur windows à condition de bien le préparer mais de vraiment bien le préparer !

Je vais éviter de me répéter et simplement vous rediriger vers mon précédent article qui vous décrit en détail Comment fusionner Windows et Linux grâce à WSL. Étape obligatoire pour la suite !

Installer Docker Desktop

Pour utiliser Docker, il faut bien entendu installer Docker Desktop qui vous permettra de gérer vos containers et vos images. Pour ce faire, c'est par ici: https://www.docker.com/products/docker-desktop/

Rien de compliqué, un installateur en ligne droite.

Une fois l’installation terminée, vous devrez redémarrer votre machine.

En ligne de commande ?

Alors oui on a un utilitaire windows tout beau mais on va continuer à faire un peu de ligne de commande. Car en réalité le soft que l’on vient d’installer contient les ressources nécessaires mais ne permet pas grand chose de plus.

Si vous souhaitez connaître la liste des commandes Docker disponible

docker

Oui c’est simple à retenir.

Petit rappel, n’oubliez pas d’executer vos commandes dans le terminal unix pour plus de performance.
Pour y accéder depuis votre terminal windows:

wsl

Des images à collectionner

Je ne vous en ai pas encore parlé, mais Docker se base sur des images.
Par image, vous pouvez comparer ça à des snapshots configurés et configurables.
Impossible de déployer un container sans image.

Notion importante à savoir, une image hérite généralement d’une autre image.
On peut donc imaginer une image php8.2 qui hérite d’une image MySQL qui hérite d’une image Debian. Et de ce fait, une image php7.6 qui héritera de cette même image MySQL et donc également de cette même image Debian.

Si vous souhaitez voir les images disponibles publiquement: https://hub.docker.com/

Un container?

Bon tu nous a parlé d’images et maintenant des containers…
Les images ne font pas déjà tout?

Et bien non, les images représentent une installation vierge et donc pré-configurée. Dans certains cas (exemple plus bas), celà pourrait vous suffire mais il est cependant possible de créer vos propres images réutilisables et préconfigurées par vos soins mais c’est une autre histoire.

Je vais vous proposer deux cas de figure:

  • L’utilisation d’une image native pour mettre en place un serveur MySQL/mariadb/PostgreSQL
  • La mise en place d’un container pour un projet Symfony sur base d’image php8.2-apache et pouvant utiliser composer

Réseaux virtuels

Autre notion à connaître, celle des réseaux. Par défaut, les containers sont isolés les uns des autres. Mais dans certains cas comme dans les exemples que nous allons voir, il est intéressant de pouvoir communiquer d’un à l’autre comme par exemple entre un container contenant une base de donnée et un container contenant le projet web.

Pour ce faire, rien de compliqué, une nouvelle commande fait le job.

docker network create myNetworkName

Vous pouvez créer autant de réseau que vos besoins le demandent.
Mais vous pouvez aussi simplement mettre tous les projets dans le même réseau…

Pour les exemples qui vont suivre, nous allons créer le réseau “dev”.

docker network create dev

Installer un container MySQL / MariaDB / PostgreSQL

Comme je vous le disais, on va commencer par un petit exercice simple: mettre en place une base de données et ce via via une simple ligne de commande.

MySQL

docker run -d --name db-mysql -e MYSQL_ROOT_PASSWORD=admin -p 3306:3306 --network dev mysql

MariaDB

docker run -d --name db-mariadb -e MARIADB_USER=kevin -e MARIADB_PASSWORD=admin -e MARIADB_ROOT_PASSWORD=admin -p 3306:3306 --network dev mariadb

PostgreSQL

docker run -d --name db-postgres -e POSTGRES_PASSWORD=admin -p 5432:5432 --network dev postgres

Mais elle fait quoi cette commande en fait?

  • -d autorise Docker à lancer ce container en arrière plan
  • --name est le nom de votre container. Il n'est connu que de vous et doit être unique. Il vous servira aussi dans le cas où vous voudriez communiquer vos containers entre eux. Si vous ne l'indiquez pas, un identifiant random sera généré… moins pratique.
  • -e spécifie une variable d'environnement. Ici ces variables servent pour la configuration des users des dbs.
  • -p indique le port que doit utiliser le container
  • –network indique le réseau interne que doit utiliser le container
  • La dernière valeur (mysql/mariadb/postgres) indique l'image à utiliser

Donc en exécutant cette simple commande, vous avez un serveur installé et lancé !

Ce container restera actif aussi longtemps que votre Docker.
Pour la prochaine utilisation, utilisez la commande suivante pour démarrer votre container:

docker start db-mysql
docker start db-mariadb
docker start db-postgres

Pour y accéder:

MySQL
Host: localhost
Port: 3306
User: root
Password: admin
MariaDB
Host: localhost
Port: 3306
User: root
Password: admin
PostgreSQL
Host: localhost
Port: 5432
User: postgres
Password: admin

Bon ca c'était relativement simple! Nous avons créé un container basé sur une simple image et les quelques infos ont été passées en paramètre.

Installer un container Apache/php

Cette fois, nous allons créer un container lié à un projet précis et vous connaissez mes habitudes, ce sera un projet Symfony. Nous allons nous baser sur trois images:

  • php8.2-apache
  • composer
  • node

Petite remarque pour composer. Si vous cherchez sur internet, vous trouverez généralement une tout autre méthode consistant à récupérer le fichier composer depuis le site officiel par une commande curl… Perso, je n’ai jamais réussi à l'utiliser. Je vous propose donc une alternative plus propre via le docker-compose

Nous allons commencer par créer un fichier docker-compose.yml à la racine de notre projet.

Ce fichier va contenir nos différents services:

version: '3'

services:

  web:
    container_name: demosf6_web
    build: docker
    volumes:
      - ./:/var/www/html
    ports:
      - "8000:80"
    networks:
      - dev

  node:
    container_name: demosf6_node
    image: node:16-alpine3.13
    command: ["yarn", "encore", "dev"]
    working_dir: /var/www/app
    volumes:
      - .:/var/www/app

  composer:
    container_name: demosf6_composer
    image: composer
    command: ["composer", "install", "--ignore-platform-reqs"]
    volumes:
      - ./:/app
    depends_on:
      - web

networks:
  dev:
    name: dev
    external: true
  • web, node et composer sont des alias pour les services
  • build: Indique que nous n’allons pas nous baser sur une image existence mais bien sur une configuration personnalisée qui se trouvera dans le fichier /docker/Dockerfile
  • container_name est le com du container qui sera utilisé pour le démarrer si nécessaire. Pensez à le préfixer avec le nom de votre projet car si vous avec plusieurs projets avec un container semblable (composer par exemple) cela va poser problème… il y a peut être moyen de le réutiliser ou de le partager mais je n’ai pas trouvé l’info.
  • ports: indique le port externe et interne du container
  • networks: Spécifie les réseaux auxquels est connecté le container
  • volumes liste des liens symboliques entre le projet et l’emplacement réel
  • image fait référence a une image du https://hub.docker.com/
  • command: Indique la commande à exécuter au lancement

La façon d’écrire est… spéciale mais c’est la méthode recommandée.
Il vous est cependant possible de l’écrire différemment

command: bundle exec thin -p 3000
command: ["bundle", "exec", "thin", "-p", "3000"]

https://docs.docker.com/compose/compose-file/compose-file-v3/#command

A l’heure actuelle il nous est impossible de lancer notre Docker car nous n’avons pas encore configurer le fichier /docker/Dockerfile

FROM php:8.1-apache

Indique que nous nous basons sur l’image php:8.1-apache pour créer notre propre image

Nous avons ensuite besoin d'ajouter des extensions php. Pour ce faire on va se baser sur docker-php-extension-installer.

ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions && \
    install-php-extensions xdebug gd pdo_mysql intl

Nous devons ensuite modifier la config apache pour accéder directement au dossier /public

ENV APACHE_DOCUMENT_ROOT=/var/www/html/public
RUN a2enmod rewrite && service apache2 restart && \
    sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf && \
    sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf

Si l’on se réfère aux bonnes pratiques, il est conseillé de n’utiliser qu’une seule fois la commande RUN car lors du processus de création de l’image, Docker va créer une nouvelle image à chaque RUN et se baser sur cette nouvelle image pour le prochain RUN… Pas certain que ce soit clair mais si vous devez retenir quelque chose: Évitez de multiplier les RUN tant que possible.

FROM php:8.1-apache

ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
ENV APACHE_DOCUMENT_ROOT=/var/www/html/public
RUN chmod +x /usr/local/bin/install-php-extensions && \
    install-php-extensions xdebug gd pdo_mysql intl && \
    a2enmod rewrite && service apache2 restart && \
    sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf && \
    sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf

Il ne vous reste plus qu’à lancer la commande suivante en étant dans le dossier de votre projet

docker-compose up -d --build

Essayer d'accéder à http://localhost:8000/

Victoire, votre projet Symfony est accessible !

Lier la base de donnée

Vous trouverez souvent des exemples sur le net de fichier docker-compose.yml qui intègre également la base de données.
Dans mon cas, en développement local, je préfère avoir ma db séparée de mon projet, question d’habitude.

Comment faire? Hé bien nous l’avons déjà fait plus tôt dans cet article lors de la création du network!
“Ha mais je n’ai qu’a configurer mon .env Symfony pour le lier?”
Alors oui mais petite spécificité, vous ne pouvez pas attaquer notre cher 127.0.0.1 pour la simple et bonne raison que vos containers reçoivent une adresse IP différente et qui risque d’être différente à chaque lancement !

Ne vous inquiétez pas, ça va encore être plus simple !
Indiquez simplement comme host de votre db, le nom de votre container Docker.

DATABASE_URL="mysql://root:admin@db-mariadb:3306/sf6demo?serverVersion=mariadb-10.11.2&charset=UTF8"

Votre projet communique désormais avec votre db !

Le mot de la fin

Quelques concepts à comprendre mais honnêtement un tel confort si comme moi vous travaillez sur plusieurs machines en même temps.
Il suffit d’installer Docker, de faire un git push, de lancer les docker-compose et votre environnement est prêt. Aucun souci de compatibilité de version php,node,... Tout se lance tout seul, ca compose, ca compile,...
Bref, je suis conquis !