Redis 分布式集群与主从复制的一键部署脚本
引言
在分布式系统中,Redis 集群因其高可用性和可扩展性成为缓存服务的首选。然而,手动部署多节点 Redis 集群往往涉及复杂的配置步骤,容易出错且效率低下。
本文介绍的 Redis 集群一键部署脚本,通过自动化流程实现环境检测、安装、配置及集群初始化,大幅简化部署流程,将传统 4 小时部署流程缩短至 15 分钟,适用于开发测试及中小型生产环境。
部署前的技术准备
系统要求
- Linux 系统(推荐 CentOS 7+),需具备 root 权限
- 至少 4GB 内存(根据集群规模调整)
- 已安装
wget
、make
、gcc
等编译工具
配置参数说明(脚本开头部分)
基础配置
ini
readonly IP=$(hostname -I | awk '{print $1}') # 自动获取主机IP地址,用于节点间通信
readonly REDIS_INSTALL_DIR="/usr/local/redis_cluster" # Redis源码编译安装目录
readonly REQUIRED_VERSION="5.0.6" # 指定安装的Redis版本(5.0.6为稳定版本)
readonly REDIS_DBS_PATH="/data/redis_dbs" # Redis数据文件存储目录,建议挂载高性能磁盘
readonly REDIS_CATALOG_PATH="/etc/redis" # Redis配置文件存储目录
readonly REDIS_EXECUTABLE="redis-server" # Redis服务可执行文件名
readonly LOG_FILE="/var/log/redis_install.log" # 部署日志文件路径
集群架构配置
端口分配规则:
- 每组集群包含3个主节点(6479/6480/6481)和3个从节点(7479/7480/7481)
- 主从节点按顺序配对:6479→7479, 6480→7480, 6481→7481
- 多组集群使用不同端口范围,避免端口冲突
yaml
readonly CLUSTER_PORTS=(6479 6480 6481 7479 7480 7481 6779 6780 6781 7779 7780 7781 6879 6880 6881 7879 7880 7881) # 集群端口配置(3组集群,每组6个节点,共18个节点)
readonly CLUSTER_GROUPS=3 # 集群组数,每组独立管理16384个哈希槽
readonly CLUSTER_MASTERS=3 # 每组集群中的主节点数量
readonly CLUSTER_NODES_PER_GROUP=6 # 每组集群中的总节点数(主节点+从节点)
单机主从配置
端口分配规则:
- 每组包含1个主节点和1个从节点
- 主从节点按顺序配对:6579→7579, 6679→7679
- 适用于不需要分片但需要高可用的场景(如Session缓存)
ini
readonly STANDALONE_PORTS=(6579 7579 6679 7679) # 单机主从端口配置(2组主从,每组2个节点,共4个节点)
readonly STANDALONE_PAIRS=2 # 单机主从组数
readonly STANDALONE_MASTERS=1 # 每组主从复制中的主节点数量
一键部署步骤
执行脚本
bash
chmod +x redis_install.sh
./redis_install.sh
# 部署日志查看
tail -f /var/log/redis_install.log
执行过程解析
脚本按以下流程自动执行(可通过日志文件 var/log/redis_install.log
查看详情):

service统一管控
bash
service redis start # 启动所有节点
service redis restart # 重新启动所有节点
service redis status # 查看运行状态
service redis stop # 停止所有节点
混合部署架构拓扑图

