Learn Redis Standalone, Cluster and Sentinel Mode With Docker

Introduction

Redis is a powerful and versatile data storage system used by many applications for its speed and adaptability. It operates in-memory, making data access lightning-fast, and supports various data types, making it suitable for different purposes. Learning Redis is essential due to its industry-wide adoption and scalability.

Setting up Redis, especially Redis cluster and Redis sentinel, on virtual machines can be a bit tricky. You have to create multiple VMs, handle a bunch of config files, mess around with networking, and dive into system settings and it just takes a lot of time to get everything set up. But here’s where Docker comes to the rescue! Docker makes running Redis a breeze, smoothing out the whole process and the best part is you can effortlessly create and get rid of your environment whenever you need to.

In a past project, I found myself needing to set up different Redis environment regularly. To simplify this, I created a tool named redis-demo using a combination of a bash script and a Makefile, transforming the entire procedure into a set of straightforward make commands. After being tested by other people for quite some time, I thought it might be beneficial to share it with you. I hope it proves handy in your learning adventures or when you’re testing things out with Redis.

In today’s post, I intend to guide you through various Redis deployment models, explaining how to set them up with my tool and demonstrating how to test the features of each model.

Let’s get started.

Git Clone

Let’s clone my tool to local.

1
git clone https://github.com/liyangau/redis-demo.git

Now you can cd to redis-demo folder, make some changes to the variables and then start testing redis.

Standalone

As the name suggested, standalone mode means Redis is running as an independent mater node, handling data reads and writes. Standalone mode is often used for simple user case, for example, I use it in the nextcloud config.

Environment set up

We can use make redis-single to spin up redis standalone mode. Container name is set to redis-demo.

1
2
3
4
➜ make redis-single
Creating new redis-demo container: 85534e852f843f2b4b833ade49ab429147735bc8b84e7d4c20571c8d32d15de7
Redis demo is running on host IPs and ports at: 172.19.0.2:32770
Redis demo is reachable in docker network redis-demo at: redis-demo:6379

Verification

Let’s connect to the container and see how it works.

If you have redis-cli installed on the host, you can also connect to redis via the exposed port. In above example, the exposed port is 32770 so we can connect to redis with redis-cli -h 127.0.0.1 -p 32770 -a A-SUPER-STRONG-DEMO-PASSWORD.

  • Connect to redis-demo container
    1
    docker exec -it redis-demo sh
  • Use redis-cli to authenticate and connect.

    If you are running containers with SSL, you need to add add --tls --cacert /etc/ssl/certs/ca.crt flag to connect.

    • The default username is default and we do not need to write it out.
      1
      redis-cli -h 127.0.0.1 -p 6379 -a A-SUPER-STRONG-DEMO-PASSWORD 
    • If we use different username we can connect using
      1
      redis-cli -u redis://default:[email protected]:6379
    • Write and read data examples
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      127.0.0.1:6379> keys *
      (empty array)
      127.0.0.1:6379> set demo-key demo-value
      OK
      127.0.0.1:6379> get demo-key
      "demo-value"
      127.0.0.1:6379> incr test
      (integer) 1
      127.0.0.1:6379> get test
      "1"
      127.0.0.1:6379> flushall
      OK
      127.0.0.1:6379> keys *
      (empty array)

Replication

Redis replication mode involves creating a mater-replica (master-slave) configuration where a mater node handles write operations and replicates them to one or more replica nodes. The replica nodes handles read-only operations with existing dataset.

Some key features

  • Data Redundancy: Redis replication provides data redundancy by duplicating the dataset across multiple nodes. This minimizes the risk of data loss in case of a failure.
  • Read Scalability: Replica nodes can handle read operations, allowing for improved read scalability. Multiple read-only replicas can be utilized to distribute read traffic and enhance performance.
  • High Availability: If the mater node fails, a replicate node can be promoted to a mater, ensuring continuous service and minimizing downtime.

Environment set up

We can use make redis-replication to spin up 1 mater container redis-demo and 3 replicates redis-1 redis-2 and redis-3 by default.

