在不使用 TopoLVM 的情况下部署 MySQL-MGR

本指南提供了在未安装 TopoLVM 的集群上部署 MySQL Group Replication(MGR)实例的操作说明,例如运行 MicroOS(SUSE Linux Enterprise Micro)的集群,或使用其他存储供应器的环境。

背景

挑战

MySQL-MGR 通常依赖 TopoLVM 进行动态卷供应。然而,某些环境不支持 TopoLVM:

  • MicroOS 集群:根文件系统为只读,且可能未安装 TopoLVM。
  • 裸金属集群:某些集群使用本地存储并手动进行 PV 供应。
  • 测试/评估环境:不包含 TopoLVM 的轻量级部署。

如果没有正确的存储配置,MySQL pod 将因 PVC 绑定失败或权限问题而无法启动。

解决方案

使用通过手动供应的 PersistentVolumes(PV)的本地 StorageClass。本指南涵盖以下内容:

  • 依赖安装:确保在部署 MySQL-MGR 之前已安装所有必需的 operator。
  • StorageClass 创建:使用 WaitForFirstConsumer 绑定模式设置本地 StorageClass。
  • PV 供应:创建具有正确路径、权限和回收策略的本地 PV。
  • ClickHouse 存储修复(可选):避免由 query-analytics 插件硬编码主机路径导致的 read-only file system 错误。
  • 实例创建:使用本地 StorageClass 部署 MySQL-MGR。

前提条件

所需的 Operator

在部署 MySQL-MGR 之前,请确保集群上已安装以下核心 operator:

Operator用途
application-services-core核心平台服务(包括 etcd-sync、log-agent、监控栈等)
rds-operator关系型数据库服务 operator

如果你需要 query analytics 功能(慢查询日志、查询分析),还需要以下 operator:

Operator用途
clickhouse-operatorClickHouse 数据库 operator(供 query analytics 使用)
query-analytics-operator查询分析和慢查询日志
WARNING

在只读文件系统上(例如 MicroOS),你必须在安装 query-analytics-operator 之前,先在 RdsInstaller CR 中配置 ClickHouse 存储路径。否则,operator 会立即在默认的 /cpaas/ck 主机路径下创建 ClickHouse PV,并因 read-only file system 而失败。详情请参见 步骤 3

INFO

如果你的集群使用 TopoLVM,则还需要 topolvm-operator。由于本指南覆盖的是非 TopoLVM 部署,因此此处不需要它。

你可以通过检查 CSV 来验证这些 operator 是否已安装:

kubectl get csv -A | grep -E "application-services-core|rds-operator|clickhouse-operator|query-analytics-operator"

所有 operator 在 PHASE 列中都应显示 Succeeded

集群要求

  • 生产部署建议至少有 3 个 worker 节点(MySQL-MGR 需要 3 个成员才能满足 group replication 的 quorum)。单成员部署可用于测试。
  • 每个节点需要有足够的 CPU 和内存资源。
  • 所有节点上都应有一个可写目录路径可用(例如 MicroOS 上的 /opt/local-pv/)。

步骤 1:创建本地 StorageClass

如果你的集群还没有合适的 StorageClass,请创建一个:

kubectl apply -f - <<EOF
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: mgr-local-pv
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
EOF
WARNING

必须使用 volumeBindingMode: WaitForFirstConsumer,这样只有在 pod 被调度到特定节点后,PV 才会与之绑定。这可确保数据本地性。

INFO

StorageClass 名称并不固定。本指南使用 mgr-local-pv 以将其与 Rancher 的 local-path 动态供应器区分开。你可以使用任意名称,但在后续步骤中必须保持一致引用。

步骤 2:供应本地 PersistentVolumes

每个 MySQL-MGR 成员需要一个 PV。对于 3 成员集群,至少创建 3 个 PV,并分布在不同节点上。

在节点上创建目录

在 MicroOS 上,根文件系统是只读的。请使用 /opt/local-pv/ 作为基础路径:

# 在每个 worker 节点上运行,或者使用特权 pod
for i in $(seq 1 9); do
  mkdir -p /opt/local-pv/pv$i
  chmod 777 /opt/local-pv/pv$i
