在 Kubernetes 集群中,我希望能够安排一个作业(使用 CronJob),该作业将挂载与给定 StatefulSet 的 1 个 Pod 相同的卷。哪个 Pod 是运行时决定的,具体取决于调度作业时 Pod 上设置的标签。
我想很多人会想知道为什么,因此描述一下我们正在做的事情和尝试做的事情:
当前设置
我们有一个 StatefulSet,它为 PostgreSQL 数据库提供服务。 (一个主库,多个副本) 我们希望能够从 StatefulSet 的 Pod 之一创建备份。
对于 PostgreSQL,我们已经可以使用 pg_basebackup
通过网络进行备份,但是我们正在运行多 TB PostgreSQL 数据库,这意味着完全流式备份(使用 pg_basebackup
)不可行。
我们目前使用 pgBackRest
来备份数据库,它允许增量备份。
由于pgBackRest
的增量备份需要访问数据卷和WAL卷,因此我们需要在与PostgreSQL实例相同的Kubernetes节点上运行备份容器,目前我们甚至在内部运行它同一 Pod 位于单独的 Container 中。
在容器内部,一个小型 api 包裹着 pgBackRest
,并且可以通过向该 api 发送 POST
请求来触发,此触发目前是使用 CronJobs 完成的。
缺点
- 每个 PostgreSQL 实例在 Pod 中都有多个容器,1 个用于服务 Postgres,1 个用于服务
pgBackRest
的小型包装器 - 作业日志仅显示成功的备份触发器,实际的备份日志是备份容器的一部分
- 将运行备份的 Pod 可能会在相对较旧的配置上运行,更改备份配置需要重新安排 Pod,这可能意味着 PostgreSQL 主节点的故障转移。
建议的设置
让 CronJob 调度一个与 StatefulSet 的 1 个 Pod 具有相同卷的 Pod。这将允许备份使用这些卷。
但是,它需要哪些卷是运行时决定的:我们可能希望在连接到主卷的卷上运行备份,或者我们可能希望使用副本的卷进行备份。主/副本可能随时发生变化,因为 PostgreSQL 主的自动故障转移是解决方案的一部分。
目前,这是不可能的,因为我无法在 CronJob 规范中找到任何方法来使用 k8s api 中的信息。
什么有效,但不是很好:
- 使用 CronJob 来安排作业
- 此作业查询 k8s API 并调度另一个作业
例如,我们可以这样做,让一个作业使用此运行时信息创建另一个作业:
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__
上述内容可能最好由 Operator 来提供,而不是仅使用 CronJob,但我想知道是否有人有上述问题的解决方案。
缺点
- 作业日志仅显示成功的备份触发器,实际备份日志是另一个作业的一部分
- 该作业需要调度 Pod 的权限,需要另一个角色/角色绑定(bind)
- 在 Bash 中使用heredocs会使事情更难阅读/解析/理解
摘要
说来话长,但这些是我们想要满足的约束:
- 运行 PostgreSQL 数据库的备份
- 这些是多 TB 数据库
- 因此需要增量备份
- 因此,我们需要挂载已运行 Pod 的相同 PV
- 因此,我们需要在与 PV 相同的 K8s 节点上运行 Pod(或容器)
- 我们希望能够在 CronJob 规范中表达这一点,而不必执行运行时 kubernetes api 调用
最佳答案
嗯,简单而简短的答案是:你通常不能。
但是让我们发挥一下创意:)
支持 RWX(读写多次)访问的存储后端数量非常有限,并且在大多数情况下,这些都是较慢的存储后端,在用于数据库时您要避免这种情况。这意味着,除非您将备份包装器作为 sidecar 运行(您现在就这样做),否则您无法真正访问不同 POD 中的 PV。
我可能会坚持您原来的方法,并进行一些调整(例如确保您永远不会因备份/配置更改而关闭主数据库)。
在最新的 K8S 集群和支持的基础设施提供商上,您可能可以查看 VolumeSnapshots用于基于快照的备份,并可能使用快照作为源来启动增量备份作业。虽然听起来有点复杂。
您还可以运行资源有限(无实时流量)的备份专用 postgres 副本 Pod,并仅在该 Pod 中嵌入备份逻辑。
关于kubernetes - 使用与 StatefulSet 中其他 1 个 Pod 相同的 PVC 来安排作业,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59562718/