MySQL Operator for Kubernetes自动实现整个生命周期

官网地址:MySQL :: MySQL Operator for Kubernetes Manual

开源地址:mysql/mysql-operator: MySQL Operator for Kubernetes

一、引言与介绍

MySQL Operator for Kubernetes 是 Kubernetes 生态中专为 MySQL 设计的专属运维工具,核心基于CRD(自定义资源) 实现,旨在彻底简化 MySQL InnoDB Cluster 的部署、运维与扩展,其核心逻辑与组件价值可通过三层清晰拆解:

1. 基础核心

整个方案的运作起点是 Kubernetes 的 CRD 扩展 ------InnoDBCluster 作为自定义资源,为 MySQL 集群提供了标准化的配置入口。用户只需定义InnoDBCluster资源的 yaml 清单(指定实例数量、配置参数等),提交至 K8s 集群后,便会成为 Operator 的 "操作指令",所有后续的集群搭建、维护动作,均由 Operator 基于该 CRD 资源的状态触发,这是实现集群自动化管理的核心基础。

2. 核心组件

(1)MySQL Operator:集群的 "自动化管家"

作为方案的核心执行者,Operator 运行于 K8s 集群的mysql-operator命名空间(默认),通过 Kubernetes Deployment 保障自身高可用。其核心作用是持续监听InnoDBCluster自定义资源的状态变化:一旦检测到资源创建、更新或异常,便自动触发一系列运维操作 ------ 包括创建管理 MySQL 服务器的 StatefulSet(绑定存储卷、配置容器组)、部署 MySQL Router、生成 ConfigMap(MySQL 配置)与 Secret(认证凭证),同时自动配置 MySQL Group Replication 实现数据同步,全程无需人工介入复杂配置。

(2)InnoDB Cluster:数据库集群的 "核心实体"

InnoDBCluster不仅是 CRD 资源名称,更是实际运行的 MySQL 数据库集群核心 ------ 由一组 MySQL 服务器(Primary/Secondary 节点)组成,通过 Operator 配置的 Group Replication 实现数据实时同步,天然具备高可用特性。它是存储数据、处理业务请求的核心载体,Operator 通过维护其节点状态、复制链路,确保集群稳定运行,避免单点故障。

(3)MySQL Router:应用与集群的 "智能网关"

MySQL Router 是 Operator 自动部署的无状态路由组件(通过 K8s Deployment 管理,支持弹性扩缩容)。其核心作用是作为应用与 InnoDB Cluster 之间的中间层:一方面为应用提供稳定的访问入口(通过 K8s Service 暴露),屏蔽集群节点拓扑变化;另一方面根据应用需求智能路由请求 ------ 将写请求转发至 Primary 节点,读请求分发至 Secondary 节点,既简化了应用连接配置,又实现了读写分离与负载均衡。

3. 核心价值

借助 CRD 的标准化配置与 Operator 的自动化管理,开发者无需深入 MySQL 底层运维细节,仅通过定义InnoDBCluster资源即可快速搭建高可用 MySQL 集群;而 MySQL Router 的路由能力与 InnoDB Cluster 的复制机制,既确保了应用访问的稳定性(无需关心节点切换),又支持根据业务负载弹性扩展,完美适配 Kubernetes 的容器化、云原生场景。

Kubernetes的MySQL作符架构图

本文采用完全资源清单的方式去部署,因为Helm我没看到官方的一个定义,找不到标准的资源镜像,然后我就懒得去install Chart包去修改values下面的镜像tag了,索性直接使用资源清单部署了。

就比如我要部署8.4.8-2.1.10版本的Chart,然后我要去提前拉取镜像嘛。(因为我第一次也是直接helm install 但是发现拉取的镜像是8.4.8)

我就登录到了Oracle Container Registry仓库,但是没有找到8.4.8-2.1.10的镜像,然后我还没有找到官方的Helm的定义,我就没有下载8.4.8的chart然后修改镜像为8.4.7,我怕CRD到时候会有不兼容之类的情况,所以这里就直接使用8.4.7去部署了一套自动化架构。

难道在不同的仓库?本人没研究这个架构太长时间,有懂得可以解释一下。

二、 架构的实现

本文采用的所有资源标准均为自己的实验环境,请生成环境自助调整资源配置大小。

资源清单方式安装8.4.7mysql-operator/deploy at 8.4.7-2.1.9 · mysql/mysql-operator

2.1. 安装Operator与CRD资源

bash 复制代码
[root@k8s-master ~/mysql-operator]# kubectl apply -f https://github.com/mysql/mysql-operator/blob/8.4.7-2.1.9/deploy/deploy-crds.yaml
[root@k8s-master ~/mysql-operator]# kubectl apply -f https://github.com/mysql/mysql-operator/blob/8.4.7-2.1.9/deploy/deploy-operator.yaml

[root@k8s-master ~/mysql-operator]# kubectl -n mysql-operator get all -o wide
NAME                                  READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
pod/mysql-operator-6c49944b64-fmbwq   1/1     Running   0          18s   10.200.36.75   k8s-node1   <none>           <none>

NAME                             READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS       IMAGES                                                               SELECTOR
deployment.apps/mysql-operator   1/1     1            1           18s   mysql-operator   container-registry.oracle.com/mysql/community-operator:8.4.7-2.1.9   name=mysql-operator

NAME                                        DESIRED   CURRENT   READY   AGE   CONTAINERS       IMAGES                                                               SELECTOR
replicaset.apps/mysql-operator-6c49944b64   1         1         1       18s   mysql-operator   container-registry.oracle.com/mysql/community-operator:8.4.7-2.1.9   name=mysql-operator,pod-template-hash=6c49944b64

彩蛋:镜像拉取方法

登录Oracle Container Registry: Oracle Container Registry

登录完成点击右上角Auth Token去新建Token

然后将自己生成的Token复制保存下来。

记住拷贝自己的Secret

bash 复制代码
ehaR9Dqba1cbkYxcMpfJ

docker登录镜像仓库

bash 复制代码
[root@k8s-master ~]# docker login container-registry.oracle.com
Username: 914XXXXX@qq.com
Password: # 输入自己生成的Secret作为密码

WARNING! Your credentials are stored unencrypted in '/root/.docker/config.json'.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/

Login Succeeded

# 拉取镜像即可,注意K8S集群最好都存在此镜像。
[root@k8s-master ~]# docker pull container-registry.oracle.com/mysql/community-operator:8.4.7-2.1.9
8.4.7-2.1.9: Pulling from mysql/community-operator
034ed5741540: Pull complete 
8850e9060076: Pull complete 
72a19e97697b: Pull complete 
cb8069ff2565: Pull complete 
20b9802feff4: Pull complete 
3290b0b94ed1: Pull complete 
cea3ba72d0c2: Pull complete 
Digest: sha256:f04a98bba815c33f649a802a1669f85ebe5ead4dcb54264babd8749b70b056f2
Status: Downloaded newer image for container-registry.oracle.com/mysql/community-operator:8.4.7-2.1.9
container-registry.oracle.com/mysql/community-operator:8.4.7-2.1.9
[root@k8s-master ~]# docker pull container-registry.oracle.com/mysql/community-router:8.4.7
8.4.7: Pulling from mysql/community-router
63ce17d7402e: Pull complete 
c1796c1f2872: Pull complete 
b54c084a1ded: Pull complete 
Digest: sha256:b1d63a7ee895db71d9c763085192a378958d2cc5bf3ec6a2a1feb9f21652d9ba
Status: Downloaded newer image for container-registry.oracle.com/mysql/community-router:8.4.7
container-registry.oracle.com/mysql/community-router:8.4.7

