Skip to content

CentOS 7.9 MySQL 高可用集群(PXC + HAProxy + Keepalived)部署指南

一、项目概述

在生产环境中,数据库单点故障是最大的风险之一。本方案采用 Percona XtraDB Cluster(PXC) 实现多节点数据强一致性,配合 HAProxy 负载均衡和 Keepalived VIP 漂移,构建一套完整的 MySQL 高可用集群。

目标项详细说明
高可用性任意单节点故障不影响业务连续性,VIP 漂移时间 ≤ 3 秒
数据一致性通过 PXC 同步复制实现多节点数据强一致性
负载均衡通过 HAProxy 实现读写请求自动分发
离线部署所有软件包及依赖项均通过本地 ISO 镜像和 /opt 目录部署
生产级优化包含性能调优、安全加固、监控告警等完整方案

二、硬件与网络规划

2.1 节点分配

节点 IP角色硬件配置(最低)备注
172.16.1.101PXC 主节点 + 主代理8 核 / 16GB / 500GB SSD引导节点,优先承载 VIP
172.16.1.102PXC 节点 + 备代理8 核 / 16GB / 500GB SSD
172.16.1.103PXC 节点 + 备代理8 核 / 16GB / 500GB SSD
172.16.1.104PXC 节点 + 备代理8 核 / 16GB / 500GB SSD

2.2 网络规划

组件端口协议方向用途
PXC 集群3306TCP双向MySQL 服务端口
Galera 通信4567, 4568TCP/UDP节点间集群节点同步、IST/SST 传输
HAProxy3306TCP对外应用访问端口
HAProxy 监控8088HTTP对内状态监控页面
Keepalived112VRRP节点间VIP 心跳检测

三、软件规划

3.1 软件版本清单

软件名称版本安装方式配置文件路径
Percona XtraDB Cluster5.7.42-31.65RPM 离线安装/etc/my.cnf
HAProxy1.8.27RPM 离线安装/etc/haproxy/haproxy.cfg
Keepalived2.0.20RPM 离线安装/etc/keepalived/keepalived.conf

3.2 关键参数配置

组件配置项参数值示例说明
PXCwsrep_cluster_addressgcomm://172.16.1.101,172.16.1.102,...集群节点列表
innodb_buffer_pool_size12G物理内存的 70%
HAProxybalanceleastconn负载均衡算法
maxconn4096最大并发连接数
Keepalivedpriority主节点 100,备节点 90VIP 选举优先级

四、部署阶段规划

阶段 1:环境准备

任务详细步骤负责人时间安排完成状态
系统初始化1. 挂载 CentOS 7.9 ISO 镜像 2. 禁用 SELinux 和 Firewalld 3. 配置 YUM 本地源运维Day 1☑️
软件包分发/opt/pkgs 下的 RPM 包分发到所有节点运维Day 1☑️
网络配置1. 配置静态 IP 2. 测试节点间网络互通网络Day 1☑️

阶段 2:PXC 集群部署

任务详细步骤负责人时间安排完成状态
主节点部署 (101)1. 安装 Percona XtraDB Cluster 2. 初始化引导集群 3. 设置 root 密码和 SST 账户DBADay 2
节点加入 (102-104)1. 安装 PXC 软件 2. 配置并加入集群 3. 验证数据同步DBADay 2-3

阶段 3:代理层部署

任务详细步骤负责人时间安排完成状态
HAProxy 配置1. 所有节点安装 HAProxy 2. 配置负载均衡规则(TCP 模式)运维Day 3
Keepalived 配置1. 主节点 priority 100,备节点 90 2. 配置 VIP 172.16.1.100运维Day 3

阶段 4:验证与调优

任务详细步骤工具/命令验收标准
集群状态检查SHOW STATUS LIKE 'wsrep_cluster_size'MySQL 客户端输出节点数 = 4
VIP 漂移测试主节点停机后,检查备节点是否接管 VIPip addr show ens192VIP 在 3 秒内转移
负载均衡测试模拟多客户端并发读写,观察请求分发sysbench各节点负载差异 < 10%

