Velero(Ark)Backup Hooks 完全指南:基于 Pod 注解与 Backup Spec 的备份钩子机制
【免费下载链接】veleroBackup and migrate Kubernetes applications and their persistent volumes项目地址: https://gitcode.com/GitHub_Trending/ve/velero
导读
本指南以仓库内 site/content/docs/v0.7.1/hooks.md 为核心,系统讲解 Velero 前身 Heptio Ark 自 v0.7.0 起引入的 Backup Hooks(备份钩子)机制:在备份 Pod 的过程中,通过 pod exec API 在指定容器内执行预置命令,从而在卷快照前后完成应用级的数据一致性操作(如冻结文件系统)。读完本文,你将掌握 pre/post 两类钩子的执行时机、Pod 注解与 Backup Spec 两种声明方式、全部注解/字段的参数语义,以及基于源码的执行原理与从 Ark 到 Velero 的注解演进。
说明:v0.7.1 文档所处的 Heptio Ark 时代,项目注解域名为
ark.heptio.com;当前仓库的现代版本已统一演化为velero.io(详见文末 演进章节),两者语法结构完全一致,可对照使用。
什么是 Backup Hooks
Backup Hooks 允许你在执行备份时,指定一条或多条命令,在该 Pod 被备份时于其某个容器内执行。这一能力由 Heptio Ark 率先支持,并完整继承至 Velero。
典型用途是保证卷快照的数据一致性:
- pre 钩子:在快照前运行,用于完成磁盘 I/O 的收尾,例如
fsfreeze --freeze(冻结文件系统,使所有待处理的磁盘 I/O 全部落盘); - 随后 Ark 对磁盘执行快照;
- post 钩子:在快照完成后运行,解除冻结状态,例如
fsfreeze --unfreeze。
钩子的执行不是经由 Shell,而是直接通过 Kubernetes 的pod exec API在目标容器内启动进程(源码注释明确说明:ExecHook is a hook that uses the pod exec API to execute a command in a container in a pod,见 pkg/apis/velero/v1/backup_types.go#L261-L262)。
Pre 与 Post 钩子的执行时机
v0.7.1 文档明确定义了两类钩子的执行阶段:
| 钩子类型 | 执行时机 | 引入版本 |
|---|---|---|
| pre 钩子 | 在任何自定义 Action 处理之前执行 | v0.7.0 之前即已支持(v0.7.0 起带pre.前缀) |
| post 钩子 | 在所有自定义 Action 完成、且自定义 Action 所追加的附加资源项(additional items)也全部备份完成之后执行 | v0.7.0 起支持 |
后者的语义非常关键:post 钩子不仅等待自定义插件逻辑结束,还要等这些插件递归扩展出的附属资源全部入库,确保"冻结 → 全量落盘 → 解冻"的闭环没有遗漏。
从当前仓库源码可以印证这一时序。在 pkg/backup/backup.go 中,pre 钩子由handleItemBlockPreHooks逐 Pod 调用itemHookHandler.HandleHooks(..., hook.PhasePre, ...)(L917-L931),而 post 钩子的执行被handleItemBlockPostHooks包裹,且必须等待该 Pod 相关的 PodVolumeBackup(PVB)全部处理完成后才逐个执行(waitUntilPVBsProcessed,见 L934-L949)。这保证了快照/文件级备份真正结束后,post 钩子(如fsfreeze --unfreeze)才被触发。
另外需要注意:现代版本(Velero 1.15+)引入 ItemBlock 机制后,若一个 ItemBlock 包含多个 Pod(如多个 Pod 共享一个 RWX 卷),会先对所有 Pod 执行 pre 钩子,再备份资源,最后统一执行所有 post 钩子,以保持多 Pod 间的冻结窗口一致。
方式一:通过 Pod 注解指定钩子
v0.7.1 文档提供了在 Pod 上直接声明钩子的方式——备份 Pod 时,Ark 读取以下注解并据此执行命令。
Pre 钩子注解
| 注解名 | 说明 |
|---|---|
pre.hook.backup.ark.heptio.com/container | 执行命令的容器名。默认为 Pod 中的第一个容器。可选。 |
pre.hook.backup.ark.heptio.com/command | 要执行的命令。需要多个参数时,以 JSON 数组形式给出,如["/usr/bin/uname", "-a"] |
pre.hook.backup.ark.heptio.com/on-error | 命令返回非零退出码时的处理策略。默认为Fail。合法值为Fail与Continue。可选。 |
pre.hook.backup.ark.heptio.com/timeout | 命令执行的最大等待时长;超时即视为钩子执行出错。默认为 30s。可选。 |
Post 钩子注解(v0.7.0+)
| 注解名 | 说明 |
|---|---|
post.hook.backup.ark.heptio.com/container | 执行命令的容器名。默认为 Pod 中的第一个容器。可选。 |
post.hook.backup.ark.heptio.com/command | 要执行的命令。多参数时以 JSON 数组形式给出,如["/usr/bin/uname", "-a"] |
post.hook.backup.ark.heptio.com/on-error | 命令返回非零退出码时的处理策略。默认为Fail。合法值为Fail与Continue。可选。 |
post.hook.backup.ark.heptio.com/timeout | 命令执行的最大等待时长;超时即视为钩子执行出错。默认为 30s。可选。 |
旧版(已废弃)注解的兼容
v0.7.0+ 继续兼容最初的 pre 钩子写法——去掉pre.前缀的旧注解名(例如hook.backup.ark.heptio.com/container)依然有效。源码中的回退逻辑位于 internal/hook/item_hook_handler.go#L219-L224:当阶段为 pre 且带前缀的注解不存在时,会尝试读取无阶段前缀的注解键。
注解解析的源码细节
从 internal/hook/item_hook_handler.go 可以看到注解解析的若干边界行为:
command注解必须存在,否则视为没有钩子(getPodExecHookFromAnnotations对空命令直接返回 nil,见 L335-L339);command若以[开头,则按 JSON 数组解析,解析失败时退化为单元素命令(parseStringToCommand,见 L366-L383);on-error注解取值若非Continue/Fail,会被忽略并交由默认行为处理(L343-L346);timeout使用time.ParseDuration解析,格式非法时打印警告并使用默认值 30s(L348-L356)。
方式二:通过 Backup Spec 指定钩子
除 Pod 注解外,钩子也可以在Backup 对象规格(spec)中集中声明,从而对一类资源(如所有带特定标签的 Pod)统一生效。完整的字段说明见 Backup API 类型文档。
以下为 Backup spec 中hooks字段的完整声明示例(节选自 site/content/docs/v0.7.1/api-types/backup.md 的备份对象全字段示例):
apiVersion: ark.heptio.com/v1 kind: Backup metadata: name: a namespace: heptio-ark spec: includedNamespaces: - '*' includedResources: - '*' hooks: resources: - # 钩子名称,会显示在备份日志中。 name: my-hook # 该钩子适用的命名空间;未指定时对所有命名空间生效。可选。 includedNamespaces: - '*' # 不适用的命名空间。可选。 excludedNamespaces: - some-namespace # 适用的资源;当前仅支持 pods。 includedResources: - pods # 不适用的资源。可选。 excludedResources: [] # 仅对匹配该标签选择器的对象生效。可选。 labelSelector: matchLabels: app: ark component: server # 在自定义 Action 执行之前运行的钩子数组。当前仅支持 "exec" 类型。 # 已废弃,请改用 pre。 hooks: # 在自定义 Action 执行之前运行的钩子数组。当前仅支持 "exec" 类型。 pre: - # 钩子类型,必须为 "exec"。 exec: # 执行命令的容器名。未指定时使用 Pod 中的第一个容器。可选。 container: my-container # 要执行的命令(数组形式)。必填。 command: - /bin/uname - -a # 命令执行出错时的处理方式。合法值为 Fail 与 Continue。默认为 Fail。可选。 onError: Fail # 等待命令执行完成的最大时长。默认为 30 秒。可选。 timeout: 10s # 在所有自定义 Action 及附加资源项处理完成后运行的钩子数组。 # 当前仅支持 "exec" 类型。 post: # 内容与 pre 相同。字段语义与源码佐证
hooks.resources[]中每个条目的结构与源码中的类型定义一一对应(见 pkg/apis/velero/v1/backup_types.go#L211-L253):
name:钩子名,用于日志与钩子结果追踪中的标识;includedNamespaces/excludedNamespaces:命名空间过滤,配合 labelSelector 组成资源选择器ResourceHookSelector,在 internal/hook/item_hook_handler.go#L385-L410 中通过applicableTo判定是否命中(namespace 包含排除关系、资源组名、标签选择器三者同时满足才触发);pre/post:分别对应PreHooks与PostHooks两个[]BackupResourceHook数组,其中exec钩子的四个子字段(container、command、onError、timeout)定义于ExecHook(L261-L280):command为必填数组,MinItems=1;onError枚举值为Continue/Fail(HookErrorMode,L282-L294):Fail:钩子出错即停止执行后续钩子,并向上返回错误;Continue:钩子出错可接受,备份继续执行后续钩子。
timeout为metav1.Duration类型。
在 internal/hook/item_hook_handler.go#L254-L305 的实现中,spec 方式的钩子按选择器过滤后依序执行;一旦某个钩子以Fail模式失败,后续钩子将不再执行(modeFailError非空即停止),但所有钩子的尝试记录仍会被收集。
实战示例:使用 pre/post 钩子冻结文件系统
文档给出的经典场景是用fsfreeze保证快照一致性:pre 钩子执行fsfreeze --freeze冻结文件系统 → Ark 完成磁盘快照 → post 钩子执行fsfreeze --unfreeze解除冻结。
仓库中的 examples/nginx-app/with-pv.yaml 是一个可直接运行的完整示例,其中 Deployment 的 Pod 模板通过注解声明了一对冻结/解冻钩子(现代版本使用velero.io域名):
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment namespace: nginx-example spec: replicas: 1 selector: matchLabels: app: nginx template: metadata: labels: app: nginx annotations: pre.hook.backup.velero.io/container: fsfreeze pre.hook.backup.velero.io/command: '["/sbin/fsfreeze", "--freeze", "/var/log/nginx"]' post.hook.backup.velero.io/container: fsfreeze post.hook.backup.velero.io/command: '["/sbin/fsfreeze", "--unfreeze", "/var/log/nginx"]' spec: volumes: - name: nginx-logs persistentVolumeClaim: claimName: nginx-logs containers: - image: nginx:1.17.6 name: nginx volumeMounts: - mountPath: "/var/log/nginx" name: nginx-logs - image: ubuntu:bionic name: fsfreeze securityContext: privileged: true volumeMounts: - mountPath: "/var/log/nginx" name: nginx-logs command: - "/bin/bash" - "-c" - "sleep infinity"若将上述注解域名替换为 v0.7.1 时代的ark.heptio.com(即pre.hook.backup.ark.heptio.com/...),该示例同样适用于本文档所描述的 Ark v0.7.x 版本。注意两个实践要点:
- 冻结操作要求
fsfreeze容器以privileged(特权)模式运行,并挂载目标文件系统目录; - 该容器需要保持存活(示例中用
sleep infinity常驻),以便钩子命令在其内部执行。
对于已运行的 Pod,也可以直接用kubectl annotate就地打注解(以现代域名为例,v0.7.1 请对应替换为ark.heptio.com):
kubectl annotate pod -n nginx-example -l app=nginx \ pre.hook.backup.velero.io/command='["/sbin/fsfreeze", "--freeze", "/var/log/nginx"]' \ pre.hook.backup.velero.io/container=fsfreeze \ post.hook.backup.velero.io/command='["/sbin/fsfreeze", "--unfreeze", "/var/log/nginx"]' \ post.hook.backup.velero.io/container=fsfreeze随后创建备份并验证钩子执行情况:
velero backup create nginx-hook-test velero backup get nginx-hook-test velero backup logs nginx-hook-test | grep hookCommand现代版本还支持通过velero backup describe <backup name>查看钩子执行结果统计(HooksAttempted与HooksFailed两个指标),失败详情会出现在Errors区段。
进阶技巧:多命令与 Shell 包装
由于钩子命令默认不在 Shell 中执行,存在两种常见诉求的解法:
多命令串联:将目标命令包装进一个 Shell,用;、&&等条件构造分隔。例如:
pre.hook.backup.velero.io/command='["/bin/bash", "-c", "echo hello > hello.txt && echo goodbye > goodbye.txt"]'使用 Pod 内环境变量:在命令开头显式引入 Shell(如/bin/sh),再通过$VAR引用容器环境变量。例如对定义了MYSQL_ROOT_PASSWORD的 mysql Pod,在执行备份前刷新并锁定 MySQL 表:
hooks: resources: - name: mysql-flush includedNamespaces: - default includedResources: - pods pre: - exec: container: mysql command: - /bin/sh - -c - mysql --password=$MYSQL_ROOT_PASSWORD -e "FLUSH TABLES WITH READ LOCK" onError: Fail注意:被调用的 Shell(/bin/sh、/bin/bash等)必须存在于目标容器镜像中。
从 Ark 到 Velero:钩子注解的演进
v0.7.1 文档是 Heptio Ark 时代的产物,此后项目更名为 Velero,钩子机制本身的语法、字段、执行语义得以完整保留,主要变化集中在注解域名与旧写法的去留:
| 版本阶段 | pre 钩子注解 | post 钩子注解 | 说明 |
|---|---|---|---|
| Ark v0.7.x(本文档) | pre.hook.backup.ark.heptio.com/* | post.hook.backup.ark.heptio.com/* | 兼容无pre.前缀的旧注解 |
| 现代 Velero | pre.hook.backup.velero.io/* | post.hook.backup.velero.io/* | 现代文档见 site/content/docs/main/backup-hooks.md |
现代源码中的注解键常量(internal/hook/item_hook_handler.go#L53-L58)证实了这一演化:
podBackupHookContainerAnnotationKey = "hook.backup.velero.io/container" podBackupHookCommandAnnotationKey = "hook.backup.velero.io/command" podBackupHookOnErrorAnnotationKey = "hook.backup.velero.io/on-error" podBackupHookTimeoutAnnotationKey = "hook.backup.velero.io/timeout"pre/post 阶段通过phasedKey组合出pre.hook.backup.velero.io/*与post.hook.backup.velero.io/*(L321-L326)。此外,Velero 还在备份钩子之外扩展了 Restore Hooks(恢复钩子)与 Init Container 钩子(对应post.hook.restore.velero.io/*、init.hook.restore.velero.io/*注解,见 L60-L71),形成覆盖备份与恢复全流程的钩子体系。
小结
Backup Hooks 是 Velero/Ark 保障卷备份数据一致性的核心机制:通过 pre/post 两阶段钩子,用户可以在快照前后执行任意容器内命令(典型的fsfreeze冻结/解冻场景),并且既能以 Pod 注解的方式零侵入地声明,也能在 Backup spec 中按命名空间、资源与标签选择器批量管理。理解container、command、on-error、timeout四个参数的含义与默认值,以及注解优先级高于 spec 钩子、pre 执行于自定义 Action 之前、post 等待所有附加项与 PVB 处理完毕的时序关系,是正确落地这一能力的关键。
【免费下载链接】veleroBackup and migrate Kubernetes applications and their persistent volumes项目地址: https://gitcode.com/GitHub_Trending/ve/velero
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考