Redis集群部署

[!note] 以下是为 Redis 7.2.4(最新版本-我替换成了latest)编写的完整 docker-compose.yml 文件,包含 6 个节点 (3 主 + 3 从) 的完整配置:

安装 #

version: '3.8'

services:
  redis-7000:
    image: redis:latest
    container_name: redis-7000
    command: redis-server /usr/local/etc/redis/redis.conf
    volumes:
      - ./config/redis-7000.conf:/usr/local/etc/redis/redis.conf
      - ./data/7000:/data
    networks:
      - redis-cluster-net
    ports:
      - "7000:7000"
      - "17000:17000"

  redis-7001:
    image: redis:latest
    container_name: redis-7001
    command: redis-server /usr/local/etc/redis/redis.conf
    volumes:
      - ./config/redis-7001.conf:/usr/local/etc/redis/redis.conf
      - ./data/7001:/data
    networks:
      - redis-cluster-net
    ports:
      - "7001:7001"
      - "17001:17001"

  redis-7002:
    image: redis:latest
    container_name: redis-7002
    command: redis-server /usr/local/etc/redis/redis.conf
    volumes:
      - ./config/redis-7002.conf:/usr/local/etc/redis/redis.conf
      - ./data/7002:/data
    networks:
      - redis-cluster-net
    ports:
      - "7002:7002"
      - "17002:17002"

  redis-7003:
    image: redis:latest
    container_name: redis-7003
    command: redis-server /usr/local/etc/redis/redis.conf
    volumes:
      - ./config/redis-7003.conf:/usr/local/etc/redis/redis.conf
      - ./data/7003:/data
    networks:
      - redis-cluster-net
    ports:
      - "7003:7003"
      - "17003:17003"

  redis-7004:
    image: redis:latest
    container_name: redis-7004
    command: redis-server /usr/local/etc/redis/redis.conf
    volumes:
      - ./config/redis-7004.conf:/usr/local/etc/redis/redis.conf
      - ./data/7004:/data
    networks:
      - redis-cluster-net
    ports:
      - "7004:7004"
      - "17004:17004"

  redis-7005:
    image: redis:latest
    container_name: redis-7005
    command: redis-server /usr/local/etc/redis/redis.conf
    volumes:
      - ./config/redis-7005.conf:/usr/local/etc/redis/redis.conf
      - ./data/7005:/data
    networks:
      - redis-cluster-net
    ports:
      - "7005:7005"
      - "17005:17005"

networks:
  redis-cluster-net:
    driver: bridge

配套操作步骤 #

  1. 创建目录和配置文件
mkdir -p redis-cluster/{config,data/{7000,7001,7002,7003,7004,7005}}
cd redis-cluster
  1. 生成所有配置文件(一键生成脚本)
for port in {7000..7005}; do
  cat > config/redis-${port}.conf <<EOF
port ${port}
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
dir /data
# 重要:替换为你的宿主机IP(非容器IP)
cluster-announce-ip 192.168.1.100
cluster-announce-port ${port}
cluster-announce-bus-port 1${port}
EOF
done
  1. 启动所有容器
podman-compose up -d
  1. 初始化集群
podman exec -it redis-7000 redis-cli --cluster create \
  $(for port in {7000..7005}; do echo -n "$(hostname -I | awk '{print $1}'):${port} "; done) \
  --cluster-replicas 1

关键配置说明 #

  1. cluster-announce-ip 参数

    • 必须设置为 宿主机的真实 IP(不能是 127.0.0.1 或容器 IP)
    • 如果使用局域网访问,例如 192.168.1.100
    • 如果使用公网访问,设置为公网 IP
  2. 端口映射规则

    • 客户端端口:7000-7005
    • 集群总线端口:17000-17005(必须开放)
  3. 安全建议(生产环境必做)

    # 在配置文件中添加以下内容
    requirepass your_strong_password
    masterauth your_strong_password
    

验证集群状态 #

# 查看集群信息
podman exec -it redis-7000 redis-cli -p 7000 cluster info

# 查看节点拓扑
podman exec -it redis-7000 redis-cli -p 7000 cluster nodes

常见问题解决 #

  1. 节点无法加入集群

    • 检查 cluster-announce-ip 是否能被其他节点访问
    • 检查防火墙是否开放 7000-700517000-17005 端口
  2. 客户端连接超时

    • 确保客户端使用 宿主机的 IP 而不是容器 IP
    • 检查 Redis 配置文件中的 portcluster-announce-port 是否一致

使用 #

Python #

from loguru import logger
from redis.cluster import RedisCluster, ClusterNode

def test_redis_cluster():
    # 使用 ClusterNode 对象,并确保端口为 int 类型
    # 可以只使用一个node也可以传入多个防止单个故障无法连接(单个连接成功后会自动发现其它)
    startup_nodes = [
        ClusterNode(host="192.168.105.156", port=7000),
        ClusterNode(host="192.168.105.156", port=7001),
        ClusterNode(host="192.168.105.156", port=7002),
    ]
    try:
        rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True)
    except Exception as e:
        logger.error(f"连接 Redis 集群失败: {e}")
        return

    # 测试写入和读取数据
    try:
        rc.set("test-key", "hello redis cluster")
        value = rc.get("test-key")
        logger.info(f"test-key: {value}")
    except Exception as e:
        logger.error(f"操作 Redis 集群失败: {e}")

if __name__ == "__main__":
    test_redis_cluster()

异步IO #

import asyncio
from loguru import logger
from redis.asyncio.cluster import RedisCluster, ClusterNode

async def test_redis_cluster():
    # 定义集群启动节点,使用 ClusterNode 对象,确保端口为 int 类型
    startup_nodes = [
        ClusterNode(host="192.168.105.156", port=7000),
        ClusterNode(host="192.168.105.156", port=7001),
        ClusterNode(host="192.168.105.156", port=7002),
    ]
    
    try:
        # 创建 Redis 集群客户端
        rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True)
    except Exception as e:
        logger.error(f"连接 Redis 集群失败: {e}")
        return

    try:
        # 异步写入数据
        await rc.set("test-key", "hello redis cluster")
        # 异步读取数据
        value = await rc.get("test-key")
        logger.info(f"test-key: {value}")
    except Exception as e:
        logger.error(f"操作 Redis 集群失败: {e}")
    finally:
        # 使用 aclose() 方法关闭客户端连接
        await rc.aclose()

if __name__ == "__main__":
    asyncio.run(test_redis_cluster())