完整部署脚本
bash
####################################################
# 文件名:redis_install.sh
# 描述:Redis分布式集群与主从复制一键部署脚本
# 架构:支持混合架构, 多组Cluster集群 + 多组单机主从
# 环境:适用于CentOS 7+/Ubuntu 18.04+, 需root权限执行
####################################################
#!/bin/bash
# ==================== 全局配置 ====================
# 基础配置
readonly IP=$(hostname -I | awk '{print $1}') # 自动获取主机IP地址,用于节点间通信
readonly REDIS_INSTALL_DIR="/usr/local/redis_cluster" # Redis源码编译安装目录
readonly REQUIRED_VERSION="5.0.6" # 指定安装的Redis版本(5.0.6为稳定版本)
readonly REDIS_DBS_PATH="/data/redis_dbs" # Redis数据文件存储目录,建议挂载高性能磁盘
readonly REDIS_CATALOG_PATH="/etc/redis" # Redis配置文件存储目录
readonly REDIS_EXECUTABLE="redis-server" # Redis服务可执行文件名
readonly LOG_FILE="/var/log/redis_install.log" # 部署日志文件路径
# 集群架构配置
readonly CLUSTER_PORTS=(6479 6480 6481 7479 7480 7481 6779 6780 6781 7779 7780 7781 6879 6880 6881 7879 7880 7881) # 集群端口配置(3组集群,每组6个节点,共18个节点)
readonly CLUSTER_GROUPS=3 # 集群组数,每组独立管理16384个哈希槽
readonly CLUSTER_MASTERS=3 # 每组集群中的主节点数量
readonly CLUSTER_NODES_PER_GROUP=6 # 每组集群中的总节点数(主节点+从节点)
# 单机主从配置
readonly STANDALONE_PORTS=(6579 7579 6679 7679) # 单机主从端口配置(2组主从,每组2个节点,共4个节点)
readonly STANDALONE_PAIRS=2 # 单机主从组数
readonly STANDALONE_MASTERS=1 # 每组主从复制中的主节点数量
# ==================== 初始化函数 ====================
init_logging() {
exec > >(tee -a "$LOG_FILE") 2>&1
echo -e "\n=== Redis部署日志 $(date '+%Y-%m-%d %H:%M:%S') ===" >> "$LOG_FILE"
}
log() {
local level=$1
local message=$2
echo -e "\n[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $message"
}
show_progress() {
local current=$1 total=$2 message=$3
local width=50 percent=$((current*100/total))
printf "\r[%-${width}s] %d%% %s" "$(printf '#%.0s' $(seq 1 $((current*width/total))))" "$percent" "$message"
[ $current -eq $total ] && echo
}
validate_config() {
# 验证集群配置
local total_ports=$((CLUSTER_GROUPS * CLUSTER_NODES_PER_GROUP))
if [ ${#CLUSTER_PORTS[@]} -ne $total_ports ]; then
log "ERROR" "集群端口数不匹配: 需要$total_ports个端口,实际配置了${#CLUSTER_PORTS[@]}个"
exit 1
fi
# 验证端口是否被占用
for port in "${CLUSTER_PORTS[@]}" "${STANDALONE_PORTS[@]}"; do
if ss -tuln | grep -q ":$port "; then
log "ERROR" "端口 $port 已被占用"
exit 1
fi
done
}
clean_environment() {
log "INFO" "开始清理部署环境..."
# 检查Redis进程是否存在
if pgrep -x "$REDIS_EXECUTABLE" > /dev/null; then
log "INFO" "检测到Redis进程正在运行,准备优雅终止..."
if pkill -INT "$REDIS_EXECUTABLE"; then
log "INFO" "已发送终止信号,等待Redis进程退出..."
sleep 3 # 等待进程优雅退出
if pgrep -x "$REDIS_EXECUTABLE" > /dev/null; then
log "WARN" "Redis进程未正常退出,尝试强制终止..."
pkill -9 "$REDIS_EXECUTABLE"
fi
log "INFO" "Redis进程已终止"
else
log "ERROR" "终止Redis进程失败,请手动检查"
return 1
fi
else
log "INFO" "未检测到Redis进程运行"
fi
# 清理数据目录
log "INFO" "清理Redis数据文件..."
if [ -d "$REDIS_DBS_PATH" ]; then
if rm -rf "$REDIS_DBS_PATH"/*; then
log "INFO" "数据文件清理完成: $REDIS_DBS_PATH"
else
log "ERROR" "数据文件清理失败,可能权限不足"
return 1
fi
else
log "INFO" "数据目录不存在,跳过清理: $REDIS_DBS_PATH"
fi
# 清理配置目录
log "INFO" "清理Redis配置文件..."
if [ -d "$REDIS_CATALOG_PATH" ]; then
if rm -rf "$REDIS_CATALOG_PATH"/*; then
log "INFO" "配置文件清理完成: $REDIS_CATALOG_PATH"
else
log "ERROR" "配置文件清理失败,可能权限不足"
return 1
fi
else
log "INFO" "配置目录不存在,跳过清理: $REDIS_CATALOG_PATH"
fi
log "INFO" "环境清理完成"
return 0
}
# ==================== Redis安装 ====================
install_redis() {
log "INFO" "开始安装Redis $REQUIRED_VERSION"
mkdir -p "$REDIS_INSTALL_DIR" || return 1
cd "$REDIS_INSTALL_DIR" || return 1
local pkg="redis-$REQUIRED_VERSION.tar.gz"
log "INFO" "下载Redis安装包..."
if ! wget -q "https://download.redis.io/releases/$pkg"; then
log "ERROR" "下载失败"
return 1
fi
log "INFO" "解压安装包..."
tar xzf "$pkg" || return 1
cd "redis-$REQUIRED_VERSION" || return 1
log "INFO" "编译安装..."
make -j$(nproc) >/dev/null 2>&1 && make install >/dev/null 2>&1 || {
log "ERROR" "编译失败"
return 1
}
yum remove -y redis >/dev/null 2>&1
REDIS_CONF_DEFAULT="$REDIS_INSTALL_DIR/redis-$REQUIRED_VERSION/redis.conf"
log "INFO" "Redis安装成功"
}
check_redis() {
if command -v "$REDIS_EXECUTABLE" &> /dev/null; then
local version=$("$REDIS_EXECUTABLE" --version | awk '{print $3}' | cut -d= -f2)
if [ "$version" == "$REQUIRED_VERSION" ]; then
log "INFO" "Redis $REQUIRED_VERSION 已安装"
REDIS_CONF_DEFAULT="$REDIS_INSTALL_DIR/redis-$version/redis.conf"
return 0
else
log "WARN" "现有版本($version)与需求版本($REQUIRED_VERSION)不同"
return 1
fi
fi
return 1
}
# ==================== 配置处理 ====================
safe_config_update() {
local file=$1
local pattern=$2
local replacement=$3
local backup="${file}.bak"
# 创建备份
cp -f "$file" "$backup" || return 1
# 执行修改
if ! sed -i "$pattern" "$file"; then
log "ERROR" "修改配置文件失败: $file"
mv -f "$backup" "$file"
return 1
fi
# 验证修改
if ! grep -q "$replacement" "$file"; then
log "ERROR" "修改未生效: $file"
mv -f "$backup" "$file"
return 1
fi
rm -f "$backup"
return 0
}
configure_cluster_nodes() {
local port=$1
local conf="$REDIS_CATALOG_PATH/${port}.conf"
log "INFO" "配置集群节点: $port"
# 创建配置文件目录
mkdir -p "$REDIS_CATALOG_PATH" || return 1
cp "$REDIS_CONF_DEFAULT" "$conf" || return 1
# 使用安全的配置更新方式
safe_config_update "$conf" '/bind 127.0.0.1/s/bind 127.0.0.1/#bind 127.0.0.1/g' "#bind 127.0.0.1" || return 1
safe_config_update "$conf" "/6379/s/6379/$port/g" "port $port" || return 1
safe_config_update "$conf" '/protected-mode yes/s/yes/no/g' "protected-mode no" || return 1
safe_config_update "$conf" "/dbfilename dump.rdb/s/dump.rdb/dump-$port.rdb/g" "dbfilename dump-$port.rdb" || return 1
# 处理dir配置
local dir_line=$(grep -n "dir ./" "$conf" | cut -d: -f1)
[ -z "$dir_line" ] && { log "ERROR" "找不到dir配置"; return 1; }
safe_config_update "$conf" "${dir_line}s|dir .*|dir $REDIS_DBS_PATH|g" "dir $REDIS_DBS_PATH" || return 1
# 集群配置
safe_config_update "$conf" '/# cluster-enabled/s/# cluster-enabled/cluster-enabled/g' "cluster-enabled yes" || return 1
safe_config_update "$conf" '/# cluster-config-file/s/# cluster-config-file/cluster-config-file/g' "cluster-config-file" || return 1
safe_config_update "$conf" '/# cluster-node-timeout/s/# cluster-node-timeout/cluster-node-timeout/g' "cluster-node-timeout" || return 1
safe_config_update "$conf" '/daemonize no/s/no/yes/g' "daemonize yes" || return 1
log "INFO" "集群节点配置完成: $port"
}
configure_standalone_nodes() {
local port=$1 master_port=$2 index=$3
local conf="$REDIS_CATALOG_PATH/${port}.conf"
log "INFO" "配置单机节点: $port"
mkdir -p "$REDIS_CATALOG_PATH" || return 1
cp "$REDIS_CONF_DEFAULT" "$conf" || return 1
safe_config_update "$conf" '/bind 127.0.0.1/s/bind 127.0.0.1/#bind 127.0.0.1/g' "#bind 127.0.0.1" || return 1
safe_config_update "$conf" "/6379/s/6379/$port/g" "port $port" || return 1
safe_config_update "$conf" '/protected-mode yes/s/yes/no/g' "protected-mode no" || return 1
case $index in
0) safe_config_update "$conf" "/dbfilename dump.rdb/s/dump.rdb/dump-cache.rdb/g" "dbfilename dump-cache.rdb" || return 1 ;;
1)
safe_config_update "$conf" "/dbfilename dump.rdb/s/dump.rdb/dump-cache-readonly.rdb/g" "dbfilename dump-cache-readonly.rdb" || return 1
echo "slaveof $IP $master_port" >> "$conf"
;;
2) safe_config_update "$conf" "/dbfilename dump.rdb/s/dump.rdb/dump-online.rdb/g" "dbfilename dump-online.rdb" || return 1 ;;
3)
safe_config_update "$conf" "/dbfilename dump.rdb/s/dump.rdb/dump-online-readonly.rdb/g" "dbfilename dump-online-readonly.rdb" || return 1
echo "slaveof $IP $master_port" >> "$conf"
;;
esac
local dir_line=$(grep -n "dir ./" "$conf" | cut -d: -f1)
[ -z "$dir_line" ] && { log "ERROR" "找不到dir配置"; return 1; }
safe_config_update "$conf" "${dir_line}s|dir .*|dir $REDIS_DBS_PATH|g" "dir $REDIS_DBS_PATH" || return 1
safe_config_update "$conf" '/daemonize no/s/no/yes/g' "daemonize yes" || return 1
log "INFO" "单机节点配置完成: $port"
}
generate_management_script() {
log "INFO" "开始生成Redis管理脚本"
# 生成Redis启动脚本内容
cat > /etc/init.d/redis << 'EOF'
#!/bin/bash
# Redis管理脚本 - 支持启动/停止/重启/状态检查所有Redis实例
# 配置常量
EXEC="/usr/local/bin/redis-server"
CLIEXEC="/usr/local/bin/redis-cli"
CONFIG_DIR="/etc/redis"
RUN_DIR="/var/run"
# 确保运行目录存在
mkdir -p "$RUN_DIR"
# 主函数
main() {
# 查找所有配置文件
local configs=$(find "$CONFIG_DIR" -type f -name "*.conf" 2>/dev/null)
if [ -z "$configs" ]; then
echo "错误:在 $CONFIG_DIR 目录下未找到配置文件"
exit 1
fi
# 处理所有实例
case "$1" in
start)
for conf in $configs; do
local port=$(echo "$conf" | sed 's/[^0-9]//g')
local pidfile="$RUN_DIR/redis_${port}.pid"
if [ -f "$pidfile" ] && kill -0 $(cat "$pidfile") 2>/dev/null; then
echo "Redis端口 $port 已运行"
continue
fi
echo "启动Redis端口 $port..."
$EXEC "$conf" &
sleep 1
if [ -f "$pidfile" ] && kill -0 $(cat "$pidfile") 2>/dev/null; then
echo "Redis端口 $port 启动成功"
else
echo "Redis端口 $port 启动失败"
fi
done
;;
stop)
for conf in $configs; do
local port=$(echo "$conf" | sed 's/[^0-9]//g')
local pidfile="$RUN_DIR/redis_${port}.pid"
if [ ! -f "$pidfile" ] || ! kill -0 $(cat "$pidfile") 2>/dev/null; then
echo "Redis端口 $port 未运行"
continue
fi
echo "停止Redis端口 $port..."
$CLIEXEC -p $port shutdown
# 等待服务停止 (30秒超时)
local timeout=30
while [ -f "$pidfile" ] && kill -0 $(cat "$pidfile") 2>/dev/null && [ $timeout -gt 0 ]; do
sleep 1
timeout=$((timeout-1))
done
# 强制终止(如果需要)
if [ -f "$pidfile" ] && kill -0 $(cat "$pidfile") 2>/dev/null; then
echo "警告: 强制终止Redis端口 $port"
kill -9 $(cat "$pidfile")
fi
echo "Redis端口 $port 已停止"
done
;;
restart)
echo "重启所有Redis实例..."
$0 stop
sleep 1
$0 start
;;
status)
echo "检查Redis实例状态..."
for conf in $configs; do
local port=$(echo "$conf" | sed 's/[^0-9]//g')
local pidfile="$RUN_DIR/redis_${port}.pid"
if [ -f "$pidfile" ] && kill -0 $(cat "$pidfile") 2>/dev/null; then
local pid=$(cat "$pidfile")
echo "Redis端口 $port (PID: $pid) 运行中"
# 尝试获取Redis信息
local info=$($CLIEXEC -p $port info 2>/dev/null)
if [ -n "$info" ]; then
local version=$(echo "$info" | grep "redis_version" | cut -d: -f2)
local memory=$(echo "$info" | grep "used_memory_human" | cut -d: -f2)
echo " 版本: $version"
echo " 内存: $memory"
fi
else
echo "Redis端口 $port 未运行"
fi
done
;;
*)
echo "用法: $0 {start|stop|restart|status}"
exit 1
;;
esac
}
# 执行主函数
main "$@"
EOF
# 赋予脚本执行权限
chmod +x /etc/init.d/redis
chown root:root /etc/init.d/redis
log "INFO" "Redis管理脚本生成完成"
return 0
}
# ==================== 集群管理 ====================
setup_cluster() {
log "INFO" "开始配置Redis集群..."
# 创建数据目录
mkdir -p "$REDIS_DBS_PATH" || return 1
# 启动所有实例
log "INFO" "启动Redis实例..."
find "$REDIS_CATALOG_PATH" -name "*.conf" | xargs -L 1 "$REDIS_EXECUTABLE" || {
log "ERROR" "启动Redis实例失败"
return 1
}
sleep 5
log "INFO" "等待Redis实例启动..."
for port in "${CLUSTER_PORTS[@]}"; do
local max_retries=10
local retry=0
while [ $retry -lt $max_retries ]; do
if redis-cli -h "$IP" -p "$port" ping >/dev/null 2>&1; then
log "INFO" "端口 $port 启动成功"
break
fi
retry=$((retry+1))
sleep 1
done
if [ $retry -eq $max_retries ]; then
log "ERROR" "端口 $port 启动失败"
return 1
fi
done
# 让节点相互认识
for ((j = 0; j < CLUSTER_GROUPS; j++)); do
now_pos=$((j * CLUSTER_NODES_PER_GROUP))
main_port=${CLUSTER_PORTS[now_pos]}
for ((i = now_pos + 1; i < now_pos + CLUSTER_NODES_PER_GROUP; i++)); do
port=${CLUSTER_PORTS[i]}
if ! redis-cli -h "$IP" -p "$main_port" cluster meet "$IP" "$port"; then
log "ERROR" "无法让节点 $port 加入集群"
return 1
fi
sleep 1
done
done
# 配置主从关系
for ((j = 0; j < CLUSTER_GROUPS; j++)); do
now_pos=$((j * CLUSTER_NODES_PER_GROUP))
for ((p = 0; p < CLUSTER_MASTERS; p++)); do
master_pos=$((now_pos + p))
slave_pos=$((master_pos + CLUSTER_MASTERS))
master_port=${CLUSTER_PORTS[master_pos]}
slave_port=${CLUSTER_PORTS[slave_pos]}
master_id=$(redis-cli -h "$IP" -p "$slave_port" cluster nodes | grep "$master_port" | awk '{print $1}')
if [ -z "$master_id" ]; then
log "ERROR" "无法获取主节点ID: $master_port"
return 1
fi
if ! redis-cli -h "$IP" -p "$slave_port" cluster replicate "$master_id"; then
log "ERROR" "配置主从复制失败: $master_port -> $slave_port"
return 1
fi
sleep 1
done
done
sleep 2 # 等待主从关系稳定
log "INFO" "开始分配集群槽点..."
local total_slots=16384
local slots_per_master=$((total_slots / CLUSTER_MASTERS))
local remaining_slots=$((total_slots % CLUSTER_MASTERS))
log "INFO" "总槽点数: $total_slots, 主节点数: $CLUSTER_MASTERS, 每主节点分配: $slots_per_master 个槽点"
# 存储所有集群的主节点信息
declare -a all_primary_masters # 存储每个集群的第一个主节点(用于最终验证)
# 收集所有主节点端口并分配槽点
for ((j = 0; j < CLUSTER_GROUPS; j++)); do
local master_ports=()
local now_pos=$((j * CLUSTER_NODES_PER_GROUP))
for ((p = 0; p < CLUSTER_MASTERS; p++)); do
local master_pos=$((now_pos + p))
master_ports+=("${CLUSTER_PORTS[master_pos]}")
done
# 分配槽点
local start_slot=0
for ((i = 0; i < CLUSTER_MASTERS; i++)); do
local end_slot=$((start_slot + slots_per_master - 1))
local port=${master_ports[i]}
# 处理最后一个主节点分配剩余槽点
if [ $i -eq $((CLUSTER_MASTERS - 1)) ]; then
end_slot=$((start_slot + slots_per_master + remaining_slots - 1))
fi
log "INFO" "集群 $((j+1)): 分配槽点 $start_slot-$end_slot 到主节点 $port"
# 使用 redis-cli 命令分配槽点
if ! redis-cli -h "$IP" -p "$port" cluster addslots $(seq $start_slot $end_slot); then
log "ERROR" "集群 $((j+1)): 槽点分配失败: $start_slot-$end_slot 到 $port"
return 1
fi
start_slot=$((end_slot + 1))
done
# 记录每个集群的第一个主节点(用于最终验证)
all_primary_masters[$j]=${master_ports[0]}
log "INFO" "集群 $((j+1)): 槽点分配命令已执行,等待统一验证..."
done
# 等待所有集群稳定
log "INFO" "所有集群槽点分配命令已执行,等待集群稳定..."
sleep 10 # 根据集群规模调整等待时间
# 统一验证所有集群
log "INFO" "开始统一验证所有集群的槽点分配..."
for ((j = 0; j < CLUSTER_GROUPS; j++)); do
local primary_master=${all_primary_masters[$j]}
local slots_assigned=0
local timeout=30
local start_time=$(date +%s)
log "INFO" "集群 $((j+1)): 验证槽点分配状态..."
# 增加等待循环,确保集群有足够时间同步
while true; do
slots_assigned=$(redis-cli -h "$IP" -p "$primary_master" cluster info | grep "cluster_slots_assigned" | awk -F: '{print $2}' | tr -d '[:space:]')
# 检查是否获取到有效数据
if [ -z "$slots_assigned" ] || ! [[ "$slots_assigned" =~ ^[0-9]+$ ]]; then
log "INFO" "集群 $((j+1)): 暂时无法获取槽点信息,继续等待..."
elif [ "$slots_assigned" -eq 16384 ]; then
log "INFO" "集群 $((j+1)): 槽点分配验证通过!"
break
else
log "INFO" "集群 $((j+1)): 槽点分配中: $slots_assigned/16384,继续等待..."
fi
# 检查是否超时
local elapsed=$(( $(date +%s) - start_time ))
if [ "$elapsed" -ge "$timeout" ]; then
log "ERROR" "集群 $((j+1)): 槽点分配验证超时!"
break
fi
sleep 2
done
# 最终验证
if [ "$slots_assigned" -ne 16384 ]; then
log "ERROR" "集群 $((j+1)): 槽点分配不完整: $slots_assigned/16384"
# 获取该集群的所有主节点信息
local master_ports=()
local now_pos=$((j * CLUSTER_NODES_PER_GROUP))
for ((p = 0; p < CLUSTER_MASTERS; p++)); do
local master_pos=$((now_pos + p))
master_ports+=("${CLUSTER_PORTS[master_pos]}")
done
# 输出详细诊断信息
log "ERROR" "集群 $((j+1)): 所有节点槽点分配详情:"
for port in "${master_ports[@]}"; do
log "ERROR" "集群 $((j+1)): 节点 $port 信息:"
redis-cli -h "$IP" -p "$port" cluster nodes | while read line; do
log "ERROR" "$line"
done
done
return 1
fi
done
# 输出所有集群的最终状态
log "INFO" "所有集群槽点分配验证通过!"
log "INFO" "集群状态汇总:"
for ((j = 0; j < CLUSTER_GROUPS; j++)); do
local primary_master=${all_primary_masters[$j]}
log "INFO" "集群 $((j+1)) 状态:"
redis-cli -h "$IP" -p "$primary_master" cluster info | while read line; do
log "INFO" "$line"
done
log "INFO" "------------------------"
done
log "INFO" "Redis集群配置完成"
return 0
}
# ==================== 主流程 ====================
main() {
init_logging
log "INFO" "开始部署Redis集群"
log "INFO" "主机IP: $IP"
# 1. 定义步骤执行顺序(有序数组)
local step_names=(
"验证配置"
"清理环境"
"检查Redis安装"
"安装Redis"
"配置集群节点"
"配置单机节点"
"设置集群"
"生成管理脚本"
)
# 2. 定义步骤名称到函数的映射(关联数组)
declare -A steps=(
["验证配置"]="validate_config"
["清理环境"]="clean_environment"
["检查Redis安装"]="check_redis"
["安装Redis"]="install_redis"
["配置集群节点"]="configure_cluster_nodes"
["配置单机节点"]="configure_standalone_nodes"
["设置集群"]="setup_cluster"
["生成管理脚本"]="generate_management_script"
)
local total_steps=${#step_names[@]}
local current_step=0
# 执行部署步骤
for step in "${step_names[@]}"; do
((current_step++))
show_progress "$current_step" "$total_steps" "$step"
case "$step" in
"检查Redis安装")
if ! check_redis; then
install_redis || exit 1
fi
;;
"配置集群节点")
for port in "${CLUSTER_PORTS[@]}"; do
configure_cluster_nodes "$port" || exit 1
done
;;
"配置单机节点")
local index=0
for port in "${STANDALONE_PORTS[@]}"; do
local master_port=""
if (( index % 2 == 1 )); then
master_port=${STANDALONE_PORTS[$index-1]} # 前一个端口(偶数索引)为主节点
else
master_port="" # 主节点不需要设置master_port(函数中忽略)
fi
configure_standalone_nodes "$port" "$master_port" "$index" || exit 1
((index++))
done
;;
*)
${steps[$step]} || exit 1
;;
esac
done
log "INFO" "部署成功完成"
echo -e "\n部署结果:"
echo "集群端口: ${CLUSTER_PORTS[*]}"
echo "单机端口: ${STANDALONE_PORTS[*]}"
echo "管理命令: service redis {start|stop|restart|status}"
}
# 执行主函数
main "$@"