Migrating to Docker
As we continue to expand beyond a traditional agency delivery model, we find ourselves more and more often working as an embedded part of a client’s own delivery team, as we’ve been doing with the University of Derby and Imperial War Museums.
We can’t predict the platform or toolset that the client will be using so we need to be able to work with all of them. Having a consistent, portable development stack is crucial to how we successfully deliver projects in these environments.
One of our key focus areas over the last few months has been improving the developer experience in these situations by building tools which are simple to use and easy to setup cross-platform.
Enter Docker.
In a little over a month, alongside client commitments, we’ve migrated all of our projects (including years-old legacy sites) to use a consistent Docker setup as the underlying development stack. And it was easier than we ever imagined.
Every project now includes a docker-compose.yml file. This is essentially the entire hosting infrastructure boiled down to a single configuration file, and is one of Docker’s greatest strengths. You can distribute this one file to just about any platform or environment, run a single command, and you’ve got an entire virtual hosting platform complete with inter-server networking in seconds. It just works.
All of our Drupal projects are based on a starter docker-compose.yml file (which you can find in full in our D8 quickstart project), and any project-specific requirements are then incorporated. Some use Solr; many use PHPUnit or Behat for automated tests; Drupal 7 sites don’t support the most recent PHP version; and so on.
services: # Database service. mariadb: # Wodby MariaDB image. image: wodby/mariadb:10.1-2.3.3 volumes: # Keep a persistent datastore (optional). - ./.persist/mysql:/var/lib/mysql # PHP runtime service. php: # Wodby Drupal/PHP image. image: wodby/drupal-php:7.1-2.4.3 volumes: # Project source files. Cached volume for MacOS performance. - ./:/var/www/html:cached # Persistent storage for public & private files (optional). - ./.persist/public:/var/www/html/docroot/sites/default/files - ./.persist/private:/private # Webserver service. nginx: # Wodby Nginx service for Drupal. image: wodby/drupal-nginx:8-1.13-2.4.2 depends_on: - php volumes: # Project source files. Cached volume for MacOS performance. - ./:/var/www/html:cached # Persistent storage for public files (optional). - ./.persist/public:/var/www/html/docroot/sites/default/files
As you can see in the example above, much of the infrastructure is based on the fantastic Wodby Drupal containers. They have taken services which Drupal commonly relies on, such as PHP, Nginx, MariaDB and so on, and have pre-configured and tuned them to work well for Drupal development. This means that we just need to plug them together rather than starting from scratch, and greatly simplifies the setup and maintenance of our development environments.
One big improvement of this approach when compared to our previous foray into Docker has been the use of a HTTP reverse proxy, in this case Træfik, to simplify accessing the development environment. Gone are the days of trying to work out which random IP address and port your site is running on. You simply configure a hostname, https://localhost/ by default, and Træfik manages it for you. And yes, it supports local SSL out of the box!
services: # HTTP proxy service. traefik: image: traefik command: -c /dev/null --web --docker --logLevel=INFO --defaultEntryPoints='https,http' --entryPoints='Name:https Address::443 TLS:/certs/local.crt,/certs/local.key' --entryPoints='Name:http Address::80' ports: # HTTP forwarding. - '80:80' # HTTPS forwarding. - '443:443' # Traefik dashboard - '8080:8080' volumes: # Docker socket access for managing container traffic. - /var/run/docker.sock:/var/run/docker.sock # Self-signed SSL certificates. - ./.persist/certs:/certs
Træfik allows you to use labels to define how your web traffic should be made available. Because Træfik can talk directly to the docker runtime (through the docker.sock socket connection) it’s able to read these labels and re-configure itself dynamically as containers are started and stopped.
services: nginx: # Wodby Nginx service for Drupal. image: wodby/drupal-nginx:8-1.13-2.4.2 labels: # Forward http://localhost/ and http://docker.local/ to nginx. - 'traefik.backend=nginx' - 'traefik.port=80' - 'traefik.frontend.rule=Host:localhost' - 'traefik.frontend.rule=Host:docker.local'
So what’s next?
Although Docker is seen primarily as a tool for hosting microservices, there’s no reason why it should be limited to long-running services. It can be just as useful as a consistent task-runner.
We’re already running composer through Docker so we can build project dependencies in a consistent manner:
# Build in a docker environment. Depends on docker services running. build-docker: docker-up # Run composer install in the Wodby Drupal-PHP container. docker-compose exec php composer install
As our front-end tools continue to advance we plan to switch to using Docker containers for running our compilation toolchain. This will help ensure we’re compiling code consistently for the deployment target, regardless of where the project was built. Watch this space!
Careers at Deeson.
Transparent pay scales, flexible and distributed working, 24 days paid holiday per year, and a mandatory paid sabbatical every five years.