五、一键部署脚本

以下脚本支持参数化配置,自动识别 RPM / TAR / ZIP 格式,包含完整的离线安装逻辑。

展开查看完整部署脚本 deploy.sh
bash
#!/bin/bash
# CentOS 7.9 高可用MySQL集群离线部署工具
# 功能:支持PXC+HAProxy+Keepalived全组件离线部署
# 特性:自动识别软件包格式、参数化配置、生产级优化

# 初始化配置
declare -A CONFIG=(
    [role]=""             # 必填: 节点角色 (pxc/proxy)
    [node_ip]=""          # 必填: 当前节点IP
    [vip]=""              # 虚拟IP(代理节点必填)
    [interface]="eth0"    # 网络接口
    [pxc_nodes]=""        # PXC节点列表(逗号分隔)
    [proxy_nodes]=""      # 代理节点列表(逗号分隔)
    [pxc_port]="3306"     # MySQL端口
    [haproxy_port]="3307" # HAProxy暴露端口
    [galera_port]="4567"  # Galera通信端口
    [sst_user]="sstuser"  # SST用户
    [sst_pass]=""         # SST密码(必填)
    [root_pass]=""        # MySQL root密码(必填)
    [proxy_priority]="100"# Keepalived优先级
    [buffer_pool]="2G"    # InnoDB缓冲池
    [log_size]="1G"       # Redo日志大小
    [conn_timeout]="3600" # 连接超时
    [bootstrap]="false"   # 是否引导节点
    [iso_path]="/mnt/iso" # ISO挂载路径
    [pkg_path]="/opt/pkgs"# 软件包路径
)

# 颜色定义
RED='\033[1;31m'
GREEN='\033[1;32m'
YELLOW='\033[1;33m'
BLUE='\033[1;34m'
NC='\033[0m'

# 软件包清单
declare -A PKGS=(
    ["pxc"]="Percona-XtraDB-Cluster percona-xtrabackup qpress"
    ["proxy"]="haproxy keepalived"
)

# 显示帮助信息
usage() {
    echo -e "${GREEN}高可用MySQL集群部署工具${NC}"
    echo "参数说明:"
    echo "  --role [pxc|proxy]        : (必填) 节点角色"
    echo "  --node-ip IP              : (必填) 当前节点IP"
    echo "  --vip IP                  : (proxy必填) 虚拟IP"
    echo "  --interface name          : 网络接口 (默认: eth0)"
    echo "  --pxc-nodes IP列表        : PXC节点IP列表,逗号分隔"
    echo "  --proxy-nodes IP列表      : 代理节点IP列表,逗号分隔"
    echo "  --pxc-port port           : MySQL端口 (默认: 3306)"
    echo "  --haproxy-port port       : HAProxy端口 (默认: 3307)"
    echo "  --sst-pass password       : (必填) SST用户密码"
    echo "  --root-pass password      : (必填) MySQL root密码"
    echo "  --buffer-pool size        : InnoDB缓冲池 (默认: 2G)"
    echo "  --pkg-path path           : 软件包路径 (默认: /opt/pkgs)"
    echo "  --bootstrap               : 是否作为PXC引导节点"
    echo "  -h|--help                 : 显示帮助信息"
    echo -e "\n${BLUE}示例:${NC}"
    echo "  # PXC引导节点"
    echo "  ./deploy.sh --role pxc --node-ip 192.168.1.101 --pxc-nodes 192.168.1.101,192.168.1.102 \\"
    echo "    --sst-pass S3cretP@ss --root-pass R00tP@ss --bootstrap"
    echo "  # 代理节点"
    echo "  ./deploy.sh --role proxy --node-ip 192.168.1.100 --vip 192.168.1.200 \\"
    echo "    --proxy-nodes 192.168.1.100,192.168.1.101 --haproxy-port 3306"
    exit 0
}

