KubeSphere 最佳实战:一文搞定,在 Kubernetes 集群上部署主从复制 MySQL
2024 年云原生运维实战文档 99 篇原创计划 第 049 篇 |KubeSphere 最佳实战「2024」系列 第 021 篇
你好,欢迎来到运维有术。
今天,我分享一篇专题文档,这份文档源自运维有术星球成员的特别约稿。他目前面临的挑战是:在 Kubernetes 集群上实现 MySQL 主从复制集群的部署,尽管已经探索了数日,但仍未寻得一个满意的解决方案。
尽管我通常不建议在单一 Kubernetes 集群上实施 MySQL 主从复制方案,但为了满足我们成员的需求,我进行了深入研究,经过数日的探索和实际测试后,撰写了本文。有类似需求的读者可以作为参考。
本文将作为您的技术指南,详细指导您如何利用已有的虚拟机和容器化部署 MySQL 主从复制的经验,实现在同一个 Kubernetes 集群上部署主从复制模式的 MySQL 集群。
实战服务器配置(架构1:1复刻小规模生产环境,配置略有不同)
主机名 | IP | CPU | 内存 | 系统盘 | 数据盘 | 用途 |
---|---|---|---|---|---|---|
ksp-registry | 192.168.9.90 | 4 | 8 | 40 | 200 | Harbor 镜像仓库 |
ksp-control-1 | 192.168.9.91 | 4 | 8 | 40 | 100 | KubeSphere/k8s-control-plane |
ksp-control-2 | 192.168.9.92 | 4 | 8 | 40 | 100 | KubeSphere/k8s-control-plane |
ksp-control-3 | 192.168.9.93 | 4 | 8 | 40 | 100 | KubeSphere/k8s-control-plane |
ksp-worker-1 | 192.168.9.94 | 8 | 16 | 40 | 100 | k8s-worker/CI |
ksp-worker-2 | 192.168.9.95 | 8 | 16 | 40 | 100 | k8s-worker |
ksp-worker-3 | 192.168.9.96 | 8 | 16 | 40 | 100 | k8s-worker |
ksp-storage-1 | 192.168.9.97 | 4 | 8 | 40 | 400 | ElasticSearch/Longhorn/Ceph/NFS |
ksp-storage-2 | 192.168.9.98 | 4 | 8 | 40 | 300 | ElasticSearch/Longhorn/Ceph |
ksp-storage-3 | 192.168.9.99 | 4 | 8 | 40 | 300 | ElasticSearch/Longhorn/Ceph |
ksp-gpu-worker-1 | 192.168.9.101 | 4 | 16 | 40 | 100 | k8s-worker(GPU NVIDIA Tesla M40 24G) |
ksp-gpu-worker-2 | 192.168.9.102 | 4 | 16 | 40 | 100 | k8s-worker(GPU NVIDIA Tesla P100 16G) |
ksp-gateway-1 | 192.168.9.103 | 2 | 4 | 40 | 自建应用服务代理网关/VIP:192.168.9.100 | |
ksp-gateway-2 | 192.168.9.104 | 2 | 4 | 40 | 自建应用服务代理网关/VIP:192.168.9.100 | |
ksp-mid | 192.168.9.105 | 4 | 8 | 40 | 100 | 部署在 k8s 集群之外的服务节点(Gitlab 等) |
合计 | 15 | 68 | 152 | 600 | 2100 |
实战环境涉及软件版本信息
- 操作系统:openEuler 22.03 LTS SP3 x86_64
- KubeSphere:v3.4.1
- Kubernetes:v1.28.8
- KubeKey: v3.1.1
- MySQL:v5.7.44
1. 部署方案规划
重要说明:
MySQL 社区为了支持“黑人的命也是命”(Black Lives Matter, BLM)运动,从 8.0 版本起,在其文档和术语中不再使用可能带有种族歧视色彩的词汇。具体来说,Master 将被 Source 替代,而 Slave 将改为 Replica。
在本文中,我们将遵循这一新命名规则,尽管在描述时仍会沿用大家熟悉的“主从”表述,但在涉及组件名称和配置文件时,将统一使用 Source 来指代主节点,Replica 来指代从节点。
1.1 部署架构图
1.2 部署方案说明
- my.cnf 配置文件抽取为 ConfigMap
之前我分享的 Docker 部署主从复制模式 MySQL 实战 一文中也将 conf、logs、data 等数据挂载到宿主机上,在 k8s 上将配置文件抽离出来做成 ConfigMap,主从节点使用不同的配置。
- MySQL 初始化时使用的密码信息抽取为 Secret
MySQL 部署初始化时可以提供 root 用户的密码,本方案使用 Secret,且主从节点使用相同的配置。
- 使用 StatefulSet 创建 MySQL 服务
部署 MySQL 服务使用 StatefulSet 而不选择 Deployment。
- 使用 PVC 持久化数据
使用 StatefulSet 的 volumeClaimTemplates 功能动态创建 PVC 存储卷声明。
- 集群内访问使用 DNS 提供稳定的域名
主从节点分别创建 Headless Service 服务,主从同步时使用 Headless Service 服务的 DNS 域名。
- 应用访问
k8s 集群内的应用,访问 MySQL 主节点的 Headless Service 对应的 DNS 域名,集群外的应用通过 mysql-source-external
服务的 NodePort 端口访问 MySQL 主节点。
2. 创建 ConfigMap
2.1 主节点 ConfigMap 配置
请使用 vi
编辑器,新建从节点 my.cnf
资源清单文件 mysql-source-cnf.yaml
,并输入以下内容:
kind: ConfigMap
apiVersion: v1
metadata:
name: mysql-source-cnf
data:
mysqld.cnf: |-
[mysqld]
# performance settings
lock_wait_timeout = 3600
open_files_limit = 65535
back_log = 1024
max_connections = 2048
max_connect_errors = 1000000
table_open_cache = 1024
table_definition_cache = 1024
thread_stack = 512K
sort_buffer_size = 4M
join_buffer_size = 4M
read_buffer_size = 8M
read_rnd_buffer_size = 4M
bulk_insert_buffer_size = 64M
thread_cache_size = 768
interactive_timeout = 600
wait_timeout = 600
tmp_table_size = 32M
max_heap_table_size = 32M
innodb_open_files = 1024
# TLS 配置
tls_version = TLSv1.2
# Replication settings
server_id = 1
log_bin = mysql-bin
log_bin_index = mysql-bin.index
binlog_format = row
2.2 从节点 ConfigMap 配置
请使用 vi
编辑器,新建从节点 my.cnf
资源清单文件 mysql-replica-cnf.yaml
,并输入以下内容:
kind: ConfigMap
apiVersion: v1
metadata:
name: mysql-replica-cnf
namespace: default
annotations:
kubesphere.io/creator: admin
data:
mysqld.cnf: |-
[mysqld]
# performance settings
lock_wait_timeout = 3600
open_files_limit = 65535
back_log = 1024
max_connections = 2048
max_connect_errors = 1000000
table_open_cache = 1024
table_definition_cache = 1024
thread_stack = 512K
sort_buffer_size = 4M
join_buffer_size = 4M
read_buffer_size = 8M
read_rnd_buffer_size = 4M
bulk_insert_buffer_size = 64M
thread_cache_size = 768
interactive_timeout = 600
wait_timeout = 600
tmp_table_size = 32M
max_heap_table_size = 32M
innodb_open_files = 1024
# TLS 配置
tls_version = TLSv1.2
# Replication settings
server_id = 2
log_bin = mysql-bin
log_bin_index = mysql-bin.index
binlog_format = row
relay-log = mysql-relay-bin
relay-log-index = mysql-relay-bin.index
skip_slave_start = 1
log_slave_updates = 1
read_only = 1
2.3 创建资源
执行下面的命令,创建 ConfigMap 资源。
代码语言:bash复制$ kubectl apply -f mysql-source-cnf.yaml
$ kubectl apply -f mysql-replica-cnf.yaml
3. 创建 Secret
3.1 Secret 配置
我们创建一个 Secret 用来存储 MySQL root 用户的密码,主从使用相同的 Secret 配置。
运行 echo -n "OpsXlab2024!" | base64 -w0
命令生成 base64 编码的密码。
使用 vi
编辑器,新建 MySQL Secret 资源清单文件 mysql-secret.yaml
,并输入以下内容:
kind: Secret
apiVersion: v1
metadata:
name: mysql-secret
data:
MYSQL_ROOT_PASSWORD: T3BzWGxhYjIwMjQh
type: Opaque
3.2 创建资源
执行下面的命令,创建 Secret 资源。
代码语言:bash复制$ kubectl apply -f mysql-secret.yaml
4. 创建 Service
4.1 创建主节点 headless 服务
使用 vi
编辑器,新建 MySQL 主节点 headless 资源清单文件 mysql-source-svc.yaml
,并输入以下内容:
kind: Service
apiVersion: v1
metadata:
name: mysql-source-headless
labels:
app: mysql-source
spec:
ports:
- name: tcp-3306
protocol: TCP
port: 3306
targetPort: 3306
selector:
app: mysql-source
clusterIP: None
type: ClusterIP
执行下面的命令,创建资源。
代码语言:bash复制$ kubectl apply -f mysql-source-svc.yaml
4.2 创建从节点 headless 服务
使用 vi
编辑器,新建 MySQL 从节点 headless 资源清单文件 mysql-replica-svc.yaml
,并输入以下内容:
kind: Service
apiVersion: v1
metadata:
name: mysql-replica-headless
labels:
app: mysql-replica
spec:
ports:
- name: tcp-3306
protocol: TCP
port: 3306
targetPort: 3306
selector:
app: mysql-replica
clusterIP: None
type: ClusterIP
执行下面的命令,创建资源。
代码语言:bash复制$ kubectl apply -f mysql-replica-svc.yaml
4.3 创建外部访问服务
我们使用最简单的 NodePort 方式发布 k8s 集群上的 MySQL 服务给外部应用访问,指定的端口为 31306。
使用 vi
编辑器,新建 MySQL 从节点 headless 资源清单文件 mysql-external-svc.yaml
,并输入以下内容:
apiVersion: v1
kind: Service
metadata:
name: mysql-source-external
spec:
type: NodePort
selector:
app: mysql-source
ports:
- port: 3306
targetPort: 3306
nodePort: 31306
执行下面的命令,创建资源。
代码语言:bash复制$ kubectl apply -f mysql-external-svc.yaml
5. 创建 StatefulSet
在 Kubernetes 集群中部署数据库服务时,我们面临着选择有状态服务(StatefulSet)与无状态服务(Deployment)之间的决策。对于 MySQL 这类数据库服务,我们选择使用 StatefulSet 而不是 Deployment,原因如下:
- 稳定的网络身份:StatefulSet 为每个 Pod 分配了一个持久且唯一的网络标识符,这对于 MySQL 这类需要固定主机名或网络地址以维持主从复制关系的数据库服务至关重要。
- 持久化存储:StatefulSet 易于与持久化存储卷结合使用,确保数据库数据的持久保存,即便是在 Pod 重启或重新调度后。
- 适合有状态应用:StatefulSet 是为有状态应用设计的,如数据库和消息队列,它提供了必要的支持来维护这些应用的状态。
5.1 创建主节点 StatefulSet
使用 vi
编辑器,新建 MySQL 主节点 StatefulSet 资源清单文件 mysql-source-sts.yaml
,并输入以下内容:
kind: StatefulSet
apiVersion: apps/v1
metadata:
name: mysql-source
labels:
app: mysql-source
spec:
replicas: 1
selector:
matchLabels:
app: mysql-source
template:
metadata:
labels:
app: mysql-source
spec:
volumes:
- name: host-time
hostPath:
path: /etc/localtime
type: ''
- name: config
configMap:
name: mysql-source-cnf
defaultMode: 420
containers:
- name: mysql-source
image: mysql:5.7.44
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: MYSQL_ROOT_PASSWORD
resources:
limits:
cpu: '2'
memory: 4Gi
requests:
cpu: 100m
memory: 100Mi
volumeMounts:
- name: host-time
mountPath: /etc/localtime
- name: data
mountPath: /var/lib/mysql
- name: config
readOnly: true
mountPath: /etc/mysql/conf.d/
imagePullPolicy: IfNotPresent
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
storageClassName: nfs-sc
volumeMode: Filesystem
serviceName: mysql-source-headless
执行下面的命令,创建资源。
代码语言:bash复制$ kubectl apply -f mysql-source-sts.yaml
5.2 创建从节点 StatefulSet
使用 vi
编辑器,新建 MySQL 从节点 StatefulSet 资源清单文件 mysql-replica-sts.yaml
,并输入以下内容:
kind: StatefulSet
apiVersion: apps/v1
metadata:
name: mysql-replica
labels:
app: mysql-replica
spec:
replicas: 1
selector:
matchLabels:
app: mysql-replica
template:
metadata:
labels:
app: mysql-replica
spec:
volumes:
- name: host-time
hostPath:
path: /etc/localtime
type: ''
- name: config
configMap:
name: mysql-replica-cnf
defaultMode: 420
containers:
- name: mysql-replica
image: mysql:5.7.44
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: MYSQL_ROOT_PASSWORD
resources:
limits:
cpu: '2'
memory: 4Gi
requests:
cpu: 100m
memory: 100Mi
volumeMounts:
- name: host-time
mountPath: /etc/localtime
- name: data
mountPath: /var/lib/mysql
- name: config
readOnly: true
mountPath: /etc/mysql/conf.d/
imagePullPolicy: IfNotPresent
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
storageClassName: nfs-sc
volumeMode: Filesystem
serviceName: mysql-replica-headless
执行下面的命令,创建资源。
代码语言:bash复制$ kubectl apply -f mysql-replica-sts.yaml
5.3 验证 MySQL 主从 Pod 状态
- 查看 Pod 状态
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysql-replica-0 1/1 Running 0 8s 10.233.94.20 ksp-worker-1 <none> <none>
mysql-source-0 1/1 Running 0 12s 10.233.96.173 ksp-worker-3 <none> <none>
- 验证自定义配置是否生效
$ kubectl exec -it mysql-source-0 -- mysql -u root -p -e "show variables like '%max_conn%';"
Enter password:
-------------------- ---------
| Variable_name | Value |
-------------------- ---------
| max_connect_errors | 1000000 |
| max_connections | 2048 |
-------------------- ---------
6. 配置主从同步
6.1 主节点配置
- 进入 MySQL 主节点容器内部
$ kubectl exec -it mysql-source-0 -- mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or g.
Your MySQL connection id is 2
Server version: 5.7.44-log MySQL Community Server (GPL)
Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.
mysql>
- 创建主从同步用户
在主服务器上创建同步使用的用户 repuser
并赋予指定的权限。
-- 创建用户并设置密码
mysql> CREATE USER 'repuser'@'%' IDENTIFIED BY 'ChangeMe';
Query OK, 0 rows affected (0.01 sec)
-- 赋予权限
mysql> GRANT REPLICATION SLAVE,REPLICATION CLIENT ON *.* TO 'repuser'@'%';
Query OK, 0 rows affected (0.01 sec)
-- 刷新权限
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)
repuser: 主从复制用户,实际使用中请替换 ChangeMe: 主从复制用户的密码,实际使用中请替换
- 查看 Master 状态
mysql> show master status;
------------------ ---------- -------------- ------------------ -------------------
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
------------------ ---------- -------------- ------------------ -------------------
| mysql-bin.000003 | 773 | | | |
------------------ ---------- -------------- ------------------ -------------------
1 row in set (0.00 sec)
注意 : 记录 File 列的值 mysql-bin.000003 和 Position 列的 773,从库同步时会用到。
6.2 从节点配置
- 进入 MySQL 从节点容器内部
$ kubectl exec -it mysql-replica-0 -- mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or g.
Your MySQL connection id is 2
Server version: 5.7.44-log MySQL Community Server (GPL)
Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.
mysql>
- 配置主从同步
mysql> change master to master_host='mysql-source-headless.default.svc.cluster.local',master_port=3306,master_user='repuser',master_password='ChangeMe',master_log_file='mysql_bin.000003',master_log_pos=773;
Query OK, 0 rows affected, 2 warnings (0.03 sec)
说明:
master_host: 同步主节点的 DNS,使用 mysql-source-headless.default.svc.cluster.local master_port: 主节点服务端口 master_user: 主节点创建的同步用户的名称 master_password: 主节点创建的同步用户的密码 master_log_file: 主库 Show Master 状态时, File 字段的值 master_log_pos: 主库 Show Master 状态时, Position 字段的值
- 启动主从同步
mysql> start slave;
Query OK, 0 rows affected (0.01 sec)
- 查看主从同步状态
# 查看从从库状态
mysql> show slave statusG;
正确执行后,输出结果如下 :
代码语言:sql复制mysql> show slave status G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: mysql-source-headless.default.svc.cluster.local
Master_User: repuser
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000003
Read_Master_Log_Pos: 773
Relay_Log_File: mysql-relay-bin.000005
Relay_Log_Pos: 986
Relay_Master_Log_File: mysql-bin.000003
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: 773
Relay_Log_Space: 2949023
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: 1
Master_UUID: 95818d7b-7baf-11ef-819e-1af693721830
Master_Info_File: /var/lib/mysql/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates # 重点注意
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.00 sec)
7. 验证主从同步
确认同步状态,在主节点新建数据库及表并添加数据,然后在从节点查询以验证主从同步是否正常。
7.1 主库写入数据
- 创建数据库
mysql> create database opsxlab;
Query OK, 1 row affected (0.01 sec)
- 创建表
# 切换数据库
mysql> use opsxlab;
Database changed
# 创建表
mysql> create table member(name varchar(10), phone varchar(11));
Query OK, 0 rows affected (0.03 sec)
- 插入数据
mysql> insert into member(name, phone) values ("opsxlab", "18888888888");
Query OK, 1 row affected (0.02 sec)
- 查看数据
# 查看表
mysql> show tables;
-------------------
| Tables_in_opsxlab |
-------------------
| member |
-------------------
1 row in set (0.00 sec)
# 查看表内数据
mysql> select * from member;
--------- -------------
| name | phone |
--------- -------------
| opsxlab | 18888888888 |
--------- -------------
1 row in set (0.00 sec)
7.2 从库查看数据
- 查看数据库
mysql> show databases;
--------------------
| Database |
--------------------
| information_schema |
| mysql |
| opsxlab |
| performance_schema |
| sys |
--------------------
5 rows in set (0.01 sec)
- 查看表
# 切换数据库
mysql> use opsxlab;
Database changed
# 查看表
mysql> show tables;
-------------------
| Tables_in_opsxlab |
-------------------
| member |
-------------------
1 row in set (0.00 sec)
- 查看数据
mysql> select * from member;
--------- -------------
| name | phone |
--------- -------------
| opsxlab | 18888888888 |
--------- -------------
1 row in set (0.00 sec)
从结果中可以看到从库也已经同步了刚在主库创建的opsxlab
库、member
表及相应的数据。
至此,我们已经完成了在 KubeSphere 管理的 Kubernetes 集群上手动部署 MySQL 主从同步的全过程。
以上,就是我今天分享的全部内容。下一期分享的内容还没想好,敬请期待开盲盒。
如果你喜欢本文,请分享、收藏、点赞、评论! 请持续关注 @运维有术,及时收看更多好文!
欢迎加入 「知识星球|运维有术」 ,获取更多的 KubeSphere、Kubernetes、云原生运维、自动化运维、AI 大模型等实战技能。未来运维生涯始终有我坐在你的副驾。
免责声明:
- 笔者水平有限,尽管经过多次验证和检查,尽力确保内容的准确性,但仍可能存在疏漏之处。敬请业界专家大佬不吝指教。
- 本文所述内容仅通过实战环境验证测试,读者可学习、借鉴,但严禁直接用于生产环境。由此引发的任何问题,作者概不负责!
Get 本文实战视频(请注意,文档视频异步发行,请先关注)
- B 站|运维有术
版权声明
- 所有内容均属于原创,感谢阅读、收藏,转载请联系授权,未经授权不得转载。