How I develop in PHP with CoreOS and Docker

Notice!!

Since Docker and all tools provided are moving fast, you shouldn’t be reading this post. We’ve written a new post about PHP development and Docker at Yappa. You can read it here: http://tech.yappa.be/docker-php-development

I’ve been using the Vagrant provisioned-with-Ansible-setup for a while now. But for the last month(s) I’ve been playing around with things like: Docker, boot2docker, CoreOS, etcd, .. I managed to setup a fast and easy way to develop my PHP applications. Symfony2 is my preferred weapon of choice, so I’ll explain how I’m developing a Symfony2 app.

The software stack

Most of these applications are installed with an automated Mac installation/configuration manager I’ve created a while ago. MacPlan, built on top of Ansible.

You can also download and install these applications manually or use Homebrew and Homebrew Cask to install them.

brew install vagrant brew cask install virtualbox brew install docker brew install docker-compose

Vagrant and CoreOS

I’m working on Mac, so I need a virtual machine to run Docker server. I used to run boot2docker as a Docker host, but I recently switched to CoreOs because it feels faster. I didn’t benchmarked it yet.

Configure coreos-vagrant

git clone git@github.com:coreos/coreos-vagrant.git cd coreos-vagrant cp config.rb.sample config.rb cp user-data.sample user-data

Edit config.rb , uncomment and edit the following lines.

$expose_docker_tcp = 2375 # expose docker to other hosts then localhost .. $share_home = true # mount your Mac home dir in the vagrant box .. $vm_memory = 2048 $vm_cpus = 4 .. $forwarded_ports = { 2375 => 2375 } # use localhost as docker ip on your Mac

Once these things are done, you can start the CoreOS Vagrant box.

vagrant up

Vagrant will ask for sudo permission to change the /etc/hosts file on your Mac.

... ==> core-01: adding to ( /etc/hosts ) : 172.17.8.101 core-01 # VAGRANT: fc39e1aea8341bd5d302603e416aa3de (core-01) / 53f5faee-f39b-486e-a004-a6db956eaab1 Password: ==> core-01: Setting hostname... ==> core-01: Configuring and enabling network interfaces... ==> core-01: Exporting NFS shared folders... ==> core-01: Preparing to edit /etc/exports. Administrator privileges will be required... ==> core-01: Mounting NFS shared folders... ==> core-01: Machine already provisioned. Run ` vagrant provision ` or use the ` --provision ` ==> core-01: to force provisioning. Provisioners marked to run always will still run.

You can test the docker daemon (already) by running:

$ vagrant ssh -c "docker ps" CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES Connection to 127.0.0.1 closed.

Configure Docker client on Mac

The Docker client on your Mac don’t know where to look for Docker server (yet).

$ docker ps FATA[0000] Get http:///var/run/docker.sock/v1.18/containers/json: dial unix /var/run/docker.sock: no such file or directory. Are you trying to connect to a TLS-enabled daemon without TLS?

The easiest way is to add to following line at the bottom of ~/.bash_profile . Or update your dotfiles in MacPlan or personal dotfiles management repository.

echo "export DOCKER_HOST=tcp://localhost:2375" >> ~/.bash_profile

Open a new terminal or export the variable in your current terminal.

export DOCKER_HOST = tcp://localhost:2375

At this moment, Docker client will have access to Docker server on your virtual Machine with CoreOS.

$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

No containers running yet.

Docker compose

I’m always starting with (almost) the same docker-compose.yml file. This files describes the docker services I want to use.

In short the following services are used:

A data container

container An Nginx webserver on port 80

webserver on port 80 PHP-FPM listening in port 9000

listening in port 9000 A MySQL server

server Mailcatcher

Data Container

I’m using this to get some volumes shared accros different containers (PHP, Nginx, ..) without file permission issues. Also the vendor folder will be symlinked to a volume on the docker containers, this speeds up the io.

Nginx

A standard webserver setup with a minimal amount of setup. I’m using a pre-built image, yappabe/docker-nginx.

PHP-FPM

A PHP-FPM instance waiting on a TCP socket, port 9000 to process the PHP files. Also a pre-built image, yappabe/docker-php. It’s possible to use an older versions of PHP, by using a tag on the docker image.

