Benefits of WordPress + Docker in a production environment + troubleshooting guide.

Hello,

Even though we are a Rails development team, we use WordPress for our blog. We love Rails, but WordPress provides a robust solution for our blogging needs. And lately, we have been experimenting with Docker + WordPress.

So, why Docker?

There are quite a few straightforward reasons why we use Docker with WordPress, namely:

  • Docker reduces clutter.
  • We can run a couple of Rails applications and WordPress instances on the same server.
  • It helps with version conflict management in Rails apps
  • It allows for the duplication of a production server into staging for testing purposes.

We can “dockerize” an application, run it in our laptops, run tests and then deploy with confidence that it will work. If you are new to Docker, you should read What is Docker and Dockerizing applications. So, the plan is to apply this process to our application. However, I found some issues while building the instances. I would like to share those issues and how I managed to solve them.

Creating a WordPress instance

There are multiple instances ready to use on the Docker hub.

Try a simple search. I first tried this instance but didn’t like the lack of documentation, and that I wasn’t able to save the uploaded files in a persistent volume. Then I tried this other one but wasn’t able to modify wp-config.php without some hacking. So, I built my own WordPress image instead.

Dockerfile

This file is responsible for creating the docker instance. You can check the Dockerfile instance here. Let me explain each line, so that it all makes sense.

FROM framallo/php-fpm