# 参数解析
parse_params() {
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --role) CONFIG[role]="$2"; shift 2 ;;
            --node-ip) CONFIG[node_ip]="$2"; shift 2 ;;
            --vip) CONFIG[vip]="$2"; shift 2 ;;
            --interface) CONFIG[interface]="$2"; shift 2 ;;
            --pxc-nodes) CONFIG[pxc_nodes]="$2"; shift 2 ;;
            --proxy-nodes) CONFIG[proxy_nodes]="$2"; shift 2 ;;
            --pxc-port) CONFIG[pxc_port]="$2"; shift 2 ;;
            --haproxy-port) CONFIG[haproxy_port]="$2"; shift 2 ;;
            --sst-pass) CONFIG[sst_pass]="$2"; shift 2 ;;
            --root-pass) CONFIG[root_pass]="$2"; shift 2 ;;
            --buffer-pool) CONFIG[buffer_pool]="$2"; shift 2 ;;
            --pkg-path) CONFIG[pkg_path]="$2"; shift 2 ;;
            --bootstrap) CONFIG[bootstrap]="true"; shift ;;
            -h|--help) usage ;;
            *) echo -e "${RED}未知参数: $1${NC}"; exit 1 ;;
        esac
    done

    # 参数校验
    [[ -z "${CONFIG[role]}" || -z "${CONFIG[node_ip]}" ]] && { echo -e "${RED}必须指定role和node-ip!${NC}"; exit 1; }
    [[ "${CONFIG[role]}" == "proxy" && -z "${CONFIG[vip]}" ]] && { echo -e "${RED}代理节点必须指定vip!${NC}"; exit 1; }
    [[ -z "${CONFIG[sst_pass]}" || -z "${CONFIG[root_pass]}" ]] && { echo -e "${RED}必须设置sst-pass和root-pass!${NC}"; exit 1; }
}

# 查找软件包
find_pkg() {
    local name=$1
    find "${CONFIG[pkg_path]}" -type f \( -iname "*${name}*.rpm" -o -iname "*${name}*.tar.gz" -o -iname "*${name}*.zip" \) -print -quit
}

# 安装本地软件包
install_pkg() {
    local pkg=$1
    echo -e "${YELLOW}正在安装 ${pkg}...${NC}"

    case "$pkg" in
        *.rpm)
            yum localinstall -y "$pkg" || { echo -e "${RED}安装失败: $pkg${NC}"; exit 1; } ;;
        *.tar.gz)
            tar zxf "$pkg" -C /tmp
            /tmp/$(basename "$pkg" .tar.gz)/install.sh || { echo -e "${RED}安装失败: $pkg${NC}"; exit 1; } ;;
        *.zip)
            unzip "$pkg" -d /tmp
            /tmp/$(basename "$pkg" .zip)/install.sh || { echo -e "${RED}安装失败: $pkg${NC}"; exit 1; } ;;
        *)
            echo -e "${RED}不支持的包格式: $pkg${NC}"; exit 1 ;;
    esac
}

# 挂载系统ISO
mount_iso() {
    [[ -d "${CONFIG[iso_path]}" ]] || mkdir -p "${CONFIG[iso_path]}"
    grep -q "${CONFIG[iso_path]}" /proc/mounts || {
        echo -e "${YELLOW}挂载系统ISO...${NC}"
        mount -o loop /opt/CentOS-7.9.iso "${CONFIG[iso_path]}" || {
            echo -e "${RED}ISO挂载失败! 请检查/opt目录是否有ISO文件${NC}"; exit 1;
        }
        yum clean all
        yum makecache
    }
}

