我已经使用 docker 构建了一个包含 6 个节点的 Redis 集群,端口来自
7000-7005
,每个 Redis 使用 redis.conf
,如下所示:
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
protected-mode no
在文件“docker-compose.yaml”中:
version: "3.9"
services:
redis-node-1:
container_name: redis-node-1
image: redis/redis-stack-server:latest
volumes:
- ./build/redis/redis.conf:/usr/local/etc/redis/redis.conf
- ./data/redis-node-1:/data
ports:
- "7000:7000"
- "17000:17000"
entrypoint: [ "redis-server", "/usr/local/etc/redis/redis.conf", --port,"7000" ]
networks:
redis_cluster_network:
ipv4_address: 173.18.0.5
redis-node-2:
container_name: redis-node-2
image: redis/redis-stack-server:latest
volumes:
- ./build/redis/redis.conf:/usr/local/etc/redis/redis.conf
- ./data/redis-node-2:/data
ports:
- "7001:7001"
- "17001:17001"
entrypoint: [ "redis-server", "/usr/local/etc/redis/redis.conf", --port,"7001" ]
networks:
redis_cluster_network:
ipv4_address: 173.18.0.6
redis-node-3:
container_name: redis-node-3
image: redis/redis-stack-server:latest
volumes:
- ./build/redis/redis.conf:/usr/local/etc/redis/redis.conf
- ./data/redis-node-3:/data
ports:
- "7002:7002"
- "17002:17002"
entrypoint: [ "redis-server", "/usr/local/etc/redis/redis.conf", --port,"7002" ]
networks:
redis_cluster_network:
ipv4_address: 173.18.0.7
redis-node-4:
container_name: redis-node-4
image: redis/redis-stack-server:latest
volumes:
- ./build/redis/redis.conf:/usr/local/etc/redis/redis.conf
- ./data/redis-node-4:/data
ports:
- "7003:7003"
- "17003:17003"
entrypoint: [ "redis-server", "/usr/local/etc/redis/redis.conf", --port,"7003" ]
depends_on:
- redis-node-1
- redis-node-2
- redis-node-3
networks:
redis_cluster_network:
ipv4_address: 173.18.0.8
redis-node-5:
container_name: redis-node-5
image: redis/redis-stack-server:latest
volumes:
- ./build/redis/redis.conf:/usr/local/etc/redis/redis.conf
- ./data/redis-node-5:/data
ports:
- "7004:7004"
- "17004:17004"
entrypoint: [ "redis-server", "/usr/local/etc/redis/redis.conf", --port,"7004" ]
depends_on:
- redis-node-1
- redis-node-2
- redis-node-3
networks:
redis_cluster_network:
ipv4_address: 173.18.0.9
redis-node-6:
container_name: redis-node-6
image: redis/redis-stack-server:latest
volumes:
- ./build/redis/redis.conf:/usr/local/etc/redis/redis.conf
- ./data/redis-node-6:/data
ports:
- "7005:7005"
- "17005:17005"
entrypoint: [ "redis-server", "/usr/local/etc/redis/redis.conf", --port,"7005" ]
depends_on:
- redis-node-1
- redis-node-2
- redis-node-3
networks:
redis_cluster_network:
ipv4_address: 173.18.0.10
redis-cluster-creator:
container_name: redis-cluster-creator
image: redis/redis-stack-server:latest
command: 'redis-cli -p 7002 --cluster create 173.18.0.5:7000 173.18.0.6:7001 173.18.0.7:7002 173.18.0.8:7003 173.18.0.9:7004 173.18.0.10:7005 --cluster-replicas 1 --cluster-yes'
depends_on:
- redis-node-1
- redis-node-2
- redis-node-3
- redis-node-4
- redis-node-5
- redis-node-6
networks:
redis_cluster_network:
ipv4_address: 173.18.0.11
networks:
redis_cluster_network:
driver: bridge
ipam:
driver: default
config:
- subnet: 173.18.0.0/16
但是当我尝试使用以下方式连接 go-redis 时:
redisClient := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: cfg.RedisCache.Address,
Password: cfg.RedisCache.Password,
PoolSize: cfg.RedisCache.PoolSize,
MaxRetries: cfg.RedisCache.MaxRetries,
ReadOnly: true,
RouteRandomly: true,
})
err = redisClient.ForEachShard(ctx, func(ctx context.Context, shard *redis.Client) error {
return shard.Ping(ctx).Err()
})
if err != nil {
apiLogger.Fatal(err)
}
我总是从每个节点之一收到错误,例如:
dial tcp 173.18.0.7:7000: i/o timeout
我错过了一些配置吗?
我已阅读 go-redis 指南。它告诉我,我应该等待所有 Redis 节点运行,然后再在 docker-compose 中运行应用程序。但我只将 docker-compose 用于 redis,不包括我的应用程序。我确信所有redis节点都已完成运行。而且这个错误仍然发生。
我在尝试运行固定IP时也遇到了类似的问题,所以我发现如下:
这是 docker-compose.yml 的示例
version: '3'
networks:
redis-cluster-compose:
driver: bridge
services:
redis-node-1:
container_name: node1
image: redis:latest
ports:
- 7000:7000
networks:
- redis-cluster-compose
hostname: redis-node-1
volumes:
- ./7000:/redis
command: redis-server /redis/redis.conf
redis-node-2:
container_name: node2
image: redis:latest
ports:
- 7001:7001
networks:
- redis-cluster-compose
hostname: redis-node-2
volumes:
- ./7001:/redis
command: redis-server /redis/redis.conf
redis-node-3:
container_name: node3
image: redis:latest
ports:
- 7002:7002
networks:
- redis-cluster-compose
hostname: redis-node-3
volumes:
- ./7002:/redis
command: redis-server /redis/redis.conf
redis-cluster-creator:
container_name: cluster-creator
image: redis:latest
ports:
- 6999:6999
networks:
- redis-cluster-compose
command: redis-cli -p 7000 --cluster create redis-node-1:7000 redis-node-2:7001 redis-node-3:7002 --cluster-replicas 0 --cluster-yes
depends_on:
- redis-node-1
- redis-node-2
- redis-node-3
这是
main.go
:
package main
import (
"context"
"fmt"
"time"
"github.com/redis/go-redis/v9"
)
func main() {
rdb := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{"localhost:7000", "localhost:7001", "localhost:7002"},
})
ctx := context.Background()
// Writing data
key, message := "test", fmt.Sprintf("[%s] Test", time.Now())
err := rdb.Set(ctx, key, message, 0).Err()
if err != nil {
panic(err)
}
// Reading data
val, err := rdb.Get(ctx, key).Result()
if err != nil {
panic(err)
}
fmt.Println("key:", val)
}
备注:Go 程序不在 Docker 中运行,如果需要,可能需要对 URL 进行一些调整。
活动部件太多。例如,网络部分完全不需要运行:
version: "3.9"
x-vars:
redis-shared-config: &redis-shared-config
entrypoint: [ "redis-server", "/usr/local/etc/redis/redis.conf", --port,"6379" ]
image: redis/redis-stack-server:latest
healthcheck:
test: [ "CMD", "redis-cli", "ping" ]
interval: 5s
timeout: 5s
retries: 5
configs:
- source: redis-conf
target: /usr/local/etc/redis/redis.conf
configs:
redis-conf:
file: ./build/redis/redis.conf
volumes:
redis-data-1:
redis-data-2:
redis-data-3:
redis-data-4:
redis-data-5:
redis-data-6:
services:
redis-node-1:
container_name: redis-1
<<: *redis-shared-config
volumes:
- "redis-data-1:/data"
redis-node-2:
container_name: redis-2
<<: *redis-shared-config
volumes:
- "redis-data-2:/data"
redis-node-3:
container_name: redis-3
<<: *redis-shared-config
volumes:
- "redis-data-3:/data"
redis-node-4:
container_name: redis-4
<<: *redis-shared-config
volumes:
- "redis-data-4:/data"
redis-node-5:
container_name: redis-5
<<: *redis-shared-config
volumes:
- "redis-data-5:/data"
redis-node-6:
container_name: redis-6
<<: *redis-shared-config
volumes:
- "redis-data-6:/data"
redis-cluster-creator:
container_name: redis-cluster-creator
image: redis/redis-stack-server:latest
# Make the run of redis-cluster-creator service idempotent
# by checking the cluster health before creating it.
# We do this so that we can make our app a dependent service
# of the redis-cluster-creator service.
#
# Explanation of the command:
# If the cluster is already created the part before || will be
# successful and exit with 0 and the part after || will not be executed.
#
# If the cluster is not created the part before || will
# fail and the part after || will be executed.
# If successful, the create command will exit with 0.
command: |
bash -c "redis-cli --cluster check redis-node-1:6379 || (echo Setting up redis cluster; redis-cli --cluster create redis-node-1:6379 redis-node-2:6379 redis-node-3:6379 redis-node-4:6379 redis-node-5:6379 redis-node-6:6379 --cluster-replicas 1 --cluster-yes)"
depends_on:
redis-node-1:
condition: service_healthy
redis-node-2:
condition: service_healthy
redis-node-3:
condition: service_healthy
redis-node-4:
condition: service_healthy
redis-node-5:
condition: service_healthy
redis-node-6:
condition: service_healthy
app:
image: myapp:latest
platform: linux/amd64
build:
context: ./build/app
environment:
- MY_APP_REDIS_ADDRESSES=redis-node-1:6379,redis-node-2:6379,redis-node-3:6379,redis-node-4:6379,redis-node-5:6379,redis-node-6:6379
depends_on:
redis-cluster-creator:
condition: service_completed_successfully
redis-node-1:
condition: service_healthy
redis-node-2:
condition: service_healthy
redis-node-3:
condition: service_healthy
redis-node-4:
condition: service_healthy
redis-node-5:
condition: service_healthy
redis-node-6:
condition: service_healthy
我将应用程序放在这里不仅仅是为了方便,而是为了必要:您指示 redis 绑定到
0.0.0.0
,大致翻译为
您可以掌握的每个IP地址。
现在,据我了解,这就是幕后发生的事情:您联系一个集群节点,请求一个密钥(所有内容都在 Redis 中的密钥后面),然后您将被重定向到分片的主节点保持所述值。但集群只知道该主机的内部IP,在您给出的示例中
173.18.0.7
。然而,它的 IP 范围很可能在您的网络上不可用(这些是公共 IP),更不用说主机了,除非您碰巧为 MediaCom 工作或成为 MediaCom 的客户:
$ whois 173.18.0.7
% IANA WHOIS server
% for more information on IANA, visit http://www.iana.org
% This query returned 1 object
refer: whois.arin.net
inetnum: 173.0.0.0 - 173.255.255.255
organisation: ARIN
status: ALLOCATED
whois: whois.arin.net
changed: 2008-02
source: IANA
# whois.arin.net
NetRange: 173.16.0.0 - 173.31.255.255
CIDR: 173.16.0.0/12
NetName: MEDIACOM-RESIDENTIAL-CUST
NetHandle: NET-173-16-0-0-1
Parent: NET173 (NET-173-0-0-0-0)
NetType: Direct Allocation
OriginAS:
Organization: Mediacom Communications Corp (MCC-244)
RegDate: 2008-05-19
Updated: 2012-02-24
Ref: https://rdap.arin.net/registry/ip/173.16.0.0
OrgName: Mediacom Communications Corp
OrgId: MCC-244
Address: 1 Mediacom Way
City: Mediacom Park
StateProv: NY
PostalCode: 10918
Country: US
RegDate: 2008-02-05
Updated: 2018-08-29
Comment: For abuse issues contact [email protected]
Ref: https://rdap.arin.net/registry/entity/MCC-244
<skipped for brevity>
因此,最有可能的罪魁祸首是相当奇怪的网络配置。
您可以在
https://github.com/mwmahlberg/redis-78155633找到 docker compose 以及用于测试的小型 Go 应用程序和随附的
Dockerfile
。