I created a base image php-fpm that allows you to run any PHP application. It is based on ubuntu:14.04.

ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update
RUN apt-get install -y  curl mysql-client
RUN rm -rf /var/lib/apt/lists/*

The first line is to avoid mysql to ask for a root user and password. Then I updated the apt-cache and install curl and mysql client. Last, I removed the apt-cache to reduce the instance size.

ENV wordpress_VERSION 4.1.1
ENV wordpress_UPSTREAM_VERSION 4.1.1
ENV wordpress_SHA1 15d38fe6c73121a20e63ccd8070153b89b2de6a9
ENV DB_NAME wordpress_development
ENV DB_USER root
ENV DB_PASS root
ENV AUTH_KEY            --w,=nO-t>g:EOH>e-ZXs!7x(: W4:}1A2$E?Sn9P>TW-[=:u[nc-eQ<vIi<6|wh
ENV SECURE_AUTH_KEY     PlM~WQ/9-~V:-3&be`nxuaghz@JyN!]SzVr_]lAM2b?QH(d(|`.z_;1jIE4kY&f+
ENV LOGGED_IN_KEY       K]6*uCb-m~>zj5C1krtu:>2VT(WlI/Jl5T~Pov2-`r+Zb5s3i6&aIN$*/+k/~sLN
ENV NONCE_KEY           ~; xvP`h^{Pl9zaD#/!f@M21BAk0#sKg>*P+=1LV+FY+;HNE)%Y`4(Xq|&})fCj^
ENV AUTH_SALT           A2|G[jvSLB+z dy S/ S>(lLyzxDvJ8(ps1(F%~x]eRD`UHv(h*IDjye+SYV-a;O
ENV SECURE_AUTH_SALT    9cv/Hy~a;qr]4)i*udy-/$non@_:CU0SIdm-L[WH^k_}s:Jq[)HV,Wu8na<_;ef3
ENV LOGGED_IN_SALT      {d*4OCrk9x`|cb-4EBK7=ewJ3D]y%z,7mSEd:8?=eP![zD.O`<Uubt-u%@TA+x T
ENV NONCE_SALT          z6G5thFC]JIW]|ZQIBgZ?zBb^!N#3-Un=)`!Xb/,Yd8[2&}.W{ITu?=PE0oZ,<8^
ENV WP_RELOCATE true

These are environment variables that you can change when you run the instance. I think is important to provide with sensible defaults that allow you to quickly test the instance without much thought.

RUN rm /var/www/index.php

The base image php-fpm has an index.php file that allows you to test it out. I removed it to provide WordPress a clean folder.

RUN curl -o wordpress.tar.gz -SL https://wordpress.org/wordpress-${wordpress_UPSTREAM_VERSION}.tar.gz \
    && echo "$wordpress_SHA1 *wordpress.tar.gz" | sha1sum -c - \
    && tar -xzf wordpress.tar.gz -C /var/ \
    && rm wordpress.tar.gz

This actually installs WordPress. It’s designed to be in one line so that Docker can cache it completely

RUN cp -r /var/wordpress/* /var/www

Then I copy WordPress to the correct folder.

ADD wp-config.template.php /var/www/wp-config.template.php

I copy a template to generate wp-config.php. When the instance starts I run the 40_parse_wordpress_config.sh init script. I use bash to render the template using ENV variables. For instance, ${DB_NAME} will be converted to wordpress_development. I found the template parser script here.

The issue is that PHP doesn’t inherit the environment variables, so I had to modify wp-config.php in order to set all the variables that the application needs.

RUN chown -R www-data:www-data /var/www
RUN chmod 755 -R /var/www/

I prepare the files to be accessible by nginx and PHP

VOLUME /var/www

This line allows you to create persistent storage for the uploaded files and other application changes.

ADD *.sh /
RUN chmod +x /*.sh

These lines prepare the init scripts. I add them and set as executable scripts.

CMD for f in /*.sh; do $f ; done

When the instance starts it will run each init script. The last one is 50_init.sh from php-fpm which runs nginx and php-fpm. All the previous scripts should execute and stop to allow the init process to work.

Docker compose

Previously known as fig, Docker’s Compose allows you to run multiple Docker instances together. Since WordPress requires a database instance, I created a docker-compose.yml file.

WordPress:
  build: .
  # image: framallo/php-fpm
  links:
    - db:mysql
  ports:
    - 8080:80
  environment:
    DB_NAME: WordPress_development
    DB_USER: root
    DB_PASS: 12345Abc

The “build” attribute is the Dockerfile folder. “Links” allows you to connect it with mysql. “Ports” refers to the exposed ports. The format is :. “Environment” allows you to setup the instance

db:
  image: mariadb
  environment:
    MYSQL_ROOT_PASSWORD: 12345Abc

This is to run the database. The password in MYSQL_ROOT_PASSWORD should be the same as DB_PASS

Creating a data volume

In order to run it in production, you need to save the uploaded files. You can use a data volume for that.

In docker-compose.yml you need to add the following:

volumes_from:
  - wordpressdata

And add another image to docker-compose.yml:

wordpressdata:
  image: framallo/wordpress
  volumes:
    - /var/www
  command: echo 'wordpress data initialized'

So every file that is uploaded or modified will be saved in the wordpressdata image.

Just be careful when you are cleaning up docker and removing old images because you could delete wordpressdata.

Another nginx instance.

If you run the example docker-compose.yml it will work. However, WordPress will be connected to port 80 and we won’t be able to run another instance. We need to add another nginx to act as a reverse proxy.

I added the following lines to docker-compose.yml:

proxy:
  image: framallo/nginx
  links:
    - wordpress
  ports:
    - 80:80
  volumes:
    - sites:/etc/nginx/sites-template
  environment:
    VERBOSE_TEMPLATES: true

And removed the ports attribute in WordPress. Now the proxy connects to WordPress using the env variables and can expose multiple applications.

Here’s where we end for now. In conclusion, after having solved the minor issues mentioned above, we found Docker to be a very useful and efficient tool to manage distributed applications. Give it it a try when you can. Let me know if you have any questions about how our instance works, or check out our documentation here.

Thanks for reading!

Default 2015 08 24 Docker Default 2015 08 24 Docker Copy

Even though we are a Rails development team, we use WordPress for our blog. We love Rails, but WordPress provides a robust solution for our blogging needs. And lately, we have been experimenting with Docker + WordPress.