1
2
3
4
5
6
7
8
9
10
11
➜ make redis-replication
Creating new redis-demo container: 180016caf4d5a06ca90d19310608d8b3e34e5b3736537e1ab99d2d4ac3bd4e5a
Redis demo is running on host IPs and ports at: 172.19.0.2:32776
Redis demo is reachable in docker network redis-demo at: redis-demo:6379

Creating replicate redis-1 container: badab00f3d493841550674bd2d4cc32de019ed1d61eae4064cd7666f4ac2bf93
Creating replicate redis-2 container: e0c85df843751a4138784b899aaa966505603e44bf472f0989b67fc9d31dd4f2
Creating replicate redis-3 container: 0da09efa55c131fd79c05e8aec5981459bac4c168c2cee324003bd8fc1693caf
Your replicate nodes on host IPs and ports are:172.19.0.3:32777,172.19.0.4:32778,172.19.0.5:32779
In docker network redis-demo, your replicate nodes hostnames and ports are:redis-1:6379,redis-2:6379,redis-3:6379
In docker network redis-demo, your replicate nodes IPs and ports are:172.19.0.3:6379,172.19.0.4:6379,172.19.0.5:6379

Verification

Check replication

1
docker exec -it redis-demo sh
1
redis-cli -a A-SUPER-STRONG-DEMO-PASSWORD
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:3
slave0:ip=172.19.0.3,port=6379,state=online,offset=28,lag=1
slave1:ip=172.19.0.4,port=6379,state=online,offset=28,lag=1
slave2:ip=172.19.0.5,port=6379,state=online,offset=28,lag=1
master_failover_state:no-failover
master_replid:9c426dd61e940e280c4436f1fa6d5445e76678b7
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:28
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:28

Writes to mater node

Next let’s try writing some data to the mater node.

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set demo-key demo-value
OK
127.0.0.1:6379> get demo-key
"demo-value"
127.0.0.1:6379> incr test
(integer) 1
127.0.0.1:6379> get test
"1"

Reads from replicate

Let’s see what we get in the replica nodes.

1
docker exec -it redis-1 sh
1
redis-cli -a A-SUPER-STRONG-DEMO-PASSWORD
1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> keys *
1) "demo-key"
2) "test"
127.0.0.1:6379> get demo-key
"demo-value"
127.0.0.1:6379> get test
"1"
127.0.0.1:6379> incr test
(error) READONLY You can't write against a read only replica.
127.0.0.1:6379>

As we can see we have the same data on the replica node and it is read only.

Redis Cluster

Redis Cluster is a distributed implementation of Redis that provides automatic sharding and high availability. Unlike traditional Redis setups, a Redis Cluster is designed to scale horizontally by dividing the dataset among multiple nodes, each responsible for a subset of the data.

Some key features

  • Multiple master node: Redis Cluster supports multiple mater nodes, allowing users to write data concurrently to different parts of the dataset. This enhances write throughput and overall system efficiency.
  • Automatic Sharding: Redis Cluster automatically divides the dataset into multiple shards, allowing it to distribute the load across different nodes.
  • Multiple Copies of Data: Each piece of data is stored on multiple nodes (mater and replicas), enhancing data redundancy and minimizing the risk of data loss.

Environment set up

