Docker Lab – Containerising your website, Part 1 (Nginx)

For many years now, 45RPMSoftware has maintained its own webservers (hosted on virtual machines by Rackspace).  These webservers have been set up in the ‘traditional’ manner, with a LAMP (Linux, Apache, MySQL, PHP) stack.  They’ve worked well and so I’ve ignored them, which isn’t the right way to run a website and especially not one which hosts your business.For many years now, 45RPMSoftware has maintained its own webservers (hosted on virtual machines by Rackspace).  These webservers have been set up in the ‘traditional’ manner, with a LAMP (Linux, Apache, MySQL, PHP) stack.  They’ve worked well and so I’ve ignored them, which isn’t the right way to run a website and especially not one which hosts your business.

The time has come to make a change.  The first change that I made was to the stack.  Out goes Apache and MySQL and in comes Nginx and MariaDB – changing my LAMP stack to a LEMP stack.  The next change was a decision to containerise all the services required by 45RPMSoftware by using Docker.  There are many reasons why I decided to do this, and many hurdles that I had to overcome – not least caused by my own inexperience with Docker itself.

For my purposes the primary benefits of Docker are:

  • Security.  Each service (Nginx, PHP, MariaDB etc) runs in a container which is completely segregated, able to communicate with other services only according to the rules configured in the Docker configuration.  No Docker container is allowed to look at the processes running in another container.
  • Maintainability.  Because each container is self contained, deleting a service when it is no longer required is as simple as deleting its container.  All temporary files and configuration (barring those configuration files which you have mounted elsewhere (read on for details)) will be removed when you delete the container.  Similarly, installing a new version of a service is as simple as reinstalling its container.
  • Portability. In theory (I haven’t tried it) Docker also improves portability, if I want to move to a different OS or to a different service provider (AWS, for example), all I need is my configuration file – Docker will handle consistency between these different environments and so the move should be close to seamless.

Other people might have other reasons for containerisation and Docker in particular. You might like it for the potential benefits that it brings to CI/CD, or for rapid deployment. All you need to know is that it is an extraordinarily useful tool.

So how did I get my LEMP stack up and running?  These steps will, I hope, help you build a containerised environment using Docker.  This assumes that you’re starting with a clean sheet of paper, a fresh install of Linux and that you’ve registered a domain name and pointed it at your server.  I’m assuming that you know how to do these steps!

As always, if you have a better way of doing things don’t keep it to yourself – share it with me so that I can learn from your experience and share it with everyone else.

Installing Docker.

The first step, of course, is to install Docker.  Do this as follows:

cd ~ 
mkdir installers 
cd installers/ 
wget https://get.docker.com 
mv index.html install_docker.sh 
chmod +x install_docker.sh  
./install_docker.sh  
sudo usermod -aG docker <your username>

Installing Docker Compose.

Docker Compose takes a lot of the effort out of using Docker.  With one configuration file, docker-compose.yml, you can set up multiple services with one command.  You can also stop and delete multiple services with one command.  Docker makes building software infrastructure easier – Docker Compose makes Docker easier. Install it as follows:

cd ~/installers/ 
sudo curl -L https://github.com/docker/compose/releases/download/1.17.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose 
sudo chmod +x /usr/local/bin/docker-compose

At the time of writing, version 1.17.1 was the latest version.  Check for the latest version here https://github.com/docker/compose/releases/ – looking for versions which are tagged ‘Latest Release’ – and substitute the version number accordingly.

Configuring your Containers.

I find that the best way to configure your containers is one at a time.  Get Nginx working first and then add PHP.  Get PHP working and then add MariaDB.  Get MariaDB working – and then add anything else that you’d like to add to your setup.

Creating your directories

First of all create your root Docker directory in your home folder.  I called mine lemp-compose because it contains the files necessary to compose LEMP. Replace <your website name> with, surprisingly, the name of your website.  For www.45rpmsoftware.com I could use www.45rpmsoftware.com, but it isn’t necessary – so I just use 45rpmsoftware

cd ~ 
mkdir -p lemp-compose/nginx/vhosts/sites-enabled 
mkdir -p lemp-compose/nginx/vhosts/sites-available 
mkdir -p lemp-compose/public/<your website name>/htdocs 
mkdir -p lemp-compose/logs

Setting up the Nginx Container

Next create your configuration file – docker-compose.yml.  You might use vi or emacs to edit the file, but I prefer the easy life so I’ll be using nano.  The important thing is what the yml file contains:

cd ~/lemp-compose 
nano docker-compose.yml

Add the following lines to the docker-compose.yml file

version: '2'

