Créer un serveur d'applications Web - 2ème partie

Comment créer un serveur virtuel avec Docker correspondant à la configuration du serveur physique ?

2 avril 2017

Introduction

Cet article est le second d'une série concernant la création et l'exploitation d'un serveur d'applications Web.

Ce 2ème article traite de la génération de la machine Docker correspondant à la configuration du serveur physique créé dans le 1er article Créer un serveur d'applications Web - 1ère partie.

Les contraintes de notre serveur :

Docker

Docker permet d'empaqueter une application et ses dépendances dans un conteneur isolé. Dans notre cas, Docker permet de créer un serveur virtuel léger (également appelé conteneur) ayant la même configuration que le serveur physique.
Ce conteneur (ou serveur virtuel) peut être facilement déployé sur les différents postes des développeurs.

Tous les développeurs travaillent ainsi avec la même version du serveur comme s'il était installé sur leur poste :

1) Le développeur ne peut en aucun cas modifier :

2) Le développeur peut modifier au démarrage du serveur virtuel :

3) Le développeur peut continuellement modifier :

Génération du serveur virtuel (Configuration immutable)

supervisor

Docker ne peut exécuter qu'un seul processus à la fois, par exemple un démon nginx ou un démon php-fpm, mais nous souhaitons exécuter au démarrage et simultanément nginx, php-fpm et les différentes applications Node.js.

supervisor permet d'exécuter plusieurs processus / démons dans un conteneur.

Chaque processus géré par supervisor doit être configuré pour ne pas utiliser son propre mode daemon :

Les processus gérés par supervisor sont indiqués dans un fichier supervisord.conf qui sera automatiquement copié dans le serveur virtuel à sa génération.

Ci-dessous le contenu de notre fichier supervisord.conf :

[supervisord]
nodaemon=true               

[program:cron]
command=/usr/sbin/cron -f
numprocs=1
autostart=true
autorestart=true

[program:php-fpm7.0]
command=/usr/sbin/php-fpm7.0 -F
numprocs=1
autostart=true
autorestart=true

[program:nginx]
command=/usr/sbin/nginx
numprocs=1
autostart=true
autorestart=true

[program:glchat]
directory=/home/projects/nodejs/GL-Chat
command=nodejs app.js
autostart=true
autorestart=true

[program:gltodo]
directory=/home/projects/nodejs/GL-ToDo
command=nodejs app.js
autostart=true
autorestart=true

Dockerfile

Le serveur virtuel est généré à l'aide d'un fichier DockerFile dans lequel est décrit l'ensemble des applications et les dépendances à installer.

Dans notre cas, le DockerFile doit :

Ci-dessous le contenu de notre fichier Dockerfile :

# Utiliser Ubuntu 16.04
FROM ubuntu:16.04

RUN apt-get update && apt-get upgrade -y

# installer cron pour exécuter des tâches périodiquement
RUN apt-get install -y cron

# installer ca certificates
RUN apt-get install -y ca-certificates

# installer supervisor pour lancer les démons/services au démarrage
RUN apt-get install -y supervisor

# installer nginx
RUN apt-get install -y nginx

# installer php 7 et les librairies
RUN apt-get install -y php7.0 php7.0-fpm php7.0-sqlite3 php7.0-xml php7.0-curl

# installer node.js
RUN apt-get install -y nodejs

# créer un nouvel utilisateur appelé "projects"
RUN useradd -m projects

# ajouter une ligne au fichier de configuration de nginx
# pour indiquer d'inclure automatiquement le fichier nginx_shared_core.conf 
RUN echo "\ninclude /etc/nginx/nginx_shared_core.conf;" >> /etc/nginx/nginx.conf

# créer les répertoires par défaut et supprimer la configuration par défaut de nginx
RUN mkdir -p /run/php && mkdir -p /home/projects/www && rm /etc/nginx/sites-enabled/default

# copier le fichier local build/supervisord.conf dans le répertoire du serveur virtuel /etc/supervisor/supervisord.conf
COPY build/supervisord.conf /etc/supervisor/supervisord.conf

# partager le répertoire "/var/log" du serveur virtuel sur le poste local
VOLUME ["/var/log"]

# copier le fichier build/start.sh du poste local à la racine du serveur virtuel
COPY build/start.sh /start.sh

# exécuter start.sh au démarrage de la machine
ENTRYPOINT ["/bin/bash", "/start.sh"]

