Hussein Galal is a Linux System Administrator, with experience in Linux, Unix, Networking, and open source technologies like Nginx, Apache, PHP-FPM, Passenger, MySQL, LXC, and Docker. You can follow Hussein on Twitter @galal_hussein.

I recently used Docker and Rancher to set up a Redis cluster on Digital Ocean. Redis clustering provides a way to share data across multiple Redis instances, keys are distributed equally across instances using hash slots. Redis clusters provide a number of nice features, such as data resharding and availability between instances.

Availability is provided using Redis replication, where each master instance has a slave instance which replicate its data, for more information about redis cluster, see Redis cluster official documentation.

In this post, I will setup and configure a Redis cluster on the same host, and I will setup and configure a Redis cluster on Docker instances on different hosts using Rancher. I’ll explain the limitations of using a single host to setup the Redis cluster without using Rancher.

Why Rancher?

First, Rancher provides me a set of features to help manage docker containers beyond a single host. Second, and more importantly, it provides an overlay network that would allow my Docker containers to more easily communicate across hosts. In essence, Rancher provides a virtual private network for the registered hosts and will allow Docker containers to communicate under the same subnet:

Without Rancher, hosts running Docker containers will be isolated from each other, therefore Docker containers can only communicate with each other through the public ip’s of the hosts of different cloud service providers.

[]Prerequisites Of The Setup

  1. Digital Ocean Account: To create the instances which we will register with Rancher.
  2. Docker installed: To be able to create and manage Docker instances.
  3. Docker Hub Account: To be able to push your Redis image, as we will see later in the post.

That is all you need to begin the setup, I will create 7 Digital Ocean instances (with Docker 1.4 installed), each Digital Ocean instance will contain one Docker instance, of course you can create the whole cluster inside one or two Digital Ocean virtual machines, but I am using multiple machines to ensure the highest availability possible between Redis instances.

Redis clustering can not be started unless 6 Redis instances join the cluster. The 7th machine is for the installing Rancher.

[]Installing Rancher

First create a Digital Ocean machine to install Rancher on, login to your Digital Ocean account and click create droplet.

[redis-cluster.jpg]

After that make sure to select Docker 1.4 under Applications:

[docker-do.png]

To install Rancher, ssh login to the machine and run this command:

$ssh [email protected]
[email protected]:~#docker run -d --name rancher-server -p 8080:8080 rancher/server

This command will create a Docker instance with a Rancher server that listens on port 8080 and proxy that port to port 8080 on the host. After running that command wait a few minutes until the server is ready, To make sure that the server is running type this command:

[email protected]:~# docker logs rancher-server

You should see something like the following output:

 20:02:41.943 [main] INFO  ConsoleStatus - [DONE ] [68461ms] Startup Succeeded, Listening on port 8080

Now access this machine on port 8080: http://104.236.253.116:8080

[rancher-interface.png]

Register Digital Ocean’s instances With Rancher

Once Rancher is up and running, create 6 Digital Ocean Instances, and then ssh login to each instance and run this command:

 [email protected]# docker run -it --privileged -v /var/run/docker.sock:/var/run/docker.sock rancher/agent http://104.236.253.116:8080

where 104.236.253.116 is the ip address of the managment instance.

This will register the 6 machines on Rancher. Once this is finished, you will see the following when you access the Rancher management instance:

[register-machines.png]

Creating Redis Docker Image

Now for the Redis cluster part, I created a Docker image of the Redis cluster container, here is the Dockerfile of the image:

From ubuntu:14.04
MAINTAINER hussein-galal [email protected]
ENV CACHE_FLAG 0
# Install and Upgrade the System
RUN apt-get update
RUN apt-get upgrade -yqq
# Install the dependencies
RUN apt-get install -yqq build-essential gcc g++ openssl wget curl git-core libssl-dev libc6-dev ruby
# Clone the Unstable Version of redis that contains redis-cluster
RUN git clone -b 3.0 https://github.com/antirez/redis.git
# Install Redis and its Tools
WORKDIR /redis
RUN make
RUN gem install redis
# Add the Configuration of the cluster
ADD conf/redis.conf redis.conf
ADD run.sh /run.sh
ENV REDIS_NODE_PORT=7000
ENTRYPOINT ["/bin/bash","/run.sh"]

Lets go through this Dockerfile line by line:

  • The first 3 lines say that we will going to use Ubuntu 14.04 as our base image, the name of the maintainer of this Docker file, and finally the cached flag which we will change if we want to rebuild the image and not use the cached one.
  • The following two lines are used to update the apt cache, and upgrade the system by installing the latest updates.
