Build a Website with WordPress, Let’s-Encrypt and Docker

Building a website with docker is absolutely an enjoyable experience. With prebuilt images we are allowed to focus more on wiring and configuring different components instead of taking time to setup environment for each one of them. And with several configuration files, we are able to reproduce the same process from scratch as many times as we want.

To quantify the simplicity, this whole process of building an SSL encrypted, Google Analytics monitored website with stunning UI will take you less than 10 minutes. Sounds attractive? Then let’s get started!

Prerequisite

  • Host with clean Ubuntu 18.04 LTS
  • Domain with A record pointing to the host’s IP (in my case “bd16s.com”)

Objectives Breakdown

  • Step 1: Install docker and docker-compose
  • Step 2: Start WordPress container backed by MySQL container
  • Step 3: Run linuxserver/letsencrypt image
    • This image will fetch the SSL Certificate
    • This image will also bring up an Nginx server, which can serve as a reverse proxy server
  • Step 4: Configure Google Analytics

Step 1: Install docker and docker-compose

Install docker

There are many good tutorials for installing docker on Ubuntu, for example this one from DigitalOcean is very clear and detailed. Here I take some commands and explanations we need from the post.

# First, update your existing list of packages:
sudo apt update

# Next, install a few prerequisite packages which let apt use packages over HTTPS:
sudo apt install apt-transport-https ca-certificates curl software-properties-common

# Then add the GPG key for the official Docker repository to your system:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

# Add the Docker repository to APT sources:
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"

# Next, update the package database with the Docker packages from the newly added repo:
sudo apt update

# Finally, install Docker:
sudo apt install docker-ce

At this point, we should have docker-ce installed on host, and the docker daemon should be running.

Install docker-compose

The install procedure for docker-compose can be found from here in Docker Documentation. Replace version number 1.23.2 with the latest version listed on GitHub release page.

# Download the latest version of Docker Compose:
sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

# Apply executable permissions to the binary:
sudo chmod +x /usr/local/bin/docker-compose

Now docker-compose is installed.

Step 2: Start WordPress and MySQL

Prepare docker-compose.yml file:

version: '3.3'

services:
   wordpress:
     depends_on:
        - mysql
     image: wordpress:5.0.2
     volumes:
        - ./wordpress_files/var/www/html:/var/www/html
     expose:
       - "80"
     restart: always
     container_name: wordpress
     environment:
       WORDPRESS_DB_HOST: mysql:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: <wordpress_password_placeholder>
   db_mysql:
     image: mysql:5.7
     volumes:
       - ./mysql_data/var/lib/mysql:/var/lib/mysql
     expose:
       - "3306"
     restart: always
     container_name: wordpress-mysql
     environment:
       MYSQL_ROOT_PASSWORD: <root_password_placeholder>
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: <wordpress_password_placeholder>

This file tells docker-compose to start to containers, one with PHP and WordPress and the other with MySQL. Each container has a shard mount with local storage, thus data will be persisted outside the containers and so containers themselves can be seen as stateless. With this configuration, the exposed ports are only accessible from within the docker bridge, but not outside. This is because we are going to use a reverse proxy server in front of it, and only that server should be exposed to public. Once configured as above, start containers by running:

sudo docker-compose up -d

Reference:

Step 3: Run linuxserver/letsencrypt

For free SSL/TLS Certificates, the most popular provider is “Let’s Encrypt”. It is popular enough that docker images are created and actively maintained. Here we use image from linuxserver/letsencrypt, which will handle the verification process, and renew the certificate every month. It also provides an Nginx server out of the box, which can be used as a reverse proxy server for wordpress.

Start Container

Since we only have one container to start, follow the instruction from official documentation and simply run it from terminal with the following command:

sudo docker run -d --network="wordpress_default"\
  --cap-add=NET_ADMIN \
  --name=letsencrypt \
  -v <path to data>:/config \
  -e PGID=<gid> -e PUID=<uid> \
  -e EMAIL=<email> \
  -e URL=bd16s.com \
  -e SUBDOMAINS=www \
  -e VALIDATION=http \
  -p 80:80 -p 443:443 \
  -e TZ=<timezone> \
  linuxserver/letsencrypt:196

Here use id <username> to get PGID and PUID, and since we want this container to talk to other containers in the virtual network, we need to specify the network name.

It will take some time to validate the domain and get the certificate for the first time. Once the container is fully started, it will create the config folder.

Edit Nginx Proxy Configuration

From the config folder we need to tell Nginx to forward requests to wordpress container. go to config/nginx/site-confs and edit the default file. Here is the config file I’m using.

# redirect all traffic to https
server {
  listen 80;
  listen [::]:80;
  server_name _;
  return 301 https://$host$request_uri;
}