We can use make redis-cluster to create up a 6 node (3 mater and 3 replica) redis cluster.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
➜ make redis-cluster
Creating redis-cluster-1 container: 697ef9df45a4bf04c7614138b34d6f959a99885bf9cd613315f3b9ee30eba99b
Creating redis-cluster-2 container: f92093b08ae107224be8a025ddf3367d2467ba901cad04462b7dd9a62b31705c
Creating redis-cluster-3 container: 8efa8b7664fc0cb16f4f7adc2e52857a7ef099808d8e5544c68547e60b91a5af
Creating redis-cluster-4 container: 5e28750485506c2573982c07a6f7fb3071e00a41705a16f892ce5f572d96ac07
Creating redis-cluster-5 container: d62947edc17be3ca21e6cb41c4ae517abf7583797ac87d99bd66af7468d57628
Creating redis-cluster-6 container: 9319fb002de255f884db4d79f568f81204f220539775cc1538efad7437fe0659
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.19.0.6:6379 to 172.19.0.2:6379
Adding replica 172.19.0.7:6379 to 172.19.0.3:6379
Adding replica 172.19.0.5:6379 to 172.19.0.4:6379
M: 282034c3843c500e614f050d3269070ed73ef0e7 172.19.0.2:6379
slots:[0-5460] (5461 slots) master
M: 73b6e9c9d3ede98d0894d81fe11c9f179417b44e 172.19.0.3:6379
slots:[5461-10922] (5462 slots) master
M: cdc1bf783023ffecd71d239314d8d2b73324b45f 172.19.0.4:6379
slots:[10923-16383] (5461 slots) master
S: 36d41b890a2a7570d98d9e3b2913a511828d3097 172.19.0.5:6379
replicates cdc1bf783023ffecd71d239314d8d2b73324b45f
S: 7ebb9aed40af088e9cb1be63e221b83e4d5d7958 172.19.0.6:6379
replicates 282034c3843c500e614f050d3269070ed73ef0e7
S: 2adb51f1f5d2df666300aa9f907530f232e2dd81 172.19.0.7:6379
replicates 73b6e9c9d3ede98d0894d81fe11c9f179417b44e
>>> 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 172.19.0.2:6379)
M: 282034c3843c500e614f050d3269070ed73ef0e7 172.19.0.2:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 7ebb9aed40af088e9cb1be63e221b83e4d5d7958 172.19.0.6:6379
slots: (0 slots) slave
replicates 282034c3843c500e614f050d3269070ed73ef0e7
M: cdc1bf783023ffecd71d239314d8d2b73324b45f 172.19.0.4:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 36d41b890a2a7570d98d9e3b2913a511828d3097 172.19.0.5:6379
slots: (0 slots) slave
replicates cdc1bf783023ffecd71d239314d8d2b73324b45f
S: 2adb51f1f5d2df666300aa9f907530f232e2dd81 172.19.0.7:6379
slots: (0 slots) slave
replicates 73b6e9c9d3ede98d0894d81fe11c9f179417b44e
M: 73b6e9c9d3ede98d0894d81fe11c9f179417b44e 172.19.0.3:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
Your cluster nodes on host IPs and ports are:172.19.0.2:32780,172.19.0.3:32781,172.19.0.4:32782,172.19.0.5:32783,172.19.0.6:32784,172.19.0.7:32785
In docker network redis-demo, your cluster nodes IPs and ports are:172.19.0.2:6379,172.19.0.3:6379,172.19.0.4:6379,172.19.0.5:6379,172.19.0.6:6379,172.19.0.7:6379
In docker network redis-demo, your cluster nodes hostnames and ports are:redis-cluster-1:6379,redis-cluster-2:6379,redis-cluster-3:6379,redis-cluster-4:6379,redis-cluster-5:6379,redis-cluster-6:6379

Verification

Check cluster

We can log in to any node to see the information. Let’s pick redis-cluster-3

1
docker exec -it redis-cluster-3 sh

Make sure you add -c for cluster mode.

1
redis-cli -c -a A-SUPER-STRONG-DEMO-PASSWORD

As we can see cluster mode is enabled, redis-cluster-3 is the mater node and it has 1 replica connected.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
127.0.0.1:6379> role
1) "master"
2) (integer) 2084
3) 1) 1) "172.19.0.5"
2) "6379"
3) "2084"
127.0.0.1:6379> info cluster
# Cluster
cluster_enabled:1
127.0.0.1:6379> cluster nodes
b471871e21a2a40ac6035e4e80dfc6acf15032de 172.19.0.7:6379@16379 slave d64626d741794aa72c4a619d9880e9664bf43a12 0 1701958680062 2 connected
459995180318fe9d1f976b42994d762889b9d096 172.19.0.4:6379@16379 myself,master - 0 1701958678000 3 connected 10923-16383
8cb586eb8e82172dbfacb7dea79518028e5e9bad 172.19.0.2:6379@16379 master - 0 1701958679053 1 connected 0-5460
d64626d741794aa72c4a619d9880e9664bf43a12 172.19.0.3:6379@16379 master - 0 1701958679000 2 connected 5461-10922
a7986c2ef567c14f3e9c8ff9cd25d8518673727b 172.19.0.6:6379@16379 slave 8cb586eb8e82172dbfacb7dea79518028e5e9bad 0 1701958679053 1 connected
a5d2d9364ddbee8b991af337e70f0f419a51d556 172.19.0.5:6379@16379 slave 459995180318fe9d1f976b42994d762889b9d096 0 1701958678046 3 connected

