制作数据库镜像 xtrabackup 镜像
xtrabackup 地址:
https://www.percona.com/downloads/Percona-XtraBackup-LATEST/Percona-XtraBackup-8.0.9/binary/redhat/7/x86_64/
1
2
3
4
5
6
7
8
9
10
11
|
FROM centos:7
ADD percona-xtrabackup-80-8.0.9-1.el7.x86_64.rpm /
RUN rpm --rebuilddb && \
yum -y install wget hostname mariadb && \
yum -y install nmap-ncat.x86_64 && \
yum -y localinstall percona-xtrabackup-80-8.0.9-1.el7.x86_64.rpm
RUN rm -rf percona-xtrabackup-80-8.0.9-1.el7.x86_64.rpm
EXPOSE 3307
|
升级版本到 8.0.28
1
2
3
4
5
6
7
8
9
10
11
|
FROM centos:7
ADD percona-xtrabackup-80-8.0.29-1.el7.x86_64.rpm /
RUN rpm --rebuilddb && \
yum -y install wget hostname mariadb && \
yum -y install nmap-ncat.x86_64 && \
yum -y localinstall percona-xtrabackup-80-8.0.9-1.el7.x86_64.rpm
RUN rm -rf percona-xtrabackup-80-8.0.9-1.el7.x86_64.rpm
EXPOSE 3307
|
创建命令为:
1
|
docker build -t xtrabackup:8.0.29 . -f Dockerfile.8.0.29
|
安装服务:
1
2
3
4
5
|
1、配置并安装好 Master 节点的 MySQL;
2、通过XtraBackup将Master节点的数据备份到指定目录;
3、将第2步备份出来的目录,连同备份信息文件,拷贝到Slave的 /var/lib/mysql下,然后执行 CHANGE MASTER TO指令;
4、启动 Slave 节点,执行 START SLAVE指令;
5、在这个集群中添加更多的Slave节点;
|
ConfigMap 为 Master/Slave 节点分配不同的配置文件
根据开头介绍的 ConfigMap 概念可知,使用 ConfigMap 可以很好解决 MySQL 集群中 Master 和 Slave 节点配置文件不一致的问题。ConfigMap 的配置信息如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
[root@k8s-6-m1 mysql-new]# cat configmap.yml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql
namespace: mysql
labels:
app: mysql
data:
master.cnf: |
# Master配置
[mysqld]
log-bin
bind-address=0.0.0.0
slave.cnf: |
# Slave配置
[mysqld]
super-read-only
bind-address=0.0.0.0
|
Secret 为 集群配置密码
1
2
3
4
5
6
7
8
9
10
11
12
13
|
[root@k8s-6-m1 mysql-new]# cat secret.yml
---
# Secret 为 mysql集群配置密码
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
namespace: mysql
labels:
app: mysql
type: Opaque
data:
password: dVVOMzgjJXBx # echo -n 'uUN38#%pq' |base64
|
Service 为 StatefulSet 和用户提供服务发现
这里我们需要创建两个 service,两个 Service 的配置如下:
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
|
[root@k8s-6-m1 mysql-new]# cat mysql-readwrite.yml
---
#给mysql部署外部访问端口并指定mysql-0访问可读写
apiVersion: v1
kind: Service
metadata:
name: mysql-readwrite
namespace: mysql
labels:
app: mysql
spec:
ports:
- name: mysql
port: 3306
nodePort: 30306
selector:
statefulset.kubernetes.io/pod-name: mysql-0
type: NodePort
[root@k8s-6-m1 mysql-new]# cat mysql-service.yml
---
#创建services,主从分别是mysql和mysql-read.
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: mysql
labels:
app: mysql
spec:
ports:
- name: mysql
port: 3306
clusterIP: None
selector:
app: mysql
---
apiVersion: v1
kind: Service
metadata:
name: mysql-read
namespace: mysql
labels:
app: mysql
spec:
ports:
- name: mysql
port: 3306
selector:
app: mysql
|
使用 StatefulSet 搭建 MySQL 集群
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
|
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
namespace: mysql
labels:
app: mysql
spec:
selector:
matchLabels:
app: mysql
serviceName: mysql
replicas: 3
template:
metadata:
labels:
app: mysql
spec:
initContainers:
- name: init-mysql
image: www.harbor.mobi/kuboard/mysql:8.0.26
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
command:
- bash
- "-c"
- |
set -ex
# 从 Pod 的序号,生成 server-id
[[ $(hostname) =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
echo [mysqld] > /mnt/conf.d/server-id.cnf
# 由于 server-id 不能为 0,因此给 ID 加 100 来避开它
echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
# 如果 Pod 的序号为 0,说明它是 Master 节点,从 ConfigMap 里把 Master 的配置文件拷贝到 /mnt/conf.d 目录下
# 否则,拷贝 ConfigMap 里的 Slave 的配置文件
if [[ ${ordinal} -eq 0 ]]; then
cp /mnt/config-map/master.cnf /mnt/conf.d
else
cp /mnt/config-map/slave.cnf /mnt/conf.d
fi
volumeMounts:
- name: conf
mountPath: /mnt/conf.d
- name: config-map
mountPath: /mnt/config-map
- name: clone-mysql
image: www.harbor.mobi/kuboard/xtrabackup:8.0.29
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
command:
- bash
- "-c"
- |
set -ex
# 拷贝操作只需要在第一次启动时进行,所以数据已经存在则跳过
[[ -d /var/lib/mysql/mysql ]] && exit 0
# Master 节点(序号为 0)不需要这个操作
[[ $(hostname) =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
[[ $ordinal == 0 ]] && exit 0
# 使用 ncat 指令,远程地从前一个节点拷贝数据到本地
ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
# 执行 --prepare,这样拷贝来的数据就可以用作恢复了
xtrabackup --prepare --target-dir=/var/lib/mysql
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
containers:
- name: mysql
image: www.harbor.mobi/kuboard/mysql:8.0.26
args: ["--default-authentication-plugin=mysql_native_password"]
env:
- name: MYSQL_ALLOW_EMPTY_PASSWORD
value: "1"
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
ports:
- name: mysql
containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 500m
memory: 1Gi
livenessProbe: #存活探针健康检测
exec:
command:
- /bin/sh
- "-c"
- MYSQL_PWD="${MYSQL_ROOT_PASSWORD}"
- mysqladmin ping
#tcpSocket:
#port: 3306
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
readinessProbe: #就绪探针健康检测
exec:
command:
- /bin/sh
- "-c"
- MYSQL_PWD="${MYSQL_ROOT_PASSWORD}"
- mysql -h 127.0.0.1 -u root -e "SELECT 1"
#tcpSocket:
# port: 3306
initialDelaySeconds: 5
periodSeconds: 2
timeoutSeconds: 1
- name: xtrabackup
image: www.harbor.mobi/kuboard/xtrabackup:8.0.29
ports:
- name: xtrabackup
containerPort: 3307
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
command:
- bash
- "-c"
- |
set -ex
cd /var/lib/mysql
# 从备份信息文件里读取 MASTER_LOG_FILE 和 MASTER_LOG_POS 这 2 个字段的值,用来拼装集群初始化 SQL
if [[ -f xtrabackup_slave_info ]]; then
# 如果 xtrabackup_slave_info 文件存在,说明这个备份数据来自于另一个 Slave 节点
# 这种情况下,XtraBackup 工具在备份的时候,就已经在这个文件里自动生成了 "CHANGE MASTER TO" SQL 语句
# 所以,只需要把这个文件重命名为 change_master_to.sql.in,后面直接使用即可
mv xtrabackup_slave_info change_master_to.sql.in
# 所以,也就用不着 xtrabackup_binlog_info 了
rm -f xtrabackup_binlog_info
elif [[ -f xtrabackup_binlog_info ]]; then
# 如果只是存在 xtrabackup_binlog_info 文件,说明备份来自于 Master 节点,就需要解析这个备份信息文件,读取所需的两个字段的值
[[ $(cat xtrabackup_binlog_info) =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
rm xtrabackup_binlog_info
# 把两个字段的值拼装成 SQL,写入 change_master_to.sql.in 文件
echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\
MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in
fi
# 如果存在 change_master_to.sql.in,就意味着需要做集群初始化工作
if [[ -f change_master_to.sql.in ]]; then
# 但一定要先等 MySQL 容器启动之后才能进行下一步连接 MySQL 的操作
echo "Waiting for mysqld to be ready(accepting connections)"
until mysql -h 127.0.0.1 -uroot -p${MYSQL_ROOT_PASSWORD} -e "SELECT 1"; do sleep 1; done
echo "Initializing replication from clone position"
# 将文件 change_master_to.sql.in 改个名字
# 防止这个 Container 重启的时候,因为又找到了 change_master_to.sql.in,从而重复执行一遍初始化流程
mv change_master_to.sql.in change_master_to.sql.orig
# 使用 change_master_to.sql.orig 的内容,也就是前面拼装的 SQL,组成一个完整的初始化和启动 Slave 的 SQL 语句
mysql -h 127.0.0.1 -uroot -p${MYSQL_ROOT_PASSWORD} << EOF
$(< change_master_to.sql.orig),
MASTER_HOST='mysql-0.mysql.mysql',
MASTER_USER='root',
MASTER_PASSWORD='${MYSQL_ROOT_PASSWORD}',
MASTER_CONNECT_RETRY=10;
START SLAVE;
EOF
fi
# 使用 ncat 监听 3307 端口。
# 它的作用是,在收到传输请求的时候,直接执行 xtrabackup --backup 命令,备份 MySQL 的数据并发送给请求者
exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c \
"xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root --password=${MYSQL_ROOT_PASSWORD}"
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
volumes:
- name: conf
emptyDir: {}
- name: config-map
configMap:
name: mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- "ReadWriteMany"
storageClassName: mysql-cluster
resources:
requests:
storage: 3Gi
|
部署读写服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
[root@k8s-6-m1 mysql-new]# cat mysql-readwrite.yml
---
#给mysql部署外部访问端口并指定mysql-0访问可读写
apiVersion: v1
kind: Service
metadata:
name: mysql-readwrite
namespace: mysql
labels:
app: mysql
spec:
ports:
- name: mysql
port: 3306
nodePort: 30306
selector:
statefulset.kubernetes.io/pod-name: mysql-0
type: NodePort
|
部署完成之后设置主从同步
先进入 master 主节点即 mysql-0 容器;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# 查看 master 主节点的域名,使用 nslookup 工具,这里可以看到 master 域名是 mysql-0.mysql-headless.mysql-test.svc.cluster.local
[root@master ~]# nslookup 100.89.161.144
144.161.89.100.in-addr.arpa name = 100-89-161-144.mysql-readwrite.mysql-test.svc.cluster.local.
144.161.89.100.in-addr.arpa name = mysql-0.mysql-headless.mysql-test.svc.cluster.local.
[root@master ~]# kubectl exec -it mysql-0 -c mysql -n mysql-test /bin/bash
root@mysql-0:/# mysql -uroot -p
# 这里可以添加用来同步的用户,也可以直接 root 用户直接同步。
# 查看 master 主节点状态
mysql> show master status \G;
************\*\*\************* 1. row ************\*\*\*************
File: mysql-0-bin.000004
Position: 1550
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set:
1 row in set (0.00 sec)
|
下一步进入从节点 mysql-1 mysql-2 容器里面加入主节点
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
|
[root@master ~]# kubectl exec -it mysql-1 -c mysql -n mysql-test /bin/bash
root@mysql-0:/# mysql -uroot -p
### 准备就绪后就开始加入主节点
# 设置主库连接(这里用域名连接,IP 地址重启会更改导致主从不能同步)
mysql> change master to master_host='mysql-0.mysql-headless.mysql-test.svc.cluster.local',master_user='root',master_password='uUN38#%pq',master_log_file='mysql-0-bin.000004',master_log_pos=0,master_port=3306;
Query OK, 0 rows affected, 2 warnings (15.15 sec)
# 启动从库同步
mysql> start slave;
Query OK, 0 rows affected (0.14 sec)
# 查看从从库状态
mysql> show slave status \G;
************\*\*\************* 1. row ************\*\*\*************
Slave_IO_State: Waiting for master to send event
Master_Host: mysql-0.mysql-headless.mysql-test.svc.cluster.local
Master_User: root
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-0-bin.000004
Read_Master_Log_Pos: 1722
Relay_Log_File: mysql-1-relay-bin.000002
Relay_Log_Pos: 373
Relay_Master_Log_File: mysql-0-bin.000004
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
# 当看到从库状态 2 项都是 yes.就表示同步成功.
# 如果同步不成功需要尝试执行以下命令,直到 2 项都是 yes 即同步成功
mysql> stop slave;
Query OK, 0 rows affected, 1 warning (2.13 sec)
mysql> reset slave;
Query OK, 0 rows affected, 1 warning (13.27 sec)
mysql> start slave;
Query OK, 0 rows affected, 1 warning (2.87 sec)
|
解决问题时:
1
2
3
4
5
6
|
1.登陆从库:mysql -h127.0.0.1 -uroot -P3307 -pxxxx
2.查看从库同步情况:mysql> show slave status\G;
3.关闭从库
4.修改my.cnf
加入 slave-skip-errors = 1062 【slave-skip-errors为只读参数,不支持在线更改】
5.启动从库 6.验证同步情况
|
参考:
https://zhuanlan.zhihu.com/p/143855883
https://gitee.com/owen_tao/mysql-kubernetes/
https://blog.csdn.net/qq_38900565/article/details/102486100
https://www.jianshu.com/p/d87a50272310
https://www.cnblogs.com/determined-K/p/14692845.html
https://blog.csdn.net/m0_57480266/article/details/120671076