Docker 部署 PXC 5.7 单机集群

前言

本文主要介绍如何使用 Docker 部署 Percona XtraDB Cluster 5.7 集群(单机三个节点),并基于 Haproxy + Keepalived 实现双机热备方案。

系列教程

官方文档

部署准备

整体部署架构

提示

PXC + Haproxy + Keepalived 双机热备架构的介绍可以阅读 这里 的内容。

整体部署规划

  • 软件版本说明
软件版本描述
PXC 镜像 5.7.43
Haproxy 镜像 2.8.3
Keepalived 服务 1.3.5
  • Docker 部署 PXC 集群(三个节点)+ Haproxy(两个节点 - 主备)
节点名称容器名称容器 IP 容器数据卷容器数据卷目录操作系统
PXC 节点一 pxc-node1172.30.0.2pxc-v1/var/lib/docker/volumes/pxc-v1/_data/Debian 11
PXC 节点二 pxc-node2172.30.0.3pxc-v2/var/lib/docker/volumes/pxc-v2/_data/Debian 11
PXC 节点三 pxc-node3172.30.0.4pxc-v3/var/lib/docker/volumes/pxc-v3/_data/Debian 11
Haproxy 节点一 haproxy-node1172.30.0.5haproxy-v1/var/lib/docker/volumes/haproxy-v1/_data/Debian 11
Haproxy 节点二 haproxy-node2172.30.0.6haproxy-v2/var/lib/docker/volumes/haproxy-v2/_data/Debian 11
  • Keepalived 服务器安装