services:
  nginx:
  image: 'bitnami/nginx:latest'
  ports:
    - '80:8080'
    - '443:8443'
  volumes:
    - ./logs:/opt/bitnami/nginx/logs
    - ./nginx/vhosts:/bitnami/nginx/conf/vhosts
    - ./public:/opt/bitnami/nginx/html

Quit out of the editor – and we’ll start Nginx.  Initially we don’t want to use Nginx – we just want to copy some configuration files out of the container so that we can edit them without fear of losing our changes if the container gets deleted or updated. Docker containers are supposed to be stateless – so don’t make changes to any configuration contained within them.  Use volumes to make sure that all configuration that will change is outside the container.

Start Docker by typing:

cd ~/lemp-compose 
docker-compose up -d

The -d parameter tells Docker to start detached.  In practical terms this means that it’ll return you to the command prompt straight away (and your website will stay running even if you log out).

Next find out what the id of your newly started container is.

docker ps

This will return a response something like the following.  If it doesn’t then Docker isn’t installed or working properly – so work your way back through the steps up to this point checking carefully for mistakes or error messages.

CONTAINER ID        IMAGE                    COMMAND                  CREATED             STATUS              PORTS                                         NAMES 
673b1e25c03a        bitnami/nginx:latest     "/app-entrypoint.s..."   3 hours ago         Up 3 hours          0.0.0.0:80->8080/tcp, 0.0.0.0:443->8443/tcp   lempcompose_nginx_1

You are particularly interested in the container ID.  You’ll need this to access the contents of the container – or even to log into the container.  In this example the container ID is 673b1e25c03a

Now copy the configuration files out of the container by typing, replacing <containerID> with the container ID:

cd ~/lemp-compose 
docker cp <containerId>:/bitnami/nginx/conf/nginx.conf ./nginx/nginx.conf 
docker cp <containerId>:/bitnami/nginx/conf/fastcgi.conf ./nginx/fastcgi.conf

Now you can stop Docker and edit the docker-compose.yml file to include a couple of extra configuration lines.

cd ~/lemp-compose 
docker-compose stop 
nano docker-compose.yml

Edit the docker-compose.yml file so that it looks like this:

version: '2'

services:
  nginx:
  image: 'bitnami/nginx:latest'
  ports:
    - '80:8080'
    - '443:8443'
  volumes:
    - ./nginx/nginx.conf:/bitnami/nginx/conf/nginx.conf
    - ./nginx/fastcgi.conf:/bitnami/nginx/conf/fastcgi.conf
    - ./logs:/opt/bitnami/nginx/logs
    - ./nginx/vhosts:/bitnami/nginx/conf/vhosts
    - ./public:/opt/bitnami/nginx/html

Configuring your Website

Nginx Configuration

Now edit nginx.conf to include sites-enabled by searching for the vhosts include line (probably by searching for all instances of include and checking to see if they say vhosts/*.conf) and commenting it out (if necessary).  Then add the following line to the http section (it should end up looking something like this)

#  include "/opt/bitnami/nginx/conf/vhosts/*.conf"; 
include "/opt/bitnami/nginx/conf/vhosts/sites-enabled/*.conf";

It’s also a good idea, for security purposes, to hide the Nginx version number.  There’s no need to make a crackers life any easier than it is already.  Add the following line to your nginx.conf file in the http section.

# Don’t show the Nginx version number (in error pages / headers)
server_tokens off;

Website Configuration

Now you need to set up the configuration for your website, which is in ~/nginx/vhosts/sites-available

nano ~/nginx/vhosts/sites-available/<your website name>.conf

Edit it as follows, replacing the server_name domain name with the domain name(s) of your website.

server {
 listen 0.0.0.0:8080;
 server_name <your domain name>;
 server_tokens off;
 
 error_log "/opt/bitnami/nginx/logs/<your website name>-error.log";
 access_log "/opt/bitnami/nginx/logs/<your website name>-access.log";
 
 location / {
  root /opt/bitnami/nginx/html/<your website name>/htdocs;
  index index.html index.htm; 
 }
 
 location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
  expires max;
  log_not_found off;
 }
}

Having created the configuration for your website in sites-available you now need to link to it in sites-enabled in order that nginx can include it at startup.

cd ~/nginx/vhosts/sites-enabled
ln -s ~/nginx/vhosts/sites-available/<your website name>.conf .

If you’ve pulled all of that off correctly, you should be able to put index.html into ~/lemp-compose/public/<your website name>/htdocs and start Docker:

cd ~/lemp-compose 
docker-compose up -d

Enter your website URL into your browser and (fingers crossed) your index.html will be displayed, served up by your containerised instance of Nginx.  Go and have a well earned cup of tea before moving onto the next stage – setting up php (which I’ll deal with in a future article).

Leave a Reply

Your email address will not be published. Required fields are marked *