CuriousMind
CuriousMind

Reputation: 8943

Spinning up Redis server in sentinel mode using testcontainer for integration tests in Spring Boot

I recently learned of testcontainers, so I wanted to use it for an integration test cases, for a Spring Boot application.

I have tried many combinations to achieve this, but it isn't working / starting. For example, the below is the latest code I tried:

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.wait.strategy.Wait;

public class RedisSentinelIntegrationTest {

    private static final int REDIS_MASTER_PORT = 6379;
    private static final int REDIS_SENTINEL_PORT_1 = 26379;
    private static final int REDIS_SENTINEL_PORT_2 = 26380;

    @Test
    void testRedisSentinel() {
        Network network = Network.newNetwork();

        // Redis Master
        GenericContainer<?> redisMaster = new GenericContainer<>("redis:6.0.12")
                .withExposedPorts(REDIS_MASTER_PORT)
                .withNetwork(network)
                .waitingFor(Wait.forListeningPort());

        redisMaster.start();

        // Redis Sentinel 1
        GenericContainer<?> redisSentinel1 = new GenericContainer<>("redis:6.0.12")
                .withExposedPorts(REDIS_SENTINEL_PORT_1)
                .withNetwork(network)
                .withCommand("redis-server", "--sentinel", "yes", "--sentinel-announce-ip", "redis-sentinel-1", "--sentinel-announce-port", String.valueOf(REDIS_SENTINEL_PORT_1))
                .waitingFor(Wait.forListeningPort());

        redisSentinel1.start();

        // Redis Sentinel 2
        GenericContainer<?> redisSentinel2 = new GenericContainer<>("redis:6.0.12")
                .withExposedPorts(REDIS_SENTINEL_PORT_2)
                .withNetwork(network)
                .withCommand("redis-server", "--sentinel", "yes", "--sentinel-announce-ip", "redis-sentinel-2", "--sentinel-announce-port", String.valueOf(REDIS_SENTINEL_PORT_2))
                .waitingFor(Wait.forListeningPort());

        redisSentinel2.start();

        String sentinelUrl1 = "redis://" + redisSentinel1.getContainerIpAddress() + ":" + redisSentinel1.getMappedPort(REDIS_SENTINEL_PORT_1);

        String sentinelUrl2 = "redis://" + redisSentinel2.getContainerIpAddress() + ":" + redisSentinel2.getMappedPort(REDIS_SENTINEL_PORT_2);

     // .. do something

       

        // Stop the containers
        redisMaster.stop();
        redisSentinel1.stop();
        redisSentinel2.stop();
    }
}

But to my dismay it never works; isn't testcontainers supposed to ease life? I tried their official website, but again they haven't given any such example.

How can I use Redis testcontainers to run in sentinel mode and successfully run it? What issues might I have in the set up?

Upvotes: 0

Views: 1025

Answers (1)

murtiko
murtiko

Reputation: 273

I had success with the following code:

// setup a network
Network networkLocal = Network.newNetwork();

// Redis Local Master
redisLocalMaster = new GenericContainer<>("redis:5.0.3-alpine").
    withExposedPorts(6379).
    withNetwork(networkLocal).withNetworkAliases("redis-local-master");

redisLocalMaster.start();
System.out.println("local master redis started");

// Redis Local Slave
redisLocalSlave = new GenericContainer<>("redis:5.0.3-alpine").
    withExposedPorts(6379).
    withNetwork(networkLocal).withNetworkAliases("redis-local-slave").
    withCommand("redis-server", "--replicaof", "redis-local-master", "6379");
redisLocalSlave.start();
System.out.println("local slave redis started");

String sentinelConfString = "sentinel monitor mymaster redis-local-master 6379 2";
File sentinelConf = File.createTempFile("sentinel-local", "conf");
Files.write(sentinelConf.toPath(), sentinelConfString.getBytes(),
    StandardOpenOption.TRUNCATE_EXISTING);

// Redis Local Sentinel 1
redisLocalSentinel1 = new GenericContainer<>("redis:5.0.3-alpine").
    withNetwork(networkLocal).
    withNetworkAliases("redis-local-sentinel-1").withExposedPorts(26379).
    withCommand("redis-sentinel", "/etc/sentinel.conf").
    withStartupTimeout(Duration.ofSeconds(30)).
    withFileSystemBind(sentinelConf.getAbsolutePath(), "/etc/sentinel.conf");
redisLocalSentinel1.start();

// Redis Local Sentinel 2
redisLocalSentinel2 = new GenericContainer<>("redis:5.0.3-alpine").
    withNetwork(networkLocal).
    withNetworkAliases("redis-local-sentinel-2").withExposedPorts(26379).
    withCommand("redis-sentinel", "/etc/sentinel.conf").
    withStartupTimeout(Duration.ofSeconds(30)).
    withFileSystemBind(sentinelConf.getAbsolutePath(), "/etc/sentinel.conf");
redisLocalSentinel2.start();

// Redis Local Sentinel 3
redisLocalSentinel3 = new GenericContainer<>("redis:5.0.3-alpine").
    withNetwork(networkLocal).
    withNetworkAliases("redis-local-sentinel-3").
    withExposedPorts(26379).
    withCommand("redis-sentinel", "/etc/sentinel.conf").
    withStartupTimeout(Duration.ofSeconds(30)).
    withFileSystemBind(sentinelConf.getAbsolutePath(), "/etc/sentinel.conf");
redisLocalSentinel3.start();

And i can connect to this setup using the following code:

HashSet<String> localSentinels = new HashSet<>();
localSentinels.add(redisLocalSentinel1.getHost() + ":" + redisLocalSentinel1.getFirstMappedPort());
localSentinels.add(redisLocalSentinel2.getHost() + ":" + redisLocalSentinel2.getFirstMappedPort());
localSentinels.add(redisLocalSentinel3.getHost() + ":" + redisLocalSentinel3.getFirstMappedPort());

List<String> localSentinelsList = Arrays.asList(localSentinels.toArray(new String[0]));
    
JedisSentinelPool local = new JedisSentinelPool("mymaster", localSentinels);

The basic idea was to have 2 redis servers (one master & one replica) and 3 sentinel nodes. This is the std way of using a sentinel based setup afaik. Connecting replica to master and configuring sentinels required some additional steps. Most probably they are easier if you use redis specific test containers instead of generic ones?

Upvotes: 1

Related Questions