done
WARNING
  • 路径:在 MicroOS 上,不要使用 /data 或其他根级路径——它们会因 read-only file system 而失败。请改用 /opt/local-pv/
  • 权限chmod 777 只是一个方便的临时方案,因为 MySQL 容器以非 root 用户运行,其 UID/GID 可能与宿主机不匹配。如果没有写权限,mysqld --initialize 会因 Permission denied 而失败。对于生产环境,建议将所有权设置为 MySQL 容器使用的特定 UID(通常为 999:999),而不是使用 777
  • 残留数据:如果复用 PV 目录,请先使用 rm -rf /opt/local-pv/pvN/* 删除所有现有文件。如果数据目录不为空,MySQL 初始化会失败。

创建 PV 资源

首先,识别节点名称:

kubectl get nodes -o wide

然后创建 PV,并将每个 PV 绑定到特定节点。对于跨 3 个节点的 3 成员集群:

# 定义节点名称
NODE1=<node1-hostname>
NODE2=<node2-hostname>
NODE3=<node3-hostname>

# 每个节点创建 3 个 PV(共 9 个),以支持扩容和重新供应
nodes=("$NODE1" "$NODE2" "$NODE3")
for n in "${!nodes[@]}"; do
  node="${nodes[$n]}"
  for i in 1 2 3; do
    idx=$(( n * 3 + i ))
    kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-$idx
spec:
  capacity:
    storage: 20Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: mgr-local-pv
  local:
    path: /opt/local-pv/pv$idx
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - $node
EOF
  done
done

验证所有 PV 都已创建且可用:

kubectl get pv -l '!pv-is-managed' | grep mgr-local-pv

在继续之前,所有 PV 都应显示 Available 状态。

INFO
  • 应使用 persistentVolumeReclaimPolicy: Delete,而不是 Retain。使用 Retain 时,PV 在 PVC 删除后会进入 Released 状态,且无法在不手动清理的情况下重新使用。
  • 请注意,对于手动供应的本地 PV,删除 PV 对象不会自动清理磁盘上的数据。你在复用前仍可能需要手动清空目录。
  • 建议创建多于最小需求数量的 PV,以便支持扩容和重新供应。不过,额外的 PV 不能帮助故障切换:如果某个节点丢失,绑定到该节点的 PV 无法迁移。节点丢失后的恢复需要手动重新供应和数据重新同步。

步骤 3(可选):在只读文件系统上配置 ClickHouse 存储路径

INFO

仅在你计划使用 query analytics 功能(慢查询日志、查询分析)时才需要执行此步骤。如果不使用 query analytics,可以跳过此步骤——MySQL-MGR 实例仍可正常工作。

query-analytics 插件会使用主机目录 PV 部署 ClickHouse。默认情况下,主机路径为 /cpaas/ck。在 MicroOS 或其他根文件系统只读的系统上,这会导致 ClickHouse pod 启动失败,并出现以下错误:

mkdir /cpaas: read-only file system
WARNING

你必须在安装 query-analytics-operator 之前 配置 hostPathRdsInstaller → QueryAnalyticsInstaller 的合并只会在首次创建 QueryAnalyticsInstallerqai)CR 时执行。一旦 operator 安装完成,qai CR 已存在,后续对 RdsInstaller 的修改将不会传播。若要修复已存在的部署,请参见 修复使用错误 hostPath 的现有部署

在安装 query-analytics-operator 之前配置 hostPath

  1. 先安装 rds-operator(如果尚未安装)。RdsInstaller CR 会在 rds-operator 初始化时创建。

  2. 找到 RdsInstaller 资源:

    kubectl get rdsinstaller -A
  3. 编辑 RdsInstaller,覆盖 ClickHouse 的主机路径:

    kubectl edit rdsinstaller <name> -n <namespace>

    spec.slowSQLCK.hostPath 设置为节点上的可写路径:

    spec:
      slowSQLCK:
        hostPath: /opt/local-pv/ck
    INFO

    在 MicroOS 上,请使用 /opt/ 下的路径(例如 /opt/local-pv/ck)。该目录会通过 DirectoryOrCreate 类型自动创建。

  4. 然后安装 clickhouse-operatorquery-analytics-operator。ClickHouse PV 将使用已配置的路径创建,而不是默认的 /cpaas/ck

修复使用错误 hostPath 的现有部署

如果 query-analytics-operator 已使用默认路径安装且 ClickHouse pod 已失败,仅编辑 RdsInstaller.spec.slowSQLCK.hostPath 还不够——因为合并到 QueryAnalyticsInstaller 的操作只会在 qai 首次创建时执行。你还必须强制重新创建 qai(或者直接编辑 qai)。

选项 A — 重新创建 qai 以重新执行合并:

  1. 按照上面的预安装操作更新 RdsInstaller

    kubectl edit rdsinstaller <name> -n <namespace>
    # 将 spec.slowSQLCK.hostPath 设置为 /opt/local-pv/ck
  2. 删除 ClickHouse 安装和旧 PV:

    kubectl delete chi pxc-ck -n <clickhouse-namespace>
    kubectl get pv | grep pxc-ck
    kubectl delete pv <pv-name>
  3. 删除 QueryAnalyticsInstaller CR。query-analytics-operator 会重新创建它,并合并更新后的 RdsInstaller 配置:

    kubectl delete qai qai
  4. 验证新的 hostPath

    kubectl get qai qai -o jsonpath='{.spec.slowSQL.hostPath}{"\n"}'
    kubectl get pv pxc-ck-pv -o jsonpath='{.spec.hostPath.path}{"\n"}'

    两者都应显示已配置的路径(例如 /opt/local-pv/ck)。

选项 B — 直接编辑 qai(更快,但在下次重新创建之前,RdsInstaller 会与 qai 不一致):

kubectl edit qai qai
# 将 spec.slowSQL.hostPath 设置为 /opt/local-pv/ck
kubectl delete chi pxc-ck -n <clickhouse-namespace>
kubectl delete pv <old-pv-name>

query-analytics-operator 会使用 qai 上设置的路径重新创建 ClickHouse 安装和 PV。

步骤 4:创建 MySQL-MGR 实例

  1. 创建密码 secret:

    kubectl -n ${namespace} create secret generic mgr-${instance_name}-password \
      --from-literal=clusterchecker=${password} \
      --from-literal=exporter=${password} \
      --from-literal=manage=${password} \
      --from-literal=root=${password}
  2. 使用本地 StorageClass 创建 MySQL CR:

    kubectl apply -n ${namespace} -f - <<EOF
    apiVersion: middleware.alauda.io/v1
    kind: Mysql
    metadata:
      labels:
        mysql/arch: mgr
      name: ${instance_name}
    spec:
      mgr:
        enableStorage: true
        members: 3
        monitor:
          enable: true
        resources:
          server:
            limits:
              cpu: "2"
              memory: 4Gi
            requests:
              cpu: "2"
              memory: 4Gi
        router:
          replicas: 2
          resources:
            limits:
              cpu: 800m
              memory: 640Mi
            requests:
              cpu: 800m
              memory: 640Mi
          svcRO:
            type: ClusterIP
          svcRW:
            type: ClusterIP
        volumeClaimTemplate:
          spec:
            resources:
              requests:
                storage: 20Gi
            storageClassName: mgr-local-pv
      params:
        mysql:
          mysqld:
            character_set_server: utf8mb4
            default_storage_engine: InnoDB
            default_time_zone: "+08:00"
      version: "8.0"
    EOF
    INFO
    • 密码 secret 必须遵循命名规范 mgr-${instance_name}-password。operator 会自动通过这个名称发现它。
    • volumeClaimTemplate 部分将 storageClassName 设置为 mgr-local-pv(或你的 StorageClass 名称)。这将替代默认的 sc-topolvm
    • version 字段是必需的。MySQL 8.0 请使用 "8.0"
  3. 监控实例状态:

    kubectl get mysql ${instance_name} -n ${namespace} -w

    等待 STATE 字段显示 ready

  4. 验证 PVC 绑定和 pod 调度位置:

    kubectl get pvc -n ${namespace}
    kubectl get pod -n ${namespace} -o wide | grep ${instance_name}

    确认每个 PVC 都已绑定到 PV,且 pod 已分布到不同节点。

故障排查

PVC 卡在 Pending

现象:PVC 一直处于 Pending 状态,pod 无法调度。

原因:没有可用的 PV 与 StorageClass 名称或节点亲和性匹配,或者 PV 处于 Released 状态。

修复

# 检查 PV 状态
kubectl get pv | grep mgr-local-pv
# 查看 PVC 事件以获取详细信息
kubectl describe pvc -n <namespace> <pvc-name>
# 验证 PV 和 PVC 之间的 StorageClass 名称是否一致
kubectl get pv <pv-name> -o jsonpath='{.spec.storageClassName}'

MySQL pod 卡在 CrashLoopBackOff

现象:MySQL pod 在初始化期间因 Permission denied 崩溃。

原因:PV 目录对非 root 的 MySQL 用户没有写权限。

修复

# 在调度该 pod 的节点上
chmod 777 /opt/local-pv/pvN

MySQL pod 因 “data directory has files in it” 失败

现象mysqld --initialize 由于数据目录不为空而失败。

原因:PV 目录中残留了上一次部署的数据。

修复

# 在节点上,或通过特权 pod
rm -rf /opt/local-pv/pvN/*

PV 卡在 Released 状态

现象:PV 显示 Released 状态,无法绑定到新的 PVC。

原因:PV 创建时使用了 persistentVolumeReclaimPolicy: Retain

修复:删除处于 Released 状态的 PV,并使用 persistentVolumeReclaimPolicy: Delete 重新创建:

kubectl delete pv <pv-name>
# 然后按照步骤 2 重新创建

实例卡在 ErrorReconcile

现象:MySQL 实例显示 ErrorReconcile 条件,且 STATE 字段显示 available 而不是 ready,尽管所有 pod 都在运行。

原因:operator 的 reconcile 循环在更新 CR 状态时遇到了冲突错误。

修复:为 MySQL CR 添加注解以触发一次新的 reconcile:

kubectl annotate mysql ${instance_name} -n ${namespace} \
  force-reconcile="$(date +%s)" --overwrite

ClickHouse pod 因 “read-only file system” 失败

现象:ClickHouse pod 启动失败,并报错 mkdir /cpaas: read-only file system

原因:query-analytics 插件默认的 ClickHouse 主机路径(/cpaas/ck)在只读文件系统上不存在。

修复:按照 步骤 3,在 RdsInstaller CR 中将 spec.slowSQLCK.hostPath 设置为可写路径。