How to scale Laravel horizontally with Docker

Introduction

Key figures such as Jeffery Way, Matt Stauffer and of course Taylor Ottwell himself bought about what amounts to a paradigm shift for many PHP developers. They introduced many of the core principals, design patterns and best practices that other communities such as Ruby and Java have enjoyed for years.

Homestead was created by Taylor to ease creation of PHP development environments, it offers developers a consistent environment across projects and is fully compatible with the Laravel ecosphere.

So, what is docker and why should I be using it?

Docker is basically programmable infrastructure, or more simply: Docker lets you create a server environment for your project from a configuration file. With docker you are always running in the same environment whether in production, development or testing. As your environment is managed by a file, it is checked into source control giving teams the same consistency across the codebase and even differing versions of the codebase.

Why is this important?

Have you ever been working on a project and heard the phrase “well, it works on my machine”. Developers will often install service dependancies such as RabbitMQ, Redis, MySQL and ElasticSearch to assist in a project. Currently they have to notify all the developers of each dependancy or checkin large virtual machine orchestrations. This can be a nightmare, not even mentioning version conflicts between dependencies and also between different version of the codebase. Complexity to the nth degree…

Docker containers

Docker separates each individual processes running on a linux host machine into containers. For example you may have containers running PHP-FPM, MySQL and Redis. These processes (read containers) all run in a totally isolated environment, suffering no conflicts. Think of containers as name-spacing for linux, just like PHP name-spaces Docker containers are isolated and conflict free!

Containers are host agnostic in the fact they can be running on the same host machine, a different host or even a completely different cloud provider. This enables very powerful horizontal scaling and fail-over mechanisms. But more on that later…

Let’s get down to business, show me some examples!

I will demonstrate the power of the Docker ecosystem through another tool called Docker Compose a container orchestration tool built upon Docker. This tool enables us to start multiple Docker containers at once from a single configuration file, rather then starting each container individually.

Firstly visit Docker and install the Docker toolbox.

Now let’s clone a fresh installation of Laravel:

git clone https://github.com/laravel/laravel.git 
cd laravel
composer install --prefer-dist

Once Laravel is cloned and it’s dependancies are installed open up your editor and create a file called docker-compose.yml in the root of the project with the following content:

We also need to start a Docker Machine to host our containers, as OSX and Windows do not support linux containers at present. With Docker this VM is no different from interacting with a remote cloud provider such as digital Ocean or AWS. Docker has drivers for all the major cloud providers, we will be using the VirtualBox driver to start a machine locally.

docker-machine create laravel-app-host --driver virtualbox

Make sure you edit your hosts file to reflect the host we created above laravel.local, your docker-machine may have a different IP address:

# /etc/hosts file
192.168.99.100 laravel.local # the IP of your docker-machine

To start our laravel application simply type the following in the root of the project:

docker-compose up

Docker will now download all the layers for each of our services, load_balancer, cache, database and PHP HHVM. Then boot a container for each of these services.

It will take some time to initially download all the layers for each container, although this is much much faster then provisioning a Homestead VM. Once your docker machine has an image stored you can start as many containers from it as you like… and lightning fast!

Once booted, visit http://laravel.local to view your running laravel application, voila! To stop the containers running press command+c (windows crtl+c) to exit the processes. You may also type the following to terminate any running containers:

docker-compose stop

Horizontally scaled Laravel, the easy way!

Say you have a website that has high traffic or is running a mission critical application that requires 99.99% uptime. You will need to scale your Laravel application across multiple servers, running multiple instances of the application to handle load and any failures. Running behind a load-balancer such as HAProxy or Nginx.

How do you manage that in a development environment? Sure you could develop your app on a single instance, but then you will never know how it runs when scaled. Also you could boot multiple VMs, although that is an absolute NIGHTMARE, having all the problems previously described, times ten.

With docker its so so very simple… with all our containers now stopped lets scale our web server:

docker-compose scale web=3
docker-compose up

Notice how fast it was to start:

  • 3 x PHP HHVM web servers
  • 1 x MySQL database
  • 1 x Redis cache
  • 1 x HAProxy load balancer

At present we are not able to see which web container is serving our requests behind the load balancer, lets fix that. In your editor open the view file: resources/views/welcome.blade.php and add the following:

<div class="content">
<div class="title">Laravel 5</div>
 <h3>{{ gethostname() }}</h3> <!-- paste this line -->
</div>

Save the file and visit http://laravel.local and you will now see the hostname of the container that served our request. Keep refreshing the page and watch the hostname change. There you have it horizontally scaled, distributed and fail-safe Laravel..!

To test fail-over we can use docker to stop one of the containers like so:

docker ps

Find a web container and stop it via its container id or name:

docker stop laravel_web_2

Refreshing http://laravel.local you will notice we are a container down and still running strong!

Closing thoughts

You may use an alternative PHP image, for this example I’m using an image we created here at ethicaljobs.com.au. It is based on HHVM / Nginx and has impressive response times compared to PHP-FPM. Also its production hardened. https://github.com/andrewmclagan/nginx-hhvm-docker

Now go run free young developer, without the woes of Virtual Machines!

Welcome to the wonderful world that is docker…