php : image : yappabe/php:5.4

The following tags are possible:

5.6

5.4

5.3

default will be the latest built: 5.6

MySQL

A normal Mysql server, listening on port 3306. You can define the default credentials and pre-create a database. You can also leave these settings empty for random credentials and no database on creation.

Run the container

Starting form my default setup you can download the docker-compose.yml file and run the containers. Open a new terminal window.

composer create-project --no-install symfony/framework-standard-edition projectX cd projectX curl -O https://gist.githubusercontent.com/jverdeyen/850bc70015eff9f7ef35/raw/f7819f762ea7a3ddb9873f44557ed040728e8100/docker-compose.yml docker-compose up

Docker will start pulling the images, but this will only be done once. After that, you shoud see an output log of the started containers.

Creating projectx_app_1... Creating projectx_mailcatcher_1... Creating projectx_mysql_1... Pulling image tutum/mysql:latest... .. mysql_1 | 150706 7:51:03 [ Note] Event Scheduler: Loaded 0 events mysql_1 | 150706 7:51:03 [ Note] /usr/sbin/mysqld: ready for connections. mysql_1 | Version: '5.5.43-0ubuntu0.14.04.1' socket: '/var/run/mysqld/mysqld.sock' port: 3306 ( Ubuntu )

Open a new terminal window to check of all containers are running.

$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4d228350470f yappabe/nginx:latest "/run.sh" 3 minutes ago Up 2 minutes 0.0.0.0:80->80/tcp, 443/tcp projectx_nginx_1 a9f1580cfd84 yappabe/php:latest "/usr/sbin/php5-fpm 3 minutes ago Up 2 minutes 9000/tcp projectx_php_1 337246de94d2 tutum/mysql:latest " /run.sh " 3 minutes ago Up 2 minutes 0.0.0.0:3306->3306/tcp projectx_mysql_1 7ac07efc9df1 yappabe/mailcatcher:latest " mailcatcher --smtp- 3 minutes ago Up 3 minutes 0.0.0.0:1025->1025/tcp, 0.0.0.0:1080->1080/tcp projectx_mailcatcher_1 91a1ff1f1d90 yappabe/data:latest "/bin/sh" 3 minutes ago Up 3 minutes projectx_app_1

Install vendors

Now we need to install our vendors.

docker exec -i -t projectx_php_1 bash -c 'cd /var/www/app/ && ln -sf /vendor /var/www/app/vendor && composer install'

Fill in the correct credentials for MySQL when asked. Fill in “mysql” as database host as localhost will not work. This is because of the Docker Compose magic, it links all the containers together and updates their hosts file accordingly.

Clearing the Symfony2 Cache is also easy.

docker exec -i -t projectx_php_1 bash -c '/var/www/app/app/console cache:clear'

Now you can visit your Symfony2 application at http://172.17.8.101 , and start developing.

Since app_dev.php has an internal check on localhost, you can’t visit the page. An easy fix is to remove the following lines from app_dev.php .

// This check prevents access to debug front controllers that are deployed by accident to production servers. // Feel free to remove this, extend it, or make something more sophisticated. if ( isset ( $_SERVER [ 'HTTP_CLIENT_IP' ]) || isset ( $_SERVER [ 'HTTP_X_FORWARDED_FOR' ]) || ! ( in_array ( @ $_SERVER [ 'REMOTE_ADDR' ], array ( '127.0.0.1' , 'fe80::1' , '::1' )) || php_sapi_name () === 'cli-server' ) ) { header ( 'HTTP/1.0 403 Forbidden' ); exit ( 'You are not allowed to access this file. Check ' . basename ( __FILE__ ) . ' for more information.' ); }

Extra: Improve performance

There are some quick tips to improve the speed of a Symfony2 application while developing.

Use /dev/shm as your cache and logs location

as your cache and logs location Finetune the nfs mount command in the coreos-vagrant Vagrantfile

Next?

I’ve also written something about automagic discover docker containers with dnsdock and CoreOs.

Setting up a DNS service discovery with SkyDNS or DNSDock

Thanks for reading

Feel free to leave a comment if you have remarks or like this post

Facebook Google+ LinkedIn Share this on → Twitter