Migrating an existing WordPress + nginx + php5-fpm + mysql website to Docker containers: lessons learned

I’ve covered in previous posts why I wanted to Dockerize my site and move to containers, you can read about it in my other posts shared here. Having played with Docker for personal projects for several months at this point, I thought it was going to be easy, but ran into several issues and unexpected decisions that I needed to make. In this post I’ll summarize a few of these issues and learning points.

Realizing the meaning of ‘containers are ephemeral’, or ‘where do I put my application data’?

Docker images are the blueprint for a container, while the container is a running instance of an image. It’s clear from the Docker docs and elsewhere that you should treat your containers as ‘ephemeral’, meaning they only exist while they’re up and running, their state is temporary, and once they are discarded their state is also lost.

This is an easy concept to grasp at a high level, but in practice this leads to important and valid questions, like ‘so where does my data go’? This became very apparent to me when transferring my existing WordPress data. First, I have data in MySQL tables that needs to get imported into the new MySQL server running in a container. Second, where does the wordpress/wp-content go that in my case contains nearly 500MB of uploaded images from my 2,000+ posts?

The data for MySQL was easy to address, as the official MySQL docker image is already set up to use Docker’s data volume feature by default to externalize your MySQL data files outside of your running container.

The issue of where to put my WordPress wp-content containing 500MB of upload files is what caused my ahah moment with data volumes. Naively, you can create an image and use the COPY command to copy any number of files into an image, including even 500MB of images, but when you start to move this image around, like pushing it to a repository or a remote server, you quickly realize you’ve created something that is impractical. Making incremental changes to a image containing this quantity of files you quickly find that you’re unable to push it anywhere quickly.

To address this, I created an image with nginx and php5-fpm installed, but used Docker’s bind mount to reference and load my static content outside the container.

Now I have my app in containers, how do I actually deploy to different servers?

Up until this point I’ve built and run containers locally, I’ve set up a local Docker Repository for pushing images to for testing, but the main reasons I was interested in for this migration was to enable:

  • building and testing the containers locally
  • testing deployment to a VM server with an identical setup to my production KVM hosted server
  • pushing to my production server when I was ready to deploy to my live site

Before the Windows and MacOS naive Docker installations, I thought docker-machine was just a way to deploy to a locally running Docker install in a VM. It never occurred to me that you can also use the docker-machine command to act on any remote Docker install too.

It turns out even setting a env var DOCKER_HOST to point to the IP of any remote Docker server will enable you to direct commands to that remote server. I believe part of the ‘docker-machine create’ setup helps automate setting up TLS certs for communicating with your remote server, but you can also do this manually following the steps here. I took this approach because I wanted to use the certs from my dev machine as well as my GitLab build machine.

I used this approach to build my images locally, and then on committing my Dockerfile and source changes to my GitLab repo, I also set up a CI Pipeline to run the same commands and push automatically to a locally running test VM server, and then manually to push to my production server.

I’ll cover my GitLab CI Pipeline setup in an upcoming post.

How do you monitor an application running in containers?

I’ve been looking at a number of approaches. Prometheus looks like a great option, and I’ve been setting this up on my test server to take a look. I’m still looking at a few related options, maybe even using Grafana to visualize metrics. I’ll cover this in a future post too.

nginx + php5-fpm response lag on first requests

I’m in the middle of migrating this existing site to Docker containers and moving to a new VPS host. Part of my motivation for the move is to capture the customized configs for each of the servers, so I can easily move the whole deployment between a test environment and a production deploy. What’s prompting this is the realization that the majority of the performance tweaks I made during the first native install I have captured in various blog posts here, but to recreate these install steps I would need to go back to each of those articles and get the details in order to repeat them elsewhere. That’s not a particularly repeatable process.

I’m close to switch from my current non-Docker install (here, as of 2/27/18) to my test install now running on Docker. I’ll share more about how that is configured in future posts, but I just wanted to capture one nginx + php5-fpm specific config that had me stumped for a few days.

