Serveur Web avec Docker : Nginx, PHP et PostgreSQL

Utiliser Docker pour mettre en place un serveur Web intégrant une base de données PostgreSQL, PHP-FPM et servi par Nginx.

5 minutes de lecture

Un serveur Web incluant généralement un logiciel serveur (Nginx), un interpréteur de script CGI (PHP) et d’un système de gestion de base de données (PostgreSQL), nous allons voir comment mettre en place ces trois composants en utilisant Docker.

Serveur Web avec Docker : Nginx, PHP et PostgreSQL

Nginx et PHP FPM

Récemment, j’expliquais comment simplement mettre en place un serveur Nginx qui supporte PHP à l’aide de Docker. L’image php:fpm-alpine utilisée y était minimaliste et n’embarquait que très peu d’extension PHP. De plus, aucune base de données n’était présente dans notre infra : c’est ce point là que nous allons corrigé ici.

Pour rappel, nous avions créé l’arborescence suivante dans laquelle nous retrouvons le fichier de configuration nginx.conf, les journaux Nginx et le contenu du site Web dans www :

1
2
3
4
5
6
7
8
├── docker-compose.yml
└── docker-web
    ├── log
    │   ├── access.log
    │   └── error.log
    ├── nginx.conf
    └── www
        └── index.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
version: '3'

services:
    web-nginx:
        image: nginx:stable-alpine
        container_name: web-nginx
        volumes:
            - "./docker-web/www:/usr/share/nginx/html:ro"
            - "./docker-web/log:/var/log/nginx"
            - "./docker-web/nginx.conf:/etc/nginx/nginx.conf:ro"
        ports:
            - "127.0.0.1:80:80"

    web-php:
        image: php:fpm-alpine
        container_name: web-php
        volumes:
            - "./docker-web/www:/script:ro"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
user                       nginx;
worker_processes           1;

error_log                  /var/log/nginx/error.log warn;
pid                        /var/run/nginx.pid;

events {
    worker_connections     1024;
}

http {
    include                /etc/nginx/mime.types;
    default_type           application/octet-stream;
    
    log_format             main  '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"';
    access_log             /var/log/nginx/access.log main;
    
    sendfile               on;
    keepalive_timeout      65;
    server_tokens          off;

    server {
        listen             80;
        server_name        localhost;
        
        location / {
            root           /usr/share/nginx/html;
            index          index.php index.html index.htm;
        }
        
        error_page         500 502 503 504 /50x.html;
        location = /50x.html {
            root           /usr/share/nginx/html;
        }

        location ~ \.php$ {
            root           /usr/share/nginx/html;
            include        fastcgi_params;
            fastcgi_pass   web-php:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /script$fastcgi_script_name;
        }
    }
}

Après avoir vérifié que nos deux containers web-nginx et web-php sont bien démarrés et que le serveur Nginx est accessible depuis un navigateur Web, nous allons modifier le fichier index.php pour qu’il se connecte à la base de données et affiche la version du SGBD (Système de Gestion de Base de Données, NDLR).

1
2
3
4
5
6
7
8
<?php
$dbconn = pg_connect('host=web-pgsql port=5432 dbname=foobar user=foobar password=foobar')
    or die('Could not connect');
     
echo '<pre>' . var_export(pg_version($dbconn), true) . '</pre>';

pg_close($dbconn);
?>

Si tous ce passe bien, vous devriez obtenir une erreur … wait, what?

Call to undefined function

Personnaliser l’image Docker de PHP

Le serveur nous indique qu’il y a une erreur : Call to undefined function pg_connect(). En effet, l’image Docker standard de PHP n’embarque que très peu de module. Il est précisé sur la page DockerHub PHP comment ajouter des extensions PHP supplémentaires. Nous allons donc créer notre propre image Docker de PHP pour y intégrer le client PostgreSQL.

