I have being working with Symfony since 2012, and I never feel as confident developing and deploying like I feel now with Docker.
I use to develop at home with Ubuntu but I've Mac at the office, and was a little bit tricky at the beginning.
I'll not explain what is docker machine, docker engine or docker compose, his documentation is much better than I can explain, believe me.
Installing docker
First download Docker toolbox you will need VirtualBox or similar.
Be familiar with Docker Machine, is where docker will run, and you will need it on the future.
To start the machine:
docker-machine start default
To connect the client with docker-engine:
eval $(docker-machine env default)
It's a good idea add it to .bash_profile
Now you have docker installed and running. Great!
No.
The virtual machine it's super slow. To solve that we'll change the file system to nfs, thanks to https://github.com/adlogix/docker-machine-nfs.
To install it run:
curl -s https://raw.githubusercontent.com/adlogix/docker-machine-nfs/master/docker-machine-nfs.sh |
sudo tee /usr/local/bin/docker-machine-nfs > /dev/null && \
sudo chmod +x /usr/local/bin/docker-machine-nfs
Exec on the terminal:
docker-machine-nfs default
Ok, you are done and the machine rebooted with nfs. Maybe you don't know it but this simple change improves a lot the development process.
Preparing the environment
Let's go with Symfony, I always use the installer:
symfony new demo
cd demo
Done, now the images.
mkdir -p etc/build/dev
cd etc/build/dev
Start with PHP
We'll need few things for our dev environment:
- The fpm config
- The ssh connection to configure the IDE for remote interpreter
- Xdebug for, obviously, debug.
*/etc/build/dev/php7/php-fpm.conf
[global]
error_log = /app/var/logs/php-fpm
daemonize = no
[www]
; if we send this to /proc/self/fd/1, it never appears
access.log = /app/var/logs/php-fpm
; this does the trick for changing the user
user = jarco
group = jarco
listen = [::]:9000
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
clear_env = no
; Ensure worker stdout and stderr are sent to the main error log.
catch_workers_output = yes
chdir = /app/web
/etc/build/dev/php7/php.ini
memory_limit=256M
[Date]
date.timezone = Europe/Madrid
/etc/build/dev/php7/Dockerfile
FROM php:7.0-fpm
### Install some requirements
RUN apt-get update && apt-get install -y git zlib1g-dev libmcrypt-dev supervisor openssh-server \
&& mkdir -p /var/log/supervisor \
&& mkdir -p /var/run/sshd \
&& rm -rf /var/lib/apt/lists/*
RUN docker-php-ext-install bcmath mbstring opcache pcntl zip mcrypt pdo_mysql \
## APCu
&& pecl install apcu \
&& docker-php-ext-enable apcu \
## Xdebug
&& pecl install xdebug-beta \
&& docker-php-ext-enable xdebug \
&& echo "xdebug.cli_color=1\nxdebug.remote_autostart=1\nxdebug.remote_connect_back=1" > /usr/local/etc/php/conf.d/xdebug.ini
#Add composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
COPY supervisor/supervisor.conf /etc/supervisor/conf.d/supervisord.conf
COPY php.ini /tmp/php.ini_extension
RUN cat /tmp/php.ini_extension >> /usr/local/etc/php/php.ini \
&& rm /tmp/php.ini_extension
COPY php-fpm.conf /usr/local/etc/php-fpm.conf
RUN useradd -ms /bin/bash jarco
RUN usermod -u 1000 jarco
# SSH configuration to connect on development environment with the interpreter
RUN echo 'root:jarcodev' | chpasswd
RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config \
&& sed '[email protected]\s*required\s*[email protected] optional [email protected]' -i /etc/pam.d/sshd
RUN echo "export VISIBLE=now" >> /etc/profile
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
WORKDIR /app
Go for Nginx
Add symfony.dev to your hosts file.
/etc/build/nginx/symfony.conf
server {
server_name symfony.dev www.symfony.dev;
root /app/web;
location / {
# try to serve file directly, fallback to app.php
try_files $uri /app.php$is_args$args;
}
# DEV
# This rule should only be placed on your development environment
# In production, don't include this and don't deploy app_dev.php or config.php
location ~ ^/(app_dev|config)\.php(/|$) {
fastcgi_pass php:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
# When you are using symlinks to link the document root to the
# current version of your application, you should pass the real
# application path instead of the path to the symlink to PHP
# FPM.
# Otherwise, PHP's OPcache may not properly detect changes to
# your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
# for more information).
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
}
# PROD
location ~ ^/app\.php(/|$) {
fastcgi_pass php:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
# Prevents URIs that include the front controller. This will 404:
# http://symfony.dev/app.php/some-path
# Remove the internal directive to allow URIs like this
internal;
}
}
/etc/build/nginx/Dockerfile
FROM nginx:latest
# Default nginx config
COPY symfony.conf /etc/nginx/conf.d/app.conf
The code
Docker uses volumes to share the files. Instead of copy inside the container as you can do when build the image to deploy, in dev you'll create a volume and share it to the containers that need it
/etc/build/dev/code/Dockerfile
FROM busybox:latest
VOLUME /app
Logs
I use to use Kibana to read the logs and debug. I just follow willdurand/elk and you can find the configuration for symfony here: https://github.com/jorge07/symfony3-docker-starter/tree/master/etc/build/dev/elk/logstash
The orchestration
Important! You will need Docker Compose > 1.6
docker-compose.yml
version: '2'
services:
code:
build: etc/build/dev/code
container_name: symfony_code
volumes:
- ./:/app
nginx:
build: etc/build/dev/nginx
container_name: symfony_nginx
ports:
- "80:80"
links:
- php
volumes_from:
- code
volumes:
- ./var/logs/nginx:/var/log/nginx
php:
build: etc/build/dev/php7
container_name: symfony_php
volumes:
- ~/.ssh:/root/ssh
volumes_from:
- code
ports:
- "9000:9000"
- "2222:22"
links:
- code
- redis
redis:
image: redis:3.0.7-alpine
ports:
- 6379:6379
container_name: symfony_redis
elk:
image: willdurand/elk
container_name: symfony_elk
ports:
- 81:80
volumes:
- ./etc/build/dev/elk/logstash:/etc/logstash
- ./etc/build/dev/elk/logstash/patterns:/opt/logstash/patterns
volumes_from:
- code
Ok, after all that work:
docker-compose up -d
Install dependencies:
docker exec -it symfony_php composer install
And voila!
Now you know more or less all the components that run and how to run it together.
To make it easier I've this proyect on GitHub with the instructions to run it inside.
Pro tip
I suppose some of you can have private repositories, to install it with composer add to PHP Dockerfile:
# Make ssh dir
RUN mkdir /root/.ssh/
# Copy over private key, and set permissions
ADD id_rsa /root/.ssh/id_rsa
That's the resume of my experience, if you have any problem leave me a comment and I'll try to help you.