2.2. 部署InnoDBCluster+Router

要用 kubectl 创建 InnoDB 集群, 首先创建一个包含新MySQL凭证的秘密 root 用户,一个名为"my-root-secret"的秘密示例:

bash 复制代码
[root@k8s-master ~/mysql-operator]# kubectl create ns mysql-cluster
namespace/mysql-cluster created
[root@k8s-master ~/mysql-operator]# kubectl create secret generic my-root-secret \
  --from-literal=rootUser=root \
  --from-literal=rootHost=% \
  --from-literal=rootPassword='String@1307!' \
  --namespace mysql-cluster
secret/my-root-secret created

用新创建的用户配置新的MySQL InnoDB集群。这个例子是InnoDBCluster定义 创建三个 MySQL 服务器实例和两个 MySQL 路由器实例:

bash 复制代码
[root@k8s-master ~/mysql-operator/resource-yaml]# cat mysql-innodb-cluster.yaml 
apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: my-cluster
  namespace: mysql-cluster
spec:
  version: "8.4.7"
  instances: 3
  secretName: my-root-secret
  edition: community
  tlsUseSelfSigned: true

  # --- 1. 备份策略配置 (关键修复点) ---
  backupProfiles:
    - name: nfs-profile
      dumpInstance:
        storage:
          # 必须使用全称
          persistentVolumeClaim:
            claimName: mysql-backup-pvc

  # --- 2. 数据库参数优化 (针对 4G 内存环境) ---
  mycnf: |
    [mysqld]
    # 限制内存池为 512M,给系统留余地
    innodb_buffer_pool_size=512M
    # 开启性能监控以支持 InnoDB 集群
    performance_schema=ON
    max_connections=150
    innodb_log_file_size=128M
    binlog_expire_logs_seconds=604800
    # 减少文件系统缓存压力
    innodb_flush_method=O_DIRECT

  # --- 3. 资源配额 (QoS) ---
  podSpec:
    resources:
      limits:
        cpu: "500m"
        memory: "1.5Gi"
      requests:
        cpu: "100m"
        memory: "512Mi"

  # --- 4. 数据存储配置 ---
  datadirVolumeClaimTemplate:
    storageClassName: "nfs-csi"
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        storage: 10Gi

  # --- 5. 读写分离 Router 配置 ---
  router:
    instances: 2
    podSpec:
      # 强制调度到打过标签的节点
      nodeSelector:
        mysql-router: "true"
      containers:
      - name: router
        image: container-registry.oracle.com/mysql/community-router:8.4.7
        imagePullPolicy: IfNotPresent
        resources:
          requests:
            cpu: "100m"
            memory: "128Mi"
          limits:
            cpu: "200m"
            memory: "256Mi"

在把备份要用到了PV给创建出来

bash 复制代码
[root@k8s-master ~/mysql-operator/resource-yaml]# cat mysql-backup-pvc.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-backup-pvc
  namespace: mysql-cluster
spec:
  # 确保这里用的是你集群里正确的 NFS StorageClass 名字
  storageClassName: "nfs-csi" 
  accessModes:
    - ReadWriteMany  # 推荐:NFS 一般支持多读写,方便查看
  resources:
    requests:
      storage: 10Gi

然后将资源给创建出来

bash 复制代码
[root@k8s-master ~/mysql-operator/resource-yaml]# kubectl apply -f mysql-backup-pvc.yaml 
[root@k8s-master ~/mysql-operator/resource-yaml]# kubectl get -f mysql-backup-pvc.yaml
NAME               STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
mysql-backup-pvc   Bound    pvc-2c7017a9-75ef-4523-94c9-dfcf6a30b819   10Gi       RWX            nfs-csi        <unset>                 3m37s

注意,在创建InnoDBCluster之前,请将两个要部署Router的节点打上对应的标签,来方便分节点调度。

bash 复制代码
[root@k8s-master ~]# kubectl label nodes k8s-node1 mysql-router=true
node/k8s-node1 labeled
[root@k8s-master ~]# kubectl label nodes k8s-node2 mysql-router=true
node/k8s-node2 labeled
[root@k8s-master ~/mysql-operator/resource-yaml]# kubectl apply -f mysql-innodb-cluster.yaml 

[root@k8s-master ~/mysql-operator/resource-yaml]# kubectl -n mysql-cluster get po,pv,pvc -o wide
NAME                                    READY   STATUS    RESTARTS   AGE     IP               NODE        NOMINATED NODE   READINESS GATES
pod/my-cluster-0                        2/2     Running   0          4m37s   10.200.36.82     k8s-node1   <none>           2/2
pod/my-cluster-1                        2/2     Running   0          4m37s   10.200.36.81     k8s-node1   <none>           2/2
pod/my-cluster-2                        2/2     Running   0          4m37s   10.200.169.167   k8s-node2   <none>           2/2
pod/my-cluster-router-67fd998b7-6m865   1/1     Running   0          2m39s   10.200.36.83     k8s-node1   <none>           <none>
pod/my-cluster-router-67fd998b7-xrrx7   1/1     Running   0          2m39s   10.200.169.168   k8s-node2   <none>           <none>

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE     VOLUMEMODE
persistentvolume/pvc-2c7017a9-75ef-4523-94c9-dfcf6a30b819   10Gi       RWX            Delete           Bound    mysql-cluster/mysql-backup-pvc       nfs-csi        <unset>                          4m50s   Filesystem
persistentvolume/pvc-4df4cd5a-35c8-40de-a500-922ebfd8dc2d   10Gi       RWO            Delete           Bound    mysql-cluster/datadir-my-cluster-2   nfs-csi        <unset>                          4m36s   Filesystem
persistentvolume/pvc-c0d0a765-00bd-4d4c-be94-2f2f79d7acc4   10Gi       RWO            Delete           Bound    mysql-cluster/datadir-my-cluster-1   nfs-csi        <unset>                          4m36s   Filesystem
persistentvolume/pvc-e5d7e917-d24e-4835-9798-03de85a6c054   10Gi       RWO            Delete           Bound    mysql-cluster/datadir-my-cluster-0   nfs-csi        <unset>                          4m37s   Filesystem

