From b3b7b5928469f2c078fbf9cbf0faee8368231194 Mon Sep 17 00:00:00 2001 From: joy Date: Thu, 20 Nov 2025 10:41:39 +0800 Subject: [PATCH] 123 --- scripts/redis.sh | 295 +++++++++++++++++++++++++++++++++++++++++++++++ scripts/ssh.sh | 195 +++++++++++++++++++++++++++++++ 2 files changed, 490 insertions(+) create mode 100644 scripts/redis.sh create mode 100644 scripts/ssh.sh diff --git a/scripts/redis.sh b/scripts/redis.sh new file mode 100644 index 0000000..698eba4 --- /dev/null +++ b/scripts/redis.sh @@ -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 < /etc/systemd/system/redis.service </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 < /etc/systemd/system/redis.service </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 < /etc/systemd/system/redis-sentinel.service </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 diff --git a/scripts/ssh.sh b/scripts/ssh.sh new file mode 100644 index 0000000..6772882 --- /dev/null +++ b/scripts/ssh.sh @@ -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