Commençons par créer un fichier Dockerfile dans le répertoire docker-web/.build-php (vous pouvez le placer ailleurs). C’est dans ce fichiers que nous allons décrire comment construire l’image :

  • Nous partons de l’image php:fpm-alpine
  • Nous y ajoutons les paquets postgresql-dev afin de disposer des bibliothèques nécessaire aux extensions PHP PostgreSQL
  • L’image php:fpm-alpine embarque des scripts pour faciliter l’ajout d’extension, nous les utilisons pour ajouter pgsql et pdo_pgsql.
1
2
3
4
5
FROM php:fpm-alpine
RUN apk update && apk add --no-cache \
        postgresql-dev \
    && docker-php-ext-install -j$(nproc) pgsql \
    && docker-php-ext-install -j$(nproc) pdo_pgsql

Il faut également modifier le fichier docker-compose.yml pour préciser que nous voulons utiliser notre image plutôt que php:fpm-alpine. Nous renseignons pour cela la directive build afin d’indiquer à Docker Compose de construire l’image si nécessaire (premier démarrage, modification du fichier Dockerfile, etc…), ou d’utiliser la dernière image construite si aucune modification n’a été opérée depuis le dernier build.

1
2
3
4
5
web-php:
    build: ./docker-web/.build-php
    container_name: web-php
    volumes:
        - "./docker-web/www:/script:ro"
host:~# docker-compose up -d

Si tous ce passe bien, vous devriez obtenir une erreur … wait, again?

Unable to connect to PostgreSQL server

Le container PostgreSQL

Évidement, il nous manque le container PostgreSQL, que nous avons déjà nommé ci-dessus dans le fichier index.php : web-pgsql.

host:~# mkdir -p docker-web/data # Pour stocker le contenu de la base de données

Pas besoin de faire compliqué ici, nous allons simplement utiliser l’image officielle et Docker Compose pour configurer et démarrer le container PostgreSQL :

  • Le container est nommé web-pgsql ;
  • Le répertoire ./docker-web/data est monté pour y stocker le contenu de la base de données ;
  • Les variables d’environnement suivantes sont définies :
    • POSTGRES_PASSWORD, cette variable définit le mot de passe de l’utilisateur POSTGRES_USER pour PostgreSQL ;
    • POSTGRES_USER, cette variable créera l’utilisateur foobar. S’il n’est pas spécifié, l’utilisateur par défaut de postgres sera utilisé ;
    • POSTGRES_DB, cette variable créera la base de données foobar. S’il n’est pas spécifié,la valeur de POSTGRES_USER sera utilisée.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
version: '3'

services:
    web-nginx:
        image: nginx:stable-alpine
        container_name: web-nginx
        volumes:
            - "./docker-web/www:/usr/share/nginx/html:ro"
            - "./docker-web/log:/var/log/nginx"
            - "./docker-web/nginx.conf:/etc/nginx/nginx.conf:ro"
        ports:
            - "127.0.0.1:80:80"

    web-php:
        image: php:fpm-alpine
        container_name: web-php
        volumes:
            - "./docker-web/www:/script:ro"

    web-pgsql:
        image: postgres:alpine
        container_name: web-pgsql
        volumes:
            - "./docker-web/data:/var/lib/postgresql/data"
        environment:
            POSTGRES_USER: foobar
            POSTGRES_PASSWORD: foobar
            POSTGRES_DB: foobar
host:~# docker-compose up -d

Si tous ce passe bien, vous devriez obtenir une erreur … Ah non, pas cette fois-ci, maintenant que tout est en place la fonction pg_version() renvoie les infos des versions client/serveur de PostgreSQL.

Tout fonctionne

Références

  1. PHP (Docker Official Images) - Docker Hub
  2. Nginx (Docker Official Images) - Docker Hub
  3. PostgreSQL (Docker Official Images) - Docker Hub
Licence Creative Commons Cet article est mis à disposition selon les termes de la Licence Creative Commons Attribution-ShareAlike 4.0 International. Cet article a été publié il y a 394 jours, son contenu peut être inexact, voire erroné, et l'application des conseils ou consignes présents dans cet article doit être fait à votre propre appréciation. L'auteur de l'article ne pourra être tenu responsable des inconvénients pouvant résulter de l'application des conseils et consignes énoncés dans cet article.
comments powered by Disqus