NAME                                         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE     VOLUMEMODE
persistentvolumeclaim/datadir-my-cluster-0   Bound    pvc-e5d7e917-d24e-4835-9798-03de85a6c054   10Gi       RWO            nfs-csi        <unset>                 4m37s   Filesystem
persistentvolumeclaim/datadir-my-cluster-1   Bound    pvc-c0d0a765-00bd-4d4c-be94-2f2f79d7acc4   10Gi       RWO            nfs-csi        <unset>                 4m37s   Filesystem
persistentvolumeclaim/datadir-my-cluster-2   Bound    pvc-4df4cd5a-35c8-40de-a500-922ebfd8dc2d   10Gi       RWO            nfs-csi        <unset>                 4m37s   Filesystem
persistentvolumeclaim/mysql-backup-pvc       Bound    pvc-2c7017a9-75ef-4523-94c9-dfcf6a30b819   10Gi       RWX            nfs-csi        <unset>                 4m50s   Filesystem

# 确保三个都已经在线即可
[root@k8s-master ~/mysql-operator/resource-yaml]# kubectl -n mysql-cluster get innodbclusters.mysql.oracle.com 
NAME         STATUS   ONLINE   INSTANCES   ROUTERS   AGE
my-cluster   ONLINE   3        3           2         4m58s

资源创建完成之后可以查看对应的数据目录是否以及初始化到了数据

bash 复制代码
# 查看数据目录
[root@k8s-master ~/mysql-operator/resource-yaml]# cd /data/nfs-server/pvc-4df4cd5a-35c8-40de-a500-922ebfd8dc2d/
[root@k8s-master /data/nfs-server/pvc-4df4cd5a-35c8-40de-a500-922ebfd8dc2d]# ll
total 135792
-rw-r----- 1 27 sudo  4194304 Jan 27 10:29 '#ib_16384_0.dblwr'
-rw-r----- 1 27 sudo 12582912 Jan 27 09:46 '#ib_16384_1.dblwr'
drwxr-s--- 2 27 sudo     4096 Jan 27 09:47  '#innodb_redo'/
drwxr-s--- 2 27 sudo     4096 Jan 27 09:47  '#innodb_temp'/
-rw-r----- 1 27 sudo        0 Jan 27 09:48  GCS_DEBUG_TRACE
-rw-r----- 1 27 sudo       56 Jan 27 09:46  auto.cnf
-rw------- 1 27 sudo     1705 Jan 27 09:46  ca-key.pem
-rw-r--r-- 1 27 sudo     1108 Jan 27 09:46  ca.pem
-rw-r--r-- 1 27 sudo     1108 Jan 27 09:46  client-cert.pem
-rw------- 1 27 sudo     1705 Jan 27 09:46  client-key.pem
-rw-r----- 1 27 sudo     5721 Jan 27 09:47  ib_buffer_pool
-rw-r----- 1 27 sudo 12582912 Jan 27 10:29  ibdata1
-rw-r----- 1 27 sudo 12582912 Jan 27 09:47  ibtmp1
-rw-r----- 1 27 sudo      236 Jan 27 09:48  my-cluster-2-relay-bin-group_replication_applier.000001
-rw-r----- 1 27 sudo 15400214 Jan 27 10:29  my-cluster-2-relay-bin-group_replication_applier.000002
-rw-r----- 1 27 sudo      116 Jan 27 09:48  my-cluster-2-relay-bin-group_replication_applier.index
-rw-r----- 1 27 sudo      158 Jan 27 09:48  my-cluster-2-relay-bin-group_replication_recovery.000001
-rw-r----- 1 27 sudo       59 Jan 27 09:48  my-cluster-2-relay-bin-group_replication_recovery.index
-rw-r----- 1 27 sudo        2 Jan 27 09:47  my-cluster-2.pid
-rw-r----- 1 27 sudo      181 Jan 27 09:46  my-cluster.000001
-rw-r----- 1 27 sudo      181 Jan 27 09:47  my-cluster.000002
-rw-r----- 1 27 sudo 15528012 Jan 27 10:29  my-cluster.000003
-rw-r----- 1 27 sudo       60 Jan 27 09:47  my-cluster.index
drwxr-s--- 2 27 sudo     4096 Jan 27 09:46  mysql/
-rw-r----- 1 27 sudo 32505856 Jan 27 10:29  mysql.ibd
drwxr-s--- 2 27 sudo     4096 Jan 27 09:48  mysql_innodb_cluster_metadata/
-rw-r----- 1 27 sudo      124 Jan 27 09:46  mysql_upgrade_history
-rw-r----- 1 27 sudo     1771 Jan 27 09:48  mysqld-auto.cnf
drwxr-s--- 2 27 sudo     4096 Jan 27 09:48  performance_schema/
-rw------- 1 27 sudo     1705 Jan 27 09:46  private_key.pem
-rw-r--r-- 1 27 sudo      452 Jan 27 09:46  public_key.pem
-rw-r--r-- 1 27 sudo     1108 Jan 27 09:46  server-cert.pem
-rw------- 1 27 sudo     1705 Jan 27 09:46  server-key.pem
drwxr-s--- 2 27 sudo     4096 Jan 27 09:46  sys/
-rw-r----- 1 27 sudo 16777216 Jan 27 10:29  undo_001
-rw-r----- 1 27 sudo 16777216 Jan 27 10:29  undo_002

2.3 将Router暴露出去

bash 复制代码
# 创建Route的SVC端口暴露,可以在外部使用图形化工具直接运维
[root@k8s-master ~/mysql-operator/resource-yaml]# cat mysql-route-svc.yaml 
apiVersion: v1
kind: Service
metadata:
  name: my-cluster-router-lb
  namespace: mysql-cluster
  labels:
    app.kubernetes.io/name: my-cluster-router-lb
spec:
  type: NodePort
  selector:
    app.kubernetes.io/component: router
    app.kubernetes.io/name: mysql-router
  ports:
    - name: mysql-rw
      port: 30306
      targetPort: 6446
      nodePort: 30306  # 读写端口暴露给外部
    - name: mysql-ro
      port: 30307
      targetPort: 6447
      nodePort: 30307  # 只读端口暴露给外部
  sessionAffinity: None


[root@k8s-master ~/mysql-operator/resource-yaml]# kubectl apply -f mysql-route-svc.yaml
service/my-cluster-router-lb created
[root@k8s-master ~/mysql-operator/resource-yaml]# kubectl -n mysql-cluster get svc
NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                                                                    AGE
my-cluster             ClusterIP   10.110.26.7     <none>        3306/TCP,33060/TCP,6446/TCP,6448/TCP,6447/TCP,6449/TCP,6450/TCP,8443/TCP   13m
my-cluster-instances   ClusterIP   None            <none>        3306/TCP,33060/TCP,33061/TCP                                               13m
my-cluster-router-lb   NodePort    10.102.39.114   <none>        30306:30306/TCP,30307:30307/TCP                                            7s

测试连接

bash 复制代码
[root@k8s-master ~/mysql-operator/resource-yaml]# telnet 10.0.0.6 30306
Trying 10.0.0.6...
Connected to 10.0.0.6.
Escape character is '^]'.
P
8.4.7-router~
             b1߮nC1Cn,H$o8q~caching_sha2_password^]
telnet> quit
Connection closed.
[root@k8s-master ~/mysql-operator/resource-yaml]# telnet 10.0.0.6 30307
Trying 10.0.0.6...
Connected to 10.0.0.6.
Escape character is '^]'.
P
8.4.7-routerAh@#N!W߮hPmW0Igu]^4!caching_sha2_password^]
telnet> quit
Connection closed.