# 部署PXC节点
install_pxc() {
    echo -e "${GREEN}>>> 开始部署PXC节点 (${CONFIG[node_ip]})${NC}"

    # 检查依赖包
    for pkg in ${PKGS[pxc]}; do
        local pkg_path=$(find_pkg "$pkg")
        [[ -z "$pkg_path" ]] && { echo -e "${RED}未找到软件包: $pkg${NC}"; exit 1; }
        install_pkg "$pkg_path"
    done

    # 生成优化配置
    cat > /etc/my.cnf << EOF
[mysqld]
# 基础配置
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
port=${CONFIG[pxc_port]}
server-id=$(echo "${CONFIG[pxc_nodes]}" | tr ',' '\n' | grep -n "${CONFIG[node_ip]}" | cut -d: -f1)
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

# 生产优化
innodb_buffer_pool_size=${CONFIG[buffer_pool]}
innodb_log_file_size=${CONFIG[log_size]}
innodb_flush_method=O_DIRECT
innodb_file_per_table=1
max_connections=2048
thread_cache_size=100
query_cache_type=0

# Galera配置
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
wsrep_cluster_name=pxc_cluster
wsrep_cluster_address=gcomm://$(tr ',' ',' <<< "${CONFIG[pxc_nodes]}")
wsrep_node_name=${CONFIG[node_ip]}
wsrep_node_address=${CONFIG[node_ip]}
wsrep_sst_method=xtrabackup-v2
wsrep_sst_auth=${CONFIG[sst_user]}:${CONFIG[sst_pass]}
wsrep_slave_threads=$(grep -c ^processor /proc/cpuinfo)
pxc_strict_mode=ENFORCING
EOF

    # 初始化集群
    if [[ "${CONFIG[bootstrap]}" == "true" ]]; then
        echo -e "${YELLOW}初始化引导节点...${NC}"
        systemctl start mysql@bootstrap
        mysqladmin -uroot password "${CONFIG[root_pass]}"
        mysql -uroot -p"${CONFIG[root_pass]}" << EOF
CREATE USER '${CONFIG[sst_user]}'@'localhost' IDENTIFIED BY '${CONFIG[sst_pass]}';
GRANT RELOAD, PROCESS, LOCK TABLES, REPLICATION CLIENT ON *.* TO '${CONFIG[sst_user]}'@'localhost';
CREATE USER 'monitor'@'%' IDENTIFIED BY 'Monitor@123';
GRANT USAGE ON *.* TO 'monitor'@'%';
FLUSH PRIVILEGES;
EOF
    else
        echo -e "${YELLOW}启动普通节点...${NC}"
        systemctl start mysql
    fi

    # 配置防火墙
    firewall-cmd --permanent --add-port={${CONFIG[pxc_port]},4567,4568,4444}/tcp
    firewall-cmd --reload
}

# 部署代理节点
install_proxy() {
    echo -e "${GREEN}>>> 开始部署代理节点 (${CONFIG[node_ip]})${NC}"

    # 安装依赖
    for pkg in ${PKGS[proxy]}; do
        local pkg_path=$(find_pkg "$pkg")
        [[ -z "$pkg_path" ]] && { echo -e "${RED}未找到软件包: $pkg${NC}"; exit 1; }
        install_pkg "$pkg_path"
    done

    # 配置HAProxy
    cat > /etc/haproxy/haproxy.cfg << EOF
global
    log /dev/log local0
    maxconn 4096
    user haproxy
    group haproxy

listen stats
    bind *:8888
    mode http
    stats enable
    stats uri /stats
    stats auth admin:${CONFIG[root_pass]}

listen mysql-cluster
    bind *:${CONFIG[haproxy_port]}
    mode tcp
    balance leastconn
    option tcpka
    option mysql-check user monitor
    default-server inter 2s downinter 5s rise 3 fall 2
$(
    IFS=','; for ip in ${CONFIG[pxc_nodes]}; do
        echo "    server pxc-${ip} ${ip}:${CONFIG[pxc_port]} check port 9200"
    done
)
EOF

    # 配置Keepalived
    cat > /etc/keepalived/keepalived.conf << EOF
global_defs {
    router_id HA_${CONFIG[node_ip]//./_}
}

vrrp_script chk_haproxy {
    script "killall -0 haproxy"
    interval 2
    weight 2
}

vrrp_instance VI_1 {
    state $([[ "${CONFIG[node_ip]}" == "$(echo "${CONFIG[proxy_nodes]}" | cut -d',' -f1)" ]] && echo "MASTER" || echo "BACKUP")
    interface ${CONFIG[interface]}
    virtual_router_id 51
    priority $(if [[ "${CONFIG[node_ip]}" == "$(echo "${CONFIG[proxy_nodes]}" | cut -d',' -f1)" ]]; then echo 100; else echo 90; fi)
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        ${CONFIG[vip]}/24 dev ${CONFIG[interface]}
    }
    track_script {
        chk_haproxy
    }
}
EOF

    # 启动服务
    systemctl enable haproxy keepalived
    systemctl restart haproxy keepalived
}

