Blogception - How this Blog is setup
Update: This post does not reflect my current setup anymore, check this post to find my new Setup.
Welcome to my first Post on my personal Blog. In this Post, I'm going to outline how I built this Blog and what technologies and hosting setup I'm using.
You maybe already guessed on which platform this Blog is built on, just by looking at the current Theme or checking the source of this page. The Blog is running on WordPress.
WordPress is easy to setup and really extensible. In the past, I've tried and even built different Blog engines but I came back to WordPress. After all, at the time of writing this WordPress is powering 25% of the Internet.
WordPress is the most popular CMS on the Internet, this also makes it lucrative for hackers to find security vulnerabilities to mass target and attack WordPress installations to distribute malware. Therefore security is a big priority in my setup.
The first step to more security is installing WordPress inside a Docker Container. There are many WordPress Dockerfiles available on DockerHub ready to download. There even is an official Docker Image provided, you can check it out here. I decided to build my own image for reasons I will explain later on. This post will simply describe the architecture of my setup and not every detailed configuration. I will write a follow-up post with a detailed explanation of the configurations and Dockerfiles and post them to my Github page.
An important requirement for me was to have a "stateless setup". The setup in a container enables me to scale and replicate to multiple nodes. To achieve this had to make some adjustments to how the docker image is setup.
WordPress by itself is heavily stateful. The WordPress code is saved on the filesystem ( which also represents a state in some way ). For instance, your wp-config.php is saved on the filesystem which contains important configuration details. The installed themes and plugins are saved in the wp-content directory and of course, the Database contains all the Posts and configuration about your site. Updates of WordPress itself also need to update the PHP code stored on the filesystem.
The Database component will never be truly stateless and we will deploy Mysql as a separate instance in a container. The scaling of the Database itself should also be handled independently.
In a typical WordPress setup, we would install WordPress directly on the filesystem and modify the configuration via a text editor. We would be adding our own themes directly into the installation. This approach is hard to maintain and if you want to run your WordPress Blog on 2 separate nodes you have to make a copy of all files. Updating WordPress via the UI would also not work with multiple nodes.
Support for different Environments
I also wanted to be able to setup different environments to develop locally and later deploy to test, staging and production servers.
Easy way to handle WordPress Updates
WordPress updates should be able to be tested and applied to production nodes without to much manual work.
Steps to support all our Requirments
Eliminating Filesystem state
So first I needed to get rid of the filesystem as the main state representation. There are different ways to solve this Problem.
Version Control WordPress
One possibility would be to use Git to version control the WordPress code itself including your own themes and plugins codes. This way one could write scripts that execute a "git pull" on your production nodes to update all production servers with the newest version of your code. This would also enable us to have different environments. Any updates or plugin changes done via the Wordpress Admin UI have to be manually checked in to Git and distributed across all nodes.
Use Docker to build a WordPress Version
Docker images can be tagged and stored in a central registry. Docker images are built via Dockerfiles. In a Dockerfile, we can download the WordPress Code and make our own modifications to it in a reproducible way. The official WordPress Docker image does exactly that, but it also creates a Docker Volume for the installation itself. When we run the official container, a separate Directory for this Docker Volume is created on our local filesystem. This enables the user to log into the WordPress Admin and install plugins and themes and after restarting the container the data is still available. This makes the Filesystem stateful and we do not want that.
To solve this I built my own Dockerfile and skipped the creation of a Docker Volume for the WordPress installation. I added all my own themes and plugins via the Dockerfile while building the image with "docker build". This provides a big advantage, that the state is now represented in the Dockerfile and the filesystem is now stateless, anytime the docker container is restarted the filesystem is reset to the state in the Dockerfile. The drawback is that any manual changes introduced via the WordPress Admin are lost during a restart of the container. But I can take this drawback for a stateless filesystem and much better security.
I'm sure there are other ways to deploy and scale a WordPress installation. But for this Setup, I have used the approach via the Dockerfile.
WordPress Media Files
The Media Library also defines local state with all the Files stored in wp-upload. To fix this we can offload our media files to a CDN provider like Google Cloud Storage or Amazon S3. I have used a WordPress Plugin for that.
To support multiple environments we can either add some entry point Scripts in the Docker container that replaces WordPress configuration variables or fully map a wp-config.php file into the container via Docker Volume mappings. This in conjunction with Docker-Compose we are able to run a local Dev environment and use the same Docker image to run in production.
This Blog is running on the Web, which means it is accessible via the HTTP Protocol. This means that we need a Webserver which is able to execute the Wordpress PHP code every time an HTTP Request is made by the browser. I have used use Nginx together with PHP-fpm. Nginx executes the PHP code via FastCGI.
I added Nginx directly into the Dockerfile that includes the Wordpress installation.
There are many hosting providers that allow you to deploy a Wordpress installation. Some are fully managed and others are more flexible. For my setup, I choose the Google Cloud because it enables fine grade resource control and the scaling to multiple nodes. There also is extensive Docker support via a hosted Container Registry and Google Container Engine.
For the Database, I decided to use the Google Managed Mysql Service CloudSQL. I choose to use the managed Service because it frees me from managing yet another piece of software. Monitoring the Database and applying updates or managing data with backups scripts is a very resource intensive task. For this reason, I'm willing to pay a little more than I otherwise would have to, if I was running a Mysql instance myself.
For the actual deployment of the Docker Container that includes Wordpress, I'm using the open source Cluster Manager Kubernetes.
Kubernetes is developed by Google engineers together with the open source community. The concepts of Kubernetes are derived from a Google internal Cluster Manager called Borg. Kubernetes makes it easy to deploy Docker containers onto a Cluster of compute instances.
We could setup Kuberenetes on any Hosting provider. This again requires the need for maintenance to monitor and update all the Services needed to run a Kubernetes cluster. Luckily Google also provides a service for that, called Google Container Engine. Container Engine offers a fully managed Master Node, this master node is automatically updated. The slave nodes show up as normal compute instances that are running a special Image which includes all the services that are required to run Kuberentes.
So that's it for now. To summarize in one Sentence; This Blog is operating on Wordpress, versioned in a Docker Image, served via Nginx and deployed via Kubernetes, managed by Google Container Engine that is running on Google Compute Engine. More detailed posts will follow and I will open source all of my Dockerfiles needed for this setup.