Dec 272015
 
Wordpress in a Docker

When you read this, this blog moved do a (currently) Google Cloud VM Instance using Docker.

There’s a lot of examples how to set up containers for this combination of WordPress and MySQL, but the migration from an existing installation was nowhere mentioned. Thus here my notes from the end to the start:

Run this blog in Docker

  1. To start I need
    1. a server which can run Docker
    2. Docker 1.9.x as I need the volume command
    3. a MySQL dump of the cold copied DB files: mysql-backup.dump
    4. a tar archive from the wordpress directory: wordpress-backup.tar.gz
    5. a DNS entry to point to the Docker host
    6. Port 80 on the Docker host exposed to a public IP
  2. On the Docker host, create 3 empty data volumes which will host the persistent DB data, wordpress directory and backups:
    docker volume create --name blog-mysql-data
    docker volume create --name wordpress-data
    docker volume create --name blog-backup
  3. Populate blog-backup with the backup files:
    docker run -it -v blog-backup:/backup -v /home/harald:/data debian:8 bash
    cp /data/mysql-backup.dump /data/wordpress-backup.tar.gz /backup
    exit
  4. blog-backup is a volume which contains backups in /backup of the wordpress directory as well as the (cold) mysql DB. Extract like this:
    docker run -it -v blog-backup:/backup \
    -v blog-mysql-data:/var/lib/mysql \
    -v wordpress-data:/var/www/html debian:8 bash
    cd /var/lib
    tar xfv /backup/mysql-backup.dump
    cd /var/www/html
    tar xfv /backup/wordpress-backup.tar.gz
    exit
  5. Start MySQL Docker container first
    docker run --name blog-mysql \
    -v blog-mysql-data:/var/lib/mysql \
    -d \
    mysql/mysql-server:5.7
  6. Now start WordPress (and replace the passwords and account of course)
    docker run --name blog-app \
    -e WORDPRESS_DB_PASSWORD=MY_PASSWORD \
    -e WORDPRESS_DB_USER=MY_ACCOUNT \
    --link blog-mysql:mysql \
    -v wordpress-data:/var/www/html \
    -p 80:80 \
    -w /var/www/html/wordpress \
    -d \
    wordpress:latest

Google Cloud Configuration

When creating the VM which runs Docker, make sure you get Docker 1.9 or newer as the docker volume command does not exist until then. For now (December 2015) that means to choose the beta CoreOS instance for your Google Cloud VM.

Be able to copy files to the VM.

Beside this, make http and https traffic externally visible and remember the IP assigned to the VM.

DNS

My DNS is on linode.com, so I have to change the blog DNS entry there. TTL is now 5min (instead of 1h default) to make testing a bit faster.

Alternative during testing it’s sufficient to make the machine which runs the browser to point the FQDN to the current IP. The Docker host or container does not care about its external IP.

Initial Polulation of the DB Filesystem

The first population of the MySQL data was a bit tedious and manual due to the unexpected upgrade needed.

  1. Have a dump from the SQL DB:
     mysqldump -pMY_ADMINPW --all-databases > mysql-backup.dump
  2. Run mysql with the dump being available. Mount the directory which has the dump in /data2 and the (empty) blog-mysql-data under /var/lib/mysql and set an initial DB root password:
    docker run -it \
    -v /home/harald/dockerstuff/mysql-data:/data2 \
    -v blog-mysql-data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=mypw -P mysql/mysql-server:5.7
  3. Since we did not name this container, we have to find its name or container ID:
    CONTAINERID=`docker ps --format "{{.ID}} {{.Image}}" | grep mysql-server | awk '{print $1}'`
    docker exec -it $CONTAINERID bash
  4. Inside the mysql container now run the import:
    mysql -u root -pmypw </data2/mysql-backup.dump
  5. and do a DB upgrade and stop mysql:
    mysql_upgrade -u root -p mypw
    pkill -SIGTERM mysqld
  6. By now the DB root password is change to the one from the dump. And blog-mysql-data now has a working MySQL DB with the last dump we took.

 

Initial Population of the WordPress Filesystem

