Site, resurrected

Lately I’ve been busy resurrecting this site, using Docker containers to automate the creation of the runtime environment. In case you haven’t heard of Docker containers, here’s what the official Docker site has to say about them:

Docker containers wrap a piece of software in a complete filesystem that contains everything needed to run: code, runtime, system tools, system libraries – anything that can be installed on a server. This guarantees that the software will always run the same, regardless of its environment.

While a technology like Docker is probably more likely to be used for enterprise applications, the need for software to “always run the same” is just as applicable to personal sites like this. In this post I’ll try to show you how I’ve leveraged Docker to bring some consistency to my site’s development and production environments.

Software requirements

A WordPress site such as this needs a few different pieces of software in order to run:

  • WordPress
  • Apache HTTPD (for serving WordPress files)
  • MySQL (for storing WordPress data)

Without something like Docker I would have to install and configure this software manually both on my local machine (for development) and on my remote machine (for production). With Docker, however, I can simply create some YAML-based config files and have the Docker Compose component do the work for me. Following’s how I did this.

Docker Compose config

To define the software requirements, or “services” in Dockerspeak, I created three config files: one config file per environment (development and production) and one config file for the attributes of the services that are common to these environments.

common-services.yml
Contains service definitions for development and production.
docker-compose.yml
Contains service definitions for development only. Extends services defined in common-services.yml via the extends config option.
docker-compose.production.yml
Contains service definitions for production only. Extends services defined in common-services.yml via the extends config option.

MySQL service definition

The MySQL service definition is identical in both development and production. As such it can be defined in common-services.yml and brought into a specific environment by extension.

Here’s the definition (from common-services.yml):

services:

...

  wordpressdb:

    image: mysql:5.7

    container_name: wordpressdb

    env_file: ./wordpressdb/env/wordpress.common.env

    volumes:

      - ./wordpressdb/cnf:/etc/mysql/conf.d/

      - ../data:/var/lib/mysql

...

volumes:

  data:

… and here’s the extension (from docker-compose.yml and docker-compose.production.yml):

  wordpressdb:

    extends:

      file: common-services.yml

      service: wordpressdb



MySQL service config options

image
Defines the MySQL image upon which to base the container.
container_name
Defines the name to use for the container. Not strictly necessary but without it Docker will create a name by itself.
env_file
Points to a plain-text file that defines environment variables that will be accessible within the container, e.g., the username and password for the database.
volumes
Defines directory mappings between host and container. One such mapping is a data volume that will be used to persist data to the host. (See top-level volumes config option in common-services.yml.)

WordPress service definition

The WordPress service definition has some differences between development and production. As such it can be defined in part in common-services.yml and extended in a specific environment.

Here’s the definiton (from common-services.yml):

  wordpress:

    image: wordpress:4.7-apache

    container_name: wordpress

    ports:

      - 80:80

… and here’s the extension (from docker-compose.yml and docker-compose.production.yml):

  wordpress:

    extends:

      file: common-services.yml

      service: wordpress

    volumes:

      - ../html:/var/www/html

      - ./wordpress/conf:/etc/apache2/sites-available

    links:

      - wordpressdb:mysql

    env_file:

      - ./wordpress/env/wordpress.common.env

      - ./wordpress/env/wordpress.${environment}.env

WordPress service config options

image
Defines the WordPress image upon which to base the container. This particular version of WordPress includes Apache HTTPD, so I don’t have to worry about creating a separate service just for this.
container_name
Defines the name to use for the container.
ports
Defines port mappings between host and container. In this case port 80 on the host is being mapped to the same port on the container. This allows me to run the site on the default HTTP port in both development and production.
volumes
Defines directory mappings between host and container. In this case the config tells the container where on the host it can expect to find the WordPress files and the Apache vhost config file.
links
Allows the WordPress container to access the MySQL container.
env_file
Points to a plain-text file that defines environment variables that will be accessible within the container. In this case there are two such files: one for environment variables that are common to environments and one for environment variables that are specific to an environment, e.g., WP_SITEURL, WP_HOME.

Building the services