RUN apt-get update
RUN apt-get upgrade -yqq
  • This line will install the packages we will need to compile Redis and install its tools.
RUN apt-get install -yqq build-essential gcc g++ openssl wget curl git-core libssl-dev libc6-dev ruby
  • The following line will download the Redis source code from the official GitHub repo, note that we installed the 3.0 version which is the unstable version but it is the version that implements the Redis cluster.
RUN git clone -b 3.0 https://github.com/antirez/redis.git
  • The following 3 lines will change the current directory to redis/ directory, then compile the Redis source code, and finally will install the Redis client gem which contains the Redis cluster tools that we will use.
WORKDIR /redis
RUN make
RUN gem install redis
  • The following 4 lines will copy the configuration file and the running script to the Docker image, then assign REDIS_NODE_PORT variable to 7000, and finally execute the script as the entrypoint of the image.
ADD conf/redis.conf redis.conf
ADD run.sh /run.sh
ENV REDIS_NODE_PORT=7000
ENTRYPOINT ["/bin/bash","/run.sh"]

Now lets take a look at the configuration file and the running script:

redis.config
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes

This simple configuration file will do the following:

  • Enable the cluster mode for the Redis image.
  • Define the configuration file of Redis cluster.
  • Set the timeout for each Redis server to 5000.
/run.sh
#Running Redis Cluster Node
./src/redis-server /redis/redis.conf --port $REDIS_NODE_PORT

This line will start the Redis server with the custom configuration we just made, and bind it to the port we defined in the Dockerfile.

Now you can build this image and then push it to the Docker Hub to be able to use it with Rancher:

~/redis_cluster_node# docker build -t husseingalal/redis_cl_node
~/redis_cluster_node# docker push husseingalal/redis_cl_node

Now you will be asked to login to your Docker Hub account, after login, the Docker image will be pushed successfully.

Create The Redis Cluster With Rancher

Now I can create a Docker container on each of the Digital Ocean machine’s we registered early on Rancher, by clicking create container in the Rancher UI:

Rancher will open a dialog window where you can choose the options for the Docker containers you will create.

[create\_redis\_node.png]

As you can see, I named the container to be Redis_node1, at set the Source Image to pull the image we just created (husseingalal/redis_cl_node).

At Network, choose “Manage Network on docker0”, this will enable one of the unique features of Rancher which allows the Docker containers to communicate with each other as if they are in the same network, I will explain this feature later in the post.

Make sure to choose no at the Console option.

Now on the left bar, choose Ports:

[redis\_port.png]

This panel will let us export and proxy any port on the Docker machine to the host dynamically, and since we use a Docker instance on each Digital Ocean machine, we can export the port 7000 on each Docker instance we create.

After that click Create.

I repeated the previous step for each machine we registered with Rancher, now you should see something like this:

[redis\_cluster\_nodes.png]

You can see that on each Digital Ocean instance a Network Agent container was created automatically by Rancher, that was the result of choosing to manage the network on docker0.

Rancher implements IPsec tunneling through registered hosts to create a virtual private network between the hosts.

Another nice feature is using the shell, if you click on the terminal icon besides each container it will open a shell to the container, you can use this shell to debug and tweak any configuration on each container.

[execute
shell.png]

Start The Cluster

Now all the cluster nodes are up and running, but we need to start the cluster. To start the cluster we will use the ./redis-trib.rb to start the cluster.

Now open a shell at any redis instance, run the following command:

[email protected]:/redis# cd src
[email protected]:/redis#./redis-trib.rb create --replicas 1 10.42.251.230:7000 10.42.27.151:7000 10.42.27.160:7000 10.42.138.181:7000 10.42.11.78:7000 10.42.151.100:7000

Those ips are the ips of the Redis Docker instances we just created, note that all the ips in the same local network, due to the IPsec tunneling that Rancher created.

Now you should see that output which indicates that the cluster up and running:

>>> Creating cluster
Connecting to node 10.42.251.230:7000: OK
Connecting to node 10.42.27.151:7000: OK
Connecting to node 10.42.27.160:7000: OK
Connecting to node 10.42.138.181:7000: OK
Connecting to node 10.42.11.78:7000: OK
Connecting to node 10.42.151.100:7000: OK
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
10.42.251.230:7000
10.42.27.151:7000
10.42.27.160:7000
Adding replica 10.42.138.181:7000 to 10.42.251.230:7000
Adding replica 10.42.11.78:7000 to 10.42.27.151:7000
Adding replica 10.42.151.100:7000 to 10.42.27.160:7000
M: 14de36d31c2e40f4d5419690e18b7c529d254071 10.42.251.230:7000
   slots:0-5460 (5461 slots) master
