如何备份 Kubernetes MySQL Operator 集群

Oracle 的 MySQL Operator for Kubernetes 是一种在集群内自动配置 MySQL 数据库的便捷方式。 Operator 的一项主要功能是集成的自动备份支持,可提高弹性。 备份定期将数据库复制到外部存储。

本文将指导大家设置备份到与 Amazon S3 兼容的对象存储服务。 还将了解如何将备份存储在 Oracle 云基础设施 (OCI) 存储或集群内的本地持久卷中。


准备数据库集群

在 Kubernetes 集群中安装 MySQL Operator 并创建一个简单的数据库实例用于测试目的。 复制下面的 YAML 并将其保存到 mysql.yaml

apiVersion: v1
kind: Secret
metadata:
  name: mysql-root-user
stringData:
  rootHost: "%"
  rootUser: "root"
  rootPassword: "P@$$w0rd"
 
---

apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: mysql-cluster
spec:
  secretName: mysql-root-user
  instances: 3
  tlsUseSelfSigned: true
  router:
    instances: 1

使用 Kubectl 应用配置:

$ kubectl apply -f mysql.yaml

等待几分钟,MySQL operator 会配置 Pod。 使用 Kubectl 的 get pods 命令检查进度。 我们应该看到四个正在运行的 Pod:一个 MySQL 路由器实例和三个 MySQL 服务器副本。

$ kubectl get pods
NAME                                    READY   STATUS    RESTARTS   AGE
mysql-cluster-0                         2/2     Running   0          2m
mysql-cluster-1                         2/2     Running   0          2m
mysql-cluster-2                         2/2     Running   0          2m
mysql-cluster-router-6b68f9b5cb-wbqm5   1/1     Running   0          2m

定义备份计划

MySQL operator 需要两个组件才能成功创建备份:

  • 定义备份运行时间的备份计划。
  • 配置存储位置和 MySQL 导出选项的备份配置文件。

时间表和配置文件是彼此独立创建的。 这使我们可以使用相同的配置文件按不同的计划运行多个备份。

每个计划和配置文件都与特定的数据库集群相关联。 它们被创建为 InnoDBCluster 对象中的嵌套资源。 我们使用 MySQL 运算符创建的每个数据库都需要其自己的备份配置。

备份计划由数据库的 spec.backupSchedules 字段定义。 每个项目都需要一个计划字段,该字段指定何时使用 cron 表达式运行备份。 这是一个每小时开始备份的示例:

apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: mysql-cluster
spec:
  secretName: mysql-root-user
  instances: 3
  tlsUseSelfSigned: true
  router:
    instances: 1
   backupSchedules:
    - name: hourly
      enabled: true
      schedule: "0 * * * *"
      backupProfileName: hourly-backup

backupProfileName 字段引用要使用的备份配置文件。 我们将在下一步中创建它。


创建备份配置文件

配置文件在 spec.backupProfiles 字段中定义。 每个配置文件都应该有一个名称和一个配置备份操作的 dumpInstance 属性。

apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: mysql-cluster
spec:
  secretName: mysql-root-user
  instances: 3
  tlsUseSelfSigned: true
  router:
    instances: 1
  backupSchedules:
    - name: hourly
      enabled: true
      schedule: "0 * * * *"
      backupProfileName: hourly-backup
  backupProfiles:
    - name: hourly-backup
      dumpInstance:
        storage:
          # ...

备份存储是在 dumpInstance.storage 字段中基于每个配置文件配置的。 我们需要提供的属性取决于我们使用的存储类型。


S3 存储

MySQL 操作员可以将我们的备份直接上传到与 S3 兼容的对象存储提供商。 要使用此方法,我们必须创建一个 Kubernetes 密钥,其中包含一个带有我们的凭据的 aws CLI 配置文件。

将以下内容添加到 s3-secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: s3-secret
stringData:
  credentials: |
    [default]
    aws_access_key_id = YOUR_S3_ACCESS_KEY
    aws_secret_access_key = YOUR_S3_SECRET_KEY

替换为我们自己的 S3 访问密钥,然后使用 Kubectl 创建秘密:

$ kubectl apply -f s3-secret.yaml
secret/s3-secret created

接下来将以下字段添加到备份配置文件的 storage.s3 部分:

  • bucketName – 要将备份上传到的 S3 存储桶的名称。
  • prefix – 设置此项以将前缀应用于我们上传的文件,例如 /my-app/mysql。 该前缀允许我们在存储桶中创建文件夹树。
  • endpoint——当我们使用第三方 S3 兼容存储时,将此设置为我们的服务提供商的 URL。 如果我们使用的是 Amazon S3,则可以省略此字段。
  • config – 包含我们的凭据文件的机密的名称。
  • profile – 要在凭据文件中使用的配置文件的名称。 这在上面的示例中设置为默认值。

这是一个完整的例子:

apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: mysql-cluster
spec:
  secretName: mysql-root-user
  instances: 3
  tlsUseSelfSigned: true
  router:
    instances: 1
  backupSchedules:
    - name: hourly
      enabled: true
      schedule: "0 * * * *"
      backupProfileName: hourly-backup
  backupProfiles:
    - name: hourly-backup
      dumpInstance:
        storage:
          s3:
            bucketName: backups
            prefix: /mysql
            config: s3-secret
            profile: default

