Docker-compose pour la production ( 1 / 3 )

Docker-compose pour la production ( 1 / 3 )

Je me donne maintenant comme objectif de mettre ce "Hello World" en production. Comme je le disais dans l'article précédent, le but va être d'envoyer mes images de production sur le Container Registry de gitlab.

Texte alternatif

Pour commencer, identifiez-vous à votre registry à l'aide la commande docker docker login registry.gitlab.com

Une fois authentifié, rajoutez à la racine de votre projet un .env contenant la variable d'environnement pointant vers votre container registry.

# .envCONTAINER_REGISTRY=registry.gitlab.com/yoann.chocteau/demo_docker

Cette variable d'environnement sera utilisée par docker-compose lors de nos builds et nos pushs.

Maintenant nous allons devoir construire nos images de production à l'aide d'un Dockerfile que nous allons mettre à la racine de notre projet.

# Je définis un alias à l'image que je vais créer, app_nginxFROM nginx:1.19.3-alpine as app_nginx# On lui ajoute la conf qui va lui permettre de communiquer avec PHPADD docker/nginx/conf.d /etc/nginx/conf.d# Et bien sûr, on ajoute notre fichier index.php contenant notre fabuleux Hello World sur /srv/app/ COPY index.php /srv/app/ # Même principe pour app_phpFROM php:7.4.12-fpm-alpine as app_phpRUN docker-php-ext-install mysqli pdo pdo_mysqlCOPY index.php /srv/app/

Une fois que mon Dockerfile est créé, je vais pouvoir mettre en place mon docker-compose-build.yaml.

version: '3.8' services:    app:        image: ${CONTAINER_REGISTRY}/app        build:            context: ./            target: app_php            cache_from:                - ${CONTAINER_REGISTRY}/app     nginx:        image: ${CONTAINER_REGISTRY}/nginx        build:            context: ./            target: app_nginx            cache_from:                - ${CONTAINER_REGISTRY}/nginx

Ce docker-compose-build.yaml va me permettre de créer deux images pour la production, nginx et app. On notera que j'ai à la fois une clé image et une clé build. La clé build permet de définir où se trouve le Dockerfile pour créer mon image ainsi que la cible ( app_nginx / app_php ) et la clé image contient le nom de donné à mon image une fois construite.

On va pouvoir construire nos deux images de production avec la commande docker-compose -f docker-compose-build.yaml build --pull, l'option -f permettant de spécifier à docker-compose le fichier à utiliser pour construire les images.

❯ docker-compose -f docker-compose-build.yaml build --pull...Successfully built c4f7135b307fSuccessfully tagged registry.gitlab.com/yoann.chocteau/demo_docker/nginx:latest

Il ne nous reste plus qu'à envoyer ces images sur notre Container Registry avec la commande suivante

❯ docker-compose -f docker-compose-build.yaml pushPushing app (registry.gitlab.com/yoann.chocteau/demo_docker/app:latest)...The push refers to repository [registry.gitlab.com/yoann.chocteau/demo_docker/app]...

Si tout s'est bien passé, vous devriez les voir sur votre gitlab.

Texte alternatif

Ces images là contiennent désormais mon application de production. Un simple "Hello World" pour le moment, mais ce code est dans mon image ( à la différence de ma stack de dev présentée précédemment où il s'agissait d'un montage de volume ).

Maintenant, je vais devoir faire mon docker-compose-prod.yaml qui lui aura la responsabilité de déployer mes conteneurs en production.

version: '3.8' services:    app:        image: ${CONTAINER_REGISTRY}/app        # En cas de problème... redémarre !        restart: always     nginx:        image: ${CONTAINER_REGISTRY}/nginx        restart: always        depends_on:            - app        ports:            - 8080:80

Comme on peut le constater, il ressemble beaucoup à mon docker-compose.yml mais ne possède pas de clé build pour les services nginx et app, et pas non plus de base de donnée.

Faites un docker-compose down si votre stack de développement est encore en cours, puis lancez la commande suivante :

❯ docker-compose -f docker-compose-prod.yaml up -d