# Les ports 25 et 465 pour les mails et les ports 80 et 443 pour HTTP sont ouverts
EXPOSE 25 80 443 465

Pour permettre au développeur de visualiser immédiatement le résultat lorsqu'il modifie les codes sources, il faut désactiver le cache de nginx en ajoutant l'option sendfile off; au fichier de configuration nginx des différentes applications nommé projects.tmpl.

Exécution du serveur virtuel (Configuration modifiable)

Variables d'environnements

Les variables d'environnements du poste de travail de chaque développeur peuvent être différentes.

Une solution est de modifier les fichiers de configuration à la volée avec les variables d'environnements locales afin que le serveur virtuel s'initialise avec les bonnes valeurs à son démarrage.

Si par exemple le poste développeur a des variables d'environnements pour configurer son serveur de mail :

export SMTP_HOST="mail.host.net"
export SMTP_USERNAME="emmanuel.rkr@glicer.com"
export SMTP_PASSWORD="****************"

et si le fichier de configuration de nginx est :

fastcgi_param  SMTP_HOST  "${SMTP_HOST}";
fastcgi_param  SMTP_USERNAME "${SMTP_USERNAME}";
fastcgi_param  SMTP_PASSWORD "${SMTP_PASSWORD}";

Il devient avant le démarrage du serveur virtuel :

fastcgi_param  SMTP_HOST  "mail.host.net";
fastcgi_param  SMTP_USERNAME "emmanuel.rkr@glicer.com";
fastcgi_param  SMTP_PASSWORD "****************";

Le script render-templates.sh permet de remplacer automatiquement les chaînes du type {$variable} dans tous les fichiers d'un répertoire par la valeur de la variable d'environnement correspondante.

Le script est exécuté avant de démarrer le serveur virtuel :

./render-templates.sh ./sites-templates ./sites-enabled

Répertoires partagés et translation des ports

Pour éviter les conflits de ports entre les applications du développeur et ceux du serveur virtuel, l'option -p permet de translater les ports 80 et 443 du serveur nginx.

Quelques répertoires sont partagés entre le poste local et le serveur virtuel grâce à l'option -v. Les fichiers modifiés dans ces répertoires partagés du poste local sont également modifiés dans le serveur virtuel.

Ci-dessous la ligne de commande pour exécuter la serveur virtuel dans notre cas :

docker run -p 8888:80 -p 8889:443 --name projects \
-v $1:/etc/nginx/nginx_shared_core.conf \
-v $2:/etc/nginx/sites-enabled \
-v $3:/home/projects \
glicer/docker-x64-ubuntu-nginx-php7-nodejs

Prochain article

Le prochain article traitera du téléchargement et l'importation des statistiques d'utilisation du serveur dans Piwik.


Laisser un commentaire

noob - 11/04/2017 14:11

Merci pour la série d'articles .. Personnellement, j'ai appris beaucoup et ça m'intéresse d'autant plus que je souhaite me créer un environnement de dev via Docker (en remplacement de VM).
Sur cet article, j'ai quand même deux questions.
Si j'ai bien compris vous proposez un seul conteneur Docker pour l'ensemble des besoins (et pas un conteneur pour nginx, un pour nodejs, ...) ?
La deuxième question étant le sens de : "Le supervisor permet d'exécuter plusieurs processus / démons dans un conteneur. Chaque processus géré par supervisor doit être configuré pour ne pas utiliser son propre mode daemon" La machine lancée par le superviseur ne doit pas gérer ses daemon, c'est le supervisor qui s'en charge ? Dans le but que "Docker ne peut exécuter qu'un seul processus à la fois" et en référence au choix d'un seul Docker mentionné plus haut ? Thank's in advance


dev_glicer - 12/04/2017 16:14

@noob : Merci du retour
* Notre serveur de production n'utilise pas Docker, les services sont directement sur le serveur, Docker permet dans notre cas de simuler le serveur de production en mettant tous les services dans le même conteneur (comme le serveur).
Si votre serveur de production exploite Docker, il est effectivement préférable de faire un conteneur pour chaque service (un pour nginx, un pour nodejs, etc...)
* Comme Docker a été conçu pour exécuter qu'un seul processus, il faut utiliser supervisor qui est un seul processus qui permet d'exécuter plusieurs processus. :)