With this configuration in place it becomes trivial to build the services for development and production.

To build for development I can simply update my repo on my local machine and enter the following command: docker-compose up --build -d. This command will combine the config from common-services.yml and docker-compose.yml and use the result.

To build for production I can simply update my repo on my remote machine and enter the following command: docker-compose -f docker-compose.production.yml --build -d. This command will combine the config from common-services.yml and docker-compose.production.yml and use the result.

Create Friendlier WordPress URLs With the Apache Rewrite Module

This week on the Web has led me to several worthwhile technological discoveries. While I would have been—to plagiarize a Blackadderism—as excited as a very excited person who has a special reason to be excited to write about, say, running JavaScript-based macros in a Google Drive spreadsheet or editing live CSS with the User CSS extension for Safari, I’ve chosen instead to expound upon creating reader-friendly URLs in WordPress—a nicely self-referential topic when you consider that DavidSmithWeb (hereafter “DSW” if you’ll pardon the mock legalese) is itself based on WP.

A decided (and, in my experience, uncharacteristic for WP) awkwardness I encountered upon deploying my WP site for the first time was the decidedly unglamorous URLs the platform created for my pages. Here, for instance, is the URL it generated for my main blog page:

//www.davidsmithweb.com/?page_id=58

And here’s the one it generated for my last blog post:

//www.davidsmithweb.com/?p=72

Deeply unattractive, I’m sure you’ll agree. So how did I go about making them more appealing?

It all started with a little tweaking of my Apache configuration. I first needed to make sure my server’s rewrite module was available. The “mod_rewrite” module, as it’s known, “provides a rule-based rewriting engine to rewrite requested URLs on the fly,” according to the Apache documentation. While the specifics of the rewriting process are beyond the scope of this post (that, and I don’t yet know enough about regular expressions!), checking for and enabling the module was fairly straightforward. For a list of available Apache modules, I went to my command line and navigated to the “mods-available” directory:

cd /etc/apache2/mods-available

There I found. as expected, a file named “rewrite.load.”

I then enabled the module by returning to the command line and entering the a2enmod command followed by the name of the relevant module:

a2enmod rewrite

(Entering the “load” file extension appeared to be unnecessary.)

In order for the enabling to take effect I found it was also necessary to restart Apache. From the command line I did it thus:

service apache2 restart

I then navigated to the “mods-enabled” directory to confirm the module had indeed been enabled, i.e., that the “rewrite.load” file was present in the directory:

cd /etc/apache2/mods-enabled

With my command line duties done (at least for the time being), I then turned to the more aesthetically pleasing—if not quite as nerdy—WP administrative interface. WP conveniently offers the ability to create reader-friendly URLs via its “Permalink Settings” screen (under Settings > Permalinks). While the more adventurous WP users among us may wish to choose custom URL structures, the less daring may content themselves with the several predefined options WP offers. Ever the pioneer, I chose the following custom structure:

/blog/%year%/%monthnum%/%postname%/

The only slight inconvenience I encountered here was that I needed to make my “.htaccess” file writable by WP. To wit, I returned once more to the command line, navigated to my site’s public folder, and gave the “.htaccess” file full read-write permissions:

chmod 666 .htaccess

I then returned to WP to save the changes to my site’s permalink settings. Finally I returned to the command line to reinstate the “.htaccess” file’s original permissions:

chmod 644 .htaccess

And that, as they say, was that. Remember those dowdy-looking URLs we encountered at the top of the post? Let’s have a look at them now! Here’s the URL for my main blog page:

//www.davidsmithweb.com/blog/

And here’s the one for my last blog post:

//www.davidsmithweb.com/blog/2013/01/1password-the-summit-of-password-managers/

Much more enticing, I’m sure you’ll agree.


Note 1: At the command line I needed superuser privileges in order to tinker with my Apache configuration to the extent that was necessary, i.e., I preceded each command with sudo.


Note 2: Here are some details of my current system configuration, in case they’re helpful:

  • Ubuntu 12.04
  • Apache 2.2.22
  • WordPress 3.4

Note 3: Mileage may vary with the specific commands and directory structures described in this post.