M: 9f33e772ccb4163b56ffa1bb92a51b60bc4b3744 10.42.27.151:7000
   slots:5461-10922 (5462 slots) master
M: dffd80171644a3d510d879ec6f988329774defef 10.42.27.160:7000
   slots:10923-16383 (5461 slots) master
S: 0047ea05b59c7b47cc77ac3a6b7356776e104fb8 10.42.138.181:7000
S: e7cf1d84e72a0dc42677abe618a2c0bf3fafc927 10.42.11.78:7000
   replicates 9f33e772ccb4163b56ffa1bb92a51b60bc4b3744
S: d91c05946fd96abd3f1784b9447c7db71100fecf 10.42.151.100:7000
   replicates dffd80171644a3d510d879ec6f988329774defef
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join........
>>> Performing Cluster Check (using node 10.42.251.230:7000)
M: 14de36d31c2e40f4d5419690e18b7c529d254071 10.42.251.230:7000
   slots:0-5460 (5461 slots) master
M: 9f33e772ccb4163b56ffa1bb92a51b60bc4b3744 10.42.27.151:7000
   slots:5461-10922 (5462 slots) master
M: dffd80171644a3d510d879ec6f988329774defef 10.42.27.160:7000
   slots:10923-16383 (5461 slots) master
M: 0047ea05b59c7b47cc77ac3a6b7356776e104fb8 10.42.138.181:7000
   slots: (0 slots) master
   replicates 14de36d31c2e40f4d5419690e18b7c529d254071
M: e7cf1d84e72a0dc42677abe618a2c0bf3fafc927 10.42.11.78:7000
   slots: (0 slots) master
   replicates 9f33e772ccb4163b56ffa1bb92a51b60bc4b3744
M: d91c05946fd96abd3f1784b9447c7db71100fecf 10.42.151.100:7000
   slots: (0 slots) master
replicates dffd80171644a3d510d879ec6f988329774defef
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

And voila, the cluster is up and running.

[]Seting up Redis Cluster Using Single Host

Using single host to hold the redis cluster will expose some of the limitations that Rancher resolves.

To set up a redis cluster on a single host, create a Digital Ocean machine with Docker 1.4 installed, and execute the following commands:

# docker pull husseingalal/redis_cl_node
# docker run -d --name redis_node_1 -p 7000:7000 husseingalal/redis_cl_node
# docker run -d --name redis_node_2 -p 7001:7000 husseingalal/redis_cl_node
# docker run -d --name redis_node_3 -p 7002:7000 husseingalal/redis_cl_node
# docker run -d --name redis_node_4 -p 7003:7000 husseingalal/redis_cl_node
# docker run -d --name redis_node_5 -p 7004:7000 husseingalal/redis_cl_node
# docker run -d --name redis_node_6 -p 7005:7000 husseingalal/redis_cl_node

To know the private ips of the redis nodes, you should use Docker inspect:

#docker inspect redis_node_X | grep "IPAddress"

Where X is the number of the node, then to start the cluster, we should install Redis tools and use the redis-trib.rb script under the src directory of the source code:

# git clone -b 3.0 https://github.com/antirez/redis.git
# apt-get update && apt-get install ruby
# gem install redis
# cd redis/src
# ./redis-trib.rb create --replicas 1 172.17.0.2:7000 172.17.0.4:7000 172.17.0.5:7000 172.17.0.6:7000 172.17.0.7:7000 172.17.0.8:7000

Now we should see the same output we saw earlier using Rancher, but now we will be exposed to several limitations:

  • High Availability feature is now missing, and if the Digital Ocean machine goes down all of our cluster will go down and fail.
  • We can’t use the private ips of the Docker containers to setup a full stack application, we can only use the public ip of the Digital Ocean machine with the different ports of the Redis containers.

That’s why Rancher’s cross container networking is so useful. It allows me to launch the Redis instances spread across multiple hosts, ultimately providing HA capability for the Redis cluster.

Conclusion

In this post we installed Rancher to create a Redis cluster, we registered 6 machines with Rancher, and on each machine we created a custom Redis Docker container, and finally we ran the cluster using the redis-trib.rb command. Using Rancher, we were able to create a private network for communication between the clustered Redis machines.

If you’re interested in learning more, please schedule a demo of Rancher.

Hussein Galal is a Linux System Administrator, with experience in Linux, Unix, Networking, and open source technologies like Nginx, Apache, PHP-FPM, Passenger, MySQL, LXC, and Docker. You can follow Hussein on Twitter @galal_hussein.