01/06/2018 - DOCKER, SYMFONY, NGINX
This example makes use of Nginx, MySQL and PHP-FPM docker containers to run Symfony application. All you have to do is, create files listed below and copy your symfony application into the root folder or do the opposite. After all, just run the docker-compose command to build the system. That's all!
I would suggest you to checkout the alternative structure version right at the bottom of this post.
$ tree -a
.
├── docker
│ ├── mysql
│ │ ├── Dockerfile
│ │ ├── init.sh
│ │ └── mysqld.cnf
│ ├── nginx
│ │ ├── default.conf
│ │ ├── Dockerfile
│ │ └── nginx.conf
│ └── php
│ ├── app.sh
│ ├── Dockerfile
│ ├── init.sh
│ ├── install.sh
│ ├── php.ini
│ └── www.conf
├── docker-compose.yml
├── public # You can remove this
│ └── index.php
├── .env
└── .env.dist # Create a .env file from this
###> symfony ###
APP_ENV=dev
APP_SECRET=secret
APP_DB_USER=user
APP_DB_PASS=pass
###< symfony ###
###> docker ###
MYSQL_ROOT_PASSWORD=root
###< docker ###
Defining the php
service as shown below gives you chance to copy application specific config files into container. For instance, a file from app/config
folder.
version: '3'
services:
mysql:
build:
context: ./docker/mysql
hostname: nginx
user: mysql
ports:
- 3306:3306
volumes:
- ./var/database:/var/lib/mysql:rw
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
APP_DB_USER: ${APP_DB_USER}
APP_DB_PASS: ${APP_DB_PASS}
php:
build:
context: ../symfony4-docker-platform
dockerfile: ./docker/php/Dockerfile
args:
APP_ENV: ${APP_ENV}
hostname: php
depends_on:
- mysql
ports:
- 9000:9000
volumes:
- .:/app:cached
working_dir: /app
environment:
APP_ENV: ${APP_ENV}
APP_SECRET: ${APP_SECRET}
APP_DB_USER: ${APP_DB_USER}
APP_DB_PASS: ${APP_DB_PASS}
nginx:
build:
context: ./docker/nginx
hostname: nginx
depends_on:
- mysql
- php
ports:
- 80:80
volumes:
- .:/app:cached
FROM mysql:5.7.22
COPY init.sh /docker-entrypoint-initdb.d
COPY mysqld.cnf /etc/mysql/mysql.conf.d
#!/bin/bash
printf "\n\033[0;44mPreparing the database\033[0m\n"
# Makes sure that the database is up before running database queries
echo "Checking if the database is up ..."
while ! mysqladmin ping -h"localhost" --silent; do
echo "Waiting for database to come up ..."
sleep 2
done
echo "Database is up ..."
# Create an application specific non-root user with all privileges
create="CREATE USER IF NOT EXISTS '${APP_DB_USER}'@'%' IDENTIFIED BY '${APP_DB_PASS}';"
grant="GRANT ALL PRIVILEGES ON *.* TO '${APP_DB_USER}'@'%' IDENTIFIED BY '${APP_DB_PASS}' WITH GRANT OPTION;"
mysql -u root -p${MYSQL_ROOT_PASSWORD} -e "$create$grant"
[mysqld]
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
datadir = /var/lib/mysql
symbolic-links = 0
character_set_server=utf8
collation_server=utf8_unicode_ci
explicit_defaults_for_timestamp = 1
FROM nginx:1.13.8
COPY default.conf /etc/nginx/conf.d
COPY nginx.conf /etc/nginx
server {
listen 80 default_server;
server_name localhost; # OR app.com www.app.com
root /app/public;
location / {
try_files $uri /index.php$is_args$args;
}
location ~ ^/index\.php(/|$) {
fastcgi_pass php:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_hide_header X-Powered-By;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
internal;
}
location ~ \.php$ {
return 404;
}
error_log /var/log/nginx/app_error.log;
access_log /var/log/nginx/app_access.log;
}
user nginx;
# 1 worker process per CPU core.
# Check max: $ grep processor /proc/cpuinfo | wc -l
worker_processes 2;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
# Tells worker processes how many people can be served simultaneously.
# worker_process (2) * worker_connections (2048) = 4096
# Check max: $ ulimit -n
worker_connections 2048;
# Connection processing method. The epoll is efficient method used on Linux 2.6+
use epoll;
}
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;
# Used to reduce 502 and 504 HTTP errors.
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
# The sendfile allows transfer data from a file descriptor to another directly in kernel.
# Combination of sendfile and tcp_nopush ensures that the packets are full before being sent to the client.
# This reduces network overhead and speeds the way files are sent.
# The tcp_nodelay forces the socket to send the data.
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# The client connection can stay open on the server up to given seconds.
keepalive_timeout 65;
# Hides Nginx server version in headers.
server_tokens off;
# Disable content-type sniffing on some browsers.
add_header X-Content-Type-Options nosniff;
# Enables the Cross-site scripting (XSS) filter built into most recent web browsers.
# If user disables it on the browser level, this role re-enables it automatically on serve level.
add_header X-XSS-Protection "1; mode=block";
# Prevent the browser from rendering the page inside a frame/iframe to avoid clickjacking.
add_header X-Frame-Options DENY;
# Enable HSTS to prevent SSL stripping.
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
# Prevent browser sending the referrer header when navigating from HTTPS to HTTP.
add_header 'Referrer-Policy' 'no-referrer-when-downgrade';
# Sets the maximum size of the types hash tables.
types_hash_max_size 2048;
# Compress files on the fly before transmitting.
# Compressed files are then decompressed by the browsers that support it.
gzip on;
include /etc/nginx/conf.d/*.conf;
}
FROM php:7.2-fpm
ARG APP_ENV
COPY ./docker/php/init.sh /tmp
RUN chmod +x /tmp/init.sh
RUN /tmp/init.sh
COPY ./docker/php/install.sh /tmp
RUN chmod +x /tmp/install.sh
RUN /tmp/install.sh
COPY ./docker/php/www.conf /usr/local/etc/php-fpm.d/www.conf.default
COPY ./docker/php/php.ini /usr/local/etc/php/conf.d
COPY ./docker/php/app.sh /
RUN chmod +x /app.sh
RUN /app.sh
ENV LANG en_GB.UTF-8
ENV LANGUAGE en_GB:en
ENV LC_ALL en_GB.UTF-8
RUN apt-get autoremove --purge \
&& apt-get -y clean \
&& rm -rf /var/lib/apt/lists/*
[PHP]
date.timezone = Europe/London
log_errors = On
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
display_errors = Off
max_execution_time = 60
memory_limit = 256M
[opcache]
; http://symfony.com/doc/current/performance.html
opcache.enable_cli = 1
opcache.memory_consumption = 256
opcache.max_accelerated_files = 20000
realpath_cache_size = 4096K
realpath_cache_ttl = 600
[www]
user = www-data
group = www-data
listen = nginx:9000
; Dynamicaly chooses how the process manager will control the number of child processes.
pm = dynamic
; The maximum number of child processes to be created.
; This option sets the limit on the number of simultaneous requests that will be served.
; Availalbe RAM in MB / Average RAM used by php-fpm processes in MB = max_children
; 1500MB / 30MB = 50 (minus a bit)
pm.max_children = 40
; The number of child processes created on startup.
; min_spare_servers + (max_spare_servers - min_spare_servers) / 2
pm.start_servers = 2
; The desired minimum number of idle server processes.
pm.min_spare_servers = 2
; The desired maximum number of idle server processes.
; 2 or 4 times of the CPU core
pm.max_spare_servers = 4
; The number of requests each child process should execute before respawning.
; This can be useful to work around memory leaks in 3rd party libraries.
pm.max_requests = 500
#!/bin/bash
printf "\n\033[0;44mChecking the existence of 'APP_ENV' variable\033[0m\n"
if [[ -z "${APP_ENV}" ]]
then
printf "\033[0;31mVariable does not exist.\033[0m\n\n"
exit 1;
fi
printf "\033[0;32mVariable exists.\033[0m\n\n"
#!/bin/bash
printf "\n\033[0;44mInstalling system packages for the \"${APP_ENV}\" environment\033[0m\n"
apt-get update
apt-get install -y --no-install-recommends zip unzip nano tree locales
sed -i 's/# en_GB.UTF-8 UTF-8/en_GB.UTF-8 UTF-8/g' /etc/locale.gen
ln -s /etc/locale.alias /usr/share/locale/locale.alias
locale-gen en_GB.UTF-8
ln -snf /usr/share/zoneinfo/Europe/London /etc/localtime
echo Europe/London > /etc/timezone
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
docker-php-ext-install opcache pdo_mysql
docker-php-ext-enable opcache
if [ "${APP_ENV}" == "dev" ] || [ "${APP_ENV}" == "test" ]
then
pecl install xdebug
docker-php-ext-enable xdebug
fi
#!/bin/bash
printf "\n\033[0;44mPreparing the application for the \"${APP_ENV}\" environment\033[0m\n"
if [ "${APP_ENV}" == "dev" ] || [ "${APP_ENV}" == "test" ]
then
echo "Run symfony commands for \"dev\" or \"test\" environments"
# composer install --no-interaction
else
echo "Run symfony commands for \"prod\" or \"stag\" environments"
# composer install --no-interaction --no-dev --optimize-autoloader
fi
echo "Run symfony commands for all environments"
# bin/console doctrine:migrations:migrate --no-interaction
# ...
printf "\n\033[0;44mBringing the \"${APP_ENV}\" environment up\033[0m\n"
$ docker-compose up -d --build
Run command below to obtain IP address first.
$ echo $(docker network inspect {your-network-name-goes-here} | grep Gateway | grep -o -E '[0-9\.]+')
172.18.0.1
$ curl 172.18.0.1 # Or localhost
# You should get a nice response
.
├── app # Symfony application goes in here
│ └── public
│ └── index.php
├── docker
│ ├── mysql
│ │ ├── Dockerfile
│ │ ├── init.sh
│ │ └── mysqld.cnf
│ ├── nginx
│ │ ├── default.conf
│ │ ├── Dockerfile
│ │ └── nginx.conf
│ └── php
│ ├── Dockerfile
│ ├── init.sh
│ ├── install.sh
│ ├── php.ini
│ └── www.conf
├── .env
├── .env.dist # Create a .env file from this
└── docker-compose.yml
version: '3'
services:
mysql:
build:
context: ./docker/mysql
hostname: nginx
user: mysql
ports:
- 3306:3306
volumes:
- ./data/database:/var/lib/mysql:rw
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
APP_DB_USER: ${APP_DB_USER}
APP_DB_PASS: ${APP_DB_PASS}
php:
build:
context: ../api-doc
dockerfile: ./docker/php/Dockerfile
args:
APP_ENV: ${APP_ENV}
hostname: php
ports:
- 9000:9000
depends_on:
- mysql
volumes:
- ./app:/app:cached
working_dir: /app
environment:
APP_ENV: ${APP_ENV}
APP_DB_USER: ${APP_DB_USER}
APP_DB_PASS: ${APP_DB_PASS}
nginx:
build:
context: ./docker/nginx
hostname: nginx
ports:
- 80:80
depends_on:
- mysql
- php
volumes:
- ./app:/app:cached