Dockerize Me – Moving Containers To Another Host – Part 4

In part 3 of this series we’ve created a reverse proxy setup with Docker containers in the public cloud to serve the content of several web services from independent docker containers with individual domain names. In this episode I’ll test the concept that containerized applications can easily be moved from one host to another.

For this exercise we can transfer the ‘private’ WordPress installation of part 1 to the reverse proxy setup on the virtual server in the public cloud. For the WordPress blog this will change two things:

  • It will be reachable via a domain name (e.g. blog3.domain.com) and not only via its IP address
  • It will be reachable from the standard ports 80 and 443 instead of port 8000

Due to these differences, the docker-compose.yml file and the content of the WordPress database need to change a bit. But first things first, let’s transfer the WordPress blog from the old host to the new host. After a docker-compose down in the project directory, tar the complete directory that contains the docker-compose.yml file and the sub-directories that contain the WordPress and the MySQL persistent files and move the tar file to the other server:

# In this example the project folder is called local-blog

cd ..
tar cvzf local-blog.tar.gz local-blog 

On the new server untar the folder again and rename it.

tar xvzf local-blog.tar.gz
mv local-blog third-project
cd third-project

To make this project work with the reverse proxy setup of part 3, a few changes are required in the docker-compose.yml file of the WordPress installation. For one thing the port mapping from port 80 (inside) to port 8000 (outside) is no longer required, so we can remove that line. To make the reverse proxy containers aware that there is a new project, three environment variables have to be added to the WordPress container description. When the container is started, the nginx reverse proxy will check the new containers for these environment variables and then automatically generate a LetsEncrypt certificate for the domain. Also, a new vhost entry is crated to forward requests for that domain to this WordPress instance. In the next part of this series will have a closer look at how the reverse proxy setup can actually do this. And finally, this compose project needs to become part of the webproxy network that the reverse proxy uses.

So here’s the modified docker-compose.yml file of the project with the changes marked in blue:

version: '3.3'
 services:
    db:
      image: mysql:5.7
      volumes:
        - ./db_data:/var/lib/mysql
      restart: always
      environment:
        MYSQL_ROOT_PASSWORD: somewordpress
        MYSQL_DATABASE: wordpress
        MYSQL_USER: wordpress
        MYSQL_PASSWORD: wordpress
 wordpress:
      depends_on:
        - db
      image: wordpress:latest
      ports:
        - "8000:80"
      restart: always
      volumes: 
        - ./wordpress:/var/www/html
      environment:
        WORDPRESS_DB_HOST: db:3306
        WORDPRESS_DB_USER: wordpress
        WORDPRESS_DB_PASSWORD: wordpress
        WORDPRESS_DB_NAME: wordpress
        VIRTUAL_HOST: blog3.domain.com
        LETSENCRYPT_HOST: blog3.domain.com
        LETSENCRYPT_EMAIL: noreply@test.com
 networks:
    default:
      external:
        name: webproxy

After having made sure that the DNS entry of the new domain name (blog3.domain.com) also points to the server, the transferred WordPress blog can be brought up as follows:

docker-compose up -d

It will take a minute or so until the blog becomes available via the new domain name because the Letsencrypt certificate has to be requested first. Once done, the reverse proxy will forward the request to the correct container but the request ends up in an error message. That is because the WordPress blog has written the IP address and port number at which the site was previously available into the database. Also, links to inline images that were previously uploaded for blog posts do not yet point to the correct domain. This can be corrected by dumping the database contents to a file, changing all occurrences of the (old) IP+port to the new domain name (without a port) and dumping back the file into the database. Here’s how this is done from the project directory:

# Dump the database
docker exec DATABASE_CONTAINER_NAME sh -c 'exec mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" wordpress' > backup-wp-db.sql

# Modify 
sed -i 's+http://192.168.42.240:8000+https://blog3.domain.com+g' backup-wp-db.sql

# Write it back
cat backup-wp-db.sql | docker exec -i DATABASE_CONTAINER_NAME sh -c '/usr/bin/mysql -u root --password="$MYSQL_ROOT_PASSWORD" wordpress'

# Restart the Worpress containers just to be sure
docker-compose down
docker-compose up -d

All right, we are almost there. If you try to access the new domain name after these changes it is likely that you still end up with an error message, because the web browser has still cached the previous response. Restarting the browser might help. If it doesn’t, clear cookies and site data and restart the browser again.

And that’s it, the blog has moved! While that is nice, there are still two questions from the previous episode that I would like to address in follow up posts:

  • How does the reverse proxy part of the setup notice that a new internal server is started and for which domain name a Letsencrypt certificate should be requested?
  • What is the chain of trust for the remote proxy setup, i.e. whom do I have to trust to keep this installation secure (in addition to the folks at nginx, WordPress and Docker)?