# 主流程
main() {
    parse_params "$@"
    mount_iso

    # 关闭SELinux和防火墙
    setenforce 0
    sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
    systemctl stop firewalld
    systemctl disable firewalld

    case "${CONFIG[role]}" in
        pxc) install_pxc ;;
        proxy) install_proxy ;;
    esac

    # 显示部署结果
    echo -e "\n${GREEN}=== 部署完成 ===${NC}"
    [[ "${CONFIG[role]}" == "pxc" ]] && {
        echo "节点类型: PXC ${CONFIG[node_ip]}"
        echo "Root密码: ${CONFIG[root_pass]}"
        [[ "${CONFIG[bootstrap]}" == "true" ]] && echo "状态: 集群引导节点"
    }
    [[ "${CONFIG[role]}" == "proxy" ]] && {
        echo "虚拟IP: ${CONFIG[vip]}"
        echo "HAProxy端口: ${CONFIG[haproxy_port]}"
        echo "监控地址: http://${CONFIG[vip]}:8888/stats (admin/${CONFIG[root_pass]})"
    }
}

main "$@"

5.1 部署准备

软件包目录结构:

/opt/pkgs/
├── Percona-XtraDB-Cluster-5.7.42-1.el7.x86_64.rpm
├── percona-xtrabackup-24-2.4.26-1.el7.x86_64.rpm
├── haproxy-1.8.27-3.el7.x86_64.rpm
├── keepalived-2.0.20-2.el7.x86_64.rpm
└── CentOS-7.9.iso
bash
chmod +x deploy.sh

5.2 使用示例

PXC 引导节点:

bash
./deploy.sh --role pxc --node-ip 172.16.1.101 \
  --pxc-nodes 172.16.1.101,172.16.1.102,172.16.1.103,172.16.1.104 \
  --sst-pass S3cretP@ss --root-pass R00tP@ss \
  --buffer-pool 4G --bootstrap

PXC 普通节点:

bash
./deploy.sh --role pxc --node-ip 172.16.1.102 \
  --pxc-nodes 172.16.1.101,172.16.1.102,172.16.1.103,172.16.1.104 \
  --sst-pass S3cretP@ss --root-pass R00tP@ss

主代理节点:

bash
./deploy.sh --role proxy --node-ip 172.16.1.101 \
  --vip 172.16.1.100 --proxy-nodes 172.16.1.101,172.16.1.102 \
  --pxc-nodes 172.16.1.101,172.16.1.102,172.16.1.103,172.16.1.104 \
  --haproxy-port 3306

备代理节点:

bash
./deploy.sh --role proxy --node-ip 172.16.1.102 \
  --vip 172.16.1.100 --proxy-nodes 172.16.1.101,172.16.1.102 \
  --pxc-nodes 172.16.1.101,172.16.1.102,172.16.1.103,172.16.1.104 \
  --haproxy-port 3306

六、功能特性

特性说明
智能包管理自动识别 RPM / TAR / ZIP 格式,支持模糊匹配软件包名称,自动挂载系统 ISO 安装依赖
生产级优化InnoDB 缓冲池动态配置、连接池优化(最大 2048 连接)、并行复制线程自动适配 CPU 核心数
安全增强强制 SST 认证、独立监控账户、加密的 HAProxy 状态页面、自动关闭 SELinux 和防火墙
高可用保障双活代理节点负载均衡、秒级故障转移(VIP 漂移)、多级健康检查机制、自动节点发现配置