Maintenant, si vous accédez à localhost:8080, mise à part l'erreur SQL Name does not resolve puisqu'on s'est pas encore occupé de la base de données, on peut dire que ça fonctionne. Je suis proche de voir mon Hello World ! 😃

Pourquoi la base de données n'est pas présente dans le docker-compose-prod.yaml ?

Et bien simplement parce que je veux être sûr de ne pas perdre les données de mon site. Je ne veux pas que ces données soient stockées dans un conteneur ou sur un volume docker. D'ailleurs, sur une production "sérieuse", on choisira en général d'utiliser un service de base de données infogéré.

Pour les besoins de la démo, je vais créer ma base de données mariadb sur docker mais ailleurs sur mon système pour bien garder à l'esprit que ma base de données de production ne doit pas se trouver dans mon docker-compose-prod.yaml.

J'ai donc créé un répertoire mariadb dans lequel j'ai mis le fichier docker-compose.yaml suivant

version: '3.8' services:    db:        image: mariadb:10.5.6        environment:            MYSQL_ALLOW_EMPTY_PASSWORD: "yes"        volumes:            - ./data:/var/lib/mysql

Si je relance mon docker-compose -f docker-compose-prod.yaml up -d, en l'état cela ne réglera pas mon erreur SQL. Le conteneur db n'est pas dans le même réseau que le conteneur app. Je vais donc devoir créer un réseau demo_docker afin que ma base de données et mon conteneur app puissent communiquer ensemble.

❯ docker network create demo_docker      fa7f4ac3fd8e27e4423f62157c98efaf209ba8a9a42bf7980ac1011baae01166

Une fois mon network créé, je vais devoir le définir dans mes fichiers docker-compose

# mariadb/docker-compose.yaml   version: '3.8' services:    db:        image: mariadb:10.5.6        environment:            MYSQL_ALLOW_EMPTY_PASSWORD: "yes"        # J'attache ma base de données à mon réseau demo_docker        networks:            - demo_docker        volumes:            - ./data:/var/lib/mysql networks:    # Je définis que demo_docker est un réseau externe qui se nomme demo_docker    demo_docker:        external:            name: demo_docker

Et je fais les mêmes adaptations sur mon docker-compose-prod.yaml de mon projet.

# demo_docker/docker-compose-prod.yamlversion: '3.8' services:    app:        image: ${CONTAINER_REGISTRY}/app        restart: always        networks:            - demo_docker     nginx:        image: ${CONTAINER_REGISTRY}/nginx        restart: always        networks:            - demo_docker        depends_on:            - app        ports:            - 8080:80 networks:  demo_docker:    external:      name: demo_docker

Je redémarre les conteneurs de mon projet

❯ docker-compose -f docker-compose-prod.yaml up -d --force-recreate 

ainsi que celui de mariadb

❯ docker-compose up -d --force-recreate 

Et là je retombe sur cette erreur là... plutôt bon signe !

Texte alternatif

Il ne me reste plus qu'à créer ma base de données comme dans l'article précédent, et c'est bon 😁

Texte alternatif

... Par contre, j'ai un problème ! La chaine de connexion est en dur dans le fichier index.php... et ça, c'est vraiment pas très propre ! Comment faire la distinction entre ma base de dev et celle de prod ? Et bien, la solution réside dans les variables d'environnement 😁

Pour commencer, je vais modifier mon script index.php

<?php try {    $dbh = new PDO($_SERVER['DB_DSN'], $_SERVER['DB_USER'], $_SERVER['DB_PASSWORD']);} catch (PDOException $e) {    print "Erreur !: " . $e-?>getMessage() . "<br></br>";    die();}echo "Hello World !!";

Ensuite, dans mon fichier docker-compose.yaml et dans le fichier docker-compose-prod.yaml, je vais définir ces variables d'environnements pour le conteneur app.

    app:        ...        environment:            - DB_DSN            - DB_USER            - DB_PASSWORD

Et pour finir, je dois juste rajouter dans mon fichier .env ces variables d'environnements.

DB_DSN=mysql:host=db;dbname=demoDB_USER=rootDB_PASSWORD=

Et voilà 😁 Maintenant, il me reste plus qu'à déployer tout cela sur un serveur !

Ça peut aussi vous intéresser