There’s many options for configuring the worker processes for nginx and php5-fpm. php5-fpm itself has a number of modes that control how it manages it’s worker processes. By default the process manager is ‘dynamic’ (pm = dynamic). This creates processes to handle incoming requests based on the other related config options (max_children, start servers, min_spare_servers, max_spare_servers etc).

On my current site based on recommendations I changed this to pm = ondemand in order to minimize memory usage on my 512MB VPS. One other param though had an interesting effect:

pm.process_idle_timeout = 10s;

This keeps a process alive for an additional 10s after it’s finished the current request. This seems to have an impact on the responsiveness of the WordPress site, as without this there seems to be a noticeable lag of 3-4 seconds before responses start to come back to the browser, presumably because new worker processes are needed to restart to handle the next request – by keeping them up after the last request there no lag to restart a new process.

I was almost at the point of making a no-go decision based on the laggy performance, but adding this one param has fixed the laggy behavior, and now I’m looking all set. Given that I’ve jumped from a 512MB VPS to a 4GB VPS, I’m less concerned about keeping memory usage to a minimum this time so I haven’t changed from dynamic to ondemand in the Docker config for my new nginx + php5-fpm config, but this one param is worth knowing about.

Moving my nginx+mysql WordPress VPS native install to Docker containers on a KVM VPS

My WordPress blog that you’re reading right now is running on nginx and MySQL installed on a cheap OpenVZ VPS. I’ve been running on a $2.50 VPS from Virmach for the past 6 months or so and been very happy with the service. I spent a bunch of time tweaking the nginx and MySQL config params to run in < 512MB, which it does comfortably, but nginx and MySQL are both installed directly on the Ubuntu VM instance and it would be great of I could make this setup more easily movable between cloud providers (or even to have a local copy of the setup for testing, vs the live site).

I’ve been spending a lot of time playing with Docker and Kubernetes, so it seems logical that I should move the site into containers and then this will allow me to explore other deployment options.

Migration Steps – find a KVM VPS

As far as I know you can’t install Docker in an OpenVZ virtualized VPS container, so first step I need to move to a KVM based VPS so I can install Docker (and possibly Kubernetes). I’ve been shopping the deals on lowendbox.com and there’s plenty of reasonably deals for around $5/month for various combinations of 2 to 4GB RAM and 2 to 4 vCPU.

Dockerize nginx, MySQL and WordPress

I’ve been playing with this already. I’ve picked up my own combo of favorite/useful WordPress plugins, so I’ll probably share a generic set of Dockerfiles and then leave it up to anyone if they want to use them to customize your own WordPress install in the container.

Configure a local dev/test environment Docker setup vs production environment Docker setup on my VPS

This makes a lot of sense and is a benefit of using containers. This will allow me to test my config locally, and then push to my production node. I’ve been looking at using Rancher to help with this, but still got lots to learn.

More updates to come as my project progresses.

WordPress migration complete! (From OpenShift Online to a VPS)

If you’re reading this then I’ve successfully migrated this WordPress site from Red Hat OpenShift Online to hosting in a Virtual Private Server (VPS). I had a rather long list of tasks for the migration, including:

  • Exporting content from the old site and importing to the new
  • Re-issuing my SSL certificate and installing on the new server
  • Updating my DNS config
  • Unassociating my deployed app on Openshift with my domain name / alias
  • Installing my WordPress plugins, such as reCAPTCHA, view counter, the importer etc

I’m probably still missing some minor config items, but at this point I think I’m far enough to make the switch, so the site is now live on my new VPS hosting.

Given that I’m only running on a 2 core, 512MB RAM VPS, the new site is surprisingly snappy, and dare I say it, noticeably quicker than when it was running before on OpenShift Online? I’m sure I’ve still got plenty to tweak and configure, but so far so good, and I’m pleased with the transition!