在 Kubernetes 集群中,我希望能够安排一个作业(使用 CronJob),该作业将挂载与给定 StatefulSet 的 1 个 Pod 相同的卷。哪个 Pod 是运行时决定,取决于在调度 Job 时在 Pod 上设置的标签。
我想很多人会想知道为什么,因此描述一下我们正在做什么和试图做什么:
当前设置
我们有一个为 PostgreSQL 数据库提供服务的 StatefulSet。(一个主,多个副本)我们希望能够从 StatefulSet 的其中一个 pod 创建备份。
对于 PostgreSQL,我们已经可以使用 进行网络备份pg_basebackup
,但是我们正在运行多 TB 的 PostgreSQL 数据库,这意味着完全流式备份(使用pg_basebackup
)是不可行的。
我们目前使用pgBackRest
备份数据库,它允许增量备份。
由于增量备份pgBackRest
需要访问数据卷和 WAL 卷,因此我们需要在与 PostgreSQL 实例相同的 Kubernetes Node 上运行 Backup Container,我们目前甚至在单独的 Container 中的同一个 Pod 中运行它。
在容器内部,一个小 api 环绕pgBackRest
,可以通过向POST
api 发送请求来触发,目前这个触发是使用 CronJobs 完成的。
缺点
- 每个 PostgreSQL 实例在 Pod 中都有多个容器,1 个用于服务 Postgres,1 个用于服务于一个小包装器
pgBackRest
- 作业日志仅显示成功的备份触发器,实际的备份日志是备份容器的一部分
- 将运行备份的 Pod 可能在相对较旧的配置上运行,更改备份配置需要重新调度 Pod,这可能意味着 PostgreSQL 主节点的故障转移。
建议的设置
让 CronJob 调度一个与 StatefulSet 的 Pod 具有相同 Volume 的 Pod。这将允许备份使用这些卷。
但是,它需要哪些卷是一个运行时决定:我们可能希望在连接到主卷的卷上运行备份,或者我们可能希望使用副本的卷进行备份。主/副本可能随时更改,因为 PostgreSQL 主的自动故障转移是解决方案的一部分。
目前,这是不可能的,因为我无法在 CronJob 规范中找到任何方法来使用来自 k8s api 的信息。
什么有效,但不是很好:
- 使用调度作业的 CronJob
- 这个 Job 查询 k8s api 并调度另一个Job
例如,我们可以使用以下运行时信息让一个作业创建另一个作业:
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: schedule-backup
spec:
schedule: "13 03 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: backup-trigger
image: bitnami/kubectl
command:
- sh
- -c
- |
PRIMARYPOD=$(kubectl get pods -l cluster-name=<NAME>,role=master -o custom-columns=":metadata.name" --no-headers)
kubectl apply -f - <<__JOB__
apiVersion: batch/v1
kind: Job
metadata:
name: test
spec:
volumes:
name: storage-volume
persistentVolumeClaim:
claimName:
data-volume-${PRIMARYPOD}
[...]
__JOB__
以上可能最好由操作员提供,而不仅仅是使用 CronJob,但我想知道是否有人对上述问题有解决方案。
缺点
- 作业日志仅显示成功的备份触发器,实际的备份日志是另一个作业的一部分
- 作业需要权限来安排 Pod,需要另一个角色/角色绑定
- 在 Bash 中使用 heredocs 会使事情更难阅读/解析/理解
概括
长话短说,但这些是我们想要满足的约束:
- 运行 PostgreSQL 数据库的备份
- 这些是多 TB 数据库
- 所以需要增量备份
- 因此,我们需要挂载一个已经运行的 Pod 的同一个 PV
- 因此,我们需要在与 PV 相同的 K8s 节点上运行 Pod(或容器)
- 我们希望能够在 CronJob 规范中表达这一点,而不必进行运行时 kubernetes api 调用