服务器名称服务器角色虚拟 IP 说明操作系统
Keepalived 服务器一主服务器(MASTER)172.30.0.7 安装在 Haproxy 节点一的容器内(haproxy-node1Debian 11
Keepalived 服务器二备服务器(BACKUP)172.30.0.7 安装在 Haproxy 节点二的容器内(haproxy-node2Debian 11
Keepalived 服务器三 192.168.1.160 安装在宿主机内,为了实现外网可以正常访问 Docker 容器内的虚拟 IPCentos 7

概念介绍

虚拟 IP 概念介绍

Linux 系统支持在一个网卡中定义多个 IP 地址,并将这些地址分配给多个应用程序,这些地址就是虚拟 IP,基于 Haproxy + Keepalived 的双机热备方案最关键的技术就是虚拟 IP。

Keepalived 利用了上述 Linux 系统的特性,让多台服务器去获取同一个虚拟 IP,获取到的服务器将虚拟 IP 绑定到自身的网卡,然后接受外部流量;没有抢占到虚拟 IP 的则作为备用服务器,并进行心跳检测,一旦检测到主服务器宕机,则立刻抢占虚拟 IP。

负载均衡中间件选型

PXC 集群有三个节点,如果每次都是第一个节点处理请求,那么就存在负载高、性能差、其他节点利用率不高等问题,所以更优的方案是对不同的节点都进行请求。这就需要有负载均衡中间件负责请求转发,主流的中间件有 Nginx、Haproxy 等,两者都支持 TCP/IP 协议,Nginx 额外支持插件,Haproxy 属于是老牌的中间件。在数据库集群的负载均衡领域,Haproxy 会使用的要多一些。不同中间件的对比如下图所示:

Нaproxy 双机热备方案介绍

  • Docker 创建两个 Haproxy 容器,每个容器中都单独安装 Keepalived 服务器
  • 两个 Keepalived 服务器会争抢 Docker 容器内的虚拟 IP,一个抢到后,另一个没抢到就会等待,抢到的作为主服务器,没抢到的作为备用服务器
  • 两个 Keepalived 服务器之间会进行心跳检测,如果备用服务器没有接收到主服务器的心跳响应,则说明主服务器发生故障,那么备用服务器就可以抢占虚拟 IP,继续工作
  • 客户端向虚拟 IP 发送数据库请求,当其中一个 Haproxy 节点宕机后,还有另一个 Haproxy 节点可以接替工作
  • 由于 Docker 容器内的虚拟 IP 不能被外网直接访问,所以需要借助宿主机里的 Keepalived 服务映射成外网可以访问的虚拟 IP

PXC 集群搭建

在本节中,将通过 Docker 部署 PXC 5.7 集群,其中包含三个集群节点。

提示

Percona Xtradb Cluster (PXC) 的详细介绍请看 这里 的教程。

部署规划

节点名称容器名称容器 IP 容器数据卷容器数据卷目录操作系统
PXC 节点一 pxc-node1172.30.0.2pxc-v1/var/lib/docker/volumes/pxc-v1/_data/Debian 11
PXC 节点二 pxc-node2172.30.0.3pxc-v2/var/lib/docker/volumes/pxc-v2/_data/Debian 11
PXC 节点三 pxc-node3172.30.0.4pxc-v3/var/lib/docker/volumes/pxc-v3/_data/Debian 11

提示

  • Percona XtraDB Cluster 要求最小的集群大小是 3 个节点。
  • 建议尽可能地控制 PXC 集群的规模,节点越多,数据同步速度越慢。
  • PXC 存在硬件配置短板限制,即整个集群的写吞吐量受最弱节点的限制。因此所有 PXC 节点的硬件配置要一致,否则如果一个节点变慢,整个集群会跟着变慢。

集群部署

  • 拉取镜像,官方文档详见 Docker Hub,镜像的各个版本号可以从 这里 查看
1
2
3
4
5
# 拉取镜像
sudo docker pull percona/percona-xtradb-cluster:5.7.43

# 镜像打上标签
sudo docker tag percona/percona-xtradb-cluster:5.7.43 pxc
  • 创建数据卷,存储路径为 /var/lib/docker/volumes/
1
2
3
4
5
6
7
8
9
10
# 创建数据卷
sudo docker volume create --name pxc-v1
sudo docker volume create --name pxc-v2
sudo docker volume create --name pxc-v3

# 查看所有数据卷
sudo docker volume ls

# 查看数据卷的详细信息
sudo docker volume inspect pxc-v1
  • 创建网络(专用网段)
1
2
3
4
5
# 创建网络
sudo docker network create --subnet=172.30.0.0/24 pxc-network

# 查看所有网络
sudo docker network ls
  • 创建容器,XTRABACKUP_PASSWORD 是 XtraBackup 工具备份数据库数据的密码
1
2
3
4
5
6
7
8
# 创建第一个节点
sudo docker create -p 13306:3306 -v pxc-v1:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -e XTRABACKUP_PASSWORD=123456 -e CLUSTER_NAME=pxc --name=pxc-node1 --net=pxc-network --ip 172.30.0.2 pxc

# 创建第二个节点(增加了CLUSTER_JOIN参数)
sudo docker create -p 13307:3306 -v pxc-v2:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -e XTRABACKUP_PASSWORD=123456 -e CLUSTER_NAME=pxc -e CLUSTER_JOIN=pxc-node1 --name=pxc-node2 --net=pxc-network --ip 172.30.0.3 pxc

# 创建第三个节点(增加了CLUSTER_JOIN参数)
sudo docker create -p 13308:3306 -v pxc-v3:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -e XTRABACKUP_PASSWORD=123456 -e CLUSTER_NAME=pxc -e CLUSTER_JOIN=pxc-node1 --name=pxc-node3 --net=pxc-network --ip 172.30.0.4 pxc
  • 启动节点一的容器(必须先启动节点一,等待 PXC 集群初始化操作执行完成后,再启动节点二和节点三的容器)
1
2
3
4
5
# 启动节点一
sudo docker start pxc-node1

# 查看节点一的日志信息
sudo docker logs -f pxc-node1
  • 启动节点二的容器(必须等待节点一启动成功后再启动)
1
2
3
4
5
# 启动节点二
sudo docker start pxc-node2

# 查看节点二的日志信息
sudo docker logs -f pxc-node2
  • 启动节点三的容器(必须等待节点一启动成功后再启动)
1
2
3
4
5
# 启动节点三
sudo docker start pxc-node3

# 查看节点三的日志信息
sudo docker logs -f pxc-node3

集群验证

登录 MySQL

  • 登录 MySQL 数据库,可以是 PXC 集群中的任意一个节点
1
sudo docker exec -it pxc-node1 /usr/bin/mysql -uroot -p123456

查看集群状态

  • 在 PXC 集群的任意一个节点上,执行以下 SQL 语句来查看集群状态
1
show status like 'wsrep_cluster%';

创建数据库表

  • 在 PXC 集群的任意一个节点上,执行以下 SQL 语句,然后观察其他节点是否同步创建了数据库和表。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- 创建数据库
CREATE DATABASE `percona` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 切换数据库
USE `percona`;

-- 创建用户表
CREATE TABLE `acl_user` (
`id` char(19) NOT NULL COMMENT '会员id',
`username` varchar(20) NOT NULL DEFAULT '' COMMENT '微信openid',
`password` varchar(32) NOT NULL DEFAULT '' COMMENT '密码',
`nick_name` varchar(50) DEFAULT NULL COMMENT '昵称',
`salt` varchar(255) DEFAULT NULL COMMENT '用户头像',
`token` varchar(100) DEFAULT NULL COMMENT '用户签名',
`is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`gmt_modified` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

-- 插入用户数据
INSERT INTO `acl_user` VALUES ('1','admin','96e79218965eb72c92a549dd5a330112','admin','',NULL,0,'2018-05-01 10:39:47','2018-05-01 10:39:47');

Haproxy 负载均衡

在本节中,将介绍如何使用 Haproxy 作为负载均衡服务器,将请求转发给 PXC 集群中的各个节点。特别注意,在执行以下操作之前,请先启动 PXC 集群,并确保集群可以正常运行。

部署规划

节点名称容器名称容器 IP 容器数据卷容器数据卷目录操作系统
Haproxy 节点一 haproxy-node1172.30.0.5haproxy-v1/var/lib/docker/volumes/haproxy-v1/_data/Debian 11

整体架构

当使用 PXC 集群的单个节点处理所有请求时,存在负载高、性能差、其他节点利用率不高等问题。

使用 Haproxy 做负载均衡,将请求均匀地分配给 PXC 集群中的每一个节点,单节点负载低、性能高,且所有节点都能利用起来。

创建用户

在本节中,将创建 MySQL 用户,Haproxy 后续会使用这个用户对 PXC 集群节点进行心跳检测。

  • 登录 MySQL 数据库,可以是 PXC 集群中的任意一个节点
1
sudo docker exec -it pxc-node1 /usr/bin/mysql -uroot -p123456
  • 创建数据库用户 haproxy,不指定密码和权限,只允许远程访问
1
2
3
4
5
6
7
8
# 创建用户
CREATE USER 'haproxy'@'%';

# 刷新权限信息
FLUSH PRIVILEGES;

# 查看用户列表
SELECT user, host FROM mysql.user;

应用部署

1
2
3
4
5
# 拉取镜像
sudo docker pull haproxy:2.8.3

# 镜像打上标签
sudo docker tag haproxy:2.8.3 haproxy
  • 创建数据卷,存储路径为 /var/lib/docker/volumes/
1
2
3
4
5
6
7
8
# 创建数据卷
sudo docker volume create --name haproxy-v1

# 查看所有数据卷
sudo docker volume ls

# 查看数据卷的详细信息
sudo docker volume inspect haproxy-v1
  • 创建 Haproxy 的配置文件
1
2
3
4
5
# 在数据卷目录下创建Haproxy的配置文件
sudo touch /var/lib/docker/volumes/haproxy-v1/_data/haproxy.cfg

# 编辑Haproxy的配置文件,添加以下内容
sudo vi /var/lib/docker/volumes/haproxy-v1/_data/haproxy.cfg
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
global
# 工作目录
# chroot /usr/local/etc/haproxy
# 日志文件,使用rsyslog服务中local5日志设备(/var/log/local5),等级info
log 127.0.0.1 local5 info
# 以守护进程运行
daemon

defaults
log global
mode http
# 日志格式
option httplog
# 日志中不记录负载均衡的心跳检测记录
option dontlognull
# 连接超时(毫秒)
timeout connect 5000
# 客户端超时(毫秒)
timeout client 50000
# 服务器超时(毫秒)
timeout server 50000

# 监控界面
listen admin_stats
# 监控界面的访问的IP和端口
bind 0.0.0.0:8888
# 访问协议
mode http
# URI相对地址
stats uri /dbs
# 统计报告格式
stats realm Global\ statistics
# 登陆账户信息
stats auth admin:admin
# 数据库负载均衡
listen proxy-pxc
# 访问的IP和端口
bind 0.0.0.0:3306
# 网络协议
mode tcp
# 负载均衡算法(轮询算法)
# 轮询算法:roundrobin
# 权重算法:static-rr
# 最少连接算法:leastconn
# 请求源IP算法:source
balance roundrobin
# 日志格式
option tcplog
# Haproxy使用MySQL的haproxy账户对数据库进行心跳检测
option mysql-check user haproxy
server PXC_Node_1 172.30.0.2:3306 check weight 1 maxconn 2000
server PXC_Node_2 172.30.0.3:3306 check weight 1 maxconn 2000
server PXC_Node_3 172.30.0.4:3306 check weight 1 maxconn 2000
# 使用Keepalived检测死链
option tcpka

特别注意

在上述 Haproxy 的配置文件中,不能启用 chroot /usr/local/etc/haproxy,否则 Haproxy 容器会启动失败(日志信息如下),暂时不清楚其原因。

1
2
3
4
5
6
7
8
9
10
[NOTICE]   (1) : New worker (8) forked
[NOTICE] (1) : Loading success.
[NOTICE] (8) : haproxy version is 2.8.3-86e043a
[NOTICE] (8) : path to executable is /usr/local/sbin/haproxy
[ALERT] (8) : [haproxy.main()] Cannot chroot(/usr/local/etc/haproxy).
[NOTICE] (1) : haproxy version is 2.8.3-86e043a
[NOTICE] (1) : path to executable is /usr/local/sbin/haproxy
[WARNING] (1) : Current worker (8) exited with code 1 (Exit)
[ALERT] (1) : exit-on-failure: killing every processes with SIGTERM
[WARNING] (1) : All workers exited. Exiting... (1)
  • 创建并运行 Haproxy 容器,其中的 8888 是 Haproxy 监听的 HTTP 端口(用于提供监控界面的 Web 服务),3306 是 Haproxy 监听的 MySQL 端口(用于转发请求给 PXC 集群节点)
1
2
3
4
5
# 创建并运行容器
sudo docker run -it -p 4001:8888 -p 4002:3306 -v /var/lib/docker/volumes/haproxy-v1/_data/:/usr/local/etc/haproxy --privileged --name=haproxy-node1 --net=pxc-network --ip 172.30.0.5 -d haproxy

# 查看日志信息
sudo docker logs -f haproxy-node1

部署验证

访问监控界面

在宿主机内使用浏览器访问 http://192.168.1.221:4001/dbs,打开 Haproxy 的监控界面(如下图所示),登录用户名是 admin,登录密码是 admin。如果可以正常访问 Haproxy 的监控界面,则说明 Haproxy 成功部署。

提示

值得一提的是,上述的 192.168.1.221 是宿主机的 IP 地址,4001 是 Haproxy 容器映射的 HTTP 端口。

连接 PXC 集群

通过 Haproxy 容器映射的数据库代理端口 4402 登录 PXC 集群的 MySQL 节点。如果可以正常登录,则说明 Haproxy 成功将请求转发给 PXC 集群。

1
mysql -h 192.168.1.221 -u root -P 4002 -p123456

提示

值得一提的是,上述的 192.168.1.221 是宿主机的 IP 地址,4002 是 Haproxy 容器映射的 MySQL 端口。

集群节点心跳检测

Haproxy 每隔一段时间会对 PXC 集群的节点进行心跳检测,当某个集群节点下线后,在 Haproxy 的监控界面可以观察到(如下图所示)。

提示

当 Haproxy 检测到有 PXC 集群节点处于不可用状态时,它会将该节点从负载均衡的服务器列表中剔除掉,直到该节点重新恢复到可用状态后。因此 Haproxy 可以做到一定程度的高可用,它的负载均衡跟 Nginx 有比较大的区别,后者默认不会剔除不用的服务器节点,而是会直接转发请求给故障节点。

1
2
# 关闭节点二
sudo docker stop pxc-node2

Haproxy + Keepalived 双机热备部署

在本节中,将会创建多一个 Haproxy 容器(最终 Haproxy 会有两个容器),并在宿主机和每个 Haproxy 容器内单独安装 Keepalived 服务器,以此实现 Haproxy + Keepalived 双机热备方案。特别注意,在执行以下操作之前,请先启动 PXC 集群,并确保集群可以正常运行。

双机热备部署规划

  • Docker 部署 Haproxy(两个节点 - 主备)
节点名称容器名称容器 IP 容器数据卷容器数据卷目录操作系统
Haproxy 节点一 haproxy-node1172.30.0.5haproxy-v1/var/lib/docker/volumes/haproxy-v1/_data/Debian 11
Haproxy 节点二 haproxy-node2172.30.0.6haproxy-v2/var/lib/docker/volumes/haproxy-v2/_data/Debian 11
  • Keepalived 服务器安装
服务器名称服务器角色虚拟 IP 说明操作系统
Keepalived 服务器一主服务器(MASTER)172.30.0.7 安装在 Haproxy 节点一的容器内(haproxy-node1Debian 11
Keepalived 服务器二备服务器(BACKUP)172.30.0.7 安装在 Haproxy 节点二的容器内(haproxy-node2Debian 11
Keepalived 服务器三 192.168.1.160 安装在宿主机内,为了实现外网可以正常访问 Docker 容器内的虚拟 IPCentos 7

双机热备整体架构

单节点的 Haproxy 不具备真正的高可用性,必须要有冗余设计,否则 Haproxy 宕机后,会造成整个集群不可用,如下图所示:

对 Haproxy 进行集群部署(两个节点),并使用 Keepalived 实现双机热备架构,当其中一个 Haproxy 节点宕机后,另一个 Haproxy 节点可以顶上,保证整个集群的可用性,如下图所示:

  • Docker 创建两个 Haproxy 容器,每个容器中都单独安装 Keepalived 服务器
  • 两个 Keepalived 服务器会争抢 Docker 容器内的虚拟 IP,一个抢到后,另一个没抢到就会等待,抢到的作为主服务器,没抢到的作为备用服务器
  • 两个 Keepalived 服务器之间会进行心跳检测,如果备用服务器没有接收到主服务器的心跳响应,则说明主服务器发生故障,那么备用服务器就可以抢占虚拟 IP,继续工作
  • 客户端向虚拟 IP 发送数据库请求,当其中一个 Haproxy 节点宕机后,还有另一个 Haproxy 节点可以接替工作
  • 由于 Docker 容器内的虚拟 IP 不能被外网直接访问,所以需要借助宿主机里的 Keepalived 服务映射成外网可以访问的虚拟 IP

安装第一个 Keepalive 服务

在本节中,将在上面创建第一个的 Haproxy 容器内,安装 Keepalived 服务器。

  • 使用 root 权限连接第一个 Haproxy 容器
1
2
# 连接Haproxy容器
docker exec -u 0 -it haproxy-node1 /bin/bash
  • 在第一个 Haproxy 容器内安装 Keepalived 服务器
1
2
3
4
5
# 更新包索引
apt-get update

# 安装软件
apt-get install -y vim keepalived
  • 在第一个 Haproxy 容器内配置 Keepalived
1
2
3
4
5
# 创建Keepalived的配置文件
touch /etc/keepalived/keepalived.conf

# 编辑Keepalived的配置文件,写入以下配置内容(请自行更改网卡设备参数)
vim /etc/keepalived/keepalived.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
vrrp_instance  VI_1 {
state MASTER # 必填,Keepalived 的身份(MASTER 是主服务器,BACKUP 是备服务器)
interface eth0 # 必填,Docker 的网卡设备,虚拟 IP 所在
virtual_router_id 51 # 必填,虚拟路由标识,取值在0-255之间,用来区分多个Instance的VRRP组播,同一网段内ID不能重复,主备机器的该值必须为一样
priority 100 # 必填,用来选举Master的,MASTER 权重要高于 BACKUP,数字越大优先级越高,该项取值范围是1-255(在此范围之外会被识别成默认值100)
advert_int 1 # 必填,MASTER 和 BACKUP 节点同步检查的时间间隔(单位为秒),主备之间必须一致,可以认为是健康检查的时间间隔
authentication { # 必填,主备服务器的验证方式,主备之间必须使用相同的密码才能正常通信
auth_type PASS # 必填,主备服务器的认证方式,其中有两种方式PASS和HA(IPSEC),推荐使用PASS(密码只识别前8位),主备之间必须使用相同的密码才能正常通信
auth_pass 123456
}
virtual_ipaddress { # 必填,虚拟 IP,可以设置多个虚拟 IP 地址,每行一个
172.30.0.7
}
}
  • 在第一个 Haproxy 容器内启动 Keepalived
1
service keepalived start
  • 在宿主机内查看第一个 Haproxy 容器内的进程列表,验证 Keepalived 服务是否正常运行
1
docker top haproxy-node1
  • 在宿主机内 Ping 虚拟 IP,验证虚拟 IP 是否可用
1
ping 172.30.0.7

安装第二个 Keepalived 服务

在本节中,将另外多部署一个 Haproxy 容器,并在容器内安装 Keepalived 服务器。

部署第二个 Haproxy 容器

  • 创建数据卷,存储路径为 /var/lib/docker/volumes/
1
2
3
4
5
6
7
8
# 创建数据卷
sudo docker volume create --name haproxy-v2

# 查看所有数据卷
sudo docker volume ls

# 查看数据卷的详细信息
sudo docker volume inspect haproxy-v2
  • 创建 Haproxy 的配置文件
1
2
3
4
5
# 在数据卷目录下创建Haproxy的配置文件
sudo touch /var/lib/docker/volumes/haproxy-v2/_data/haproxy.cfg

# 编辑Haproxy的配置文件,添加以下内容
sudo vi /var/lib/docker/volumes/haproxy-v2/_data/haproxy.cfg
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
global
# 工作目录
# chroot /usr/local/etc/haproxy
# 日志文件,使用rsyslog服务中local5日志设备(/var/log/local5),等级info
log 127.0.0.1 local5 info
# 以守护进程运行
daemon

defaults
log global
mode http
# 日志格式
option httplog
# 日志中不记录负载均衡的心跳检测记录
option dontlognull
# 连接超时(毫秒)
timeout connect 5000
# 客户端超时(毫秒)
timeout client 50000
# 服务器超时(毫秒)
timeout server 50000

# 监控界面
listen admin_stats
# 监控界面的访问的IP和端口
bind 0.0.0.0:8888
# 访问协议
mode http
# URI相对地址
stats uri /dbs
# 统计报告格式
stats realm Global\ statistics
# 登陆账户信息
stats auth admin:admin
# 数据库负载均衡
listen proxy-pxc
# 访问的IP和端口
bind 0.0.0.0:3306
# 网络协议
mode tcp
# 负载均衡算法(轮询算法)
# 轮询算法:roundrobin
# 权重算法:static-rr
# 最少连接算法:leastconn
# 请求源IP算法:source
balance roundrobin
# 日志格式
option tcplog
# Haproxy使用MySQL的haproxy账户对数据库进行心跳检测
option mysql-check user haproxy
server PXC_Node_1 172.30.0.2:3306 check weight 1 maxconn 2000
server PXC_Node_2 172.30.0.3:3306 check weight 1 maxconn 2000
server PXC_Node_3 172.30.0.4:3306 check weight 1 maxconn 2000
# 使用Keepalived检测死链
option tcpka

特别注意

在上述 Haproxy 的配置文件中,不能启用 chroot /usr/local/etc/haproxy,否则 Haproxy 容器会启动失败(日志信息如下),暂时不清楚其原因。

1
2
3
4
5
6
7
8
9
10
[NOTICE]   (1) : New worker (8) forked
[NOTICE] (1) : Loading success.
[NOTICE] (8) : haproxy version is 2.8.3-86e043a
[NOTICE] (8) : path to executable is /usr/local/sbin/haproxy
[ALERT] (8) : [haproxy.main()] Cannot chroot(/usr/local/etc/haproxy).
[NOTICE] (1) : haproxy version is 2.8.3-86e043a
[NOTICE] (1) : path to executable is /usr/local/sbin/haproxy
[WARNING] (1) : Current worker (8) exited with code 1 (Exit)
[ALERT] (1) : exit-on-failure: killing every processes with SIGTERM
[WARNING] (1) : All workers exited. Exiting... (1)
  • 创建并运行 Haproxy 容器,其中的 8888 是 Haproxy 监听的 HTTP 端口(用于提供监控界面的 Web 服务),3306 是 Haproxy 监听的 MySQL 端口(用于转发请求给 PXC 集群节点)
1
2
3
4
5
# 创建并运行容器
sudo docker run -it -p 4003:8888 -p 4004:3306 -v /var/lib/docker/volumes/haproxy-v2/_data/:/usr/local/etc/haproxy --privileged --name=haproxy-node2 --net=pxc-network --ip 172.30.0.6 -d haproxy

# 查看日志信息
sudo docker logs -f haproxy-node2
  • 在宿主机内使用浏览器访问 http://192.168.1.221:4003/dbs,打开 Haproxy 的监控界面(如下图所示),登录用户名是 admin,登录密码是 admin。如果可以正常访问 Haproxy 的监控界面,则说明 Haproxy 成功部署。

提示

值得一提的是,上述的 192.168.1.221 是宿主机的 IP 地址,4003 是 Haproxy 容器映射的 HTTP 端口。

安装第二个 Keepalive 服务

  • 使用 root 权限连接第二个 Haproxy 容器
1
2
# 连接Haproxy容器
docker exec -u 0 -it haproxy-node2 /bin/bash
  • 在第二个 Haproxy 容器内安装 Keepalived 服务器
1
2
3
4
5
# 更新包索引
apt-get update

# 安装软件
apt-get install -y vim keepalived
  • 在第二个 Haproxy 容器内配置 Keepalived
1
2
3
4
5
# 创建Keepalived的配置文件
touch /etc/keepalived/keepalived.conf

# 编辑Keepalived的配置文件,写入以下配置内容(请自行更改网卡设备参数)
vim /etc/keepalived/keepalived.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
vrrp_instance  VI_1 {
state BACKUP # 必填,Keepalived 的身份(MASTER 是主服务器,BACKUP 是备服务器)
interface eth0 # 必填,Docker 的网卡设备,虚拟 IP 所在
virtual_router_id 51 # 必填,虚拟路由标识,取值在0-255之间,用来区分多个Instance的VRRP组播,同一网段内ID不能重复,主备机器的该值必须为一样
priority 90 # 必填,用来选举Master的,MASTER 权重要高于 BACKUP,数字越大优先级越高,该项取值范围是1-255(在此范围之外会被识别成默认值100)
advert_int 1 # 必填,MASTER 和 BACKUP 节点同步检查的时间间隔(单位为秒),主备之间必须一致,可以认为是健康检查的时间间隔
authentication { # 必填,主备服务器的验证方式,主备之间必须使用相同的密码才能正常通信
auth_type PASS # 必填,主备服务器的认证方式,其中有两种方式PASS和HA(IPSEC),推荐使用PASS(密码只识别前8位),主备之间必须使用相同的密码才能正常通信
auth_pass 123456
}
virtual_ipaddress { # 必填,虚拟 IP,可以设置多个虚拟 IP 地址,每行一个
172.30.0.7
}
}
  • 在第二个 Haproxy 容器内启动 Keepalived
1
service keepalived start
  • 在宿主机内查看第二个 Haproxy 容器内的进程列表,验证 Keepalived 服务是否正常运行
1
docker top haproxy-node2

安装第三个 Keepalive 服务

在本节中,将在宿主机内安装第三个 Keepalive 服务器,目的是为了实现外网可以正常访问 Docker 内的虚拟 IP。特别注意,Docker 内的虚拟 IP 默认是不能被外网访问的,所以需要借助宿主机的 Keepalived 映射成外网可以正常访问的虚拟 IP。

  • 配置宿主机的防火墙,对外开放 88883306 端口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看防火墙运行状态
sudo systemctl status firewalld

# 启用防火墙
sudo systemctl start firewalld

# 配置防火墙永久开放8888和3306端口
sudo firewall-cmd --zone=public --permanent --add-port=8888/tcp
sudo firewall-cmd --zone=public --permanent --add-port=3306/tcp

# 重载防火墙配置
sudo firewall-cmd --reload

# 查看防火墙已开放的端口
sudo firewall-cmd --list-ports
  • 在宿主机内查看当前局域网的 IP 分配情况
1
2
3
4
5
# 安装nmap
sudo yum install -y nmap

# 查看当前网段所有分配的IP
nmap -sP 192.168.1.0/24
  • 在宿主机内安装 Keepalive
1
2
3
4
5
# 安装Keepalive
sudo yum install -y keepalived

# 开机自启动Keepalived
sudo systemctl enable keepalived
  • 在宿主机内配置 Keepalive
1
2
3
4
5
# 备份Keepalived的配置文件
sudo cp /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.bak

# 编辑Keepalived的配置文件,覆盖写入以下配置内容(请自行更改网卡设备参数)
sudo vim /etc/keepalived/keepalived.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
vrrp_instance VI_1 {
state MASTER
# 这里指定是宿主机的网卡,可以通过 "ip a" 命令查看当前系统上使用的网卡是哪个
interface enp0s3
virtual_router_id 100
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
# 这里是指定宿主机上的一个虚拟IP,一定要和宿主机网卡在同一个网段,例如宿主机网卡的IP是192.168.1.221,那么指定的虚拟IP可以是192.168.1.160
192.168.1.160
}
}

# 接收HTTP数据,用于访问Haproxy的Web监控页面
virtual_server 192.168.1.160 8888 {
delay_loop 3
lb_algo rr
lb_kind NAT
persistence_timeout 50
protocol TCP
# 将宿主机接收到的数据,转发给Haproxy容器内的虚拟IP和HTTP端口
real_server 172.30.0.7 8888 {
weight 1
}
}

# 接收数据库请求,用于访问Haproxy代理的MySQL端口
virtual_server 192.168.1.160 3306 {
delay_loop 3
lb_algo rr
lb_kind NAT
persistence_timeout 50
protocol TCP
# 将宿主机接收到的数据,转发给Haproxy容器内的虚拟IP和MySQL端口
real_server 172.30.0.7 3306 {
weight 1
}
}
  • 在宿主机内启动 Keepalived
1
2
3
4
5
# 启动
sudo systemctl start keepalived

# 查看运行状态
sudo systemctl status keepalived

双机热备方案部署验证

  • 在其他电脑上,如果可以通过宿主机的虚拟 IP 192.168.1.160 正常访问宿主机 Haproxy 容器中的虚拟 IP 172.30.0.7 及相应端口,则说明 PXC + Haproxy + Keepalived 的高可用集群搭建成功。
验证内容宿主机的虚拟 IP 宿主机的端口验证命令
Haproxy 的监控页面 192.168.1.1608888curl -basic -u admin:admin -I http://192.168.1.160:8888/dbs
Haproxy 的 MySQL 负载均衡 192.168.1.1603306mysql -h 192.168.1.160 -u root -P 3306 -p123456
  • 检查两个 Keepalived 节点之间是否可以正常进行心跳通信,如果不能进行心跳通信,则会发生脑裂现象(即两个 Keepalived 节点会同时抢占到虚拟 IP)
1
2
3
4
5
# 使用root权限连接Haproxy容器一
docker exec -u 0 -it haproxy-node1 /bin/bash

# 查看IP地址
ip addr
1
2
3
4
5
# 使用root权限连接Haproxy容器二
docker exec -u 0 -it haproxy-node2 /bin/bash

# 查看IP地址
ip addr
  • 在宿主机上面关闭 Haproxy 节点一的容器,然后在其他电脑上,打开浏览器访问 http://192.168.1.160:8888/dbs,如果可以正常访问 Haproxy 监控页面,则说明 Haproxy + Keepalived 的双机热备方案生效了。
1
2
# 关闭Haproxy节点一的容器
sudo docker stop haproxy-node1
  • 在宿主机上面关闭 Haproxy 节点一的容器之后,连接进 Haproxy 节点二的容器内部,使用 ip addr 命令查看 IP 地址,可以看见已经抢占到的虚拟 IP
1
2
3
4
5
# 使用root权限连接容器
docker exec -u 0 -it haproxy-node2 /bin/bash

# 查看IP地址
ip addr

Haproxy + Keepalived 双机热备完善

在本节中,将介绍上述操作完成之后,Haproxy + Keepalived 双机热备方案仍需要改进的地方。

Keepalived 服务自启动

目前集群里有两个 Keepalived 服务分别安装在不同的 Haproxy 容器内,但它们默认都没有配置自启动,也就是说 Keepalived 没有随 Haproxy 容器启动而启动。为了日后方便维护集群,建议将 Haproxy 容器内的 Keepalived 服务统一配置成自启动。可以尝试通过 Dockerfile 自主构建包含有 Haproxy + Keepalived 的 Docker 镜像。由于篇幅有限,这里不再累述。

Haproxy 保证事务持久性

Haproxy 代理 MySQL 的时候,事务持久性的问题必须解决。这个事务持久性不是 ACID 的 D(持久性,Durability),而是 Transaction Persistent,这里简单描述一下此处的事务持久性。

1
2
3
4
5
start transaction
update1...
update2...
insert3...
commit

当客户端显式开启一个事务,然后执行上述几个数据库操作,然后提交或回滚。如果使用代理软件(如 Haproxy)对 MySQL 进行代理,必须要保证这 5 个语句全都路由到同一个 MySQL 节点上,即使后端的 MySQL 采用的是多主模型(MGR、Galera 都提供多主模型),否则事务中各语句分散,轻则返回失败,重则数据不一致、提交混乱。这就是 Transaction Persistent 的概念,即让同一个事务路由到同一个后端节点。Haproxy 如何保证事务持久性呢?对于非 MySQL 协议感知的代理(LVS、Nginx、Haproxy 等),要保证事务持久性,只能通过间接的方法实现,比较通用的方法是在代理软件上监听不同的端口(实现读写分离)。具体的思路如下:

  • 1)在 Haproxy 上监听不同端口,例如 3307 端口的请求作为写端口,3306 端口的请求作为读端口。
  • 2)从后端 MySQL 节点中选一个节点 (只能是一个) 作为逻辑写节点,Haproxy 将 3307 端口的请求全都路由给这个节点。
  • 3)可以在 Haproxy 上配置多个备用写节点 (Backup),但 3307 端口在某一时刻,路由到的必须只能有一个写节点。

这样能保证事务的持久性,也能解决一些乐观锁问题。但是,如果后端是多主模型的 MGR(组复制)或 Galera,这样的代理方式将强制变为单主模型,虽然是逻辑上的强制。当然,这并非什么问题,至少到目前为止的开源技术,都建议采用单主模型。Haproxy 保证事务持久性的配置示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
listen  haproxy_3306_read_multi
bind *:3306
mode tcp
timeout client 10800s
timeout server 10800s
balance leastconn
option httpchk
option allbackups
default-server port 9200 inter 2s downinter 5s rise 3 fall 2 slowstart 60s maxconn 64 maxqueue 128 weight 100
server galera1 192.168.55.111:3306 check
server galera2 192.168.55.112:3306 check
server galera3 192.168.55.113:3306 check

listen haproxy_3307_write_single
bind *:3307
mode tcp
timeout client 10800s
timeout server 10800s
balance leastconn
option httpchk
option allbackups
default-server port 9200 inter 2s downinter 5s rise 3 fall 2 slowstart 60s maxconn 64 maxqueue 128 weight 100
server galera1 192.168.55.111:3306 check
server galera2 192.168.55.112:3306 check backup
server galera3 192.168.55.113:3306 check backup

上面的配置通过 3306 端口和 3307 端口进行读写分离,并且在负责写的 3307 端口中只有一个节点可写,其余两个节点作为 Backup 节点。对于 MySQL 的负载来说,更建议采用 MySQL 协议感知的程序来实现,例如 MySQL Router、ProxySql,MaxScale、MyCat 等数据库中间件。

Keepalived 监控 Haproxy 运行状态

上述两个 Haproxy 容器内的 Keepalived 服务,彼此仅仅是基于心跳检测来实现双机热备(故障切换)。如果第一个 Haproxy 容器内的 Keepalived 服务(Master)正常运行,而 Haproxy 自身运行异常,那么将会出现 Haproxy 负载均衡服务失效,无法切换到备用的 Haproxy 负载均衡器上,最终导致后端的 Web 服务无法收到响应。所以,应该是要基于 Shell 脚本每隔一段时间检测 Haproxy 服务是否正常运行,而不是仅仅依靠 Keepalived 主备节点之间的心跳检测。比如,当检测到 Haproxy 服务不是正常运行,首先尝试启动 Haproxy 服务;若 Haproxy 服务重启失败,就应该关闭掉该节点上的 Keepalived 服务,并发送报警邮件,这样才能自动切换到 Keepalived 服务的 Backup 节点上。详细的解决方案建议参考 这里 的教程。

PXC 集群数据备份

数据库备份方案

  • 冷备份

    • 冷备份是关闭数据库时候的备份方式,通常做法是拷贝数据文件。
    • 是简单安全的一种备份方式,但不能在数据库运行时进行备份。
    • 大型网站无法做到关闭业务备份数据,所以冷备份不是最佳选择。
  • 热备份

    • 热备份是在数据库运行状态下备份数据,MySQL 常见的热备份有 LVMXtraBackup 两种方案。
    • LVM 热备份方案
      • Linux 的分区备份命令,可以备份任何数据库。
      • 会对数据库加锁,备份期间只能读取数据库,而且命令复杂。
    • XtraBackup 热备份方案
      • 基于 InnoDB 的在线热备工具,开源免费
      • 备份过程中不锁表,快速可靠
      • 备份过程中不会打断正在执行地事务
      • 备份数据经过压缩,占用磁盘空间小
      • 能够非常快速地备份与恢复 MySQL 数据库
  • 全量备份与增量备份

    • 全量备份:备份全部数据。备份过程时间长,占用空间大。第一次备份要使用全量备份。
    • 增量备份: 只备份变化的那部分数据。备份的时间短,占用空间小。第二次以后可以使用增量备份

PXC 全量备份(暂未验证)

  • 备份操作要在某个 PXC 集群节点的 Docker 容器内执行,但应该把备份数据保存到宿主机内,因此先创建用于存储备份数据的数据卷
1
2
3
4
5
# 创建数据卷,用于存储备份数据
sudo docker volume create backup

# 查看所有数据卷
sudo docker volume ls
  • 挑选第二个 PXC 集群节点 pxc-node2,将其容器关闭并删除掉,然后重新创建一个挂载了 backup 数据卷的 pxc-node2 容器
1
2
3
4
5
6
7
8
# 关闭容器
sudo docker stop pxc-node2

# 删除容器,由于数据库的数据存放在数据卷 "pxc-v2" 中,所以数据不会丢失
sudo docker rm pxc-node2

# 创建容器(增加挂载"backup"数据卷)
sudo docker create -p 13307:3306 -v pxc-v2:/var/lib/mysql -v backup:/data -e MYSQL_ROOT_PASSWORD=123456 -e XTRABACKUP_PASSWORD=123456 -e CLUSTER_NAME=pxc -e CLUSTER_JOIN=pxc-node1 --name=pxc-node2 --net=pxc-network --ip 172.30.0.3 pxc
  • 在新创建的 pxc-node2 容器中安装 xtrabackup 工具
1
2
3
4
5
# 启动容器
sudo docker start pxc-node2

# 使用root权限连容器
sudo docker exec -u 0 -it pxc-node2 /bin/bash
1
2
3
4
5
6
7
8
# 安装存储库包
yum install -y https://repo.percona.com/yum/percona-release-latest.noarch.rpm

# 启用存储库
percona-release enable-only tools release

# 安装XtraBackup工具
yum install -y percona-xtrabackup-80
  • 使用 XtraBackup 全量备份数据库数据
1
2
3
4
5
# 创建存放备份数据的目录
mkdir -p /data/backup/full

# 全量备份数据库数据
xtrabackup --backup -uroot -p123456 --target-dir=/data/backup/full

PXC 全量还原(暂未验证)

数据库可以热备份,但是不能热还原,否则会造成业务数据和还原数据的冲突。针对 PXC 集群,为了避免在还原过程中可能出现各节点数据同步冲突的问题,需要先解散原来的集群(删除所有集群节点),然后重新创建空白数据库节点,再执行数据库冷还原操作,最后再创建其他集群节点。还原前还要将热备份保存的未提交的事务回滚,还原之后重启 MySQL 服务器。

  • 关闭并删除所有 PXC 集群节点
1
2
3
4
5
6
7
8
# 关闭容器
sudo docker stop pxc-node1 pxc-node2 pxc-node3

# 删除容器
sudo docker rm pxc-node1 pxc-node2 pxc-node3

# 删除数据卷(强烈建议在删除之前备份所有数据卷文件)
sudo docker volume rm pxc-v1 pxc-v2 pxc-v3
  • 按照之前的步骤重新创建第一个节点的容器 pxc-node1,并进入容器内,执行冷还原操作
1
2
3
4
5
6
7
8
9
10
11
# 创建第一个节点的数据卷
docker volume create pxc-v1

# 创建第一个节点的容器(增加挂载"backup"数据卷)
sudo docker create -p 13306:3306 -v pxc-v1:/var/lib/mysql -v backup:/data -e MYSQL_ROOT_PASSWORD=123456 -e XTRABACKUP_PASSWORD=123456 -e CLUSTER_NAME=pxc --name=pxc-node1 --net=pxc-network --ip 172.30.0.2 pxc

# 启动第一个节点的容器
sudo docker start pxc-node1

# 使用root权限连第一个节点的容器
docker exec -it -uroot pxc-node1 /bin/bash
1
2
3
4
5
6
7
8
9
10
11
# 准备还原
xtrabackup --prepare --target-dir=/data/backup/full/

# 开始冷还原
xtrabackup --copy-back --target-dir=/data/backup/full/

# 更改还原后的数据库文件属主
chown -R mysql:mysql /var/lib/mysql

# 断开容器连接
exit
1
2
3
4
5
# 关闭第一个节点的容器
docker stop pxc-node1

# 重启第一个节点的容器
docker start pxc-node1
  • 创建第二个与第三个节点的容器
1
2
3
4
5
# 创建第二个节点的数据卷
sudo docker volume create --name pxc-v2

# 创建第三个节点的数据卷
sudo docker volume create --name pxc-v3
1
2
3
4
5
# 创建第二个节点(增加了CLUSTER_JOIN参数)
sudo docker create -p 13307:3306 -v pxc-v2:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -e XTRABACKUP_PASSWORD=123456 -e CLUSTER_NAME=pxc -e CLUSTER_JOIN=pxc-node1 --name=pxc-node2 --net=pxc-network --ip 172.30.0.3 pxc

# 创建第三个节点(增加了CLUSTER_JOIN参数)
sudo docker create -p 13308:3306 -v pxc-v3:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -e XTRABACKUP_PASSWORD=123456 -e CLUSTER_NAME=pxc -e CLUSTER_JOIN=pxc-node1 --name=pxc-node3 --net=pxc-network --ip 172.30.0.4 pxc
1
2
3
4
5
# 启动第二节点(首次启动比较耗时间,因为需要同步第一个节点的数据)
sudo docker start pxc-node2

# 启动第三节点(首次启动比较耗时间,因为需要同步第一个节点的的数据)
sudo docker start pxc-node3

PXC 集群日常维护

特别注意

对于 PXC 集群节点的启动、关闭等操作,区分第一个节点(负责集群初始化)和其他节点,两者的操作步骤是不同的。

正确关闭集群

如果第一个节点(负责集群初始化)不是最后一个离开集群的,那么它在一般情况下就不能再以第一个节点的形式启动了。这是因为从这个节点引导集群启动可能是不安全的,即这个节点可能不包含所有更新的数据。综上所述,PXC 集群节点的正确关闭顺序,应该与它们的启动顺序相反(类似栈结构 - 先进后出),即最先启动的节点应该最后关闭。

  • PXC 集群节点容器的正确关闭顺序
1
2
3
4
5
6
7
8
# 关闭节点三
sudo docker stop pxc-node3

# 关闭节点二
sudo docker stop pxc-node2

# 关闭节点一
sudo docker stop pxc-node1
  • PXC 集群节点容器的正确启动顺序
1
2
3
4
5
6
7
8
# 启动节点一
sudo docker start pxc-node1

# 启动节点二
sudo docker start pxc-node2

# 启动节点三
sudo docker start pxc-node3

正确关闭与启动节点

如果是希望 PXC 集群关闭某个节点(非第一个节点),正确的步骤如下:

1
2
3
4
5
# 关闭节点
sudo docker stop pxc-node2

# 查看状态
sudo docker ps -a

某个节点(非第一个节点)关闭之后,其他维护工作也完成了,若希望将该节点重新加入 PXC 集群,可以执行以下命令:

1
2
# 启动节点
sudo docker start pxc-node2

第一个节点不是最后一个离开集群

如果第一个节点(负责集群初始化)不是最后一个离开集群的,不能再以第一个节点的形式启动了。

  • 第一个节点(负责集群初始化)关闭后,希望重新加入 PXC 集群,此时运行 sudo docker start pxc-node1 命令,会发现第一个节点的容器没有正常启动
  • 启动失败的原因往往是:此时从第一个节点引导集群启动可能是不安全。由于该节点不是最后一个离开集群的节点(最后停掉的节点),可能没有包含所有更新的数据
  • 如果此时所有集群节点都处于关闭状态
    • 首先逐一排查每个节点的数据卷目录,查看数据卷目录下是否存在 grastate.dat 文件,并找到 safe_to_bootstrap=1 的节点
      • 如果找到 safe_to_bootstrap=1 的节点,且它不是第一个节点(负责集群初始化)
        • 此时为了方便操作,建议先将所有节点容器关闭并删除掉,在删除所有节点容器之前,必须确保数据卷目录的数据不被误删,否则会丢失所有数据库数据
        • 然后再按照 上面的步骤,重新创建并启动每一个 PXC 集群节点容器
        • 最后再检查集群的数据是否可以正常同步
      • 如果找到 safe_to_bootstrap=1 的节点,且它是第一个节点(负责集群初始化),或者根本找不到 safe_to_bootstrap=1 的节点
        • 这时,需要通过 sudo docker volume inspect pxc-v1 得到第一个节点的数据卷目录路径,找到数据卷目录下的 grastate.dat 文件
        • 打开第一个节点下的 grastate.dat 文件,将 safe_to_bootstrap 设置为 1
        • 然后直接使用 sudo docker start pxc-node1 命令强制从第一个节点启动
        • 等第一个节点正常启动后,再接着启动其他节点
        • 最后再检查集群的数据是否可以正常同步
  • 如果此时集群里还至少有一个节点存活(例如节点三存活着)
    • 首先删除第一个节点的容器,sudo docker stop pxc-node1sudo docker rm pxc-node1
    • 重新创建一个新节点的容器,sudo docker create -p 13306:3306 -v pxc-v1:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -e XTRABACKUP_PASSWORD=123456 -e CLUSTER_NAME=pxc -e CLUSTER_JOIN=pxc-node3 --name=pxc-node1 --net=pxc-network --ip 172.30.0.2 pxc
    • 在创建新节点的容器时,是以普通节点形式(非第一个节点的形式)加入集群的,注意创建新节点容器的命令行参数是包含 -e CLUSTER_JOIN=pxc-node3,这里的 pxc-node3 是集群中存活着的节点三
    • 然后启动新节点的容器,sudo docker start pxc-node1
    • 最后再检查集群的数据是否可以正常同步

系统重启之后,PXC 集群启动失败

Docker 所在的 Windows/Linux 操作系统重启后,导致所有 PXC 集群节点都意外关闭了,此时选择 PXC 集群中的第一个节点容器进行重启,出现以下的错误信息:

1
[ERROR] WSREP: It may not be safe to bootstrap the cluster from this node. It was not the last one to leave the cluster and may not contain all the updates. To force cluster bootstrap with this node, edit the grastate.dat file manually and set safe_to_bootstrap to 1 .
  • 意思是从这个节点引导集群启动可能是不安全。由于该节点不是最后一个离开集群的节点(最后停掉的节点),可能没有包含所有更新的数据,强制从该节点启动,需要手工编辑该节点的 grastate.dat 文件,设置 safe_to_bootstrap=1
  • 当然了,一般情况下不需要强制从该节点启动,可以逐一排查每个节点下的 grastate.dat 文件,找到 safe_to_bootstrap=1 的节点。
  • 如果 safe_to_bootstrap=1 的节点是第一个节点,那么可以直接在该节点上引导 PXC 集群启动,然后再启动其他节点。
  • 如果 safe_to_bootstrap=1 的节点不是第一个节点,此时为了方便操作,建议先将所有节点容器关闭并删除掉,然后再按照 上面的步骤,重新创建并启动每一个 PXC 集群节点容器。
  • 如果所有节点的 safe_to_bootstrap 都为 0,那么只能任意选择一个节点,更改该节点下的 grastate.dat 文件,将 safe_to_bootstrap 设置为 1,然后在该节点上引导 PXC 集群启动,最后再启动其他节点。
  • 无论是上述哪种情况,都必须等待第一个节点启动成功,也就是 PXC 集群初始化完成之后,才能接着启动其他节点,最后再检查集群的数据是否可以正常同步。

提示

  • grastate.dat 文件的路径是 /var/lib/docker/volumes/xxxx/_data/grastate.dat,其中的 xxx 是容器数据卷的目录名称。

最坏情况,所有节点容器重新创建

假设由于各种原因导致整个 PXC 集群无法正常启动,此时可以将所有节点容器关闭并删除掉(必须确保数据卷目录的数据不被误删,否则会丢失所有数据库数据),然后再按照 上面的步骤,重新创建并启动每一个 PXC 集群节点容器。

参考资料