Manual fail over on replica node

As we know our current node’s replica’s IP is 172.19.0.5, we can connect to that node and manual fail over.

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> connect 172.19.0.5 6379
172.19.0.5:6379> cluster failover
OK
172.19.0.5:6379> cluster nodes
459995180318fe9d1f976b42994d762889b9d096 172.19.0.4:6379@16379 slave a5d2d9364ddbee8b991af337e70f0f419a51d556 0 1701958972000 7 connected
d64626d741794aa72c4a619d9880e9664bf43a12 172.19.0.3:6379@16379 master - 0 1701958973000 2 connected 5461-10922
a7986c2ef567c14f3e9c8ff9cd25d8518673727b 172.19.0.6:6379@16379 slave 8cb586eb8e82172dbfacb7dea79518028e5e9bad 0 1701958973246 1 connected
a5d2d9364ddbee8b991af337e70f0f419a51d556 172.19.0.5:6379@16379 myself,master - 0 1701958972000 7 connected 10923-16383
8cb586eb8e82172dbfacb7dea79518028e5e9bad 172.19.0.2:6379@16379 master - 0 1701958973000 1 connected 0-5460
b471871e21a2a40ac6035e4e80dfc6acf15032de 172.19.0.7:6379@16379 slave d64626d741794aa72c4a619d9880e9664bf43a12 0 1701958973749 2 connected

As you can see, the role for 172.19.0.4 and 172.19.0.5 had been swapped.

Writes to mater node

As we are in cluster mode, redis-cli will redirect to the right node for read and writes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
127.0.0.1:6379> set foo bar
OK
127.0.0.1:6379> keys *
1) "foo"
127.0.0.1:6379> set hello world
-> Redirected to slot [866] located at 172.19.0.2:6379
OK
172.19.0.2:6379> keys *
1) "hello"
172.19.0.2:6379> get foo
-> Redirected to slot [12182] located at 172.19.0.4:6379
"bar"
172.19.0.4:6379> keys *
1) "foo"
172.19.0.4:6379> get hello
-> Redirected to slot [866] located at 172.19.0.2:6379
"world"

Get all keys

As keys are stored on different node, keys * command only returns data on the local node. We can use --cluster call to execute commands on each node in the cluster.

1
2
3
4
5
6
7
8
9
/data # redis-cli -a A-SUPER-STRONG-DEMO-PASSWORD --cluster call 127.0.0.1:6379 KEYS "*"
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Calling KEYS *
127.0.0.1:6379: hello
172.19.0.3:6379:
172.19.0.4:6379: foo
172.19.0.2:6379: hello
172.19.0.7:6379:
172.19.0.5:6379: foo

As we can see it shows which nodes has what keys. We can also verify our set up is 1:1 mater and replica.

Redis Sentinel

Redis Sentinel is a robust and automated monitoring tool designed to enhance the high availability and fault tolerance of Redis deployments. It provides high availability for Redis when not using Redis Cluster. It works seamlessly with Redis instances, constantly monitoring their health, and facilitating automatic fail over in the event of a node or mater failure. With Redis Sentinel, users can ensure the continuous availability of their Redis infrastructure, making it an essential component for mission-critical applications.

Some key features:

  • Monitoring: Sentinel constantly checks if your master and replica instances are working as expected.
  • Automatic fail over: In case of a mater node failure, Redis Sentinel orchestrates automatic fail over by promoting a slave to be mater, ensuring uninterrupted service.
  • Configuration provider: Sentinel acts as a source of authority for clients service discovery: clients connect to Sentinels in order to ask for the address of the current Redis master responsible for a given service. If a fail over occurs, Sentinels will report the new address.

Environment set up

We can use make redis-sentinel to create redis replication and 3 sentinel containers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
➜ make redis-sentinel
Creating new redis-demo container: 0fde93a0a600d6685a32500fc8d66c74c7884af07a870473a97e59200248fe88
Redis demo is running on host IPs and ports at: 172.19.0.2:32799
Redis demo is reachable in docker network redis-demo at: redis-demo:6379