应用此清单将激活每小时数据库备份到我们的 S3 帐户。


OCI 存储

该 operator 支持 Oracle Cloud Infrastructure (OCI) 对象存储作为 S3 的替代方案。 它以类似的方式配置。 首先为你的 OCI 凭据创建一个密钥

apiVersion: v1
kind: Secret
metadata:
  name: oci-secret
stringData:
  fingerprint: YOUR_OCI_FINGERPRINT
  passphrase: YOUR_OCI_PASSPHRASE
  privatekey: YOUR_OCI_RSA_PRIVATE_KEY
  region: us-ashburn-1
  tenancy: YOUR_OCI_TENANCY
  user: YOUR_OCI_USER

接下来使用 storage.ociObjectStorage 节配置备份配置文件:

apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: mysql-cluster
spec:
  secretName: mysql-root-user
  instances: 3
  tlsUseSelfSigned: true
  router:
    instances: 1
  backupSchedules:
    - name: hourly
      enabled: true
      schedule: "0 * * * *"
      backupProfileName: hourly-backup
  backupProfiles:
    - name: hourly-backup
      dumpInstance:
        storage:
          ociObjectStorage:
            bucketName: backups
            prefix: /mysql
            credentials: oci-secret

修改 bucketName 和 prefix 字段以在你的 OCI 帐户中设置上传位置。 凭据字段必须引用包含你的 OCI 凭据的密钥。


Kubernetes 卷存储

本地持久卷是第三种存储选项。 这不太可靠,因为你的备份数据仍将驻留在 Kubernetes 集群中。 但是,它对于一次性备份和测试目的很有用。

首先创建一个持久卷和附带的声明:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: backup-pv
spec:
  storageClassName: standard
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /tmp
 
---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: backup-pvc
spec:
  storageClassName: standard
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

此示例清单不适合生产使用。 你应该为自己的 Kubernetes 发行版选择合适的存储类和卷安装模式。

接下来通过添加 storage.persistentVolumeClaim 字段配置自你的备份配置文件以使用自己的持久卷:

apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: mysql-cluster
spec:
  secretName: mysql-root-user
  instances: 3
  tlsUseSelfSigned: true
  router:
    instances: 1
  backupSchedules:
    - name: hourly
      enabled: true
      schedule: "0 * * * *"
      backupProfileName: hourly-backup
  backupProfiles:
    - name: hourly-backup
      dumpInstance:
        storage:
          persistentVolumeClaim:
            claimName: backup-pvc

之前创建的持久卷声明由 claimName 字段引用。 MySQL 操作员现在会将备份数据存入该卷。


设置备份选项

备份是使用 MySQL Shell 的 dumpInstance 实用程序创建的。 这默认导出服务器的完整转储。 该格式为每个表写入结构和分块数据文件。 使用 zstd 压缩输出。

我们可以通过 MySQL 操作员备份配置文件中的 dumpOptions 字段将选项传递给 dumpInstance

apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: mysql-cluster
spec:
  # ...
  backupProfiles:
    - name: hourly-backup
      dumpInstance:
        dumpOptions:
          chunking: false
          compression: gzip
        storage:
          # ...

此示例禁用分块输出,为每个表创建一个数据文件,并切换到 gzip 压缩而不是 zstd。 我们可以在 MySQL 文档中找到可用选项的完整参考。

恢复备份

MySQL Operator 可以使用以前从 dumpInstance 创建的文件来初始化新的数据库集群。 这允许我们将备份直接恢复到 Kubernetes 集群中。 它在恢复情况下或将现有数据库迁移到 Kubernetes 时很有用。

数据库初始化由 InnoDBCluster 对象上的 spec.initDB 字段控制。 在此节中,使用 dump.storage 对象来引用之前使用的备份位置。 该格式与备份配置文件对象中的等效 dumpInstance.storage 字段相匹配。

apiVersion: v1
kind: Secret
metadata:
  name: s3-secret
stringData:
  credentials: |
    [default]
    aws_access_key_id = YOUR_S3_ACCESS_KEY
    aws_secret_access_key = YOUR_S3_SECRET_KEY

---

apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: mysql-cluster-recovered
spec:
  secretName: mysql-root-user
  instances: 3
  tlsUseSelfSigned: true
  router:
    instances: 1
  initDB:
    dump:
      storage:
        s3:
          bucketName: backups
          prefix: /mysql/mysql20221031220000
          config: s3-secret
          profile: default

应用此 YAML 文件将创建一个新的数据库集群,该集群使用指定 S3 存储桶中的 dumpInstance 输出进行初始化。 前缀字段必须包含存储桶中转储文件的完整路径。 Operator 创建的备份将自动存储在带时间戳的文件夹中; 我们需要通过设置前缀来指示要恢复哪个。 如果要从持久卷恢复,请使用路径字段而不是前缀。


总结

Oracle 的 MySQL Operator 在 Kubernetes 集群中自动化 MySQL 数据库管理。 在本文中,你了解了如何配置 Operator 的备份系统以将完整的数据库转储存储在持久卷或对象存储桶中。

使用 Kubernetes 横向扩展 MySQL 可增加弹性,但外部备份仍然至关重要,以防集群受损或数据被意外删除。 如果需要,MySQL Operator 可以从你的备份中恢复新的数据库实例,从而简化灾后恢复过程。