rss_feed

Using Redis Sentinel with Docker Compose

July 13, 2020

Redis is an easy-to-use solution for anyone looking for a robust key-value store. It is feature-rich, but relatively simple to use and even has official Docker images. This post will not go into anymore detail as to what exactly Redis is as it assumes the reader already knows. If not, you can read about it on the official Redis website.

What we will discuss, however, is how to create a failover solution using Redis Sentinel and Docker Compose. There are several code examples in this post, so it might be easier to follow them as well as to understand the project structure on GitHub.

Redis Sentinel is essentially a mode in which the Redis server is started that watches the master Redis instance and chooses a replacement from the slave instances in the event that the master instance is unreachable.

In order for it to do this, the following needs to be configured:

  • We need to define a master instance.
  • We need to setup one or more slave instances.
  • We need to start at least three Sentinel instances.
  • They all need to communicate with each other.

So how do we do all of this?

Redis makes it relatively easy. First, we start a normal instance of Redis which will be the master instance. Then we start additional instances that will become the slaves, but when doing it, we pass a flag with the IP address/hostname and port of the master instance. This flag defines the slaves as slaves and also tells them which instance is the master. An example command looks like this:

redis-server --slaveof 127.0.0.1 6379

Now we have the first two bullet points in our list taken care of, but still need to start at least two Sentinel instances. Redis needs at least two instances so that the Sentinels can “vote” for a slave instance to become master. This is a bit trickier as we first have to define a configuration file. More on that later, but for now, here is the command to use when starting a Sentinel instance:

redis-server /redis/sentinel.conf --sentinel

All of these instances will need to be on separate servers or setup with different configurations if running on the same server. That is beyond the scope of this article though as we are going to isolate each instance in a Docker container as a solution to this problem.

To use Docker, we will need to start multiple containers at once. The best way to do that is with Docker Compose. This article will assume some background knowledge of both Docker and Docker Compose. First we need to create a Compose File that looks something like the following:

version: '3.7'

services:

  app:
    image: some-image
    links:
      - redis-sentinel

  redis-master:
    image: redis:6-alpine
    volumes:
      - "./.data:/data"
    ports:
      - "6379:6379"

  redis-slave:
    image: redis:6-alpine
    command: redis-server --slaveof redis-master 6379
    links:
      - redis-master
    volumes:
      - "./.data:/data"

  # Instance 1
  redis-sentinel:
    build:
      context: ./redis-sentinel
    links:
      - redis-master

  # Instance 2
  redis-sentinel2:
    build:
      context: ./redis-sentinel
    links:
      - redis-master

  # Instance 3
  redis-sentinel3:
    build:
      context: ./redis-sentinel
    links:
      - redis-master

docker-compose.yml:

Here you can see that I have six containers. One for the app where our application will be, one for the Redis master instance, one for a single Redis slave instance and three that serve as Sentinel instances. Alternatively, you can have a single Sentinel container and use the following to scale to multiple instances:

docker-compose scale redis-sentinel=3

It is important that our app communicate with one of the Sentinel instances rather than with one of the Redis instances because otherwise it will have no way of knowing if the master has gone down and which slave has become the master.

I use the official Redis images directly from Docker Hub for the Redis master and slave containers since there is nothing that needs to be changed or configured. Unfortunately since Redis Sentinel requires a configuration file, we will need to create our own Docker image. In the root of the project, I created a folder called “redis-sentinel”. In this folder are three files:

  • Dockerfile
  • sentinel-entrypoint.sh
  • sentinel.conf

“Dockerfile” defines our custom image, the “sentinel-entrypoint.sh” script sets the values in our configuration file and “sentinel.conf” provides the template for the Sentinel configuration.

redis-sentinel Dockerfile:

FROM redis:6-alpine

ENV SENTINEL_QUORUM 2
ENV SENTINEL_DOWN_AFTER 1000
ENV SENTINEL_FAILOVER 1000

RUN mkdir -p /redis

WORKDIR /redis

COPY sentinel.conf .
COPY sentinel-entrypoint.sh /usr/local/bin/

