Docker is a powerful tool, however learning how to use it the right way could take a long time especially with the rapidly growing ecosystem of containers which could be confusing, that is why I had the idea to start writing Painless Docker.
Painless Docker is a complete and detailed guide (for beginners and intermediate levels) to create, deploy, optimize, secure, trace, debug, log,orchestrate & monitor Docker and Docker clusters in order to create a high quality microservices applications.
This article is more detailed in the bonus chapters of Painless Docker Book.
Using Docker, Docker Swarm, Amazon RDS (Aurora) + EC2, GlusterFS & Traefik, we are going to create a highly available and scalable WordPress cluster.
I am using Amazon EC2 machines but you can use you prefered infrastructure.
Start by creating two (EC2) machines in two different availability zones.
In this tutorial, I am using:
eu-west-1aeu-west-1b
The first machine will be the manager of the Swarm cluster and the second one will be the worker.
This is just an example but it depends on how available you want your cluster to be, you may create 3 managers (or more) for more availability.
The manager will have a public IP because it will receive all of the ingoing requests and redirect them to Docker that will handle the internal load balancing between the containers living in the manager (same machine in this case) and the containers living in the worker.
Don’t forget to add an Elastic Block Store to each machine.
We should have two instances:
Using lsbsk
, we can verify that the EBS is attached to our machine:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTxvda 202:0 0 8G 0 disk `-xvda1 202:1 0 8G 0 part /xvdb 202:16 0 10G 0 disk
On each machine, create our filesystem:
sudo mkfs.xfs /dev/xvdb
GlusterFS is a scale-out network-attached storage file system.
Using common off-the-shelf hardware, you can create large, distributed storage solutions for media streaming, data analysis, and other data- and bandwidth-intensive tasks.
GlusterFS was developed originally by Gluster, Inc. and then by Red Hat, Inc., as a result of Red Hat acquiring Gluster in 2011.
Let’s start by installing GlusterFS:
apt-get install -y glusterfs-server
If you are using another OS or another Linux distribution, adapt this command to your need.
GlusterFS Logo
Notice: I created a machine with no public IP (the worker), this machine will not be able to reach Internet and install GlusterFS unless you setup an Amazon NAT instance. If you are just making some tests or not familiar with AWS, create a machine with public IP or create an EIP then assign it to the machine.
Create a mount target (brick):
mkdir -p /glusterfs/bricks/booksfordevops.com
Notice: A brick is a directory on an underlying disk filesystem. If one of the bricks goes down, there is a hardware failure to make the data available.
Use fstab file to permanently mount the EBS to the new brick:
/dev/xvdb /glusterfs/bricks/booksfordevops.com xfs defaults 0 0
Now use mount /dev/xvdb
in order to apply the modifications that we added to the fstab file.
Under each /glusterfs/bricks/booksfordevops.com
mount point a directory used for GlusterFS volume:
mkdir /glusterfs/bricks/booksfordevops.com/wordpress
Let’s add these lines to the /etc/hosts/
file on each server , we will need this in a following step:
172.31.43.166 node1172.31.14.90 node2
node1 is the manager and node2 is our worker. In order to test this configuration, you can execute: ping node2
from node1
:
PING node2 (172.31.14.90) 56(84) bytes of data.64 bytes from node2 (172.31.14.90): icmp_seq=1 ttl=64 time=0.857 ms
From the manager (node1), type the following command to establish Gluster cluster nodes trust relationship:
gluster peer probe node2
You should have peer probe: success
as an output, otherwise check your firewall settings or tail your logs.
Now we have a working GlusterFS trusted pool.
A GlusterFS storage pool is a trusted network of storage servers (node1 and node2 in our case). When we started the first server, the storage pool consisted of that server alone. When we added additional node2 storage server to the storage pool (using the probe command from the node1 storage server that is already trusted), we created a trusted storage pool of 2 servers.
An Example Of A GlusterFS Architecture
Now, from the manager server, we should create a two-way mirror volume that we will call booksfordevops-com-wordpress
using:
gluster volume create booksfordevops_com-wordpress replica 2 node1:/glusterfs/bricks/booksfordevops.com/wordpress node2:/glusterfs/bricks/booksfordevops.com/wordpress
You will get this output:
volume create: booksfordevops_com-wordpress: success: please start the volume to access data
You can see the volume if you type gluster volume list
booksfordevops_com-wordpress
Now you should start it using gluster volume start booksfordevops_com-wordpress
:
If everything is ok, you will get a similar output to this:
volume start: booksfordevops_com-wordpress: success
Our volume should be healthy but you can check its status using gluster volume status
:
Status of volume: booksfordevops_com-wordpressGluster process TCP Port RDMA Port Online Pid------------------------------------------------------------------------------Brick node1:/glusterfs/bricks/booksfordevops.com/wordpress 49152 0 Y 1868 Brick node2:/glusterfs/bricks/booksfordevops.com/wordpress 49152 0 Y 17591NFS Server on localhost N/A N/A N N/A Self-heal Daemon on localhost N/A N/A Y 1894 NFS Server on node2 2049 0 Y 17612Self-heal Daemon on node2 N/A N/A Y 17613
Task Status of Volume booksfordevops_com-wordpress------------------------------------------------------------------------------There are no active volume tasks
Now that the GlusterFS server is set, we need to setup the client side. Let’s create the directory (in each node) to be used by the client in each node of our cluster:
mkdir -p /data/booksfordevops_com-wordpress
We need to mount a shared directory on node2 from the node1 and the same directory on node1 from node2.
On node1, add this line at the end of /etc/fstab
file:
node2:/booksfordevops_com-wordpress /data/booksfordevops_com-wordpress glusterfs defaults,_netdev 0 0
On node2, add this line at the end of /etc/fstab
file:
node1:/booksfordevops_com-wordpress /data/booksfordevops_com-wordpress glusterfs defaults,_netdev 0 0
Then on both hosts, type mount -a
.
The next step is to install Docker on both hosts:
curl -fsSL https://get.docker.com/ | sh
Then initialize the Swarm cluster:
docker swarm init
Execute the last command on the manager and you will get a command to execute on the worker:
docker swarm join \ --token XXXXXX-x-xxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxx \ 172.31.43.116:2377
If everything is ok, the worker will join the cluster:
This node joined a swarm as a worker.
In this tutorial, we want to create a Wordpress blog, we can host the Mysql or the MariaDB database in our GlusterFS trusted pool but in my specefic case I created an Aurora database.
We are going to host Wordpress files in the storage pool we created, that points to /data/booksfordevops_com-wordpress
.
This is the Docker Compose v3 file that we are going to deploy:
version: '3'networks: booksfordevops_com-network:
services: wordpress: image: wordpress:4.7.3-php7.1-apache ports: - 8000:80 networks: - booksfordevops_com-network volumes: - /data/booksfordevops_com-wordpress:/var/www/html environment: WORDPRESS_DB_HOST: xxxxx.cluster-xxxxx.eu-west-1.rds.amazonaws.com:3306 WORDPRESS_DB_USER: user WORDPRESS_DB_PASSWORD: password WORDPRESS_DB_NAME: db_name WORDPRESS_TABLE_PREFIX: wp_ deploy: mode: replicated replicas: 1 labels: [APP=WORDPRESS] restart_policy: condition: always
Notice: For security reasons, do not put your docker-compose.yml file in the same directory as your Wordpress files in `/data/booksfordevops_com-wordpress
, it will be publicly accessible.
In order to deploy our website, we should execute this command:
docker stack deploy --compose-file=docker-compose.yml booksfordevops_com
We can use a similar command to the following one in order to deploy the Wordpress app:
docker network -d overlay booksfordevops_com-network
docker service create --name booksfordevops_com_wordpress \--publish 8000:80 \--mount type=bind,source=/data/wp,target=/var/www/html \-e WORDPRESS_DB_HOST=x.cluster-x.eu-west-1.rds.amazonaws.com:3306 \-e WORDPRESS_DB_USER=user \-e WORDPRESS_DB_PASSWORD=password \-e WORDPRESS_DB_NAME=db_name \-e WORDPRESS_TABLE_PREFIX=wp_ \--replicas 1 \--network booksfordevops_com-network \wordpress:4.7.3-php7.1-apache
But putting all together in a Docker Compose v3 file is a more organised way to deploy our app.
You may have some premission problems with your Wordpress fresh installation, you will need to execute these commands:
cd /data/booksfordevops_com-wordpress
chown www-data:www-data -R *
find . -type d -exec chmod 755 {} \;
find . -type f -exec chmod 644 {} \;
In both servers, you can now notice that Wordpress files are mounted from the service containers to the host volume:
ls -lrth /data/booksfordevops_com-wordpress
-rw-r--r-- 1 www-data www-data 418 Sep 25 2013 index.php-rw-r--r-- 1 www-data www-data 3.3K May 24 2015 wp-cron.php-rw-r--r-- 1 www-data www-data 364 Dec 19 2015 wp-blog-header.php-rw-r--r-- 1 www-data www-data 1.6K Aug 29 2016 wp-comments-post.php-rw-r--r-- 1 www-data www-data 3.0K Aug 31 2016 xmlrpc.php-rw-r--r-- 1 www-data www-data 5.4K Sep 27 21:36 wp-activate.php-rw-r--r-- 1 www-data www-data 4.5K Oct 14 19:39 wp-trackback.php-rw-r--r-- 1 www-data www-data 30K Oct 19 04:47 wp-signup.php-rw-r--r-- 1 www-data www-data 3.3K Oct 25 03:15 wp-load.php-rw-r--r-- 1 www-data www-data 34K Nov 21 02:46 wp-login.php-rw-r--r-- 1 www-data www-data 2.4K Nov 21 02:46 wp-links-opml.php-rw-r--r-- 1 www-data www-data 16K Nov 29 05:39 wp-settings.php-rw-r--r-- 1 www-data www-data 20K Jan 2 18:51 license.txt-rw-r--r-- 1 www-data www-data 7.9K Jan 11 05:15 wp-mail.php-rw-r--r-- 1 www-data www-data 7.3K Jan 11 17:46 readme.htmldrwxr-xr-x 18 www-data www-data 8.0K Mar 6 16:00 wp-includesdrwxr-xr-x 9 www-data www-data 4.0K Mar 6 16:00 wp-admin-rw-r--r-- 1 www-data www-data 2.7K Mar 19 19:41 wp-config-sample.php-rw-r--r-- 1 www-data www-data 3.2K Mar 19 19:41 wp-config.phpdrwxr-xr-x 4 www-data www-data 52 Mar 19 19:51 wp-content
If you check the created brick on each server, you will find the same files:
ls -lrth /glusterfs/bricks/booksfordevops.com
At this step, we have a working Wordpress installation that you can reach using the IP address of the manager and the port 8000.
Træfɪk is a HTTP reverse proxy and load balancer made to deploy microservices and supports Docker and Docker Swarm (and other backends like Mesos/Marathon, Consul, Etcd, Zookeeper, BoltDB, Amazon ECS and _Rest API_s).
Let’s create a service to run Traefik on the manager ( --constraint=node.role==manager
). The reverse proxy will run on a separate network ( --network traefik-net
).
docker network -d overlay traefik-net;
docker service create --name traefik \--constraint=node.role==manager \--publish 80:80 \--publish 8080:8080 \--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \ --network traefik-net \traefik:v1.1.0-rc1 \--docker \--docker.swarmmode \--docker.domain=traefik \--docker.watch \--web
In order to make our Wordpress website work with Traefik we are going to update its docker-compose file and add it to the same Traefik network traefik-net
. I added also some labels related to Traefik like port and frontend.rule:
version: '3'networks: traefik-net: external: true booksfordevops_com-network:
services: wordpress: image: wordpress:4.7.3-php7.1-apache ports: - 8001:80 networks: - booksfordevops_com-network - traefik-net volumes: - /data/booksfordevops_com-wordpress:/var/www/html environment: WORDPRESS_DB_HOST: xxxx.cluster-xxxxx.eu-west-1.rds.amazonaws.com:3306 WORDPRESS_DB_USER: user WORDPRESS_DB_PASSWORD: password WORDPRESS_DB_NAME: db_name WORDPRESS_TABLE_PREFIX: wp_
deploy: mode: replicated replicas: 2 labels: APP: WORDPRESS traefik.port: 80 traefik.frontend.rule: "Host:booksfordevops.com,www.booksfordevop.com"
restart_policy: condition: on-failure
Update the deployment using
docker stack deploy --compose-file=docker-compose.yml booksfordevops_com
You can check if the configured domain is accessible using a simple curl, in my case:
curl -H Host:booksfordevops.com http://127.0.0.1
You can also go to the health dashboard in order to see things like the response time and the status codes of our application.
We saw how to build a highly available Wordpress website, where storage and computing are distributed into two different regions.
Within each EC2 machine we can scale Wordpress to more than one container and have another level of resilience.
Our reverse proxy can check the health of each container and manage to redirect traffic to the working ones.
We used modern tools and technologies like:
Our website is working, you can subscribe and wait for Books For DevOps release.
This article is part of Painless Docker Book: Unlock The Power Of Docker & Its Ecosystem.
Painless Docker is a practical guide to master Docker and its ecosystem based on real world examples.
Painless Docker tends to be a complete and detailed guide to create, deploy, optimize, secure, trace, debug, log, orchestrate & monitor Docker and Docker clusters. Through this book you will learn how to use Docker in development and production environments and the DevOps pipeline between them in order to build a modern microservices applications.
If you resonated with this article, please subscribe to DevOpsLinks : An Online Community Of Diverse & Passionate DevOps, SysAdmins & Developers From All Over The World.
You can find me on Twitter, Clarity or my blog and you can also check my books: SaltStack For DevOps & The Jumpstart Up.
If you liked this post, please recommend and share it to your followers.