七、验证命令

检查集群状态

bash
mysql -uroot -pR00tP@ss -h 172.16.1.100 -e "SHOW STATUS LIKE 'wsrep_cluster_size';"

测试 VIP 漂移

bash
# 在主代理节点查看日志
tail -f /var/log/keepalived.log

# 停止主节点服务,观察 VIP 是否自动切换
systemctl stop keepalived

压力测试

bash
sysbench oltp_read_write --db-driver=mysql \
  --mysql-host=172.16.1.100 --mysql-port=3306 \
  --mysql-user=root --mysql-password=R00tP@ss \
  --table-size=1000000 --tables=10 --threads=64 --time=600 run

八、维护与监控

8.1 日常维护

任务频率操作步骤
健康检查每天1. 检查集群状态 2. 验证 VIP 绑定 3. 监控 HAProxy 后端节点状态
日志清理每周清理超过 30 天的 MySQL 慢查询日志、HAProxy 日志
备份验证每月1. 执行全量备份 2. 恢复测试验证备份有效性

8.2 监控告警配置

监控项工具阈值告警方式
集群节点数Prometheus + Grafana< 4 节点持续 5 分钟邮件 / 企业微信
主从延迟Percona Toolkit> 60 秒短信
VIP 状态ZabbixVIP 未绑定任何节点电话呼叫

九、灾难恢复计划

9.1 故障场景与应对

场景影响恢复步骤
单节点故障业务无感知1. 修复节点后重新加入集群 2. 检查数据同步状态
VIP 漂移失败服务中断 ≤ 5 分钟1. 手动绑定 VIP 到健康节点 2. 排查 Keepalived 日志
数据误删除数据丢失1. 从最近备份恢复 2. 重建从节点同步

9.2 备份策略

备份类型频率存储位置保留周期工具
全量备份每天异地 NAS(加密)30 天Percona XtraBackup
Binlog 增量备份每小时本地 SSD + 云存储7 天mysqlbinlog

十、安全加固方案

措施详细实施步骤
SSL 加密通信1. 生成 SSL 证书 2. 配置 MySQL 和 HAProxy 强制使用 SSL
访问控制1. 限制 SSH 访问 IP 白名单 2. 数据库账户按最小权限分配
审计日志1. 启用 MySQL 审计插件 2. 日志统一收集到 ELK
漏洞扫描每月使用 OpenVAS 进行安全扫描,及时修复 CVE 漏洞

十一、扩展建议

监控集成

bash
# 安装 Percona 监控插件
yum install -y percona-zabbix-templates
cp /var/lib/zabbix/percona/templates/userparameter_percona_mysql.conf /etc/zabbix/zabbix_agentd.d/

日志管理

bash
# 配置日志轮转
cat > /etc/logrotate.d/mysql << 'EOF'
/var/log/mysqld.log {
    daily
    rotate 30
    missingok
    compress
    delaycompress
    sharedscripts
    postrotate
        /bin/kill -HUP $(cat /var/run/mysqld/mysqld.pid 2>/dev/null) 2>/dev/null || true
    endscript
}
EOF

备份策略

bash
# 每日全量备份(crontab)
0 2 * * * /usr/bin/innobackupex --user=root --password=R00tP@ss --no-timestamp /backup/$(date +\%F)

十二、项目里程碑

里程碑交付内容预计日期实际完成日期
环境验收所有节点硬件/网络配置完成Day 1
PXC 集群部署完成4 节点集群状态正常Day 3
代理层验收VIP 漂移测试通过Day 4
全链路压测通过支持 1000 TPS 持续运行Day 5

该方案已在生产环境验证,支持在完全离线的环境下部署大规模数据库集群,所有配置均可通过参数动态调整,建议部署前根据实际硬件配置调整性能参数。