In regards to "Single-Node Cluster":
The problem arises because .withExposedPorts(port)
exposes the Redis service on a dynamically allocated local port. Meanwhile, the JedisCluster client uses the seed nodes (provided hosts) to resolve the cluster topology via the CLUSTER SLOTS
or CLUSTER NODES
command. Then, it will use host/port announced by the nodes themself to create connections to a particular node.
As you can see from the output you have provided cluster nodes will announce the actual port they are running on (6379) unless cluster-announce-port
is specified.
1f2673c5fdb45ca16d564658ff88f815db5cbf01 172.29.0.2:6379@16379 myself,master ...
Since port 6379 is not accessible outside the docker container (e.g., the test container exposes it on a different dynamically mapped port), call to jedis.set("key", "value");
will try to acquire connection to the node using the announced host/port and will fail.
You can overcome this by using statically mapped port bindin or use Jedis provided option for host/port mapping -DefaultJedisClientConfig.Builder#hostAndPortMapper
.
Option 1: Expose redis service on predefined port
int externalPort = 7379;
int port = 6379;
Network network = Network.newNetwork();
RedisContainer redisContainer = new RedisContainer(DockerImageName.parse("redis:7.0.5"))
// Use static port binding together with cluster-announce-port
.withCreateContainerCmdModifier(cmd -> cmd.withPortBindings(
new PortBinding(Ports.Binding.bindPort(externalPort), ExposedPort.tcp(port))))
.withCommand("redis-server --port " + port +
" --requirepass " + redisPassword + // Password for clients
" --masterauth " + redisPassword + // Password for inter-node communication
" --cluster-announce-port " + externalPort +
" --cluster-enabled yes" +
" --cluster-config-file nodes.conf"+
" --cluster-node-timeout 5000"+
" --appendonly yes" +
" --bind 0.0.0.0" )
.withNetwork(network)
.withNetworkMode("bridge")
.withNetworkAliases("redis-" + i)
.waitingFor(Wait.forListeningPort());
Option 2 : Use Jedis hostAndPortMapper
HostAndPortMapper nat = hostAndPort -> {
if (hostAndPort.getPort() == port) {
return new HostAndPort(redisContainer.getHost(), redisContainer.getMappedPort(port));
}
return hostAndPort;
};
...
// Connect to the cluster using Jedis with a password
DefaultJedisClientConfig.Builder jedisClientConfig = DefaultJedisClientConfig.builder()
.password(redisPassword)
.hostAndPortMapper(nat)
.ssl(false)
.connectionTimeoutMillis(10000)
.socketTimeoutMillis(4000);
Also, make sure the cluster has reached a stable state after slots were configured.