I tried initially to use a plan vanilla WordPress Docker as unmodified as possible, but since I needed to add plugins and themes, I tried to find a programmatic way to add them. While thinking about future updates of WordPress and its plugins, it made me realize that a separate data volume for the wordpress directory is in order. The alternative would have been to rewriting /entrypoint.sh in the original WordPress Docker container.

  1. Start the WordPress Docker container with no modifications but let it connect to the MySQL container:
    docker volume create --name wordpress-data
    docker run -it --name blog-app \
    -e WORDPRESS_DB_PASSWORD=MY_PASSWORD \
    -e WORDPRESS_DB_USER=MY_ACCOUNT \
    --link blog-mysql:mysql \
    -v wordpress-data:/var/www/html \
    -p 80:80 \
    -w /var/www/html/wordpress wordpress:latest
  2. The /entrypoint.sh script will populate /var/www/html/ with a complete WordPress instance. Changing the WORKDIR to /var/www/html/wordpress puts those files to where I need them as that’s where the files are on the old server.
  3. Now you can stop the WordPress container. The data files are kept.
  4. I had to put a lot of my uploaded images back:
    docker run -it -v /home/harald:/data2 \
    -v wordpress-data:/var/www/html debian:8 bash
  5. Inside the Debian container copy the files to wordpress/wp-content/uploads
    cp -pr /data2/wordpress/wp-content/uploads/* /var/www/html/wordpress/wp-content/uploads/
  6. MySQL container was running at this time. Starting the WordPress container now again:
    docker run -it --name blog-app \
    -e WORDPRESS_DB_PASSWORD=MY_PASSWORD \
    -e WORDPRESS_DB_USER=MY_ACCOUNT \
    --link blog-mysql:mysql \
    -v wordpress-data:/var/www/html \
    -p 80:80 \
    -w /var/www/html/wordpress wordpress:latest
  7. For testing, edit /etc/hosts of the machine with the browser to make the FQDN of the blog point to the IP of the Docker host.
  8. Now in a browser I was able to see everything from my blog, log in, update Askismet, install YAPB and install the theme Suffusion.
  9. Stop the container, mount the data volume as before, and create a tar dump of /var/www/html/wordpress as wordpress-backup.tar.gz
    docker stop blog-app
    docker run -it -v wordpress-data:/var/www/html \
    -v /home/harald:/data2 debian:8 bash
    cd /var/www/html
    tar cfvz /data2/wordpress-backup.tar.gz wordpress
    exit

At this point wordpress-data is the complete wordpress directory and I have a tar archive of it.

Outstanding Items

Backup

MySQL

MySQL is easy enough manually:

docker exec -it blog-mysql bash

Inside run a mysqldump. Then transfer the dump to a place off-site. Automate by not using bash but instead call the script to make the backup. Or run mysqldump from another server but i think that causes more network traffic. I’d need to expose the mysql ports for the latter though.

WordPress

WordPress directory is equally easy:

docker exec -it blog-app bash

Inside again run a tar and/or rsync to a remote site.

Potential Issues

  • MySQL and WordPress currently must run on the same Docker host. To have them on separate hosts a network needs to be created to connect those.
    However I would have been ok to have MySQL and WordPress in one container as I plan not to scale. Right now I use the micro-instance of Google Cloud and I’m fine with this.
  • Disk space on the Docker host. It’s limited. 10G Google gives me (resp. I assigned myself).
    volumes use all disk space they can get, so the backup volume WILL increase if I do dayly dumps inside with no expiration. I plan to move them off-size though, so I can delete old backups quickly.
  • If the Docker hosts fails/restarts, I have to manually restart my 2 containers.
  • CPU/RAM of the f1-micro instance (1 shared CPU, 0.6GB RAM): it’s enough, but memory is used up:
    total       used       free     shared    buffers     cached 
    Mem:        607700     580788      26912      33404      12284     122960 
    -/+ buffers/cache:     445544     162156 
    Swap:            0          0          0

Comments

  • Note that the debian:8 image contains neither bzip2 nor xz.