RUN chown redis:redis /redis/* && \
    chmod +x /usr/local/bin/sentinel-entrypoint.sh

EXPOSE 26379

ENTRYPOINT ["sentinel-entrypoint.sh"]

Essentially, the Dockerfile pulls the official Redis image as its base image, sets a few defaults for the Sentinel configuration as environment variables, creates a directory for the other files, then copies them into the container. Port 26379 is also exposed which is the default port for Redis Sentinel. Lastly, we define the “sentinel-entrypoint.sh” script as our entrypoint.

sentinel-entrypoint.sh:

#!/bin/sh

sed -i "s/\$SENTINEL_QUORUM/$SENTINEL_QUORUM/g" /redis/sentinel.conf
sed -i "s/\$SENTINEL_DOWN_AFTER/$SENTINEL_DOWN_AFTER/g" /redis/sentinel.conf
sed -i "s/\$SENTINEL_FAILOVER/$SENTINEL_FAILOVER/g" /redis/sentinel.conf

redis-server /redis/sentinel.conf --sentinel

The entrypoint script does nothing other than replace a few strings with the defaults that we defined as environment variables in the Dockerfile, then starts the Redis server using the “–sentinel” flag while pointing it to our configuration file.

sentinel.conf:

port 26379

dir /tmp

sentinel monitor redismaster redis-master 6379 $SENTINEL_QUORUM
sentinel down-after-milliseconds redismaster $SENTINEL_DOWN_AFTER
sentinel parallel-syncs redismaster 1
sentinel failover-timeout redismaster $SENTINEL_FAILOVER

The configuration template has the bare-minimum configuration. Here we tell Redis Sentinel which Redis instance is our master (the “redis-master 6379” part of the “sentinel monitor” line) and set a few other settings. “redis-master” is the name of our master image as defined in the Docker Compose file. There are several other things that could be configured, but we will not go into detail here. You can find more detailed information about configuring Redis Sentinel in the official documentation.

All we have left to do is start Docker Compose with:

docker-compose up

Once all of the containers have started, we can test Sentinel by getting the image id of our “redis-master” image and pausing it so that it “goes down”:

docker ps
# Copy the image id
docker pause IMAGEID

The Sentinel instances should automatically detect that the master is missing then choose a slave to become its replacement while our application should never notice that the master was gone. To restore the master, you can “unpause” it:

docker unpause IMAGEID

As when the master was gone, the Sentinel instances should automatically detect that the master instance is reachable again.

If you have any questions, comments or suggestions, please feel free to leave them in the comments below.

See the full example project on GitHub.

This article originally appeared in an older form on Alex’s Notebook.

How would you rate this post?

About the Author

Alex Seifert
Alex is a developer, a drummer and an amateur historian. He enjoys being on the stage in front of a large crowd, but also sitting in a room alone, programming something or writing about technology and history.

Related Posts

4 Comments
  1. Adel
    February 2, 2022 11:31 pm  link

    Hi,

    Thank you so much for the informative article 🙂

    I have followed everything as mentioned in the article (I also cloned the repo from Github) but unfortunately I couldn’t connect to any of the sentinels from my Java app using Jedis library

    The weird part is I can telnet the sentinels from my development machine but when trying to connect using Jedis it gives me the following error

    All sentinels down, cannot determine where is redismaster master is running…

    Can you guide me in any further investigations I should follow?

    Thank you so much

  2. March 16, 2022 6:37 am  link

    I have tried this,, but not working.. getting lot of errors. Seems the docker file is not correct

  3. Loki
    August 29, 2022 6:08 am  link

    from what I found the problem cause by

    ENV SENTINEL_QUORUM 2
    ENV SENTINEL_DOWN_AFTER 1000
    ENV SENTINEL_FAILOVER 1000

    sentinel.conf failed to get these value, and cause the problem.

  4. December 6, 2023 12:14 pm  link

    sentinel resolve-hostnames yes

Post a Comment

Your email is kept private. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Support Developer's Notebook

If you enjoyed this article, please consider contributing to Developer's Notebook so that we can continue writing about the topics we love.

Support Us →

Thank you!

— Alex Seifert, Founder and Writer