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).