一文搞定!在 Kubernetes 集群上部署主从复制 MySQL

2024-09-27 08:22:33 浏览数 (1)

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 部署架构图

ksp-mysql-source-replicaksp-mysql-source-replica

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,并输入以下内容:

代码语言: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,并输入以下内容:

代码语言: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,并输入以下内容:

代码语言: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,并输入以下内容:

代码语言: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,并输入以下内容:

代码语言:bash复制
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,并输入以下内容:

代码语言: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,并输入以下内容:

代码语言: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,并输入以下内容:

代码语言: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 状态
代码语言:bash复制
$ 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>
  • 验证自定义配置是否生效
代码语言:bash复制
$ 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 主节点配置

  1. 进入 MySQL 主节点容器内部
代码语言:bash复制
$ 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>
  1. 创建主从同步用户

在主服务器上创建同步使用的用户 repuser 并赋予指定的权限。

代码语言:sql复制
-- 创建用户并设置密码
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: 主从复制用户的密码,实际使用中请替换

  1. 查看 Master 状态
代码语言:sql复制
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 从节点配置

  1. 进入 MySQL 从节点容器内部
代码语言:bash复制
$ 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>
  1. 配置主从同步
代码语言:sql复制
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 字段的值

  1. 启动主从同步
代码语言:sql复制
mysql> start slave;

Query OK, 0 rows affected (0.01 sec)
  1. 查看主从同步状态
代码语言:bash复制
# 查看从从库状态
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 主库写入数据

  1. 创建数据库
代码语言:sql复制
mysql> create database opsxlab;
Query OK, 1 row affected (0.01 sec)
  1. 创建表
代码语言:sql复制
# 切换数据库
mysql> use opsxlab;
Database changed

# 创建表
mysql> create table member(name varchar(10), phone varchar(11));
Query OK, 0 rows affected (0.03 sec)
  1. 插入数据
代码语言:sql复制
mysql> insert into member(name, phone) values ("opsxlab", "18888888888");
Query OK, 1 row affected (0.02 sec)
  1. 查看数据
代码语言:sql复制
# 查看表
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 从库查看数据

  1. 查看数据库
代码语言:sql复制
mysql> show databases;
 -------------------- 
| Database           |
 -------------------- 
| information_schema |
| mysql              |
| opsxlab            |
| performance_schema |
| sys                |
 -------------------- 
5 rows in set (0.01 sec)
  1. 查看表
代码语言:sql复制
# 切换数据库
mysql> use opsxlab;
Database changed

# 查看表
mysql> show tables;
 ------------------- 
| Tables_in_opsxlab |
 ------------------- 
| member            |
 ------------------- 
1 row in set (0.00 sec)
  1. 查看数据
代码语言:sql复制
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 站|运维有术

版权声明

  • 所有内容均属于原创,感谢阅读、收藏,转载请联系授权,未经授权不得转载

0 人点赞