feat: support running using Docker Compose

This commit adds a Docker Compose file which will run the application
using docker. The application is fronted by Traefik, and runs using
PHP-fpm. nginx is used to accept queries from Traefik and relay them to
the PHP-fpm containers (Traefik doesn't support FastCGI directly).
Sessions are shared between the various PHP containers using memcached.

Part of this work is based on [this repo](https://github.com/wandersonwhcr/docker-nginx-php-fpm)
This commit is contained in:
Emmanuel BENOîT 2025-01-05 21:09:27 +01:00
parent bdf56ece83
commit 5110ded7e5
Signed by: Emmanuel BENOîT
SSH key fingerprint: SHA256:l7PFUUF5TCDsvYeQC9OnTNz08dFY7Fvf4Hv3neIqYpg
8 changed files with 201 additions and 0 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
includes/config.inc.php
database/config.sql
site/.htaccess
docker/.env

37
docker/Dockerfile Normal file
View file

@ -0,0 +1,37 @@
# syntax=docker/dockerfile:1.7-labs
ARG PHP_VERSION=8.3
ARG POSTGRESQL_VERSION=17
#
# Application image
#
FROM php:${PHP_VERSION}-fpm-alpine AS application
RUN <<EOF
set -e
apk add --no-cache fcgi unzip postgresql-dev libmemcached-dev \
libmemcached autoconf pkgconfig g++ zlib-dev make
docker-php-ext-install pgsql pdo pdo_pgsql
pecl install memcached
docker-php-ext-enable memcached
mkdir -p /var/www/tasks
echo "pm.status_path = /status" >> /usr/local/etc/php-fpm.d/zz-docker.conf
echo "chdir = /var/www/tasks/site" >> /usr/local/etc/php-fpm.d/zz-docker.conf
curl --silent --remote-name https://raw.githubusercontent.com/renatomefi/php-fpm-healthcheck/v0.5.0/php-fpm-healthcheck
install --owner root --group root --mode 755 php-fpm-healthcheck /usr/local/bin/php-fpm-healthcheck
rm -rf php-fpm-healthcheck
mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
EOF
HEALTHCHECK --interval=5s \
CMD php-fpm-healthcheck || exit 1
COPY --parents site includes arse /var/www/tasks/
#
# Database image
#
FROM postgres:${POSTGRESQL_VERSION} AS database
RUN mkdir /opt/tasks
COPY --parents database.sql database /opt/tasks/

15
docker/app-config.inc.php Normal file
View file

@ -0,0 +1,15 @@
<?php
ini_set('session.save_handler', 'memcached');
ini_set('session.save_path', getenv('MEMCACHED_HOST') . ':11211');
$config[ 'core' ][ 'db' ][ 'host' ] = getenv('DB_HOST');
$config[ 'core' ][ 'db' ][ 'name' ] = getenv('DB_NAME');
$config[ 'core' ][ 'db' ][ 'user' ] = getenv('DB_USER');
$config[ 'core' ][ 'db' ][ 'password' ] = file_get_contents(getenv('DB_PASSWORD_FILE'));
$config[ 'core' ][ 'pages' ][ 'baseURL' ] = getenv('BASE_URL') ?: null;
$config[ 'core' ][ 'pages' ][ 'baseTitle' ] = 'Tasks';
/* Minimal time before a completed task can be deleted. The default is one week. */
// $config[ 't-data' ][ 'minDeletionTime' ] = 7 * 3600 * 24;

93
docker/compose.yml Normal file
View file

@ -0,0 +1,93 @@
name: tasks
services:
database:
container_name: ${COMPOSE_PROJECT_NAME}-database
build:
context: ..
dockerfile: ./docker/Dockerfile
target: database
args:
POSTGRESQL_VERSION: ${POSTGRESQL_VERSION:-17}
restart: always
shm_size: 128mb
volumes:
- database:/var/lib/postgresql/data
configs:
- source: db_config
target: /opt/tasks/database/config.sql
- source: db_loader
target: /docker-entrypoint-initdb.d/init-sql.sh
environment:
DB_NAME: ${DB_NAME}
DB_USER: ${DB_USER}
DB_PASSWORD_FILE: /run/secrets/db_password
POSTGRES_PASSWORD_FILE: /run/secrets/pg_password
secrets:
- db_password
- pg_password
application:
build:
context: ..
dockerfile: ./docker/Dockerfile
target: application
args:
PHP_VERSION: ${PHP_VERSION:-8.3}
user: ${UID:-1000}:${GID:-1000}
configs:
- source: app_config
target: /var/www/tasks/includes/config.inc.php
environment:
BASE_URL: ${BASE_URL}
DB_HOST: database
DB_NAME: ${DB_NAME}
DB_USER: ${DB_USER}
DB_PASSWORD_FILE: /run/secrets/db_password
MEMCACHED_HOST: memcached
secrets:
- db_password
nginx:
image: nginx:${NGINX_VERSION:-1.19}-alpine
labels:
- traefik.enable=true
- traefik.http.routers.default.rule=Host(`${PHP_HOSTNAME:-localhost}`)
- traefik.port=80
volumes:
- ./nginx-templates:/etc/nginx/templates:ro
- ../site:/var/www/tasks/site:ro
memcached:
image: memcached:${MEMCACHED_VERSION:-1.6}-alpine
traefik:
container_name: ${COMPOSE_PROJECT_NAME}-traefik
image: traefik:${TRAEFIX_VERSION:-2.4}
ports:
- ${HOST_PORT}:80
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
command:
- --entrypoints.web.address=:80
- --log.level=DEBUG
- --providers.docker.exposedbydefault=false
- --providers.docker=true
configs:
app_config:
file: ./app-config.inc.php
db_config:
file: ./db-config.sql
db_loader:
file: ./init-sql.sh
volumes:
database:
secrets:
db_password:
environment: DB_PASSWORD
pg_password:
environment: PG_PASSWORD

6
docker/db-config.sql Normal file
View file

@ -0,0 +1,6 @@
\getenv db_name DB_NAME
\getenv webapp_user DB_USER
\getenv webapp_password DB_PASSWORD
CREATE ROLE :webapp_user WITH LOGIN ENCRYPTED PASSWORD :'webapp_password';
CREATE DATABASE :db_name WITH TEMPLATE=template0 ENCODING='UTF8';

23
docker/env.example Normal file
View file

@ -0,0 +1,23 @@
# Version of software components
MEMCACHED_VERSION=1.6
NGINX_VERSION=1.19
PHP_VERSION=8.3
POSTGRESQL_VERSION=17
TRAEFIX_VERSION=2.4
# Port to bind on the host
HOST_PORT=80
# UID and GID to run php-fpm as
UID=1000
GID=1000
# PostgreSQL superuser password
PG_PASSWORD=...
# Database name
DB_NAME=tasks
# Database access username
DB_USER=tasks
# Database access password
DB_PASSWORD=...
# Base URL, if autodetection fails
BASE_URL=

5
docker/init-sql.sh Normal file
View file

@ -0,0 +1,5 @@
#!/bin/bash
set -e
cd /opt/tasks
DB_PASSWORD="`cat "$DB_PASSWORD_FILE"`" psql -vON_ERROR_STOP=1 -f database.sql

View file

@ -0,0 +1,21 @@
server {
listen 80;
index index.php;
root /var/www/tasks/site;
location / {
try_files $uri /index.php$request_uri;
}
location /index.php/ {
fastcgi_pass application:9000;
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
include fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_NAME "/index.php";
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
}
}