🏢 官方博客地址: https://mariadb.org/mariadb-k8s-how-to-replicate-mariadb-in-k8s/
🏆 k8s
搭建 mariadb
数据库主从
🍪 准备工作
1️⃣ k8s集群
主机名 | ip |
---|---|
k8s-master | 192.168.200.10 |
k8s-node01 | 192.168.200.20 |
2️⃣ 工序:
- 编写服务的
headless Svc
- 编写
PV
- 将配置文件存储至
configmap
,用户密码存储到secret
(非必须) - 编写
Statefulset
🌟 配置 Headless
[root@k8s-master Mariadb]# cat Headless.yaml
apiVersion: v1
kind: Service
metadata:
name: mariadb
labels:
app: mariadb
spec:
ports:
- name: mariadb
port: 3306
clusterIP: None
selector:
app: mariadb
##查看
[root@k8s-master Mariadb]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10d
mariadb ClusterIP None <none> 3306/TCP 4h32m
🌟 编写 PV
需要安装
nfs
[root@k8s-master Mariadb]# cat pv.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: mariadb-pv3
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
persistentVolumeReclaimPolicy: Recycle
storageClassName: nfs
nfs:
path: /nfs/pv3
server: 192.168.200.20
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: mariadb-pv4
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
persistentVolumeReclaimPolicy: Recycle
storageClassName: nfs
nfs:
path: /nfs/pv4
server: 192.168.200.20
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: mariadb-pv5
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
persistentVolumeReclaimPolicy: Recycle
storageClassName: nfs
nfs:
path: /nfs/pv5
server: 192.168.200.20
🌟 将配置文件存储/密码至 ConfigMap/Secret
[root@k8s-master Mariadb]# cat configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
name: mariadb
labels:
app: mariadb
data:
master.cnf: |
[mysqld]
character-set-server=utf8
collation-server=utf8_general_ci
log-bin= mysql-bin #二进制日志,后面指定存放位置。如果只是指定名字,默认存放在/var/lib/mysql下(一般如果是简单的主从配置)
lower_case_table_names=1 # 不区分大小写
binlog-format=ROW #二进制日志文件格式(一般如果是简单的主从配置)
log-slave-updates=True #slave更新是否记入日志
sync-master-info=1 #值为1确保信息不会丢失
slave-parallel-threads=3 #同时启动多少个复制线程,最多与要复制的数据库数量相等即可
binlog-checksum=CRC32 #效验码
master-verify-checksum=1 #启动主服务器效验
slave-sql-verify-checksum=1 #启动从服务器效验
slave.cnf: |
[mysqld]
character-set-server=utf8
collation-server=utf8_general_ci
log-bin= mysql-bin #log-bin是二进制文件 如果是为了在中继的话就可以开,不需要没必要开
relay_log = relay-bin # 中继日志, 后面指定存放位置。如果只是指定名字,默认存放在/var/lib/mysql下
lower_case_table_names=1
##执行查看
[root@k8s-master Mariadb]# kubectl apply -f configmap.yml
[root@k8s-master Mariadb]# kubectl get configmaps
NAME DATA AGE
mariadb 2 10h
🌟 配置 StatefulSet
思路是,将 configmap
和 pod
中的 conf
目录挂载到这个 PodTemplate
中,如何使用 init
容器,判断该 pod
时 master
还是 slave
,复制不一样的配置文件,然后编写脚本来进行初始化,将 /var/lib/mysql
(mysql
数据目录)使用 pvc
挂载保证数据的可靠性,
1️⃣ 完整代码
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mariadb
labels:
app: mariadb
spec:
replicas: 2
selector:
matchLabels:
app: mariadb
serviceName: mariadb
template:
metadata:
labels:
app: mariadb
spec:
initContainers:
- name: init-mariadb
image: docker.io/library/mariadb:10.6.4
command:
- bash
- "-c"
- |
#判断pod是主节点还是从节点,注意需要pod名为 xxx-1,xxx-0 这种,当然StatefulSet会自动生成
name=$(hostname|cut -d - -f 2)
if [ name -eq 0 ]; then
cp /mnt/config-map/master.cnf /mnt/conf.d/server.cnf
echo server_id = $(expr 100 + $name) >> /mnt/conf.d/server.cnf
else
cp /mnt/config-map/slave.cnf /mnt/conf.d/server.cnf
echo server_id = $(expr 100 + $name) >> /mnt/conf.d/server.cnf
fi
volumeMounts:
- name: config
mountPath: /mnt/conf.d
- name: config-map
mountPath: /mnt/config-map
containers:
- name: mariadb
image: docker.io/library/mariadb:10.6.4
env:
- name: MARIADB_ROOT_PASSWORD
value: "000000"
volumeMounts:
- name: host-time
mountPath: /etc/localtime
- name: data
mountPath: /var/lib/mysql
- name: config
mountPath: /etc/mysql/conf.d
- name: mariadb-sidecar
image: docker.io/library/mariadb:10.6.4
command:
- bash
- "-c"
- |
set -ex
cd /etc/mysql/conf.d/
name=$(hostname|cut -d - -f 2)
until mysql -h 127.0.0.1 -uroot -p000000 -e "select 1";do sleep 1;done
#通过name判断
if [ $name -eq 0 ];then
mysql -h 127.0.0.1 -uroot -p000000 -e "grant replication slave, replication client on *.* to 'sl'@'%' identified by '000000';"
mysql -h 127.0.0.1 -uroot -p000000 -e "flush privileges;"
else
## 这里需要注意pod的名称和headless
binglogName=$(mysql -uroot -h mariadb-0.mariadb.default.svc.cluster.local -p000000 -e "show master status\G" | grep -o "mysql-bin.*")
echo $binglogName
posName=$(mysql -uroot -h mariadb-0.mariadb.default.svc.cluster.local -p000000 -e "show master status\G" | grep Position | awk '{print $2}')
echo $posName
mysql -h 127.0.0.1 -uroot -p000000 -e "stop slave;"
mysql -h 127.0.0.1 -uroot -p000000 -e "change master to master_host='mariadb-0.mariadb.default.svc.cluster.local',master_user='sl',master_password='000000',master_log_file='$binglogName',master_log_pos=$posName;"
mysql -h 127.0.0.1 -uroot -p000000 -e "start slave;"
mysql -h 127.0.0.1 -uroot -p000000 -e "show slave status\G;"
fi
tail -f /etc/passwd
volumeMounts:
- name: data
mountPath: /var/lib/mysql
- name: config
mountPath: /etc/mysql/conf.d
volumes:
- name: host-time
hostPath:
path: /etc/localtime
type: ''
- name: config
emptyDir: {}
- name: config-map
configMap:
name: mariadb
volumeClaimTemplates:
- metadata:
name: data
spec:
storageClassName: nfs
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
2️⃣ initcontainer
分析
initContainers:
- name: init-mariadb
image: docker.io/library/mariadb:10.6.4
command:
- bash
- "-c"
- |
#判断pod是主节点还是从节点,注意需要pod名为 xxx-1,xxx-0 这种,当然StatefulSet会自动生成
name=$(hostname|cut -d - -f 2)
if [ name -eq 0 ]; then
cp /mnt/config-map/master.cnf /mnt/conf.d/server.cnf
echo server_id = $(expr 100 + $name) >> /mnt/conf.d/server.cnf
else
cp /mnt/config-map/slave.cnf /mnt/conf.d/server.cnf
echo server_id = $(expr 100 + $name) >> /mnt/conf.d/server.cnf
fi
volumeMounts:
- name: config
mountPath: /mnt/conf.d
- name: config-map
mountPath: /mnt/config-map
1.镜像可以替换成 busybox
等其他镜像
2.执行了一个 command
,使用 statefulset
的 id
判断是否为主还是从,复制存储在 configmap
中的配置,注意这里是因为在 volumes
中挂载了 configmap
volumes:
- name: host-time
hostPath:
path: /etc/localtime
type: ''
- name: config
emptyDir: {}
- name: config-map
configMap:
name: mariadb
3.根据 id
计算数据库的唯一标识并重定向至配置文件中
3️⃣ 边车容器分析
- name: mariadb-sidecar
image: docker.io/library/mariadb:10.6.4
command:
- bash
- "-c"
- |
set -ex
cd /etc/mysql/conf.d/
name=$(hostname|cut -d - -f 2)
until mysql -h 127.0.0.1 -uroot -p000000 -e "select 1";do sleep 1;done
#通过name判断
if [ $name -eq 0 ];then
mysql -h 127.0.0.1 -uroot -p000000 -e "grant replication slave, replication client on *.* to 'sl'@'%' identified by '000000';"
mysql -h 127.0.0.1 -uroot -p000000 -e "flush privileges;"
else
## 这里需要注意pod的名称和headless
binglogName=$(mysql -uroot -h mariadb-0.mariadb.default.svc.cluster.local -p000000 -e "show master status\G" | grep -o "mysql-bin.*")
echo $binglogName
posName=$(mysql -uroot -h mariadb-0.mariadb.default.svc.cluster.local -p000000 -e "show master status\G" | grep Position | awk '{print $2}')
echo $posName
mysql -h 127.0.0.1 -uroot -p000000 -e "stop slave;"
mysql -h 127.0.0.1 -uroot -p000000 -e "change master to master_host='mariadb-0.mariadb.default.svc.cluster.local',master_user='sl',master_password='000000',master_log_file='$binglogName',master_log_pos=$posName;"
mysql -h 127.0.0.1 -uroot -p000000 -e "start slave;"
mysql -h 127.0.0.1 -uroot -p000000 -e "show slave status\G;"
fi
tail -f /etc/passwd
volumeMounts:
- name: data
mountPath: /var/lib/mysql
- name: config
mountPath: /etc/mysql/conf.d
1.边车容器需要对主 mariadb
容器进行主从配置操作操作
2.也是通过 id
来判断主从操作,如果是主则创建主从配置用户,如果是从则从主容器中拿取数据并配置 slave
,
3.注意边车容器不能关闭,所以需要使用某些方法来挂起
4️⃣ 主容器分析
containers:
- name: mariadb
image: docker.io/library/mariadb:10.6.4
env:
- name: MARIADB_ROOT_PASSWORD
value: "000000"
volumeMounts:
- name: host-time
mountPath: /etc/localtime
- name: data
mountPath: /var/lib/mysql
- name: config
mountPath: /etc/mysql/conf.d
1.需要通过 MARIADB_ROOT_PASSWORD
环境变量来设置数据的密码,详情请查看https://hub.docker.com/_/mariadb
2.挂载了 data
和 config
,config
在之前 init
容器中就已经复制好了配置文件,data
是使用的 pv
,在 statefulSet
的后面还有一个 pvcTemplate
,挂载至 /var/lib/mysql
用于持久化保存数据
volumeClaimTemplates:
- metadata:
name: data
spec:
storageClassName: nfs
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
3.还是通过 id
来判断主从节点的操作
5️⃣ 启动容器后查看
##执行
[root@k8s-master Mariadb]# kubectl apply -f StatefulSet.yml
statefulset.apps/mariadb created
##由于网络问题可能会重启pod几次
[root@k8s-master Mariadb]# kubectl get pod
NAME READY STATUS RESTARTS AGE
mariadb-0 2/2 Running 0 95s
mariadb-1 2/2 Running 3 92s
##查看mariadb-1边车容器配置
[root@k8s-master Mariadb]# kubectl logs -f mariadb-1 mariadb-sidecar
+ cd /etc/mysql/conf.d/
++ hostname
++ cut -d - -f 2
+ name=1
+ mysql -h 127.0.0.1 -uroot -p000000 -e 'select 1'
1
1
+ '[' 1 -eq 0 ']'
++ grep -o 'mysql-bin.*'
++ mysql -uroot -h mariadb-0.mariadb.default.svc.cluster.local -p000000 -e 'show master status\G'
+ binglogName=mysql-bin.000004
+ echo mysql-bin.000004
mysql-bin.000004
++ mysql -uroot -h mariadb-0.mariadb.default.svc.cluster.local -p000000 -e 'show master status\G'
++ grep Position
++ awk '{print $2}'
+ posName=661
+ echo 661
+ mysql -h 127.0.0.1 -uroot -p000000 -e 'stop slave;'
661
+ mysql -h 127.0.0.1 -uroot -p000000 -e 'change master to master_host='\''mariadb-0.mariadb.default.svc.cluster.local'\'',master_user='\''sl'\'',master_password='\''000000'\'',master_log_file='\''mysql-bin.000004'\'',master_log_pos=661;'
+ mysql -h 127.0.0.1 -uroot -p000000 -e 'start slave;'
+ mysql -h 127.0.0.1 -uroot -p000000 -e 'show slave status\G;'
*************************** 1. row ***************************
Slave_IO_State: Checking master version
Master_Host: mariadb-0.mariadb.default.svc.cluster.local
Master_User: sl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000004
Read_Master_Log_Pos: 661
Relay_Log_File: relay-bin.000001
Relay_Log_Pos: 4
Relay_Master_Log_File: mysql-bin.000004
Slave_IO_Running: Preparing
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 661
Relay_Log_Space: 256
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 100
Master_SSL_Crl:
Master_SSL_Crlpath:
Using_Gtid: No
Gtid_IO_Pos:
Replicate_Do_Domain_Ids:
Replicate_Ignore_Domain_Ids:
Parallel_Mode: optimistic
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
Slave_DDL_Groups: 0
Slave_Non_Transactional_Groups: 0
Slave_Transactional_Groups: 0
+ tail -f /etc/passwd
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
mysql:x:999:999::/home/mysql:/bin/sh
###查看slave状态
[root@k8s-master Mariadb]# kubectl exec -it mariadb-0 -- bash
Defaulting container name to mariadb.
Use 'kubectl describe pod/mariadb-0 -n default' to see all of the containers in this pod.
root@mariadb-0:/# mysql -uroot -p000000 -hmariadb-1.mariadb
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 17
Server version: 10.6.4-MariaDB-1:10.6.4+maria~focal-log mariadb.org binary distribution
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: mariadb-0.mariadb.default.svc.cluster.local
Master_User: sl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000004
Read_Master_Log_Pos: 661
Relay_Log_File: relay-bin.000002
Relay_Log_Pos: 555
Relay_Master_Log_File: mysql-bin.000004
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 661
Relay_Log_Space: 858
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 100
Master_SSL_Crl:
Master_SSL_Crlpath:
Using_Gtid: No
Gtid_IO_Pos:
Replicate_Do_Domain_Ids:
Replicate_Ignore_Domain_Ids:
Parallel_Mode: optimistic
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
Slave_DDL_Groups: 0
Slave_Non_Transactional_Groups: 0
Slave_Transactional_Groups: 0
1 row in set (0.001 sec)