I am a Sr. Software Developer at Oracle Cloud. The opinions expressed here are my own and not necessarily those of my employer.
1000 node Redis Cluster
In this article we will expore how to launch 1000 (one thousand) node Redis Cluster running on bare metal servers in the cloud. We will then run 2000 thousand workers (with Kubernetes pods) to create load. It will perform 1 billion writes in about an hour generating over 300GB of data.
This article assumes that the reader is familiar with Redis plus has experience with Terraform, Ansible and Kubernetes or similar tools.
Full confession - I have ran smaller installations of Redis Cluster but nowhere near this scale. This is meant to be a proof of concept to see what is possible.
- Introduction
- Launch bare metal servers with Terraform
- Provision Redis Cluster with Ansible
- Networking
- Create cluster
- Launch worker pods with Kubernetes
- Summary
- Links
Introduction
Why should we run Redis Cluster in the first place? Redis is an in-memory data store and it requires enough RAM store the entire dataset. That could be mitigated by sclaing up (getting servers with more RAM). Another issue is that Redis is mostly single-threaded so it leave unused CPU capacity. We will expore how to use Redis Cluster to take advantage of RAM and CPU across many cores and servers.
Redis Cluster shards data between nodes by dividing keys into 16,384 slots. Each node in the cluster is responsible for a portion of the slots. To determine hash slot for a specific key we simply take the CRC16 of the key mod 16384. If we had 3 nodes than first node would hold slots from 0 to 5500, second node from 5501 to 11000 and third node from 11001 to 16383. Separately we can implement replication for data redunancy.
Multi-key operations can be done on Redis cluster but all keys must be on the same node. To ensure that we can force keys to be part of the same slot by using hash tags. For that we create substring within {} in a key. Only that substring will be used to determine the slot.
Nodes can be added to or removed from cluster and Redis will move data between nodes without downtime. Some of the limitations are that we cannot shard data within one large key (List, Set, …). Also Redis Cluster allows only 1 database.
Launch bare metal servers with Terraform
First we need to create the actual infrastructure. We will use Terraform with Oracle Clould BM.Standard2.52
shapes to launch 10 bare metal servers. Each server will give us 104 cores and almost 800 GB of RAM. Alternatively we could create these via UI or CLI.
We have to specify appropriate clould credentials and other parameters such as ID of the base image. We can use any Linux distrubtion for this.
When we run terraform apply
the output will be the public IPs of the servers created.
Provision Redis Cluster with Ansible
The next step is to properly provision Redis Cluster nodes on top of Linux. We will be using Ansible but it could be done with other tools or even bash scripts. When we run ansible-playbook redis_playbook.yml -i hosts.yml
this playbook (script) will be executed on all 10 bare metal servers.
It will install OS dependencies, clone the Redis repo and compile the code. It will then create 100 subfolders, copy redis-server
executable and config file specifying different ports. Each physical server will be running 100 instances of Redis on ports 6379-6478.
After completion each Linux server will have the following folder structure:
hosts.yml
Ansible is ran from our local computer but it needs to know which remote IPs to connect to. We will copy the IPs from the terrafrom output into hosts.yml
file.
redis.conf.j2
This is the Jinja redis.conf
template that will be copied to all servers / directories telling Redis nodes that they will be part of the cluster. It will specify appropriate port (ranging from 6379-6478) in each file. It will also do other customization. For example, we are disabling saving data to optimize for speed.
Networking
We need to ensure that all Redis nodes can talk to each other. We are running Redis on ports 6379-6478. Separately Redis Cluster uses a second port for node-to-node communication. That port is generated by adding 10000 to the data port so it wil be 16379-17478. In our security list we need to allow access for both sets of ports.
For simplicity’s sake we also stopped iptables
but in real system we would need to be more security conscious.
Create cluster
Next step is to execute a command to create cluster specifying all server IPs and ports. We can use either private or public IPs. To simplify the creation of this very long bash command we will use a Python script.
SSH to any of the 10 Linux servers and execute the bash command. We will see that each Redis node has either 16 or 17 slots assigned to it.
Now if we run ~/redis-cli -c
on any our Linux servers we will connect to the entire Redis Cluster via CLI.
If we do not specify -c
flag and try set operation we may get a MOVED error message. That means that this key does not belong to the slots currently assigned to the node we are connected to.
Launch worker pods with Kubernetes
Now we will use containers and Kubernetes to generate load (we could have used a different approach). Here is a basic worker.py
that will create unique strings combining UUID and incrementing counter. It will then perfrom Redis set operations using this string.
We will be using redis-py-cluster
library which supports Redis Cluster. When our app first connects to the Redis Cluster it will receive the mapping of slots to nodes. It will use this mapping when performing read/write operation to determine which Redis node to communicate with. We are not using pipelining as keys might be on different nodes.
redis-py-cluster
library will be specified in our Pipfile. To create container we will run docker build
with this Dockerfile. Then we will need to push it to our Container Registry. We are using alpline
base image so the overall container size will be around 130 MB.
To launch 2000 Kubernetes pods we will use this worker.yml
file and run kubectl apply -f worker.yml
.
Summary
Using the configuration above we are able to perform over 1 billion set operations in about an hour. Ths is purely for our perf test and in the real world we have to deal with much more diverse data. Some of the use cases can be caching, session management or high performance computing applications that can generate tremoundous amounts of data.
There are alternatives to Redis Cluster. In a previous article we discussed ways to shard data in our application layer. In a future article we will expore tools such as Envoy Proxy.
Links
- http://redis.io/topics/cluster-tutorial
- https://redis.io/commands/cluster-nodes
- https://docs.cloud.oracle.com/iaas/Content/Compute/References/computeshapes.htm
- https://www.terraform.io/docs/providers/oci/index.html
- https://github.com/Grokzen/redis-py-cluster
- https://www.envoyproxy.io/