随着企业对高可用性和数据一致性的需求不断增长,MySQL的Percona XtraDB Cluster(简称MySQL-PXC)成为了许多场景下的首选解决方案。手动部署MySQL-PXC不仅繁琐且容易出错,尤其是在多节点环境下。为此,利用自动化工具如Ansible,可以大大简化部署流程,提高效率和一致性。本文将介绍如何编写Ansible Playbook,实现MySQL-PXC的自动化部署。
集群IP示例
准备工作
在开始之前,确保你具备以下条件:
一台控制节点(用于运行Ansible)
多台目标节点(用于部署MySQL-PXC,建议至少3台以确保高可用性)
所有目标节点已安装支持的Linux操作系统(如CentOS、Ubuntu)
SSH免密码登录配置(控制节点可无密码登录目标节点)
目标节点网络互通,且已关闭防火墙或已开放相关端口(3306、4567、4444、4568等)
前置环境准备
Jumpserver 控制节点上安装Ansible
部署所在服务器:
Jumpserver
安装命令:
yum -y install ansible
服务器主机名设置
涉及修改服务器:
master
slave
arbiter
Jumpserver
执行命令:
hostnamectl set-hostname <主机名称>
注:执行时将 <> 去掉!
服务器免密配置
Jumpserver 执行的命令:
返回root目录:
cd /root/
创建密钥信息:
ssh-keygen # 一路回车即可
获取公钥信息:
cat /root/.ssh/id_rsa.pub
master slave arbiter 执行的命令:
创建公钥存放目录:
mkdir /root/.ssh/
创建公钥存放文件:
touch /root/.ssh/authorized_keys
赋予公钥文件权限:
chmod 600 /root/.ssh/authorized_keys
将从 Jumpserver 上获取公钥信息写入公钥文件:
vi /root/.ssh/authorized_keys
保存退出:
ESC
--->:wq
验证是否免密成功:
在 Jumpserver 上连接 master slave arbiter
ssh -p 22 root@192.168.1.1
ssh -p 22 root@192.168.1.2
ssh -p 22 root@192.168.1.3
服务器安装 Docker 服务
部署所在服务器:
master
slave
arbiter
服务器本地域名解析填写
所需填写的服务器:
master
slave
arbiter
命令:
vi /etc/hosts
内容如下:
192.168.1.1 master
192.168.1.2 slave
192.168.1.3 arbiter
构建Ansible-Playbook目录
文件所在服务器:
Jumpserver
Mysql # 主项目目录
└─deploy_center.yaml # 定义角色的编排任务
└─hosts # Ansible 主机清单/配置
└─roles # Ansible 角色目录
└─mysql-pxc # PXC 角色
└─files # 静态文件目录
├─pxc.tar # Mysql-pxc 容器镜像包
├─mysql_check_primary.sh # Mysql-pxc 启动检测脚本
├─pxc_arbiter.sh # Mysql-pxc 最后启动节点
├─pxc_master.sh # Mysql-pxc 首要启动节点
└─pxc_slave.sh # Mysql-pxc 次要启动节点
└─tasks # 任务目录
└─main.yml # 定义角色的主要任务
编写Ansible 主机清单/配置(hosts)
Mysql # 主项目目录
└─deploy_center.yaml # 定义角色的编排任务
└─hosts # Ansible 主机清单/配置 <------当前位置
└─roles # Ansible 角色目录
└─mysql-pxc # PXC 角色
└─files # 静态文件目录
├─pxc.tar # Mysql-pxc 容器镜像包
├─mysql_check_primary.sh # Mysql-pxc 启动检测脚本
├─pxc_arbiter.sh # Mysql-pxc 最后启动节点
├─pxc_master.sh # Mysql-pxc 首要启动节点
└─pxc_slave.sh # Mysql-pxc 次要启动节点
└─tasks # 任务目录
└─main.yml # 定义角色的主要任务
文件内容(hosts)
[all:vars] # 全局配置
ansible_ssh_user=root # 指定 Ansible 连接远程主机时使用的SSH 用户名
ansible_ssh_port=22 # 指定连接远程主机时的SSH 端口号
ansible_become=yes # 表示在执行任务时,需要提升权限
ansible_become_method=sudo # 指定提升权限时用的方法
ansible_become_user=root # 表示提升权限后,以哪个用户身份执行命令
extension=false # 这不是 Ansible 的内置变量,可能是自定义变量,具体含义要看你的 playbook 或 role 如何使用它
ansible_python_interpreter=/usr/bin/python # 指定远程主机上 Ansible 应该使用的 Python 解释器路径
[master]
192.168.1.1
[slave]
192.168.1.2
[arbiter]
192.168.1.3
编写Mysql-pxc 启动检测脚本
Mysql # 主项目目录
└─deploy_center.yaml # 定义角色的编排任务
└─hosts # Ansible 主机清单/配置
└─roles # Ansible 角色目录
└─mysql-pxc # PXC 角色
└─files # 静态文件目录
├─pxc.tar # Mysql-pxc 容器镜像包
├─mysql_check_primary.sh # Mysql-pxc 启动检测脚本 <------当前位置
├─pxc_arbiter.sh # Mysql-pxc 最后启动节点
├─pxc_master.sh # Mysql-pxc 首要启动节点
└─pxc_slave.sh # Mysql-pxc 次要启动节点
└─tasks # 任务目录
└─main.yml # 定义角色的主要任务
文件内容(mysql_check_primary.sh)
# 等待节点进入primary
echo ""
echo "start:`date +%Y-%m-%d_%H_%M_%S`"
count=0
max_count=$1
if [[ $max_count -eq "" ]];
then
echo "max count not found, set 1";
max_count=1
fi
if [[ $max_count -ge 0 ]];
then
echo "valid max_count: $max_count";
else
echo "bad max_count: $max_count";
exit 1
fi
for ((i=0; i<40; i++));
do
echo "for: i=$i"
# 密码
password='XiaoPaLuLiuLangJi';
echo "password from env: $password";
# 执行
container_id=`sudo docker ps|grep mysql|awk -F ' ' '{print $1}'`;
echo "container_id: $container_id";
mysql_ret=`sudo docker exec -i $container_id /usr/bin/mysql -uroot -p"$password" -e "show status like 'wsrep_cluster_status'" 2>&1`;
echo "mysql_ret: $mysql_ret";
ret=`echo $mysql_ret|grep Primary|wc -l`;
echo "ret: $ret";
# 判断
if [[ $ret -eq 1 ]];
then
((count++));
echo "count: $count";
if [ $count -ge $max_count ];
then
echo "break";
break
fi
else
count=0
echo "count: $count";
fi
# sleep
echo "sleep 5";
sleep 5;
done
编写 Mysql-pxc 首要启动节点脚本
Mysql # 主项目目录
└─deploy_center.yaml # 定义角色的编排任务
└─hosts # Ansible 主机清单/配置
└─roles # Ansible 角色目录
└─mysql-pxc # PXC 角色
└─files # 静态文件目录
├─pxc.tar # Mysql-pxc 容器镜像包
├─mysql_check_primary.sh # Mysql-pxc 启动检测脚本
├─pxc_arbiter.sh # Mysql-pxc 最后启动节点
├─pxc_master.sh # Mysql-pxc 首要启动节点 <------当前位置
└─pxc_slave.sh # Mysql-pxc 次要启动节点
└─tasks # 任务目录
└─main.yml # 定义角色的主要任务
文件内容(pxc_master.sh)
#!/bin/bash
# 停止初始的容器
docker rm -f mysql-pxc
# 获取主机名
hostname=$(cat /etc/hostname)
# 写入配置文件
cat << EOF > /data/docker/dockerData/volumes/mysql-conf/_data/node.cnf
[mysqld]
pxc_strict_mode=PERMISSIVE
ignore-db-dir=lost+found
datadir=/var/lib/mysql
socket=/tmp/mysql.sock
skip-host-cache
group_concat_max_len = 10240000
max_connections=65535
max_allowed_packet=500M
wait_timeout=2880000
interactive_timeout=2880000
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
thread_cache_size=64
#coredumper
#server_id=0
binlog_format=ROW
default_storage_engine=InnoDB
innodb_flush_log_at_trx_commit = 0
innodb_flush_method = O_DIRECT
innodb_file_per_table = 1
innodb_autoinc_lock_mode=2
bind_address = 0.0.0.0
wsrep_slave_threads=2
wsrep_cluster_address=gcomm://master,slave,arbiter
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
wsrep_cluster_name=PXC
wsrep_node_address=master
wsrep_node_incoming_address=$hostname:3306
wsrep_sst_method=xtrabackup-v2
wsrep_sst_auth='xtrabackup:XiaoPaLuLiuLangJi' # 同步用户与同步密码
[client]
socket=/tmp/mysql.sock
[sst]
progress=/var/lib/mysql/sst_in_progress
EOF
# 赋权
mkdir -p /data/docker/dockerData/volumes/mysql-conf/_data/conf.d
chown 1001:docker /data/docker/dockerData/volumes/mysql-conf/_data/conf.d
chown 1001:docker /data/docker/dockerData/volumes/mysql-conf/_data/node.cnf
chmod 644 /data/docker/dockerData/volumes/mysql-conf/_data/node.cnf
# 启动容器
docker run -itd --privileged=true --restart=always --name mysql-pxc \
-v mysql-data:/var/lib/mysql \
-v mysql-conf:/etc/mysql \
-v mysql-back:/data \
-e MYSQL_ROOT_PASSWORD=XiaoPaLuLiuLangJi \
-e CLUSTER_NAME=PXC \
-e CLUSTER_JOIN=master,slave,arbiter \
-e XTRABACKUP_PASSWORD=XiaoPaLuLiuLangJi \
--net=host \
pxc:latest
编写 Mysql-pxc 次要启动节点
Mysql # 主项目目录
└─deploy_center.yaml # 定义角色的编排任务
└─hosts # Ansible 主机清单/配置
└─roles # Ansible 角色目录
└─mysql-pxc # PXC 角色
└─files # 静态文件目录
├─pxc.tar # Mysql-pxc 容器镜像包
├─mysql_check_primary.sh # Mysql-pxc 启动检测脚本
├─pxc_arbiter.sh # Mysql-pxc 最后启动节点
├─pxc_master.sh # Mysql-pxc 首要启动节点
└─pxc_slave.sh # Mysql-pxc 次要启动节点 <------当前位置
└─tasks # 任务目录
└─main.yml # 定义角色的主要任务
文件内容(pxc_slave.sh)
#!/bin/bash
# 获取主机名
hostname=$(cat /etc/hostname)
# 写入配置文件
cat << EOF > /data/docker/dockerData/volumes/mysql-conf/_data/node.cnf
[mysqld]
pxc_strict_mode=PERMISSIVE
ignore-db-dir=lost+found
datadir=/var/lib/mysql
socket=/tmp/mysql.sock
skip-host-cache
group_concat_max_len = 10240000
max_connections=65535
max_allowed_packet=500M
wait_timeout=2880000
interactive_timeout=2880000
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
thread_cache_size=64
#coredumper
#server_id=0
binlog_format=ROW
default_storage_engine=InnoDB
innodb_flush_log_at_trx_commit = 0
innodb_flush_method = O_DIRECT
innodb_file_per_table = 1
innodb_autoinc_lock_mode=2
bind_address = 0.0.0.0
wsrep_slave_threads=2
wsrep_cluster_address=gcomm://master,slave,arbiter
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
wsrep_cluster_name=PXC
wsrep_node_address=slave
wsrep_node_incoming_address=$hostname:3306
wsrep_sst_method=xtrabackup-v2
wsrep_sst_auth='xtrabackup:XiaoPaLuLiuLangJi' # 同步用户与同步密码
[client]
socket=/tmp/mysql.sock
[sst]
progress=/var/lib/mysql/sst_in_progress
EOF
# 赋权
mkdir -p /data/docker/dockerData/volumes/mysql-conf/_data/conf.d
chown 1001:docker /data/docker/dockerData/volumes/mysql-conf/_data/conf.d
chown 1001:docker /data/docker/dockerData/volumes/mysql-conf/_data/node.cnf
chmod 644 /data/docker/dockerData/volumes/mysql-conf/_data/node.cnf
# 启动容器
docker run -itd --privileged=true --restart=always --name mysql-pxc \
-v /etc/hosts:/etc/hosts \
-v mysql-data:/var/lib/mysql \
-v mysql-conf:/etc/mysql \
-v mysql-back:/data \
-e MYSQL_ROOT_PASSWORD=XiaoPaLuLiuLangJi \
-e CLUSTER_NAME=PXC \
-e CLUSTER_JOIN=master,slave,arbiter \
-e XTRABACKUP_PASSWORD=XiaoPaLuLiuLangJi \
--net=host \
pxc:latest
编写 Mysql-pxc 最后启动节点
Mysql # 主项目目录
└─deploy_center.yaml # 定义角色的编排任务
└─hosts # Ansible 主机清单/配置
└─roles # Ansible 角色目录
└─mysql-pxc # PXC 角色
└─files # 静态文件目录
├─pxc.tar # Mysql-pxc 容器镜像包
├─mysql_check_primary.sh # Mysql-pxc 启动检测脚本
├─pxc_arbiter.sh # Mysql-pxc 最后启动节点 <------当前位置
├─pxc_master.sh # Mysql-pxc 首要启动节点
└─pxc_slave.sh # Mysql-pxc 次要启动节点
└─tasks # 任务目录
└─main.yml # 定义角色的主要任务
文件内容(pxc_arbiter.sh)
#!/bin/bash
# 获取主机名
hostname=$(cat /etc/hostname)
# 写入配置文件
cat << EOF > /data/docker/dockerData/volumes/mysql-conf/_data/node.cnf
[mysqld]
pxc_strict_mode=PERMISSIVE
ignore-db-dir=lost+found
datadir=/var/lib/mysql
socket=/tmp/mysql.sock
skip-host-cache
group_concat_max_len = 10240000
max_connections=65535
max_allowed_packet=500M
wait_timeout=2880000
interactive_timeout=2880000
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
thread_cache_size=64
#coredumper
#server_id=0
binlog_format=ROW
default_storage_engine=InnoDB
innodb_flush_log_at_trx_commit = 0
innodb_flush_method = O_DIRECT
innodb_file_per_table = 1
innodb_autoinc_lock_mode=2
bind_address = 0.0.0.0
wsrep_slave_threads=2
wsrep_cluster_address=gcomm://master,slave,arbiter
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
wsrep_cluster_name=PXC
wsrep_node_address=arbiter
wsrep_node_incoming_address=$hostname:3306
wsrep_sst_method=xtrabackup-v2
wsrep_sst_auth='xtrabackup:XiaoPaLuLiuLangJi'
[client]
socket=/tmp/mysql.sock
[sst]
progress=/var/lib/mysql/sst_in_progress
EOF
# 赋权
mkdir -p /data/docker/dockerData/volumes/mysql-conf/_data/conf.d
chown 1001:docker /data/docker/dockerData/volumes/mysql-conf/_data/conf.d
chown 1001:docker /data/docker/dockerData/volumes/mysql-conf/_data/node.cnf
chmod 644 /data/docker/dockerData/volumes/mysql-conf/_data/node.cnf
# 启动容器
docker run -itd --privileged=true --restart=always --name mysql-pxc \
-v /etc/hosts:/etc/hosts \
-v mysql-data:/var/lib/mysql \
-v mysql-conf:/etc/mysql \
-v mysql-back:/data \
-e MYSQL_ROOT_PASSWORD=XiaoPaLuLiuLangJi \
-e CLUSTER_NAME=PXC \
-e CLUSTER_JOIN=master,slave,arbiter \
-e XTRABACKUP_PASSWORD=XiaoPaLuLiuLangJi \
--net=host \
pxc:latest
定义角色主要任务
Mysql # 主项目目录
└─deploy_center.yaml # 定义角色的编排任务
└─hosts # Ansible 主机清单/配置
└─roles # Ansible 角色目录
└─mysql-pxc # PXC 角色
└─files # 静态文件目录
├─pxc.tar # Mysql-pxc 容器镜像包
├─mysql_check_primary.sh # Mysql-pxc 启动检测脚本
├─pxc_arbiter.sh # Mysql-pxc 最后启动节点
├─pxc_master.sh # Mysql-pxc 首要启动节点
└─pxc_slave.sh # Mysql-pxc 次要启动节点
└─tasks # 任务目录
└─main.yml # 定义角色的主要任务 <------当前位置
文件内容(main.yml)
- name: confirm System type #判断服务器类型,获取对应类型的文件
command: "arch"
register: type #执行结果存放
# 复制镜像包
- name: Copy pxc.tar to hosts
copy:
src: pxc.tar
dest: /root/pxc.tar
# 导入mysql_pxc镜像
- name: Import docker_mysql_pxc image
become: true
shell: docker load < /root/pxc.tar
# 复制检测脚本
- name: Copy mysql_check_primary.sh to hosts
copy:
src: mysql_check_primary.sh
dest: /root/mysql_check_primary.sh
# 前置条件
- name: Preconditions
become: true
shell: |
docker stop mysql-pxc 2>/dev/null || true &&
docker rm -f mysql-pxc 2>/dev/null || true &&
docker volume rm -f mysql-data &&
docker volume rm -f mysql-conf &&
docker volume rm -f mysql-back &&
rm -rf /data/docker/dockerData/volume/mysql-back &&
rm -rf /data/docker/dockerData/volume/mysql-data &&
rm -rf /data/docker/dockerData/volume/mysql-conf &&
docker volume create mysql-data &&
docker volume create mysql-conf &&
docker volume create mysql-back
# ---------------------------------------
# 启动初始化主服务等待加入集群
- name: Start initializing the main service and wait to join the cluster
become: true
shell: docker run -itd --privileged=true --restart=always --name mysql-pxc -v mysql-data:/var/lib/mysql -v mysql-conf:/etc/mysql -v mysql-back:/data -e MYSQL_ROOT_PASSWORD=XiaoPaLuLiuLangJi -e CLUSTER_NAME=PXC -e XTRABACKUP_PASSWORD=XiaoPaLuLiuLangJi --net=host pxc:latest
when: hostnameresult.stdout | regex_search('master')
# 等待启动完成
- name: pxc-build-master-wait
shell: sh /root/mysql_check_primary.sh 6 >> /root/check_primary.log
when: hostnameresult.stdout | regex_search('master')
# ---------------------------------------
# 复制从脚本并执行
- name: Copy pxc_slave.sh to hosts
copy:
src: pxc_slave.sh
dest: /root/pxc_slave.sh
when: hostnameresult.stdout | regex_search('slave')
# 修改从容器指向IP
- name: Modify from container to IP docker_mysql_pxc_slave
become: true
shell: |
masterIP=$(cat /etc/hosts | grep master| awk '{print $1}' ) && sed -i "s#master#$masterIP#g" /root/pxc_slave.sh
slaveIP=$(cat /etc/hosts | grep slave| awk '{print $1}' ) && sed -i "s#slave#$slaveIP#g" /root/pxc_slave.sh
arbiterIP=$(cat /etc/hosts | grep arbiter| awk '{print $1}' ) && sed -i "s#arbiter#$arbiterIP#g" /root/pxc_slave.sh
when: hostnameresult.stdout | regex_search('slave')
# 启动从容器
- name: Install docker_mysql_pxc_slave
become: true
shell: sh /root/pxc_slave.sh
when: hostnameresult.stdout | regex_search('slave')
# 等待启动完成
- name: pxc-build-slave-wait
shell: sh /root/mysql_check_primary.sh 6 >> /root/check_primary.log
when: hostnameresult.stdout | regex_search('slave')
# ---------------------------------------
# 复制仲裁脚本并执行
- name: Copy pxc_arbiter.sh to hosts
copy:
src: pxc_arbiter.sh
dest: /root/pxc_arbiter.sh
when: hostnameresult.stdout | regex_search('arbiter')
# 修改仲裁容器指向IP
- name: Modify from container to IP docker_mysql_pxc_arbiter
become: true
shell: |
masterIP=$(cat /etc/hosts | grep master| awk '{print $1}' ) && sed -i "s#master#$masterIP#g" /root/pxc_arbiter.sh
slaveIP=$(cat /etc/hosts | grep slave| awk '{print $1}' ) && sed -i "s#slave#$slaveIP#g" /root/pxc_arbiter.sh
arbiterIP=$(cat /etc/hosts | grep arbiter| awk '{print $1}' ) && sed -i "s#arbiter#$arbiterIP#g" /root/pxc_arbiter.sh
when: hostnameresult.stdout | regex_search('arbiter')
# 启动仲裁容器
- name: Install docker_mysql_pxc_arbiter
become: true
shell: sh /root/pxc_arbiter.sh
when: hostnameresult.stdout | regex_search('arbiter')
# 等待启动完成
- name: pxc-build-arbiter-wait
shell: sh /root/mysql_check_primary.sh 6 >> /root/check_primary.log
when: hostnameresult.stdout | regex_search('arbiter')
# -------------------------------------
# 复制主脚本并执行
- name: Copy pxc_master.sh to hosts
copy:
src: pxc_master.sh
dest: /root/pxc_master.sh
when: hostnameresult.stdout | regex_search('master')
# 修改主容器指向IP
- name: Modify from container to IP docker_mysql_pxc_master
become: true
shell: |
masterIP=$(cat /etc/hosts | grep master| awk '{print $1}' ) && sed -i "s#master#$masterIP#g" /root/pxc_master.sh
slaveIP=$(cat /etc/hosts | grep slave| awk '{print $1}' ) && sed -i "s#slave#$slaveIP#g" /root/pxc_master.sh
arbiterIP=$(cat /etc/hosts | grep arbiter| awk '{print $1}' ) && sed -i "s#arbiter#$arbiterIP#g" /root/pxc_master.sh
when: hostnameresult.stdout | regex_search('master')
# 启动主容器
- name: Install docker_mysql_pxc_master
become: true
shell: sh /root/pxc_master.sh
when: hostnameresult.stdout | regex_search('master')
# 等待启动完成
- name: pxc-build-master-wait
shell: sh /root/mysql_check_primary.sh 6 >> /root/check_primary.log
when: hostnameresult.stdout | regex_search('master')
定义角色编排顺序
Mysql # 主项目目录
└─deploy_center.yaml # 定义角色的编排任务 <------当前位置
└─hosts # Ansible 主机清单/配置
└─roles # Ansible 角色目录
└─mysql-pxc # PXC 角色
└─files # 静态文件目录
├─pxc.tar # Mysql-pxc 容器镜像包
├─mysql_check_primary.sh # Mysql-pxc 启动检测脚本
├─pxc_arbiter.sh # Mysql-pxc 最后启动节点
├─pxc_master.sh # Mysql-pxc 首要启动节点
└─pxc_slave.sh # Mysql-pxc 次要启动节点
└─tasks # 任务目录
└─main.yml # 定义角色的主要任务
文件内容(deploy_center.yaml)
# 安装Mysql主备功能服务
- hosts:
- master
- slave
- arbiter
gather_facts: no
remote_user: root
roles:
- mysql-pxc
安装Mysql-Pxc
Mysql # 主项目目录 <------当前位置
└─deploy_center.yaml # 定义角色的编排任务
└─hosts # Ansible 主机清单/配置
└─roles # Ansible 角色目录
└─mysql-pxc # PXC 角色
└─files # 静态文件目录
├─pxc.tar # Mysql-pxc 容器镜像包
├─mysql_check_primary.sh # Mysql-pxc 启动检测脚本
├─pxc_arbiter.sh # Mysql-pxc 最后启动节点
├─pxc_master.sh # Mysql-pxc 首要启动节点
└─pxc_slave.sh # Mysql-pxc 次要启动节点
└─tasks # 任务目录
└─main.yml # 定义角色的主要任务
执行命令
# 使用ansbile-playbook调用编排文件
ansible-playbook -i ./hosts -v deploy_center.yaml