123
This commit is contained in:
parent
371d1c134c
commit
b3b7b59284
|
|
@ -0,0 +1,295 @@
|
|||
#!/bin/bash
|
||||
# Redis哨兵集群部署与卸载脚本
|
||||
|
||||
# 颜色配置
|
||||
GREEN="\033[32m"
|
||||
YELLOW="\033[33m"
|
||||
BLUE="\033[34m"
|
||||
RED="\033[31m"
|
||||
RESET="\033[0m"
|
||||
|
||||
# 提示函数
|
||||
info() { echo -e "[ ${BLUE}INFO ${RESET}] $1"; }
|
||||
success() { echo -e "[ ${GREEN}SUCCESS ${RESET}] $1"; }
|
||||
warning() { echo -e "[ ${YELLOW}WARNING ${RESET}] $1"; }
|
||||
error() { echo -e "[ ${RED}ERROR ${RESET}] $1"; exit 1; }
|
||||
|
||||
# 基础配置
|
||||
REDIS_PORT=6379
|
||||
SENTINEL_PORT=26379
|
||||
SENTINEL_NAME="mymaster"
|
||||
LOG_DIR="/var/log/redis"
|
||||
CONFIG_DIR="/etc/redis"
|
||||
SSH_OPTS="-T -o StrictHostKeyChecking=no -o LogLevel=ERROR"
|
||||
|
||||
# 全局变量
|
||||
ACTION=""
|
||||
NODES=()
|
||||
NODE_COUNT=0
|
||||
MASTER=""
|
||||
SLAVES=()
|
||||
QUORUM=1
|
||||
PASSWORD=""
|
||||
|
||||
# 参数解析(支持--install/--uninstall、--ip、--passwd参数)
|
||||
parse_args() {
|
||||
# 初始化参数变量
|
||||
local ip_provided=0
|
||||
local passwd_provided=0
|
||||
|
||||
# 遍历解析参数
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--install|--uninstall)
|
||||
ACTION="$1"
|
||||
shift
|
||||
;;
|
||||
--ip=*)
|
||||
IP_LIST="${1#*=}"
|
||||
ip_provided=1
|
||||
shift
|
||||
;;
|
||||
--passwd=*)
|
||||
PASSWORD="${1#*=}"
|
||||
passwd_provided=1
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
error "无效参数: $1 (使用帮助:--install/--uninstall --ip=IP1,IP2,IP3... --passwd=redis密码)"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 验证必要参数
|
||||
if [[ -z "$ACTION" ]]; then
|
||||
error "必须指定参数:--install/--uninstall --ip=IP1,IP2,IP3... --passwd=redis密码"
|
||||
fi
|
||||
if [[ $ip_provided -eq 0 ]]; then
|
||||
error "必须指定参数:--install/--uninstall --ip=IP1,IP2,IP3... --passwd=redis密码"
|
||||
fi
|
||||
if [[ $passwd_provided -eq 0 ]]; then
|
||||
error "必须指定参数:--install/--uninstall --ip=IP1,IP2,IP3... --passwd=redis密码"
|
||||
fi
|
||||
|
||||
# 解析节点列表
|
||||
IFS=',' read -ra NODES <<< "$IP_LIST"
|
||||
NODE_COUNT=${#NODES[@]}
|
||||
if [[ $NODE_COUNT -lt 1 ]]; then
|
||||
error "IP列表不能为空,至少需要1个节点"
|
||||
fi
|
||||
|
||||
# 配置主从节点和投票阈值
|
||||
MASTER="${NODES[0]}"
|
||||
SLAVES=("${NODES[@]:1}")
|
||||
QUORUM=$((NODE_COUNT / 2 + 1)) # 自动计算投票阈值
|
||||
}
|
||||
|
||||
# 安装流程核心函数
|
||||
install() {
|
||||
echo -e "=========================================="
|
||||
echo -e "=== ${GREEN}Redis哨兵集群安装部署${RESET} ==="
|
||||
echo -e "=========================================="
|
||||
info "部署节点总数:$NODE_COUNT 个(${NODES[*]})"
|
||||
info "主节点:$MASTER | 从节点:${SLAVES[*]}(共${#SLAVES[@]}个)"
|
||||
info "哨兵投票阈值(QUORUM):$QUORUM(自动计算:$NODE_COUNT/2 +1)"
|
||||
info "Redis密码:******(已通过命令行传入)"
|
||||
info "预估总耗时:9-14 分钟(视节点数和网络)"
|
||||
echo -e "==========================================\n"
|
||||
|
||||
# 步骤1:初始化环境
|
||||
info "步骤1/5:初始化所有节点环境 [${YELLOW}预估:5-9 分钟${RESET}]"
|
||||
info "正在安装依赖、配置日志目录和防火墙..."
|
||||
for node in "${NODES[@]}"; do
|
||||
ssh $SSH_OPTS $node "
|
||||
apt-get update -y >/dev/null 2>&1;
|
||||
apt-get install -y redis-server redis-sentinel net-tools >/dev/null 2>&1;
|
||||
mkdir -p $CONFIG_DIR $LOG_DIR;
|
||||
chown -R redis:redis $CONFIG_DIR $LOG_DIR;
|
||||
ufw disable >/dev/null 2>&1;
|
||||
iptables -F INPUT >/dev/null 2>&1;
|
||||
" >/dev/null 2>&1 || error "节点 $node 环境初始化失败"
|
||||
done
|
||||
success "步骤1完成:所有节点环境初始化完毕\n"
|
||||
|
||||
# 步骤2:配置主节点
|
||||
info "步骤2/5:配置主节点 $MASTER [${YELLOW}预估:30秒${RESET}]"
|
||||
ssh $SSH_OPTS $MASTER "
|
||||
cat > $CONFIG_DIR/redis.conf <<EOF
|
||||
bind 0.0.0.0
|
||||
protected-mode no
|
||||
port $REDIS_PORT
|
||||
requirepass $PASSWORD
|
||||
masterauth $PASSWORD
|
||||
daemonize no
|
||||
logfile "$LOG_DIR/redis.log"
|
||||
dir /tmp
|
||||
replica-serve-stale-data yes
|
||||
replica-read-only yes
|
||||
EOF
|
||||
chown redis:redis $CONFIG_DIR/redis.conf;
|
||||
cat > /etc/systemd/system/redis.service <<EOF
|
||||
[Unit]
|
||||
Description=Redis Master Node
|
||||
After=network.target
|
||||
[Service]
|
||||
User=redis
|
||||
ExecStart=/usr/bin/redis-server $CONFIG_DIR/redis.conf
|
||||
ExecStop=/usr/bin/redis-cli -p $REDIS_PORT -a $PASSWORD shutdown
|
||||
Restart=always
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl daemon-reload;
|
||||
systemctl restart redis;
|
||||
systemctl enable redis;
|
||||
" >/dev/null 2>&1 || error "主节点 $MASTER 配置失败"
|
||||
success "步骤2完成:主节点配置完毕\n"
|
||||
|
||||
# 步骤3:配置从节点
|
||||
info "步骤3/5:配置从节点 [${YELLOW}预估:${#SLAVES[@]}*30秒${RESET}]"
|
||||
info "从节点列表:${SLAVES[*]}"
|
||||
for slave in "${SLAVES[@]}"; do
|
||||
info "正在配置从节点:$slave"
|
||||
ssh $SSH_OPTS $slave "
|
||||
cat > $CONFIG_DIR/redis.conf <<EOF
|
||||
bind 0.0.0.0
|
||||
protected-mode no
|
||||
port $REDIS_PORT
|
||||
requirepass $PASSWORD
|
||||
masterauth $PASSWORD
|
||||
daemonize no
|
||||
logfile "$LOG_DIR/redis.log"
|
||||
dir /tmp
|
||||
replica-serve-stale-data yes
|
||||
replica-read-only yes
|
||||
replicaof $MASTER $REDIS_PORT
|
||||
EOF
|
||||
chown redis:redis $CONFIG_DIR/redis.conf;
|
||||
cat > /etc/systemd/system/redis.service <<EOF
|
||||
[Unit]
|
||||
Description=Redis Slave Node
|
||||
After=network.target
|
||||
[Service]
|
||||
User=redis
|
||||
ExecStart=/usr/bin/redis-server $CONFIG_DIR/redis.conf
|
||||
ExecStop=/usr/bin/redis-cli -p $REDIS_PORT -a $PASSWORD shutdown
|
||||
Restart=always
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl daemon-reload;
|
||||
systemctl restart redis;
|
||||
systemctl enable redis;
|
||||
" >/dev/null 2>&1 || error "从节点 $slave 配置失败"
|
||||
done
|
||||
success "步骤3完成:所有从节点配置完毕\n"
|
||||
|
||||
# 步骤4:配置哨兵集群
|
||||
info "步骤4/5:配置哨兵集群 [${YELLOW}预估:$NODE_COUNT*30秒${RESET}]"
|
||||
info "哨兵投票阈值:$QUORUM(需$QUORUM个哨兵确认主节点下线才触发转移)"
|
||||
for node in "${NODES[@]}"; do
|
||||
info "正在配置哨兵节点:$node"
|
||||
ssh $SSH_OPTS $node "
|
||||
cat > $CONFIG_DIR/sentinel.conf <<EOF
|
||||
port $SENTINEL_PORT
|
||||
daemonize no
|
||||
logfile "$LOG_DIR/sentinel.log"
|
||||
sentinel monitor $SENTINEL_NAME $MASTER $REDIS_PORT $QUORUM
|
||||
sentinel down-after-milliseconds $SENTINEL_NAME 5000
|
||||
sentinel auth-pass $SENTINEL_NAME $PASSWORD
|
||||
sentinel config-epoch $SENTINEL_NAME 0
|
||||
EOF
|
||||
chown redis:redis $CONFIG_DIR/sentinel.conf;
|
||||
cat > /etc/systemd/system/redis-sentinel.service <<EOF
|
||||
[Unit]
|
||||
Description=Redis Sentinel
|
||||
After=network.target
|
||||
[Service]
|
||||
User=redis
|
||||
ExecStart=/usr/bin/redis-sentinel $CONFIG_DIR/sentinel.conf
|
||||
ExecStop=/usr/bin/redis-cli -p $SENTINEL_PORT shutdown
|
||||
Restart=always
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl daemon-reload;
|
||||
systemctl restart redis-sentinel;
|
||||
systemctl enable redis-sentinel;
|
||||
" >/dev/null 2>&1 || error "哨兵节点 $node 配置失败"
|
||||
done
|
||||
success "步骤4完成:哨兵集群配置完毕\n"
|
||||
|
||||
# 步骤5:验证集群
|
||||
info "步骤5/5:验证集群状态 [${YELLOW}预估:1分钟${RESET}]"
|
||||
info "验证主节点 $MASTER..."
|
||||
master_info=$(ssh $SSH_OPTS $MASTER "redis-cli -p $REDIS_PORT -a $PASSWORD info replication | grep -E 'role:|connected_slaves'" 2>/dev/null)
|
||||
echo " $master_info"
|
||||
|
||||
for slave in "${SLAVES[@]}"; do
|
||||
info "验证从节点 $slave..."
|
||||
slave_info=$(ssh $SSH_OPTS $slave "redis-cli -p $REDIS_PORT -a $PASSWORD info replication | grep -E 'role:|master_host|master_link_status'" 2>/dev/null)
|
||||
echo " $slave_info"
|
||||
done
|
||||
|
||||
info "验证哨兵集群..."
|
||||
sentinel_info=$(ssh $SSH_OPTS $MASTER "redis-cli -p $SENTINEL_PORT info sentinel | grep 'master0:'" 2>/dev/null)
|
||||
echo " $sentinel_info"
|
||||
|
||||
success "步骤5完成:集群验证完毕\n"
|
||||
|
||||
# 部署总结
|
||||
echo -e "=========================================="
|
||||
echo -e "=== ${GREEN}部署成功!${RESET} ==="
|
||||
echo -e "☆ 集群架构:$NODE_COUNT节点(1主${#SLAVES[@]}从$NODE_COUNT哨兵)"
|
||||
echo -e "☆ 核心配置:Redis端口$REDIS_PORT | 哨兵端口$SENTINEL_PORT | 投票阈值$QUORUM"
|
||||
echo -e "☆ 功能状态:主从同步正常 | 哨兵监控正常 | 故障转移就绪"
|
||||
echo -e "=========================================="
|
||||
}
|
||||
|
||||
# 卸载流程核心函数
|
||||
uninstall() {
|
||||
echo -e "=========================================="
|
||||
echo -e "=== ${RED}Redis哨兵集群彻底卸载${RESET} ==="
|
||||
echo -e "=========================================="
|
||||
info "卸载节点列表:${NODES[*]}(共$NODE_COUNT个节点)"
|
||||
info "Redis密码:******(已通过命令行传入)"
|
||||
info "预估耗时:3-6 分钟(清理所有资源)"
|
||||
echo -e "==========================================\n"
|
||||
|
||||
# 逐节点卸载
|
||||
for i in "${!NODES[@]}"; do
|
||||
node="${NODES[$i]}"
|
||||
idx=$((i + 1))
|
||||
info "正在卸载节点($idx/$NODE_COUNT):$node"
|
||||
ssh $SSH_OPTS $node "
|
||||
# 停止服务(使用传入的密码)
|
||||
systemctl stop redis-sentinel >/dev/null 2>&1;
|
||||
redis-cli -p $REDIS_PORT -a '$PASSWORD' shutdown >/dev/null 2>&1;
|
||||
# 禁用服务
|
||||
systemctl disable redis redis-sentinel >/dev/null 2>&1;
|
||||
# 清理文件
|
||||
rm -rf $CONFIG_DIR $LOG_DIR /etc/systemd/system/redis* >/dev/null 2>&1;
|
||||
# 卸载依赖
|
||||
apt-get remove -y redis-server redis-sentinel >/dev/null 2>&1;
|
||||
apt-get autoremove -y >/dev/null 2>&1;
|
||||
" >/dev/null 2>&1 || error "节点 $node 卸载失败"
|
||||
success "节点 $node 卸载完成"
|
||||
done
|
||||
|
||||
# 卸载总结
|
||||
echo -e "\n=========================================="
|
||||
echo -e "=== ${GREEN}卸载成功!${RESET} ==="
|
||||
echo -e "✓ 已彻底清理所有Redis相关资源:"
|
||||
echo -e " - 服务:redis-server、redis-sentinel(停止+禁用)"
|
||||
echo -e " - 文件:配置目录、日志目录、systemd服务文件"
|
||||
echo -e " - 依赖:redis相关安装包及依赖组件"
|
||||
echo -e "=========================================="
|
||||
}
|
||||
|
||||
# 执行入口
|
||||
parse_args "$@"
|
||||
if [ "$ACTION" = "--install" ]; then
|
||||
install
|
||||
elif [ "$ACTION" = "--uninstall" ]; then
|
||||
uninstall
|
||||
fi
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
#!/bin/bash
|
||||
# 多节点互相同步SSH密钥脚本(支持从文件读取IP、指定用户和密码)
|
||||
|
||||
# ==================== 颜色与样式定义 ====================
|
||||
GREEN="\033[32m" # 成功/完成
|
||||
YELLOW="\033[33m" # 进行中/提示
|
||||
RED="\033[31m" # 错误/失败
|
||||
BLUE="\033[34m" # 标题/分隔线
|
||||
CYAN="\033[36m" # 信息/说明
|
||||
NC="\033[0m" # 重置颜色
|
||||
|
||||
# 打印分隔线
|
||||
print_sep() {
|
||||
echo -e "${BLUE}------------------------------------------------${NC}"
|
||||
}
|
||||
|
||||
# ==================== 参数解析与校验 ====================
|
||||
# 解析命令行参数
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--file=*)
|
||||
IP_FILE="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
--user=*)
|
||||
SSH_USER="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
--passwd=*)
|
||||
SSH_PASSWD="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}未知参数: $1${NC}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 检查必要参数
|
||||
if [ -z "$IP_FILE" ] || [ -z "$SSH_USER" ] || [ -z "$SSH_PASSWD" ]; then
|
||||
echo -e "\n${YELLOW}用法:${NC} --file=IP文件路径 --user=用户名 --passwd=密码"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查IP文件是否存在
|
||||
if [ ! -f "$IP_FILE" ]; then
|
||||
echo -e "${RED}错误:IP文件 $IP_FILE 不存在${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 从文件读取IP列表(去重、过滤空行)
|
||||
NODES=()
|
||||
while IFS= read -r ip; do
|
||||
if [ -n "$ip" ] && ! [[ " ${NODES[@]} " =~ " $ip " ]]; then
|
||||
NODES+=("$ip")
|
||||
fi
|
||||
done < "$IP_FILE"
|
||||
|
||||
if [ ${#NODES[@]} -eq 0 ]; then
|
||||
echo -e "${RED}错误:IP文件 $IP_FILE 中未找到有效IP${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ==================== 核心功能函数 ====================
|
||||
# 安装sshpass(处理自动输入密码)
|
||||
install_sshpass() {
|
||||
echo -e "\n${YELLOW}【步骤1/3】检查并安装sshpass工具...${NC}"
|
||||
if ! command -v sshpass &> /dev/null; then
|
||||
echo -e " ${YELLOW}→ sshpass未安装,正在安装...${NC}"
|
||||
if command -v apt &> /dev/null; then
|
||||
apt update -y >/dev/null && apt install -y sshpass >/dev/null
|
||||
elif command -v yum &> /dev/null; then
|
||||
yum install -y sshpass >/dev/null
|
||||
else
|
||||
echo -e " ${RED}✗ 未找到apt或yum,无法安装sshpass${NC}"
|
||||
exit 1
|
||||
fi
|
||||
# 验证安装结果
|
||||
if command -v sshpass &> /dev/null; then
|
||||
echo -e " ${GREEN}✓ sshpass安装成功${NC}"
|
||||
else
|
||||
echo -e " ${RED}✗ sshpass安装失败,请手动安装后重试${NC}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo -e " ${GREEN}✓ sshpass已安装${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# 在目标节点生成SSH密钥(若不存在)
|
||||
generate_key() {
|
||||
local node=$1
|
||||
echo -e " ${YELLOW}→ 处理节点 $node...${NC}"
|
||||
# 远程生成密钥(静默模式)
|
||||
sshpass -p "$SSH_PASSWD" ssh -o StrictHostKeyChecking=no ${SSH_USER}@$node << EOF >/dev/null 2>&1
|
||||
if [ ! -f ~/.ssh/id_rsa ]; then
|
||||
ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa
|
||||
fi
|
||||
EOF
|
||||
# 检查执行结果
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e " ${GREEN}✓ 密钥生成完成(已存在则跳过)${NC}"
|
||||
else
|
||||
echo -e " ${RED}✗ 生成失败(可能密码错误或节点不可达)${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 获取目标节点的公钥
|
||||
get_public_key() {
|
||||
local node=$1
|
||||
# 静默获取公钥,忽略错误输出
|
||||
sshpass -p "$SSH_PASSWD" ssh -o StrictHostKeyChecking=no ${SSH_USER}@$node "cat ~/.ssh/id_rsa.pub" 2>/dev/null
|
||||
}
|
||||
|
||||
# 将公钥追加到目标节点的authorized_keys
|
||||
append_public_key() {
|
||||
local pub_key="$1"
|
||||
local target_node=$2
|
||||
# 远程执行追加公钥操作
|
||||
sshpass -p "$SSH_PASSWD" ssh -o StrictHostKeyChecking=no ${SSH_USER}@$target_node << EOF >/dev/null 2>&1
|
||||
mkdir -p ~/.ssh
|
||||
chmod 700 ~/.ssh
|
||||
if ! grep -q "$pub_key" ~/.ssh/authorized_keys 2>/dev/null; then
|
||||
echo "$pub_key" >> ~/.ssh/authorized_keys
|
||||
chmod 600 ~/.ssh/authorized_keys
|
||||
fi
|
||||
EOF
|
||||
# 反馈结果
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e " ${GREEN}→ 同步成功${NC}"
|
||||
else
|
||||
echo -e " ${RED}→ 同步失败${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ==================== 主流程 ====================
|
||||
main() {
|
||||
# 欢迎信息
|
||||
clear
|
||||
print_sep
|
||||
echo -e "${BLUE} 多节点SSH密钥互相同步工具 ${NC}"
|
||||
print_sep
|
||||
echo -e " ${CYAN}节点列表:${NC} ${NODES[*]}"
|
||||
echo -e " ${CYAN}操作用户:${NC} $SSH_USER"
|
||||
echo -e " ${CYAN}开始时间:${NC} $(date +'%Y-%m-%d %H:%M:%S')"
|
||||
print_sep
|
||||
|
||||
# 1. 安装依赖工具
|
||||
install_sshpass
|
||||
print_sep
|
||||
|
||||
# 2. 为所有节点生成密钥
|
||||
node_count=${#NODES[@]}
|
||||
echo -e "\n${YELLOW}【步骤2/3】为所有节点生成SSH密钥(共 $node_count 个节点)...${NC}"
|
||||
for i in "${!NODES[@]}"; do
|
||||
echo -e "\n 节点 ${i+1}/$node_count: ${NODES[$i]}"
|
||||
generate_key ${NODES[$i]}
|
||||
done
|
||||
print_sep
|
||||
|
||||
# 3. 收集公钥并互相同步
|
||||
echo -e "\n${YELLOW}【步骤3/3】公钥互相同步(共 $node_count 个节点)...${NC}"
|
||||
for i in "${!NODES[@]}"; do
|
||||
current_node=${NODES[$i]}
|
||||
echo -e "\n 处理第 ${i+1}/$node_count 个节点: $current_node"
|
||||
|
||||
# 获取当前节点公钥
|
||||
echo -e " ${YELLOW}→ 获取公钥...${NC}"
|
||||
pub_key=$(get_public_key $current_node)
|
||||
if [ -z "$pub_key" ]; then
|
||||
echo -e " ${RED}✗ 无法获取 $current_node 的公钥${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 同步到所有节点
|
||||
echo -e " ${YELLOW}→ 同步到所有节点...${NC}"
|
||||
for target in "${NODES[@]}"; do
|
||||
echo -e " 目标节点: $target"
|
||||
append_public_key "$pub_key" $target
|
||||
done
|
||||
done
|
||||
|
||||
# 完成提示
|
||||
print_sep
|
||||
echo -e "\n${GREEN}=== 所有操作完成!===${NC}"
|
||||
echo -e " ${CYAN}验证方法:${NC} 在任意节点执行 'ssh ${NODES[1]}' 测试免密登录"
|
||||
echo -e " ${CYAN}结束时间:${NC} $(date +'%Y-%m-%d %H:%M:%S')"
|
||||
print_sep
|
||||
}
|
||||
|
||||
# 执行主流程
|
||||
main
|
||||
Loading…
Reference in New Issue