#!/bin/bash
# /usr/local/redis-cluster/deploy-cluster.sh
set -e
REDIS_CLUSTER_DIR="/usr/local/redis-cluster"
PORTS=(5001 5002 5003)
# 获取当前服务器的 IP 地址
SERVER_IP=$(hostname -I | awk '{print $1}')
REDIS_PASSWORD="Redis@12345"
echo "=== Redis集群部署开始 ==="
echo "部署配置:"
echo " - 节点数量: ${#PORTS[@]}个主节点"
echo " - 服务器IP: ${SERVER_IP}"
echo " - 端口范围: ${PORTS[*]}"
echo " - 数据目录: ${REDIS_CLUSTER_DIR}"
echo "----------------------------------------"
# 1. 停止并清理现有容器
echo "1. 清理现有容器..."
for port in "${PORTS[@]}"; do
if docker stop "redis-cluster-${port}" 2>/dev/null; then
echo " 已停止容器: redis-cluster-${port}"
fi
if docker rm "redis-cluster-${port}" 2>/dev/null; then
echo " 已删除容器: redis-cluster-${port}"
fi
done
echo " 容器清理完成"
# 2. 清理数据文件
echo "2. 清理旧数据文件..."
for port in "${PORTS[@]}"; do
rm -rf "${REDIS_CLUSTER_DIR}/${port}/data/*" 2>/dev/null || true
rm -f "${REDIS_CLUSTER_DIR}/${port}/data/nodes.conf" 2>/dev/null || true
rm -f "${REDIS_CLUSTER_DIR}/${port}/data/appendonly.aof" 2>/dev/null || true
rm -f "${REDIS_CLUSTER_DIR}/${port}/data/dump.rdb" 2>/dev/null || true
echo " 已清理节点 ${port} 数据"
done
# 创建目录结构
echo "3. 创建目录结构..."
for port in "${PORTS[@]}"; do
mkdir -p "${REDIS_CLUSTER_DIR}/${port}/conf"
mkdir -p "${REDIS_CLUSTER_DIR}/${port}/data"
chmod -R 644 "${REDIS_CLUSTER_DIR}/${port}/data"
chmod -R 744 "${REDIS_CLUSTER_DIR}/${port}/data"
chown -R 1001:1001 "${REDIS_CLUSTER_DIR}/${port}/data" 2>/dev/null || true
echo " 已创建目录: ${REDIS_CLUSTER_DIR}/${port}/{conf,data}"
done
# 4. 创建配置文件
echo "4. 生成配置文件..."
for port in "${PORTS[@]}"; do
cat >"${REDIS_CLUSTER_DIR}/${port}/conf/redis.conf" <<EOF
# Redis集群配置
port ${port}
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 15000
cluster-announce-ip ${SERVER_IP}
cluster-announce-port ${port}
cluster-announce-bus-port 1${port}
# 密码认证
requirepass ${REDIS_PASSWORD}
masterauth ${REDIS_PASSWORD}
# 持久化配置
appendonly yes
appendfsync everysec
dir /data
save 900 1
save 300 10
save 60 10000
# 网络配置
bind 0.0.0.0
protected-mode no
timeout 0
tcp-keepalive 300
# 内存管理
maxmemory 4gb
maxmemory-policy allkeys-lru
# 日志配置
daemonize no
loglevel verbose
logfile ""
# 客户端配置
maxclients 10000
# 性能配置
activerehashing yes
EOF
echo " 已生成节点 ${port} 配置文件"
done
# 5. 启动Redis容器
echo "5. 启动Docker容器..."
for port in "${PORTS[@]}"; do
echo " 启动节点 ${port} ..."
if docker run -d \
--name "redis-cluster-${port}" \
--net host \
-v "${REDIS_CLUSTER_DIR}/${port}/data:/data" \
-v "${REDIS_CLUSTER_DIR}/${port}/conf/redis.conf:/etc/redis/redis.conf" \
-v "/etc/localtime:/etc/localtime:ro" \
--restart unless-stopped \
docker.1ms.run/library/redis:7.4.5 \
redis-server /etc/redis/redis.conf; then
echo " 节点 ${port} 容器启动成功"
else
echo " 节点 ${port} 容器启动失败"
exit 1
fi
done
# 6. 等待节点启动
echo "6. 等待节点启动..."
TIMEOUT=300
INTERVAL=5
ELAPSED_TIME=0
ALL_NODES_READY=false
while [ $ELAPSED_TIME -lt $TIMEOUT ]; do
ALL_NODES_READY=true
for port in "${PORTS[@]}"; do
if ! docker exec "redis-cluster-${port}" redis-cli --no-auth-warning -p ${port} -a ${REDIS_PASSWORD} ping 2>/dev/null | grep -q "PONG"; then
ALL_NODES_READY=false
break
fi
done
if [ "$ALL_NODES_READY" = true ]; then
echo " 所有节点已准备好"
break
fi
sleep $INTERVAL
ELAPSED_TIME=$((ELAPSED_TIME + INTERVAL))
echo " 等待节点启动... (已等待 ${ELAPSED_TIME} 秒)"
done
if [ "$ALL_NODES_READY" = false ]; then
echo " 超时:节点未能在 ${TIMEOUT} 秒内启动"
exit 1
fi
# 7. 检查节点状态
echo "7. 检查节点状态:"
ALL_NODES_READY=true
for port in "${PORTS[@]}"; do
if docker exec "redis-cluster-${port}" redis-cli --no-auth-warning -p ${port} -a ${REDIS_PASSWORD} ping 2>/dev/null | grep -q "PONG"; then
echo " 节点 ${port} Redis服务正常"
else
echo " 节点 ${port} Redis服务异常"
docker logs "redis-cluster-${port}" | tail -5
ALL_NODES_READY=false
fi
done
if [ "$ALL_NODES_READY" = false ]; then
echo "节点启动检查失败,请查看日志"
exit 1
fi
# 8. 创建Redis集群
echo "8. 创建Redis集群..."
echo " 正在组建集群,请稍候..."
# 构建 Redis 集群创建命令
CLUSTER_NODES=""
for port in "${PORTS[@]}"; do
CLUSTER_NODES="${CLUSTER_NODES} ${SERVER_IP}:${port}"
done
# 执行集群创建
docker exec redis-cluster-5001 redis-cli -p 5001 -a ${REDIS_PASSWORD} --no-auth-warning --cluster create \
$CLUSTER_NODES --cluster-replicas 0 --cluster-yes && echo "集群创建成功" || echo "集群创建失败,请检查"
# 9. 等待集群稳定
echo "9. 等待集群稳定"
sleep 5
# 10. 验证集群状态
echo "10. 验证集群状态:"
echo "----------------------------------------"
CLUSTER_HEALTHY=true
for port in "${PORTS[@]}"; do
# 获取集群信息,并删除多余的换行符
cluster_info=$(docker exec "redis-cluster-${port}" redis-cli -p ${port} -a ${REDIS_PASSWORD} --no-auth-warning CLUSTER INFO 2>/dev/null || echo "无法获取集群信息")
# 提取集群状态和槽位信息
cluster_state=$(echo "$cluster_info" | grep 'cluster_state:' | cut -d: -f2 | tr -d '[:space:]' || echo "unknown")
cluster_slots_ok=$(echo "$cluster_info" | grep 'cluster_slots_ok:' | cut -d: -f2 | tr -d '[:space:]' || echo "0")
cluster_slots_assigned=$(echo "$cluster_info" | grep 'cluster_slots_assigned:' | cut -d: -f2 | tr -d '[:space:]' || echo "0")
# 清除可能存在的非数字字符(如 k,m)
cluster_slots_assigned=$(echo $cluster_slots_assigned | sed 's/[^0-9]*//g')
cluster_slots_ok=$(echo $cluster_slots_ok | sed 's/[^0-9]*//g')
# 输出调试信息
echo " [节点 ${port}] cluster_state=$cluster_state, cluster_slots_ok=$cluster_slots_ok, cluster_slots_assigned=$cluster_slots_assigned"
# 检查集群状态和槽位是否正常
if [ "$cluster_state" = "ok" ] && [ "$cluster_slots_ok" = "16384" ] && [ "$cluster_slots_assigned" = "16384" ]; then
echo "节点 ${port}: 状态正常 | 槽位: ${cluster_slots_ok}/16384"
else
echo "节点 ${port}: 状态: ${cluster_state} | 槽位: ${cluster_slots_ok}/16384"
CLUSTER_HEALTHY=false
fi
sleep 1
done
echo "----------------------------------------"
if [ "$CLUSTER_HEALTHY" = true ]; then
echo "=== Redis集群部署成功! ==="
else
echo "=== Redis集群部署完成,但有警告 ==="
fi
# 11. 显示连接信息
echo ""
echo "连接信息:"
echo "----------------------------------------"
for port in "${PORTS[@]}"; do
echo "节点${port}: redis-cli -c -h ${SERVER_IP} -p ${port} -a ${REDIS_PASSWORD}"
done
echo ""
echo "管理命令:"
echo "----------------------------------------"
echo "查看集群节点: redis-cli -c -h ${SERVER_IP} -p 5001 -a ${REDIS_PASSWORD} CLUSTER NODES"
echo "查看集群信息: redis-cli -c -h ${SERVER_IP} -p 5001 -a ${REDIS_PASSWORD} CLUSTER INFO"
echo "测试写入: redis-cli -c -h ${SERVER_IP} -p 5001 -a ${REDIS_PASSWORD} SET test_key 'hello cluster'"
echo "测试读取: redis-cli -c -h ${SERVER_IP} -p 5002 -a ${REDIS_PASSWORD} GET test_key"
echo ""
echo "数据目录:"
echo "----------------------------------------"
for port in "${PORTS[@]}"; do
echo "节点${port}: ${REDIS_CLUSTER_DIR}/${port}/data/"
done
echo ""
echo "部署时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "========================================"