├── worker-base ├── fpm │ ├── app.ini │ └── app.pool.conf ├── nginx │ ├── upstream.conf │ ├── nginx.conf │ ├── app.conf │ └── fastcgi_params ├── supervisord.conf └── Dockerfile ├── .gitignore ├── worker-dev ├── fpm │ ├── app.ini │ └── 20-xdebug.ini ├── Dockerfile └── nginx │ └── app.conf ├── worker-prod ├── Dockerfile └── build-release.sh ├── docker-compose.yml └── README.md /worker-base/fpm/app.ini: -------------------------------------------------------------------------------- 1 | date.timezone = UTC 2 | 3 | -------------------------------------------------------------------------------- /worker-base/nginx/upstream.conf: -------------------------------------------------------------------------------- 1 | upstream php-upstream { server 127.0.0.1:9000; } 2 | -------------------------------------------------------------------------------- /worker-base/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | 4 | [program:fpm] 5 | command=php5-fpm -F 6 | 7 | [program:nginx] 8 | command=nginx 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | var/ 2 | Resources/*.odp 3 | 4 | # Numerous always-ignore extensions 5 | *.diff 6 | *.err 7 | *.orig 8 | *.log 9 | *.rej 10 | *.swo 11 | *.swp 12 | *.vi 13 | *~ 14 | 15 | -------------------------------------------------------------------------------- /worker-dev/fpm/app.ini: -------------------------------------------------------------------------------- 1 | date.timezone = UTC 2 | 3 | error_reporting = E_ALL 4 | display_errors = On 5 | display_startup_errors = On 6 | html_errors = On 7 | log_errors = On 8 | xdebug.max_nesting_level = 250 9 | 10 | -------------------------------------------------------------------------------- /worker-prod/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM symfony/worker-base 2 | 3 | MAINTAINER Markus Weiland 4 | 5 | ######## 6 | 7 | RUN mkdir -p /var/www/app 8 | ADD code.tar.gz /var/www/app 9 | 10 | RUN chown -R www-data:www-data /var/www/app/app/cache /var/www/app/app/logs 11 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | db: 2 | image: mysql 3 | ports: 4 | - "3306:3306" 5 | environment: 6 | MYSQL_ROOT_PASSWORD: symfonyrootpass 7 | MYSQL_DATABASE: symfony 8 | MYSQL_USER: symfony 9 | MYSQL_PASSWORD: symfonypass 10 | worker: 11 | image: symfony/worker-dev 12 | ports: 13 | - "8080:80" 14 | environment: 15 | XDEBUG_HOST: 192.168.1.194 16 | XDEBUG_PORT: 9000 17 | XDEBUG_REMOTE_MODE: req 18 | links: 19 | - db 20 | volumes: 21 | - "var/nginx/:/var/log/nginx" 22 | - ../mySymfonyProject:/var/www/app 23 | -------------------------------------------------------------------------------- /worker-dev/fpm/20-xdebug.ini: -------------------------------------------------------------------------------- 1 | zend_extension=xdebug.so 2 | xdebug.remote_enable=1 3 | xdebug.remote_host=${XDEBUG_HOST} 4 | ; req|jit default:req 5 | xdebug.remote_mode=${XDEBUG_REMOTE_MODE} 6 | xdebug.remote_log=/var/log/xdebug.log 7 | xdebug.remote_port=${XDEBUG_PORT} 8 | xdebug.idekey= 9 | 10 | ; [0|1] ignores remote_host and connects to $_SERVER['REMOTE_ADDR'] 11 | xdebug.remote_connect_back=${XDEBUG_CONNECT_BACK} 12 | 13 | xdebug.var_display_max_depth=4 14 | xdebug.dump.GET = * 15 | xdebug.dump.POST = * 16 | xdebug.dump.REQUEST = * 17 | xdebug.dump.COOKIE = * 18 | xdebug.dump.SERVER = * 19 | xdebug.dump.SESSION = * 20 | 21 | -------------------------------------------------------------------------------- /worker-base/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | user www-data; 2 | worker_processes 4; 3 | pid /run/nginx.pid; 4 | 5 | events { 6 | worker_connections 2048; 7 | multi_accept on; 8 | use epoll; 9 | } 10 | 11 | http { 12 | server_tokens off; 13 | sendfile on; 14 | tcp_nopush on; 15 | tcp_nodelay on; 16 | keepalive_timeout 15; 17 | types_hash_max_size 2048; 18 | include /etc/nginx/mime.types; 19 | default_type application/octet-stream; 20 | access_log off; 21 | error_log off; 22 | gzip on; 23 | gzip_disable "msie6"; 24 | include /etc/nginx/conf.d/*.conf; 25 | include /etc/nginx/sites-enabled/*; 26 | open_file_cache max=100; 27 | } 28 | 29 | daemon off; 30 | 31 | -------------------------------------------------------------------------------- /worker-dev/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM symfony/worker-base 2 | 3 | MAINTAINER Markus Weiland 4 | 5 | ######## 6 | 7 | ADD nginx/app.conf /etc/nginx/sites-available/ 8 | 9 | # Allow debugging PHP scripts with Xdebug for 10 minutes before Nginx times out 10 | RUN echo "fastcgi_read_timeout 600s;" >> /etc/nginx/fastcgi_params 11 | 12 | ######## 13 | 14 | RUN apt-get install -y php5-xdebug && php5enmod xdebug 15 | ADD fpm/20-xdebug.ini /etc/php5/fpm/conf.d/ 16 | ADD fpm/20-xdebug.ini /etc/php5/cli/conf.d/ 17 | 18 | ADD fpm/app.ini /etc/php5/fpm/conf.d/ 19 | ADD fpm/app.ini /etc/php5/cli/conf.d/ 20 | 21 | ENV XDEBUG_HOST 127.0.0.1 22 | ENV XDEBUG_PORT 9000 23 | ENV XDEBUG_REMOTE_MODE jit 24 | ENV XDEBUG_CONNECT_BACK 0 25 | 26 | EXPOSE 9000 27 | -------------------------------------------------------------------------------- /worker-dev/nginx/app.conf: -------------------------------------------------------------------------------- 1 | server { 2 | server_name app; 3 | root /var/www/app/web; 4 | 5 | location / { 6 | # try to serve file directly, fallback to app.php 7 | try_files $uri /app_dev.php$is_args$args; 8 | } 9 | 10 | # DEV 11 | location ~ ^/(app_dev|config)\.php(/|$) { 12 | fastcgi_pass php-upstream; 13 | fastcgi_split_path_info ^(.+\.php)(/.*)$; 14 | include fastcgi_params; 15 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 16 | fastcgi_param HTTPS off; 17 | # Prevents URIs that include the front controller. This will 404: 18 | # http://domain.tld/app.php/some-path 19 | # Remove the internal directive to allow URIs like this 20 | internal; 21 | } 22 | 23 | error_log /var/log/nginx/app_error.log; 24 | access_log /var/log/nginx/app_access.log; 25 | } 26 | -------------------------------------------------------------------------------- /worker-base/nginx/app.conf: -------------------------------------------------------------------------------- 1 | server { 2 | server_name app; 3 | root /var/www/app/web; 4 | 5 | location / { 6 | # try to serve file directly, fallback to app.php 7 | try_files $uri /app.php$is_args$args; 8 | } 9 | 10 | # PROD 11 | location ~ ^/app\.php(/|$) { 12 | fastcgi_pass php-upstream; 13 | fastcgi_split_path_info ^(.+\.php)(/.*)$; 14 | include fastcgi_params; 15 | fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; 16 | fastcgi_param DOCUMENT_ROOT $realpath_root; 17 | fastcgi_param HTTPS off; 18 | # Prevents URIs that include the front controller. This will 404: 19 | # http://domain.tld/app.php/some-path 20 | # Remove the internal directive to allow URIs like this 21 | internal; 22 | } 23 | 24 | error_log /var/log/nginx/app_error.log; 25 | access_log /var/log/nginx/app_access.log; 26 | } 27 | -------------------------------------------------------------------------------- /worker-base/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:14.04 2 | 3 | MAINTAINER Markus Weiland 4 | 5 | RUN apt-get update && apt-get install -y supervisor 6 | 7 | ADD supervisord.conf /etc/supervisor/conf.d/ 8 | 9 | ######## 10 | 11 | RUN apt-get install -y nginx 12 | 13 | ADD nginx/nginx.conf /etc/nginx/ 14 | ADD nginx/app.conf /etc/nginx/sites-available/ 15 | ADD nginx/upstream.conf /etc/nginx/conf.d/upstream.conf 16 | ADD nginx/fastcgi_params /etc/nginx/ 17 | 18 | RUN ln -s /etc/nginx/sites-available/app.conf /etc/nginx/sites-enabled/app 19 | RUN rm /etc/nginx/sites-enabled/default 20 | 21 | ######## 22 | 23 | RUN apt-get install -y php5-common php5-cli php5-fpm php5-mcrypt php5-mysql php5-apcu php5-gd php5-imagick php5-curl php5-intl 24 | RUN php5enmod mcrypt 25 | 26 | RUN rm /etc/php5/fpm/pool.d/www.conf 27 | 28 | ADD fpm/app.ini /etc/php5/fpm/conf.d/ 29 | ADD fpm/app.ini /etc/php5/cli/conf.d/ 30 | ADD fpm/app.pool.conf /etc/php5/fpm/pool.d/ 31 | 32 | EXPOSE 80 33 | 34 | CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] 35 | -------------------------------------------------------------------------------- /worker-prod/build-release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Bakes the application code into a Docker image "symfony/worker-prod". 4 | # Simply deploy this image to your production servers. 5 | 6 | set -e 7 | 8 | COMPOSER_BIN=composer 9 | BUILD_DIR=/tmp/app-code-build 10 | # Modify to your needs 11 | REPOSITORY=git@github.com:symfony/symfony-standard.git 12 | BRANCH=2.6 13 | DOCKERFILE_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 14 | 15 | # Clean up old build files 16 | if [ -d ${BUILD_DIR} ]; then 17 | echo "Removing ${BUILD_DIR}." 18 | rm -rf ${BUILD_DIR} 19 | fi 20 | 21 | # Get the code 22 | mkdir ${BUILD_DIR} 23 | cd ${BUILD_DIR} 24 | git clone ${REPOSITORY} 25 | CODE_DIR="$(ls -d */|head -n 1)" 26 | cd ${CODE_DIR} 27 | git checkout ${BRANCH} 28 | rm -rf ${BUILD_DIR}/${CODE_DIR}.git 29 | 30 | # Get dependencies with Composer 31 | ${COMPOSER_BIN} --no-interaction install 32 | 33 | # Clear the cache so we don't have the build environment spill into production 34 | # TODO pre-warm with production environment instead 35 | app/console cache:clear --no-warmup 36 | 37 | # Package everything up 38 | tar -czf ../code.tar.gz * 39 | 40 | # Put the package into our Docker build directory 41 | cd .. 42 | cp code.tar.gz ${DOCKERFILE_DIR} 43 | 44 | # Build the Docker image 45 | echo "Building static application code image." 46 | cd ${DOCKERFILE_DIR} 47 | docker build -t symfony/worker-prod ${DOCKERFILE_DIR} 48 | 49 | rm ${DOCKERFILE_DIR}/code.tar.gz 50 | -------------------------------------------------------------------------------- /worker-base/nginx/fastcgi_params: -------------------------------------------------------------------------------- 1 | fastcgi_buffer_size 128k; 2 | fastcgi_buffers 4 256k; 3 | fastcgi_busy_buffers_size 256k; 4 | 5 | fastcgi_param QUERY_STRING $query_string; 6 | fastcgi_param REQUEST_METHOD $request_method; 7 | fastcgi_param CONTENT_TYPE $content_type; 8 | fastcgi_param CONTENT_LENGTH $content_length; 9 | 10 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 11 | fastcgi_param SCRIPT_NAME $fastcgi_script_name; 12 | fastcgi_param PATH_INFO $fastcgi_path_info; 13 | fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; 14 | fastcgi_param REQUEST_URI $request_uri; 15 | fastcgi_param DOCUMENT_URI $document_uri; 16 | fastcgi_param DOCUMENT_ROOT $document_root; 17 | fastcgi_param SERVER_PROTOCOL $server_protocol; 18 | 19 | fastcgi_param GATEWAY_INTERFACE CGI/1.1; 20 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; 21 | 22 | fastcgi_param REMOTE_ADDR $remote_addr; 23 | fastcgi_param REMOTE_PORT $remote_port; 24 | fastcgi_param SERVER_ADDR $server_addr; 25 | fastcgi_param SERVER_PORT $server_port; 26 | fastcgi_param SERVER_NAME $server_name; 27 | 28 | fastcgi_param HTTPS $https; 29 | 30 | # PHP only, required if PHP was built with --enable-force-cgi-redirect 31 | # fastcgi_param REDIRECT_STATUS 200; 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Docker containers for Symfony development & production 2 | ====================================================== 3 | 4 | This repository provides Dockerfiles for setting up ready to use Symfony development and production environments. 5 | 6 | The configuration used in this repository makes use of Docker's layered image approach in the sense that the development 7 | environment image is simply an additional layer on top of the base image of the production environment. 8 | This means your development environment will be practically identical to the production environment. 9 | 10 | Production images built from this repository are immutable and can be easily deployed to off-the-shelf Docker instances with 11 | all application code, dependencies, folders, etc. already included. 12 | 13 | Structure 14 | --------- 15 | 16 | There is a `worker-base` image file that contains PHP-FPM and NGINX. Both processes expect a Symfony project at 17 | `/var/www/app` at runtime inside the container. 18 | 19 | There is a `worker-dev` development image that extends `worker-base` and adds development functionality such as Xdebug 20 | and turns on PHP debug output. The image also sets Nginx's configuration to load `web/app_dev.php` instead 21 | of `web/app.php`. This development image expects application files to be mounted into the container to 22 | `/var/www/app` from the host which means any code changes will immediately take effect. 23 | 24 | There is a `worker-prod` production image that extends `worker-base` and simply bakes a Symfony project 25 | into the container. This image can be easily deployed as an immutable throwaway instance of the entire 26 | application. This image should be built by executing `worker-prod/build-release.sh`. 27 | 28 | Configuration 29 | ------------- 30 | 31 | Container environment variables are used to adapt application behavior to the respective environment. 32 | 33 | Add a `app/config/parameters.php` file to your project and include it after `app/config/parameters.yml` 34 | in your configuration file: 35 | 36 | ```yaml 37 | # app/config/config.yml 38 | imports: 39 | - { resource: parameters.yml } 40 | - { resource: parameters.php } 41 | 42 | # ... 43 | ``` 44 | 45 | In `parameters.php`, set Symfony parameters from environment variables as follows: 46 | 47 | ```php 48 | // app/config/parameters.php 49 | $parameters = [ 50 | // ENV_VAR_NAME => symfony parameter name 51 | 'DB_1_PORT_3306_TCP_ADDR' => 'database_host', 52 | 'DB_1_PORT_3306_TCP_PORT' => 'database_port', 53 | 'DB_1_ENV_MYSQL_DATABASE' => 'database_name', 54 | 'DB_1_ENV_MYSQL_USER' => 'database_user', 55 | 'DB_1_ENV_MYSQL_PASSWORD' => 'database_password', 56 | // ... 57 | ]; 58 | 59 | foreach ($parameters as $envVar => $sfParam) { 60 | $value = getenv($envVar); 61 | if ($value !== false && $value !== '') { 62 | $container->setParameter($sfParam, $value); 63 | } 64 | } 65 | ``` 66 | 67 | Note: All environment variables which are to be used as parameters need to be 68 | defined in `worker-base/fpm/app.pool.conf`, otherwise they will not be 69 | visible to PHP code. 70 | 71 | Running app/console 72 | ------------------- 73 | 74 | To have Symfony's ``app/console`` utility work as expected, it needs to be run from inside the container so 75 | that all environment variables are available for Symfony. 76 | 77 | Simply execute the following from the path of this repository: 78 | 79 | docker-compose run worker /var/www/app/app/console 80 | 81 | Building SfDocker images 82 | ------------------------ 83 | 84 | To build the development images, run the following commands in the repository's top folder: 85 | 86 | docker build -t symfony/worker-base worker-base 87 | docker build -t symfony/worker-dev worker-dev 88 | 89 | Then simply launch the development server with 90 | 91 | docker-compose up 92 | 93 | To build a production image, run the following command in the repository's top folder: 94 | 95 | ./worker-prod/build-release.sh 96 | 97 | Authors 98 | ------- 99 | 100 | * Markus Weiland (@advancingu) 101 | * Scott Wilson (@scooterXL) 102 | -------------------------------------------------------------------------------- /worker-base/fpm/app.pool.conf: -------------------------------------------------------------------------------- 1 | ; Start a new pool named 'app'. 2 | ; the variable $pool can we used in any directive and will be replaced by the 3 | ; pool name ('app' here) 4 | [app] 5 | 6 | ; Unix user/group of processes 7 | ; Note: The user is mandatory. If the group is not set, the default user's group 8 | ; will be used. 9 | user = www-data 10 | group = www-data 11 | 12 | ; The address on which to accept FastCGI requests. 13 | ; Valid syntaxes are: 14 | ; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific address on 15 | ; a specific port; 16 | ; 'port' - to listen on a TCP socket to all addresses on a 17 | ; specific port; 18 | ; '/path/to/unix/socket' - to listen on a unix socket. 19 | ; Note: This value is mandatory. 20 | listen = 0.0.0.0:9000 21 | 22 | ; Choose how the process manager will control the number of child processes. 23 | ; Possible Values: 24 | ; static - a fixed number (pm.max_children) of child processes; 25 | ; dynamic - the number of child processes are set dynamically based on the 26 | ; following directives. With this process management, there will be 27 | ; always at least 1 children. 28 | ; pm.max_children - the maximum number of children that can 29 | ; be alive at the same time. 30 | ; pm.start_servers - the number of children created on startup. 31 | ; pm.min_spare_servers - the minimum number of children in 'idle' 32 | ; state (waiting to process). If the number 33 | ; of 'idle' processes is less than this 34 | ; number then some children will be created. 35 | ; pm.max_spare_servers - the maximum number of children in 'idle' 36 | ; state (waiting to process). If the number 37 | ; of 'idle' processes is greater than this 38 | ; number then some children will be killed. 39 | ; ondemand - no children are created at startup. Children will be forked when 40 | ; new requests will connect. The following parameter are used: 41 | ; pm.max_children - the maximum number of children that 42 | ; can be alive at the same time. 43 | ; pm.process_idle_timeout - The number of seconds after which 44 | ; an idle process will be killed. 45 | ; Note: This value is mandatory. 46 | pm = dynamic 47 | 48 | ; The number of child processes to be created when pm is set to 'static' and the 49 | ; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. 50 | ; This value sets the limit on the number of simultaneous requests that will be 51 | ; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. 52 | ; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP 53 | ; CGI. The below defaults are based on a server without much resources. Don't 54 | ; forget to tweak pm.* to fit your needs. 55 | ; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' 56 | ; Note: This value is mandatory. 57 | pm.max_children = 20 58 | 59 | ; The number of child processes created on startup. 60 | ; Note: Used only when pm is set to 'dynamic' 61 | ; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 62 | pm.start_servers = 2 63 | 64 | ; The desired minimum number of idle server processes. 65 | ; Note: Used only when pm is set to 'dynamic' 66 | ; Note: Mandatory when pm is set to 'dynamic' 67 | pm.min_spare_servers = 1 68 | 69 | ; The desired maximum number of idle server processes. 70 | ; Note: Used only when pm is set to 'dynamic' 71 | ; Note: Mandatory when pm is set to 'dynamic' 72 | pm.max_spare_servers = 3 73 | 74 | ;--------------------- 75 | 76 | ; Make specific Docker environment variables available to PHP 77 | env[DB_1_PORT_3306_TCP_ADDR] = $DB_1_PORT_3306_TCP_ADDR 78 | env[DB_1_PORT_3306_TCP_PORT] = $DB_1_PORT_3306_TCP_PORT 79 | env[DB_1_ENV_MYSQL_DATABASE] = $DB_1_ENV_MYSQL_DATABASE 80 | env[DB_1_ENV_MYSQL_USER] = $DB_1_ENV_MYSQL_USER 81 | env[DB_1_ENV_MYSQL_PASSWORD] = $DB_1_ENV_MYSQL_PASSWORD 82 | 83 | catch_workers_output = yes 84 | 85 | --------------------------------------------------------------------------------