# main server block
server {
  listen 443 ssl http2 default_server;
  listen [::]:443 ssl http2 default_server;

  server_name _;

  # enable subfolder method reverse proxy confs
  include /config/nginx/proxy-confs/*.subfolder.conf;

  # all ssl related config moved to ssl.conf
  include /config/nginx/ssl.conf;

  client_max_body_size 0;

  location / {
    proxy_pass http://wordpress:80;
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}
Configure WordPress

Follow the instruction from Heiner’s Blog and:

  1. Install and activate plugin “SSL Insecure Content Fixer”
  2. Go to Settings >> SSL Insecure Content Fixer settings >> HTTPS detection, and select “HTTP_X_FORWARDED_PROTO (e.g. load balancer, reverse proxy, NginX)”
  3. Go to Settings >> General Settings, and set both “WordPress Address (URL)” and “Site Address (URL)” to the domain name

Step 4: Configure Google Analytics

Add plugin MonsterInsights to WordPress and set the tracking ID. This tracking ID can also be used by Google Search Console to verify the ownership of the website.

Extra Step: Sit Back and Enjoy!

Now with all jobs done, get a cup of tea and enjoy the awesome website, cheers!

Character Encoding Made Simple

Computers are smart enough to read bytes, but human beings aren’t. What we are able to read are characters, so we use encoding standards like ASCII and Unicode to map characters to bytes.

An ASCII character can fit to a byte (8 bits), but most Unicode characters cannot. So encoding forms/schemes like UTF-8 and UTF-16 are used.

ASCII

The original ASCII table is encoded on 7 bits therefore it has 128 characters.

Nowadays most readers/editors use an “extended” ASCII table (from ISO 8859-1), which is encoded on 8 bits and enjoys 256 characters (including Á, Ä, Œ, é, è and other characters useful for european languages as well as mathematical glyphs and other symbols).

Unicode

It assigns every character in all languages a unique number called a code point.

USC-2

Originally, Unicode was intended to have a fixed-width 16-bit encoding: UCS-2 (Universal Character Set in 2 Bytes). Early adopters of Unicode, like Java and Windows NT, built their libraries around 16-bit strings. Later, the scope of Unicode was expanded to include historical characters, which would require more than the 65,536 code points a 16-bit encoding would support. To allow the additional characters to be represented, Unicode Transformation Format (UTF) was introduced.

UTF-32

Unicode Transformation Format in 32 bits. It is a protocol to encode Unicode code points that uses exactly 32 bits per Unicode code point. Each 32-bit value in UTF-32 represents one Unicode code point and is exactly equal to that code point’s numerical value.

UTF-16

The encoding is variable-length, as code points are encoded with one or two 16-bit code units. UTF-16 makes the encoding more space-efficient, but 16 bits can’t represent all characters in Unicode therefore they invented “surrogate pair“. Surrogate pair uses two code units to represent a character and can get detected by looking at the first 6 bits.

UTF-8

Similar to UTF-16, the encoding is variable-length, as code points are encoded with one, two, three or four bytes. UTF-8 is the dominant character encoding for the World Wide Web, accounting for 88.7% of all Web pages in March 2017.


Side Note:

References:

HC12 Microcontroller: Timer and PWM

Timer

Main (free-running) timer is a 16-bit register TCNT. It increases every clock period, and when it overflows (from 0xFFFF to 0x0000) it will set the overflow flag register TFLG2. Any access (read or write) to TCNT will reset the TFLG2 if fast flag clear all bit of system control register TSCR1 is set.

Two main use of TSCR1 is to enable main timer and enable fast flag clear.

Two main use of TSCR2 is overflow interrupt enabling and prescaler selection. The prescaler range of HC12 is from 2^0 = 1 to 2^7 = 128.

Then we should choose how to use the timer by assigning value to register TIOS. There are 8 timer channels from 0 to 7. For each channel, 0 means input capture and 1 means output compare.

Input Capture

Input capture means “when something S1 happens on this pin, do something S2.” S1 can be either rising-edge, falling-edge or both edges. To control what event will trigger input capture, assign value to register TCTL3 and TCTL4. S2 can be setting a flag or generating an interrupt. When an event occurs, it will set the timer flag accordingly. If interrupt is enabled, an interrupt will be generated.

Output Compare

Output compare means “when TCNT is equal to some value V1, do something S2”. V1 is set by register TCx. Since there are 8 channels, there are eight 16-bit registers TC0 to TC7, and each register is consist of higher part 8-bit TCxH and lower part 8-bit TCxL. That is, when TCNT reach the value TCx, channel x will do S2. S2 can be set to high, set to low, or toggle. S2 is determined by TCTL1 and TCTL2. When an event occurs, it will set the timer flag accordingly. If interrupt is enabled, an interrupt will be generated.

PWM

PWM stands for Pulse Width Modulation, that is, the output of PWM will be a square wave with customized period and duty cycle.

To use PWM in HC12, first step is to select clock source by assigning value to register PWMCLK. There are two independent prescaler to the system clock which output two independent clock A and B. Either of them can be further divided, and the output is SA and SB. Channel 0, 1, 4, 5 uses clock A/SA, and channel 2, 3 uses clock B/SB. For each channel, assigning 0 to the corresponding bit in PWMCLK will choose A (or B), 1 will choose SA (or SB).

PWMPRCLK will set prescale. PWMSCLA and PWMSCLB will set the clock divider.

PWMCTL will choose 8-bit mode or 16-bit mode. 16-bit mode is concatenating channel k and k+1. k is the higher-order channel, k+1 is the lower-order channel. Outputs will come out from channel k+1.

PWMPOL will choose the value at the beginning of the period.

PWMCAE chooses align mode (left or center). If center mode is selected, period and duty cycle value is actually half of the real value.

PWMPERx and PWMDTYx are the most important registers. PWMPERx will set the length of the period of channel x and PWMDTYx will set the length of duty cycle of channel x.

PWME will enable the PWM channel.