深入 Ark(Velero 前身)扩展机制:Backup Hooks 与插件架构完全指南
【免费下载链接】veleroBackup and migrate Kubernetes applications and their persistent volumes项目地址: https://gitcode.com/GitHub_Trending/ve/velero
导读
本文围绕 Ark(Heptio Ark,即 Velero 的前身,v0.7.0 时代项目代号)官方文档中关于扩展核心能力的篇章展开,系统讲解 Ark 提供的两套扩展机制:Hooks(钩子)与Plugins(插件)。读完本文,你将掌握:如何在备份过程中通过 Pod 注解或 Backup Spec 注入自定义命令(pre/post 钩子)以配合数据库冻结、磁盘缓冲刷新等工作负载特定操作;以及如何通过插件体系开发自定义对象存储后端、块存储后端与逐项(per-item)备份/恢复动作,而无需重新编译 Ark 核心二进制。
说明:v0.7.0 文档仍使用项目旧名 "Ark" 与旧 API 组
ark.heptio.com/v1。随着项目更名为 Velero,当前仓库中对应实现已演进为velero.io/v1API 与新的注解前缀,文中将同时给出历史与现状的对应关系,并标注源码证据路径。
一、为什么需要扩展机制
Ark(Velero)是一个 Kubernetes 应用备份与迁移工具,其核心备份/恢复流程是通用的:收集资源、执行自定义动作、写入对象存储、恢复时回放。但真实业务场景中,单一通用流程无法覆盖所有需求:
- 备份一个正在运行数据库 Pod 时,可能需要先冻结文件系统(
fsfreeze --freeze)确保磁盘 I/O 全部落盘后再做快照; - 不同云厂商的对象存储(AWS S3、Azure Blob、GCP GCS)与块存储快照 API 各不相同;
- 某些业务资源在备份前需要临时改写(例如移除指向集群内地址的引用),恢复时又需要还原。
Ark 通过两大机制解决上述问题(见 extend.md):
- Hooks(钩子):在备份过程中,于正在运行的 Pod 容器内执行指定命令,适合"工作负载特定的命令",例如刷新磁盘缓冲、冻结数据库。
- Plugins(插件):允许开发者实现自定义的对象存储/块存储后端,或逐项(per-item)备份/恢复动作,执行任意逻辑(包括修改被备份/恢复的对象),且无需编译进 Ark 核心二进制,即可被 Ark 使用。
二、Backup Hooks:在备份中执行 Pod 内命令
Ark 在备份时支持在 Pod 的容器内执行一条或多条命令。Ark v0.7.0 引入两个阶段的钩子(详见 hooks.md):
- pre 钩子:在任何自定义动作(custom action)处理之前执行。v0.7.0 之前仅支持 pre 钩子。
- post 钩子(v0.7.0+):在所有自定义动作完成之后、且自定义动作所产生的所有附加资源(additional items)也都备份完毕之后执行。
pre 与 post 最典型的配合场景是冻结文件系统:先通过 pre 钩子执行fsfreeze --freeze,确保所有待处理的磁盘 I/O 已完成,再让 Ark 对磁盘做快照,最后用 post 钩子执行fsfreeze --unfreeze解冻。
钩子可通过两种方式指定:Pod 上的注解(annotations)与Backup Spec(备份定义)。
2.1 通过 Pod 注解指定钩子
在 Pod 上使用以下注解即可让 Ark 备份该 Pod 时执行钩子。
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。可选。 |
兼容性说明:Ark v0.7.0+ 仍然支持旧版(已弃用)的 pre 钩子写法——即注解名不带pre.前缀(如hook.backup.ark.heptio.com/container)。
在项目演进为 Velero 后,注解前缀相应变更为pre.hook.backup.velero.io/与post.hook.backup.velero.io/。从当前仓库的 内部钩子处理器实现 可以看到这一演进:
- 处理器
DefaultItemHookHandler.HandleHooks首先从注解中解析钩子,pre 阶段若找不到带阶段的注解,还会回退检查不带阶段前缀的"遗留注解键"(legacy hook annotation keys),以兼容旧写法; - 注解中解析出的钩子优先级最高:
If the pod has the hook specified via annotations, that takes priority.,随后才检查 Backup Spec 中定义的钩子; - 钩子目前只支持 Pod 资源(
We only support hooks on pods right now),对非 Pod 资源直接跳过; - 命令的解析由 parseStringToCommand 完成——单个字符串按空格拆分,JSON 数组则解析为多参数命令;
- 执行阶段由
PodCommandExecutor.ExecutePodCommand通过 Kubernetes Pod Exec API 在容器内运行命令,若onError为Fail且执行出错,pre/post 阶段会立即返回错误,从而终止该备份项的处理(详见 item_hook_handler.go)。
2.2 在 Backup Spec 中指定钩子
除注解外,还可以在 Backup 定义中按"资源选择器 + 钩子列表"的方式声明钩子。完整的字段注释可参考 Backup API Type 文档,示例结构如下:
apiVersion: ark.heptio.com/v1 kind: Backup metadata: name: a namespace: heptio-ark spec: includedNamespaces: - '*' # ... 其他备份字段 ... hooks: resources: - name: my-hook includedNamespaces: - '*' excludedNamespaces: - some-namespace includedResources: - pods excludedResources: [] labelSelector: matchLabels: app: ark component: server # DEPRECATED. 旧写法,等价于下面的 pre。 hooks: # 内容与 pre 相同 pre: - exec: container: my-container command: - /bin/uname - -a onError: Fail timeout: 10s post: # 内容与 pre 相同各字段语义:
hooks.resources:适用于特定资源的钩子数组;name:钩子名称,会显示在备份日志中;includedNamespaces/excludedNamespaces:钩子适用的命名空间范围,未指定则作用于全部命名空间;includedResources:钩子适用的资源类型,当前仅支持pods;labelSelector:钩子仅作用于匹配该标签选择器的对象;hooks:旧字段(已弃用),含义与pre相同;pre:在自定义动作执行之前运行的钩子数组,目前仅支持exec类型:exec.container:命令执行容器,缺省用 Pod 第一个容器;exec.command:命令数组(必填),如["/bin/uname", "-a"];exec.onError:错误处理方式,Fail(默认)或Continue;exec.timeout:执行超时时间,默认 30 秒;
post:在所有自定义动作及附加资源处理完成之后运行的钩子数组,内容与pre相同。
当前仓库中的类型定义佐证:上述结构在现版本中对应 backup_types.go 中的BackupHooks、BackupResourceHookSpec、BackupResourceHook与ExecHook。其中字段注释直接沿用了文档语义:PreHooks在"将条目存入备份之前执行",且先于 item action 产生的 additional items 处理;PostHooks在"所有 additional items 处理完之后执行"。ExecHook的OnError由HookErrorMode类型约束,Command有kubebuilder:validation:MinItems=1校验(至少一项),Timeout使用metav1.Duration类型。选择器匹配逻辑(命名空间/资源/标签)对应 ResourceHookSelector.applicableTo。
参考:在 Backup Spec 中指定钩子时,Ark 服务器侧的处理同样复用
DefaultItemHookHandler:先按选择器过滤出适用的resourceHooks,pre 阶段取resourceHook.Pre、post 阶段取resourceHook.Post,逐个执行exec钩子并记录到HookTracker,任一Fail模式钩子出错即中止该资源的后续钩子执行(item_hook_handler.go)。
三、Plugins:无需重编译的插件架构
Ark 的插件架构让用户无需修改/重新编译核心二进制,即可向备份与恢复流程添加自定义功能。开发者只需编写一个包含某种插件类型实现的小型二进制,再配合少量样板代码将实现暴露给 Ark;随后把这个二进制打入一个用作Ark server Pod 的 init container的容器镜像中,由 init container 将二进制拷贝到 Ark server 共享的 emptyDir 卷中供其访问。
官方提供了一个功能完整的[示例插件仓库]作为插件开发者的起点(v0.7.0 时代为 heptio/ark-plugin-example,即后续 velero-plugin-example 的前身)。
3.1 插件类型(Plugin Kinds)
Ark 当前支持以下四种插件类型:
| 插件类型 | 职责 |
|---|---|
| Object Store | 持久化与检索备份文件、备份日志、恢复日志 |
| Block Store | 备份时创建卷快照;恢复时从快照还原卷 |
| Backup Item Action | 在单个条目存入备份文件之前,对其执行任意逻辑 |
| Restore Item Action | 在单个条目恢复到集群之前,对其执行任意逻辑 |
在项目更名后,Block Store 在现版本代码中对应VolumeSnapshotter接口(见 manager.go 中的GetVolumeSnapshotter)。现版本的插件体系通过 plugin 目录 组织:
- 接口定义位于 pkg/plugin/velero,例如
ObjectStore接口(object_store.go)、BackupItemAction接口(backupitemaction/v1/backup_item_action.go)、RestoreItemAction接口(restoreitemaction/v1/restore_item_action.go); - 插件客户端管理由 pkg/plugin/clientmgmt 实现,
Manager统一负责各类型插件的获取与生命周期管理; - 插件间通信协议定义在 pkg/plugin/proto 的 gRPC
.proto文件中,例如ObjectStore.proto定义了PutObjectRequest、GetObjectRequest、DeleteObjectRequest、ListObjectsRequest、CreateSignedURLRequest等消息与ObjectStore服务,BackupItemAction.proto定义了ExecuteRequest/ExecuteResponse与BackupItemAction服务; - 每个插件进程通过独立的子进程(restartable process)加载,并提供
RestartableXxx包装层(如 restartable_object_store.go),保证插件崩溃后可重启恢复。
3.2 插件命名规范
Ark 依靠命名约定来识别插件。每个插件二进制应命名为:
ark-<plugin-kind>-<name>其中plugin-kind是objectstore、blockstore、backupitemaction、restoreitemaction之一,name在该插件类型内唯一。该命名约定确保了 Ark 服务器启动扫描插件目录时,能够将二进制正确归类到对应插件类型。
3.3 插件日志
Ark 为插件提供了[日志器],插件可用它向主 Ark server 日志或每个备份/恢复专属日志输出结构化信息。示例插件仓库中演示了如何在插件内实例化并使用该日志器。
在现版本仓库中,插件日志能力由 pkg/plugin/framework 与 pkg/plugin/clientmgmt 共同承载:插件通过日志服务把结构化日志回传至主进程,统一写入 Ark server 日志与对应备份/恢复的日志文件,便于排障时按备份粒度检索插件输出。
四、Hook 与 Plugin 的执行时序配合
理解两者的配合顺序有助于设计正确的扩展逻辑。以一次备份中的单个资源(Pod)为例,结合 hooks.md 与源码处理流程,执行顺序为:
- pre 钩子执行(来自注解或 Backup Spec,注解优先级更高);
- Ark 执行该资源的自定义动作(Backup Item Action),动作可能对资源做修改,并声明附加资源(additional items);
- Ark 备份动作产生的所有附加资源;
- post 钩子执行(同样支持注解或 Spec 两种来源)。
这也正是"冻结文件系统"示例能成立的原因:pre 冻结 → 快照落盘 → post 解冻,整个过程钩子只影响单个 Pod 的容器,不阻塞其他资源的并行备份。同时需要注意,onError: Fail的钩子一旦失败,会立即终止对应备份项的处理流程并记录错误,因此在生产环境建议先以Continue模式验证命令与超时设置,再切换为Fail。
五、实战指引:何时用 Hooks、何时用 Plugins
根据 extend.md 的定位,两类机制面向不同层次的需求:
- Hooks 适合"命令级"需求:在业务容器内跑一段命令,如
fsfreeze冻结文件系统、刷新数据库缓存、优雅停服。无需写代码,仅需注解或 Backup Spec 配置,且要求目标 Pod 内的容器存在可执行该命令的环境。 - Plugins 适合"代码级"需求:需要自定义存储后端(对象存储、块存储/快照)、或需要在备份/恢复单个条目前后执行任意逻辑(如修改对象内容、注入删除动作、改写引用)。要求编写 Go 代码并按照命名规范构建插件二进制。
两者可以并存:例如用插件做资源改写,用钩子做数据库冻结,共同作用于同一次备份。
六、当前仓库中的实现对照
文档描述的是 Ark v0.7.0 时代的能力,当前仓库(已更名 Velero)在继承的基础上做了演进,可对照以下路径深入学习:
- 钩子注解解析与执行:internal/hook/item_hook_handler.go(含
getHookAnnotation、getPodExecHookFromAnnotations、parseStringToCommand、DefaultItemHookHandler.HandleHooks); - 钩子类型定义:pkg/apis/velero/v1/backup_types.go(
BackupHooks/BackupResourceHookSpec/ExecHook); - 钩子执行器抽象:pkg/podexec/pod_command_executor.go(基于 Pod Exec API 执行命令,含超时控制);
- 插件接口定义:pkg/plugin/velero(
ObjectStore、VolumeSnapshotter、BackupItemAction、RestoreItemAction); - 插件管理客户端:pkg/plugin/clientmgmt/manager.go(插件生命周期管理与获取);
- 插件协议:pkg/plugin/proto(gRPC 服务定义);
- 插件注册与进程管理:pkg/plugin/clientmgmt/process/registry.go(按命名规范扫描、注册与拉起插件进程)。
总结
Ark(Velero)的扩展机制设计理念至今仍是其核心竞争力:Hooks 用最低成本解决"容器内命令"类需求,Plugins 用二进制隔离解决"深度定制"类需求,二者共同构成了一条从轻量配置到全功能开发的能力阶梯。理解了 extend.md 所统领的这两条主线,再对照 hooks.md、plugins.md 与 Backup API Type 文档,即可在任意版本(Ark v0.7.0 或 Velero 主线)中快速定位并实现自己的扩展需求。
【免费下载链接】veleroBackup and migrate Kubernetes applications and their persistent volumes项目地址: https://gitcode.com/GitHub_Trending/ve/velero
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考