也可以直接使用Navicat进行连接

三、备份功能

3.1 验证备份(dumpInstance

bash 复制代码
[root@k8s-master ~/mysql-operator/resource-yaml]# cat test-backup.yaml 
apiVersion: mysql.oracle.com/v2
kind: MySQLBackup
metadata:
  name: test-backup-01
  namespace: mysql-cluster
spec:
  clusterName: my-cluster
  # 这里引用的就是我们在第三步里定义的 profile 名字
  backupProfileName: nfs-profile
  
# 查看备份状态
[root@k8s-master ~/mysql-operator/resource-yaml]# kubectl -n mysql-cluster get mysqlbackups.mysql.oracle.com -w
NAME             CLUSTER      STATUS   OUTPUT   AGE
test-backup-01   my-cluster                     6s
test-backup-01   my-cluster   Running   test-backup-01-20260127-022008   14s
test-backup-01   my-cluster   Completed   test-backup-01-20260127-022008   16s

找到备份目录MySQL Operator 为了避免备份数据丢失,设计上会为每一次备份生成一个唯一的目录名,格式通常是:{备份名称}-{日期}-{时间戳}。

bash 复制代码
[root@k8s-master ~/mysql-operator/resource-yaml]# kubectl get pv -n mysql-cluster 
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
pvc-2c7017a9-75ef-4523-94c9-dfcf6a30b819   10Gi       RWX            Delete           Bound    mysql-cluster/mysql-backup-pvc       nfs-csi        <unset>                          35m
pvc-4df4cd5a-35c8-40de-a500-922ebfd8dc2d   10Gi       RWO            Delete           Bound    mysql-cluster/datadir-my-cluster-2   nfs-csi        <unset>                          34m
pvc-c0d0a765-00bd-4d4c-be94-2f2f79d7acc4   10Gi       RWO            Delete           Bound    mysql-cluster/datadir-my-cluster-1   nfs-csi        <unset>                          34m
pvc-e5d7e917-d24e-4835-9798-03de85a6c054   10Gi       RWO            Delete           Bound    mysql-cluster/datadir-my-cluster-0   nfs-csi        <unset>                          34m
[root@k8s-master ~/mysql-operator/resource-yaml]# cd /data/nfs-server/pvc-2c7017a9-75ef-4523-94c9-dfcf6a30b819/test-backup-01-20260127-022008/
[root@k8s-master /data/nfs-server/pvc-2c7017a9-75ef-4523-94c9-dfcf6a30b819/test-backup-01-20260127-022008]# ll
total 308
-rw-r----- 1 27 sudo  1504 Jan 27 10:20 @.done.json
-rw-r----- 1 27 sudo  1352 Jan 27 10:20 @.json
-rw-r----- 1 27 sudo   293 Jan 27 10:20 @.post.sql
-rw-r----- 1 27 sudo   293 Jan 27 10:20 @.sql
-rw-r----- 1 27 sudo 10021 Jan 27 10:20 @.users.sql
-rw-r----- 1 27 sudo  2937 Jan 27 10:20 mysql_innodb_cluster_metadata.json
-rw-r----- 1 27 sudo 25611 Jan 27 10:20 mysql_innodb_cluster_metadata.sql
-rw-r----- 1 27 sudo   819 Jan 27 10:20 mysql_innodb_cluster_metadata@async_cluster_members.json
-rw-r----- 1 27 sudo  1271 Jan 27 10:20 mysql_innodb_cluster_metadata@async_cluster_members.sql
-rw-r----- 1 27 sudo     9 Jan 27 10:20 mysql_innodb_cluster_metadata@async_cluster_members@@0.tsv.zst
-rw-r----- 1 27 sudo     8 Jan 27 10:20 mysql_innodb_cluster_metadata@async_cluster_members@@0.tsv.zst.idx
-rw-r----- 1 27 sudo   830 Jan 27 10:20 mysql_innodb_cluster_metadata@async_cluster_views.json
-rw-r----- 1 27 sudo  1235 Jan 27 10:20 mysql_innodb_cluster_metadata@async_cluster_views.sql
-rw-r----- 1 27 sudo     9 Jan 27 10:20 mysql_innodb_cluster_metadata@async_cluster_views@@0.tsv.zst
-rw-r----- 1 27 sudo     8 Jan 27 10:20 
......

备份没有问题,然后就可以将配置中修改为定时备份。apply一下生效即可。

bash 复制代码
# 修改mysql-innodb-cluster.yaml配置下面的备份配置
...

backupProfiles:
  - name: nfs-profile
    # 新增:定时备份的 cron 表达式(每天凌晨2点)
    schedule: "0 2 * * *"
    # 备份类型(dumpInstance 表示逻辑备份)
    dumpInstance:
      storage:
        # 绑定已创建的 PVC(必须用全称 persistentVolumeClaim,不能简写)
        persistentVolumeClaim:
          claimName: mysql-backup-pvc
    # 可选:备份保留策略(避免备份文件占满存储)
    retention:
      # 保留最近7天的备份
      days: 7
...

3.2 数据恢复设计

官方推荐数据恢复: 你需要创建一个全新的 InnoDBCluster ,并在创建时告诉它:"请用这个备份初始化自己"。 核心字段是 spec.initDB。

bash 复制代码
apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: my-cluster-restored  # 【注意】必须是个新名字,不能和旧集群冲突
  namespace: mysql-cluster
spec:
  version: "8.4.7"
  instances: 3
  router:
    instances: 1
  secretName: my-root-secret
  
  # --- 核心恢复配置 ---
  initDB:
    dump:
      name: nfs-backup-final-20260126-xxx  # 这里填你 NFS 目录下具体的那个文件夹名字
      storage:
        persistentVolumeClaim:
          claimName: mysql-backup-pvc      # 指向存放备份的那个 PVC

为什么这种方式更好?

  1. 安全:旧集群(案发现场)被保留下来了,如果恢复失败,你还有机会去旧集群里抢救数据。

  2. 原子性:新集群启动时,要么是满血复活的状态,要么就是启动失败,不会出现"数据恢复了一半"的尴尬状态。

3.3 MySQL shell恢复(loadDump

MySQL :: MySQL Shell 8.4 :: 3.1 MySQL Shell Commands

如果你不想改名字(因为业务代码可能写死了连接地址),或者 initDB 方式报错(有时候 Operator 对 PVC 的挂载路径支持有 Bug),那么最稳、最无视版本差异 的方法,手动 MySQL Shell 导入法。

bash 复制代码
清理旧数据(慎用):

如果确定旧数据不要了,直接进数据库删库,或者重建 PVC。

然后使用NFS-Server的备份恢复。

bash 复制代码
mysqlsh -u root -p'String@1307!' -h 127.0.0.1 -P 30306 \
  --util loadDump "/data/backup/nfs-backup-final-xxx" \
  --threads=4 \
  --resetProgress

3.3 OCI对象存储恢复

在 MySQL Operator (8.4+) 的逻辑中,从 OCI 对象存储恢复从本地 PVC 恢复,不仅操作逻辑不同,连背后的技术实现都有很大差异。

维度 OCI Object Storage (对象存储) PersistentVolumeClaim (你的 NFS/PV)
技术路径 真正的"一键恢复" "初始化"或"手动导入"
Operator 行为 Operator 会自动下载、解压、流式导入 Operator 通常只支持在"首次启动"时挂载
灵活性 支持从云端直接"拉取"到任何新集群 依赖物理卷的挂载,路径和权限很死板
配置字段 官方文档主推 initDB.ociObjectStorage 官方文档对 initDB.dump.persistentVolumeClaim 描述较少

官方(Oracle)的逻辑是:如果你用他们的云(OCI),它能帮你做得非常自动化。

  • 对于 OCI :支持直接在 InnoDBCluster 运行时,通过某种触发机制或者 initDB 完美拉取。

  • 对于 PVC :它更倾向于认为这是一个**"快照"** 或者**"导出目录"** 。正如你看到的,8.4 版本删除了 MySQLRestore 这种显式的恢复指令。

这里的OCIociObjectStorage 参数是专为甲骨文云(Oracle Cloud Infrastructure)设计的"亲儿子"功能,其他云目前没有这种待遇,但是你可以将你的备份存储到其他的对象存储上。

3.4 传统的mysqldump和XBK进行备份

但是备份时必须连 30307 (RO 端口) :利用 Router 的读写分离,把备份压力卸载到**从库(Secondary)**上。

方案 A:使用 mysqldump(逻辑备份,最稳)

适合数据量在 50G 以下的场景

bash 复制代码
mysqldump -u root -p'String@1307!' -h 127.0.0.1 -P 30307 \
  --all-databases \
  --single-transaction \
  --triggers --routines --events \
  --set-gtid-purged=ON \
  --master-data=2 \
  > /data/backup/full_$(date +%F).sql

方案 B:使用 Xtrabackup(物理备份,最快)

如果你的数据超过 100G,mysqldump 恢复太慢,建议用 XBK。但在 K8s 中,XBK 无法通过网络直接备份,它需要物理接触数据文件

推荐做法(Sidecar 模式或 CronJob):

  1. 创建一个临时 Pod,挂载其中一个从库(如 my-cluster-2)的 PVC。

  2. 或者通过 kubectl exec 进到一个从库 Pod 内部执行:

bash 复制代码
# 进入从库执行,避免主库 IO 阻塞
kubectl exec -it my-cluster-2 -n mysql-cluster -- xtrabackup --backup \
  --user=root --password='String@1307!' \
  --target-dir=/var/lib/mysql/backup/

考虑到"主从切换"的稳健策略

在 K8s 中,Pod 可能会漂移。为了确保备份脚本不失效,自动化脚本应该这样写:

逻辑流:

  1. 探测谁是从库 :通过 SQL select * from performance_schema.replication_group_members 确认成员状态。

  2. 锁定目标 :通过 Router 的 30307 端口进行连接。即便发生主从切换,Router 会自动把你的备份请求导向新的从库,脚本无需修改 IP。

  3. 定期清理:在 NFS 上保留最近 7 天的备份。

建议的备份计划 (Crontab)

频率 备份方式 存储位置 目的
每 1 小时 增量/日志备份 NFS 减少数据丢失窗口
每天凌晨 2 点 mysqldump 全量 NFS 长期存档,应对误删库
每周/月 Xtrabackup 全量 离线存储 灾难性硬件故障恢复

总结一句话:在 K8s 里用传统工具,核心就是"连 Router 的只读端口做备份,连读写端口做恢复"。

四、验证集群状态

4.1 查看集群状态

bash 复制代码
 [root@k8s-master ~/mysql-operator/resource-yaml]# kubectl -n mysql-cluster exec -it my-cluster-0 -- mysqlsh
Defaulted container "sidecar" out of: sidecar, mysql, fixdatadir (init), initconf (init), initmysql (init)
MySQL Shell 8.4.7

Copyright (c) 2016, 2025, 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 '\?' for help; '\quit' to exit.

# 连接集群
 MySQL  SQL > \connect root@localhost
Creating a session to 'root@localhost'
Please provide the password for 'root@localhost': ************
Fetching global names for auto-completion... Press ^C to stop.
Your MySQL connection id is 17774 (X protocol)
Server version: 8.4.7 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.

# 执行SQL语句
 MySQL  localhost:33060+ ssl  SQL > show databases;
+-------------------------------+
| Database                      |
+-------------------------------+
| information_schema            |
| mysql                         |
| mysql_innodb_cluster_metadata |
| performance_schema            |
| sys                           |
+-------------------------------+
5 rows in set (0.0017 sec)

# 切换语言类型
 MySQL  localhost:33060+ ssl  SQL > \js
Switching to JavaScript mode...

# 查看集群状态
 MySQL  localhost:33060+ ssl  JS > dba.getCluster().status()
{
    "clusterName": "my_cluster", 
    "defaultReplicaSet": {
        "name": "default", 
        "primary": "my-cluster-0.my-cluster-instances.mysql-cluster.svc.cluster.local:3306", 
        "ssl": "REQUIRED", 
        "status": "OK", 
        "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", 
        "topology": {
            "my-cluster-0.my-cluster-instances.mysql-cluster.svc.cluster.local:3306": {
                "address": "my-cluster-0.my-cluster-instances.mysql-cluster.svc.cluster.local:3306", 
                "memberRole": "PRIMARY", 
                "mode": "R/W", 
                "readReplicas": {}, 
                "replicationLag": "applier_queue_applied", 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.4.7"
            }, 
            "my-cluster-1.my-cluster-instances.mysql-cluster.svc.cluster.local:3306": {
                "address": "my-cluster-1.my-cluster-instances.mysql-cluster.svc.cluster.local:3306", 
                "memberRole": "SECONDARY", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "replicationLag": "applier_queue_applied", 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.4.7"
            }, 
            "my-cluster-2.my-cluster-instances.mysql-cluster.svc.cluster.local:3306": {
                "address": "my-cluster-2.my-cluster-instances.mysql-cluster.svc.cluster.local:3306", 
                "memberRole": "SECONDARY", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "replicationLag": "applier_queue_applied", 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.4.7"
            }
        }, 
        "topologyMode": "Single-Primary"
    }, 
    "groupInformationSourceMember": "my-cluster-0.my-cluster-instances.mysql-cluster.svc.cluster.local:3306"
}
 MySQL  localhost:33060+ ssl  JS > 

4.2 测试主从复制

bash 复制代码
# 从JS切换为SQL语言
MySQL  localhost:33060+ ssl  JS > \sql
Switching to SQL mode... Commands end with ;
-- 1. 创建数据库
CREATE DATABASE sync_test;

-- 2. 切换数据库
USE sync_test;

-- 3. 创建表
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;

-- 4. 插入一条数据
INSERT INTO users (name) VALUES ('Gemini_Test_Data');

-- 5. 查看数据
 MySQL  localhost:33060+ ssl  sync_test  SQL > SELECT * FROM users;
+----+------------------+---------------------+
| id | name             | created_at          |
+----+------------------+---------------------+
|  1 | Gemini_Test_Data | 2026-01-27 02:47:13 |
+----+------------------+---------------------+
1 row in set (0.0015 sec)
bash 复制代码
# 再打开一个窗口,连接从库查看数据
 MySQL  localhost:33060+ ssl  SQL > SELECT * FROM sync_test.users;
+----+------------------+---------------------+
| id | name             | created_at          |
+----+------------------+---------------------+
|  1 | Gemini_Test_Data | 2026-01-27 02:47:13 |
+----+------------------+---------------------+
1 row in set (0.0008 sec)
 MySQL  localhost:33060+ ssl  
bash 复制代码
主节点再插入数据然后从节点查看
# master节点
 MySQL  localhost:33060+ ssl  sync_test  SQL > INSERT INTO sync_test.users (name) VALUES ('Illegal_Write');
Query OK, 1 row affected (0.0084 sec)
 MySQL  localhost:33060+ ssl  sync_test  SQL > \quit
Bye!
#replicas节点
 MySQL  localhost:33060+ ssl  SQL > SELECT * FROM sync_test.users;
+----+------------------+---------------------+
| id | name             | created_at          |
+----+------------------+---------------------+
|  1 | Gemini_Test_Data | 2026-01-27 02:47:13 |
|  2 | Illegal_Write    | 2026-01-27 02:49:20 |
+----+------------------+---------------------+
2 rows in set (0.0012 sec)
 MySQL  localhost:33060+ ssl  SQL > exit
                                 -> ^C
 MySQL  localhost:33060+ ssl  SQL > \quit
Bye!

4.3 测试读写分离

第一步:找准端口

因为目前使用 NodePort 暴露的服务,每次重建端口可能会变,所以先确认一下当前的端口映射:

bash 复制代码
kubectl get svc -n mysql-cluster

请重点看 my-cluster-router 这一行,找到对应的映射关系:

  • RW 端口 (读写) :找到映射到容器 6446 的那个端口(例如 30306)。

  • RO 端口 (只读) :找到映射到容器 6447 的那个端口(例如 30307)。

第二步:验证"读写端口" (RW Port - 6446)

这个端口是给写操作用的,它必须始终指向 Primary (主节点) 。我们连查 3 次,应该每次都返回同一个名字

bash 复制代码
[root@k8s-master ~ ]# apt install -y mysql-client

# 记得替换 -P 后面的端口为你查到的 6446 映射端口
[root@k8s-master ~/mysql-operator/resource-yaml]# for i in {1..3}; do
  mysql -u root -p'String@1307!' -h 127.0.0.1 -P 30306 -e "select @@hostname;"
done
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------+
| @@hostname   |
+--------------+
| my-cluster-0 |
+--------------+
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------+
| @@hostname   |
+--------------+
| my-cluster-0 |
+--------------+
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------+
| @@hostname   |
+--------------+
| my-cluster-0 |
+--------------+

✅ 预期结果: 输出的 Hostname 完全一样 (例如全是 my-cluster-0)。这说明流量很稳,没乱跑。

第三步:验证"只读端口" (RO Port - 6447) ------ 关键步骤

这个端口是给读操作用的,它应该在所有 Secondary (从节点) 之间做负载均衡。我们连查 5 次,应该看到名字在轮换

bash 复制代码
[root@k8s-master ~/mysql-operator/resource-yaml]# # 记得替换 -P 后面的端口为你查到的 6447 映射端口
for i in {1..5}; do
  mysql -u root -p'String@1307!' -h 127.0.0.1 -P 30307 -e "select @@hostname;"
done
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------+
| @@hostname   |
+--------------+
| my-cluster-2 |
+--------------+
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------+
| @@hostname   |
+--------------+
| my-cluster-1 |
+--------------+
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------+
| @@hostname   |
+--------------+
| my-cluster-2 |
+--------------+
mysql: [Warning] Using a password on the command line interface can be insecure.

+--------------+
| @@hostname   |
+--------------+
| my-cluster-1 |
+--------------+
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------+
| @@hostname   |
+--------------+
| my-cluster-2 |
+--------------+

✅ 预期结果: 输出的 Hostname 会在不同的 Pod 名字之间跳变(例如 my-cluster-1 -> my-cluster-2 -> my-cluster-1...)。

🎁 附加题:验证只读属性 (防误写)

为了证明 RO 端口真的是"只读"的,咱们尝试往里面插数据,看看它会不会报错。

bash 复制代码
[root@k8s-master ~/mysql-operator/resource-yaml]# mysql -u root -p'String@1307!' -h 127.0.0.1 -P 30307 -e "create table test_db.should_fail(id int);"
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 1290 (HY000) at line 1: The MySQL server is running with the --super-read-only option so it cannot execute this statement

没问题。

4.4 测试高可用

可以看到目前cluster-0是PRIMARY状态。

bash 复制代码
 MySQL  localhost:33060+ ssl  SQL > SELECT MEMBER_HOST, MEMBER_ROLE, MEMBER_STATE  FROM performance_schema.replication_group_members;
+-------------------------------------------------------------------+-------------+--------------+
| MEMBER_HOST                                                       | MEMBER_ROLE | MEMBER_STATE |
+-------------------------------------------------------------------+-------------+--------------+
| my-cluster-1.my-cluster-instances.mysql-cluster.svc.cluster.local | SECONDARY   | ONLINE       |
| my-cluster-0.my-cluster-instances.mysql-cluster.svc.cluster.local | PRIMARY     | ONLINE       |
| my-cluster-2.my-cluster-instances.mysql-cluster.svc.cluster.local | SECONDARY   | ONLINE       |
+-------------------------------------------------------------------+-------------+--------------+
3 rows in set (0.0007 sec)

打开多个窗口实时监控MySQL-cluster和innoDBCluster的状态

bash 复制代码
[root@k8s-master ~/mysql-operator/resource-yaml]# kubectl -n mysql-cluster get po -w
NAME                                   READY   STATUS      RESTARTS   AGE
my-cluster-0                           2/2     Running     0          67m
my-cluster-1                           2/2     Running     0          67m
my-cluster-2                           2/2     Running     0          67m
my-cluster-router-67fd998b7-6m865      1/1     Running     0          65m
my-cluster-router-67fd998b7-xrrx7      1/1     Running     0          65m
test-backup-01-20260127-022008-jjzzk   0/1     Completed   0          34m


[root@k8s-master ~]# kubectl -n mysql-cluster get innodbclusters.mysql.oracle.com -w
NAME         STATUS   ONLINE   INSTANCES   ROUTERS   AGE
my-cluster   ONLINE   3        3           2         68m

然后在另外窗口直接delete掉Pod[或者直接关机也行]

bash 复制代码
[root@k8s-master ~/mysql-operator/resource-yaml]# kubectl get po -n mysql-cluster 
NAME                                   READY   STATUS      RESTARTS   AGE
my-cluster-0                           2/2     Running     0          68m
my-cluster-1                           2/2     Running     0          68m
my-cluster-2                           2/2     Running     0          68m
my-cluster-router-67fd998b7-6m865      1/1     Running     0          66m
my-cluster-router-67fd998b7-xrrx7      1/1     Running     0          66m
test-backup-01-20260127-022008-jjzzk   0/1     Completed   0          35m
[root@k8s-master ~/mysql-operator/resource-yaml]# kubectl -n mysql-cluster delete po my-cluster-0 
pod "my-cluster-0" deleted

然后持续监控MySQL-cluster和innoDBCluster的状态,直到集群在短期内自动恢复正常

bash 复制代码
# Pod以及自动恢复
[root@k8s-master ~/mysql-operator/resource-yaml]# kubectl -n mysql-cluster get po -w
NAME                                   READY   STATUS      RESTARTS   AGE
my-cluster-0                           2/2     Running     0          67m
my-cluster-1                           2/2     Running     0          67m
my-cluster-2                           2/2     Running     0          67m
my-cluster-router-67fd998b7-6m865      1/1     Running     0          65m
my-cluster-router-67fd998b7-xrrx7      1/1     Running     0          65m
test-backup-01-20260127-022008-jjzzk   0/1     Completed   0          34m
my-cluster-0                           2/2     Terminating   0          69m
my-cluster-0                           2/2     Terminating   0          69m
my-cluster-0                           2/2     Terminating   0          69m
my-cluster-0                           2/2     Terminating   0          69m
my-cluster-1                           2/2     Running       0          69m
my-cluster-1                           2/2     Running       0          69m
my-cluster-2                           2/2     Running       0          69m
my-cluster-2                           2/2     Running       0          69m
my-cluster-0                           2/2     Terminating   0          69m
my-cluster-1                           2/2     Running       0          69m
my-cluster-2                           2/2     Running       0          69m
my-cluster-0                           2/2     Terminating   0          70m
my-cluster-0                           0/2     Completed     0          70m
my-cluster-0                           0/2     Completed     0          70m
my-cluster-0                           0/2     Completed     0          70m
my-cluster-0                           0/2     Pending       0          0s
my-cluster-0                           0/2     Pending       0          0s
my-cluster-0                           0/2     Init:0/3      0          0s
my-cluster-0                           0/2     Init:0/3      0          0s
my-cluster-0                           0/2     Init:0/3      0          0s
my-cluster-0                           0/2     Init:0/3      0          1s
my-cluster-0                           0/2     Init:1/3      0          5s
my-cluster-0                           0/2     Init:1/3      0          8s
my-cluster-0                           0/2     Init:2/3      0          10s
my-cluster-0                           0/2     PodInitializing   0          12s
my-cluster-0                           1/2     Running           0          19s
my-cluster-0                           1/2     Running           0          24s
my-cluster-0                           1/2     Running           0          25s
my-cluster-0                           2/2     Running           0          30s
bash 复制代码
# InnoDB Cluster也以及恢复
[root@k8s-master ~]# kubectl -n mysql-cluster get innodbclusters.mysql.oracle.com -w
NAME         STATUS   ONLINE   INSTANCES   ROUTERS   AGE
my-cluster   ONLINE   3        3           2         68m
my-cluster   ONLINE   3        3           2         69m
my-cluster   ONLINE_PARTIAL   2        3           2         69m
my-cluster   ONLINE_PARTIAL   2        3           2         70m
my-cluster   ONLINE           3        3           2         71m

再次查看主从和高可用的关系,可以看到cluster1变成了新主

bash 复制代码
 MySQL  localhost:33060+ ssl  SQL > SELECT MEMBER_HOST, MEMBER_ROLE, MEMBER_STATE  FROM performance_schema.replication_group_members;
+-------------------------------------------------------------------+-------------+--------------+
| MEMBER_HOST                                                       | MEMBER_ROLE | MEMBER_STATE |
+-------------------------------------------------------------------+-------------+--------------+
| my-cluster-1.my-cluster-instances.mysql-cluster.svc.cluster.local | PRIMARY     | ONLINE       |
| my-cluster-0.my-cluster-instances.mysql-cluster.svc.cluster.local | SECONDARY   | ONLINE       |
| my-cluster-2.my-cluster-instances.mysql-cluster.svc.cluster.local | SECONDARY   | ONLINE       |
+-------------------------------------------------------------------+-------------+--------------+
3 rows in set (0.0009 sec)

4.5 检查集群状态

检查读写分离,没有问题即可。

bash 复制代码
# 读写分离依然生效就没有问题
[root@k8s-master ~/mysql-operator/resource-yaml]# for i in {1..5}; do
  mysql -u root -p'String@1307!' -h 127.0.0.1 -P 30307 -e "select @@hostname;"
done
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------+
| @@hostname   |
+--------------+
| my-cluster-2 |
+--------------+
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------+
| @@hostname   |
+--------------+
| my-cluster-0 |
+--------------+
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------+
| @@hostname   |
+--------------+
| my-cluster-2 |
+--------------+
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------+
| @@hostname   |
+--------------+
| my-cluster-2 |
+--------------+
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------+
| @@hostname   |
+--------------+
| my-cluster-0 |
+--------------+
[root@k8s-master ~/mysql-operator/resource-yaml]# for i in {1..3}; do
  mysql -u root -p'String@1307!' -h 127.0.0.1 -P 30306 -e "select @@hostname;"
done
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------+
| @@hostname   |
+--------------+
| my-cluster-1 |
+--------------+
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------+
| @@hostname   |
+--------------+
| my-cluster-1 |
+--------------+
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------+
| @@hostname   |
+--------------+
| my-cluster-1 |
+--------------+
[root@k8s-master ~/mysql-operator/resource-yaml]# 
bash 复制代码
 MySQL  localhost:33060+ ssl  JS > dba.getCluster().status()
{
    "clusterName": "my_cluster", 
    "defaultReplicaSet": {
        "name": "default", 
        "primary": "my-cluster-1.my-cluster-instances.mysql-cluster.svc.cluster.local:3306", 
        "ssl": "REQUIRED", 
        "status": "OK", 
        "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", 
        "topology": {
            "my-cluster-0.my-cluster-instances.mysql-cluster.svc.cluster.local:3306": {
                "address": "my-cluster-0.my-cluster-instances.mysql-cluster.svc.cluster.local:3306", 
                "memberRole": "SECONDARY", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "replicationLag": "applier_queue_applied", 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.4.7"
            }, 
            "my-cluster-1.my-cluster-instances.mysql-cluster.svc.cluster.local:3306": {
                "address": "my-cluster-1.my-cluster-instances.mysql-cluster.svc.cluster.local:3306", 
                "memberRole": "PRIMARY", 
                "mode": "R/W", 
                "readReplicas": {}, 
                "replicationLag": "applier_queue_applied", 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.4.7"
            }, 
            "my-cluster-2.my-cluster-instances.mysql-cluster.svc.cluster.local:3306": {
                "address": "my-cluster-2.my-cluster-instances.mysql-cluster.svc.cluster.local:3306", 
                "memberRole": "SECONDARY", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "replicationLag": "applier_queue_applied", 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.4.7"
            }
        }, 
        "topologyMode": "Single-Primary"
    }, 
    "groupInformationSourceMember": "my-cluster-1.my-cluster-instances.mysql-cluster.svc.cluster.local:3306"
}
 MySQL  localhost:33060+ ssl  JS > \quit
Bye!

五、基础运维MySQL Shell命令

MySQL :: MySQL Shell 8.4

MySQL Shell 是 Operator 的 "底层执行引擎",而接触的是 Operator 封装后的 K8s CR/Helm 等 "上层运维接口"

MySQL Operator 底层几乎所有核心运维操作,都是通过调用 MySQL Shell 实现的,但你作为使用者,日常运维并不需要直接写 MySQL Shell 命令(除非是手动调试 / 恢复这类特殊场景)。

5.1 核心连接与会话管理

命令(含别名) 核心功能 实用示例
\connect\c 连接 MySQL 实例(支持多协议) \c root@127.0.0.1:3306(经典协议); \c --mysqlx root@localhost:33060(X 协议); \c --ssh root@192.168.1.100:22 root@localhost:3306(SSH 隧道)
\reconnect 重新连接当前实例(连接断开时用) 直接输入:\reconnect
\disconnect 断开全局会话(保留 MySQL Shell 不退出) 直接输入:\disconnect
\status\s 查看当前连接状态(实例信息、字符集、 uptime 等) 直接输入:\s

5.2 模式切换与脚本执行(多语言适配)

命令 核心功能 实用示例
\sql 切换到 SQL 模式(执行标准 SQL) 输入 \sql 后,直接写 SQL:SELECT * FROM test.t1;
\js 切换到 JavaScript 模式(调用 AdminAPI/X DevAPI) 输入 \js 后,执行:dba.status()(集群状态)
\py 切换到 Python 模式(语法适配 Python) 输入 \py 后,执行:dba.get_cluster()
\source\./source 执行脚本文件(按当前模式解析) \source /tmp/init.sql(SQL 模式执行 SQL 脚本);\source /tmp/backup.js(JS 模式执行脚本

5.3 备份恢复与集群运维

命令 / 工具类 核心功能 实用示例(结合之前场景)
util.dumpInstance() 逻辑备份整个实例(Operator 备份底层命令) JS 模式:util.dumpInstance('/data/backup', {threads:4})
util.loadDump() 恢复 dumpInstance 备份(适配 Operator 备份文件) 命令行直接执行:mysqlsh -u root -p'pass' --util loadDump /data/backup --threads=4
dba.createCluster() 创建 InnoDB Cluster(Operator 集群初始化底层命令) JS 模式:dba.createCluster('mycluster')
dba.getCluster().status() 查看集群状态(故障排查用) JS 模式:var cluster = dba.getCluster(); cluster.status();

六、总结

在云原生技术席卷全球的当下,数据库的容器化、自动化管理已成为企业数字化转型的核心需求之一,而 MySQL Operator for Kubernetes 正是为解决这一需求而生的标杆方案 ------ 它以 CRD(自定义资源)为底层基石,串联起从部署、运维、验证到数据安全的全链路能力,将 MySQL 的高可用特性与 Kubernetes 的云原生优势深度融合,为 MySQL 集群管理提供了一套标准化、自动化、可扩展的完整解决方案。

从核心逻辑来看,整套方案的设计极具巧思:InnoDBCluster 自定义资源作为标准化配置入口,让用户无需关注复杂的底层细节,仅通过简单的 YAML 清单即可定义集群需求;MySQL Operator 扮演 "自动化管家" 的核心角色,持续监听 CRD 状态变化,自动完成 StatefulSet 部署、Group Replication 配置、Router 搭建等一系列运维操作,全程无需人工介入;InnoDB Cluster 作为数据库集群的 "核心实体",通过主从复制实现数据实时同步,天然规避单点故障;而 MySQL Router 则以 "智能网关" 身份,为应用提供稳定访问入口与读写分离能力,完美屏蔽集群拓扑变化对上层业务的影响。这四大核心要素相互支撑,构建起一套协同高效、稳定可靠的组件生态。

在落地实践层面,方案的易用性与实用性尤为突出:架构实现阶段,从 Operator 与 CRD 的快速安装,到 InnoDB Cluster 与 Router 的一键部署、路由暴露,流程简洁清晰,配合镜像拉取等实用彩蛋,大幅降低了云原生新手的入门门槛;数据安全层面,覆盖 dumpInstance/loadDump 原生工具、OCI 对象存储备份、传统 mysqldump 与 XBK 等多种备份恢复方式,既适配云原生场景,又兼容传统运维习惯,全方位保障数据不丢失;集群验证环节,通过集群状态查看、主从复制测试、读写分离验证、高可用故障演练等全方位手段,帮助用户快速确认集群是否达到生产级标准;日常运维层面,MySQL Shell 提供了统一的命令集,涵盖连接管理、模式切换、集群运维、备份恢复等核心能力,且支持多语言脚本执行,显著降低了运维人员的学习成本与操作复杂度。

更重要的是,这套方案并非简单的 "技术堆砌",而是真正切中了开发者与运维人员的核心痛点:开发者无需深入钻研 MySQL 底层复制原理与 K8s 资源调度逻辑,即可快速搭建高可用测试环境;运维人员无需面对繁琐的手动配置与故障排查,即可实现集群的规模化管理与弹性扩展,大幅提升运维效率、降低人为失误风险。无论是中小型企业的轻量化部署,还是大型企业的规模化集群管理,这套方案都能凭借其良好的扩展性与适配性灵活应对。

总而言之,MySQL Operator for Kubernetes 不仅是一套数据库管理工具,更是云原生时代 MySQL 高可用集群管理的 "最佳实践"。它以自动化简化复杂运维,以标准化保障环境一致性,以多场景适配满足不同业务需求,让 MySQL 数据库在容器化生态中实现 "即部署、即可用、即稳定",成为企业在云原生转型路上值得信赖的数据库管理优选方案。

相关推荐
Nandeska2 小时前
10、MySQL8.0新增特性
数据库·mysql
天空属于哈夫克33 小时前
企业微信外部群运营升级:API 主动推送消息开发实战
java·数据库·mysql
NGINX开源社区4 小时前
借助 Okta 和 NGINX Ingress Controller 实现 K8s OpenID Connect 身份验证
运维·nginx·kubernetes
什么都不会的Tristan4 小时前
MySQL篇
数据库·mysql
阿里云云原生5 小时前
RUM 链路打通实战:打破移动端可观测性黑洞
阿里云·云原生·rum
久绊A5 小时前
K8s集群异常Pod高效排查命令(kube-system核心场景)
云原生·容器·kubernetes
卌卄5 小时前
【Linux】MySQL安装配置
linux·运维·mysql
小二·6 小时前
Go 语言系统编程与云原生开发实战(第3篇):企业级 RESTful API 开发 —— 中间件、验证、文档与权限控制
云原生·golang·restful
Arbori_262156 小时前
clickhouse 实现mysql GROUP_CONCAT() 函数
数据库·mysql·clickhouse