Creating replicate redis-1 container: c48fc363a111f7cbb4eca282410b6d125b7c14ee92ede501e0c439d5271a80c4
Creating replicate redis-2 container: 6630a31c1f44ab7d496160f4151b0849be8737b234ea803baf3e314f68d10a3c
Creating replicate redis-3 container: 079419be809cc75c28d96ad46e7e34b6f168831ab478cc22beed853dd3f35570
Your replicate nodes on host IPs and ports are:172.19.0.3:32800,172.19.0.4:32801,172.19.0.5:32802
In docker network redis-demo, your replicate nodes hostnames and ports are:redis-1:6379,redis-2:6379,redis-3:6379
In docker network redis-demo, your replicate nodes IPs and ports are:172.19.0.3:6379,172.19.0.4:6379,172.19.0.5:6379
Creating redis-sentinel-1 container: e26eec38d7eef2d62b05af2aa8e957a40cea20588fd246e013018b5638e83169
Creating redis-sentinel-2 container: a06180b947e93165f572d6fc07dbcd9e77affe5dc8d2ee06eb3b05ea021d407d
Creating redis-sentinel-3 container: 8166045c5386392f08bc8e41bb441f3e6e206822ce39d38d2455035117cb181c
Your sentinel nodes on host IPs and ports are:172.19.0.6:32803,172.19.0.7:32804,172.19.0.8:32805
In docker network redis-demo, your sentinel nodes hostnames and ports are:172.19.0.6:26379,172.19.0.7:26379,172.19.0.8:26379
In docker network redis-demo, your sentinel nodes IPs and ports are:redis-sentinel-1:26379,redis-sentinel-2:26379,redis-sentinel-3:26379

Verification

Check sentinel

We can log in to any sentinel node. Let’s pick redis-sentinel-1

1
docker exec -it redis-sentinel-1 sh

Make sure to use port 26379.

1
redis-cli -p 26379 -a A-SUPER-STRONG-DEMO-PASSWORD

From the info, we know our deployment have 1 master which has 3 replicas and 3 sentinels for monitoring.

1
2
3
4
5
6
7
8
9
127.0.0.1:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_tilt_since_seconds:-1
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=172.19.0.2:6379,slaves=3,sentinels=3

Get Master state

We can get master node information with below command.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
127.0.0.1:26379> sentinel master mymaster
1) "name"
2) "mymaster"
3) "ip"
4) "172.19.0.2"
5) "port"
6) "6379"
7) "runid"
8) "890e0993345e21bad2234b07d1d03bd08e460218"
9) "flags"
10) "master"
11) "link-pending-commands"
12) "0"
13) "link-refcount"
14) "1"
15) "last-ping-sent"
16) "0"
17) "last-ok-ping-reply"
18) "421"
19) "last-ping-reply"
20) "421"
21) "down-after-milliseconds"
22) "5000"
23) "info-refresh"
24) "4157"
25) "role-reported"
26) "master"
27) "role-reported-time"
28) "174741"
29) "config-epoch"
30) "0"
31) "num-slaves"
32) "3"
33) "num-other-sentinels"
34) "2"
35) "quorum"
36) "2"
37) "failover-timeout"
38) "60000"
39) "parallel-syncs"
40) "1"

Get Master address

If we only need the master node IP address, we can run this command. Client does not need to check which is the master node, sentinel provides this information to user.

1
2
3
127.0.0.1:26379> sentinel get-master-addr-by-name mymaster
1) "172.19.0.2"
2) "6379"

Manual fail over

Let’s try fail over and see how sentinel promote replica.

1
2
127.0.0.1:26379> sentinel failover mymaster
OK

If we get master again, we can see the current master node has changed to 172.19.0.4.

1
2
3
127.0.0.1:26379> sentinel get-master-addr-by-name mymaster
1) "172.19.0.4"
2) "6379"

Clean up

You can make redis-cleanup to remove containers with keyword redis in their names. It is a simple way to cleaning up containers created with the tool.

That’s all I want to share with you today. I hope this tool helps you to learn redis and test your application with redis.

See you next time.