Velero 插件架构深度解析:从 Ark 插件体系到自定义扩展实战
【免费下载链接】veleroBackup and migrate Kubernetes applications and their persistent volumes项目地址: https://gitcode.com/GitHub_Trending/ve/velero
本篇技术指南以 Velero(前身 Heptio Ark)的插件架构为核心主题,系统讲解插件体系的设计理念、四种核心插件类型、基于 go-plugin 与 gRPC 的插件通信机制、插件日志规范,以及如何将自定义插件以 init container 方式接入 Velero Server 的完整实战流程。读者读完本文后,将掌握 Velero 插件的种类划分与职责边界、插件二进制的编写与注册方式,并能够基于仓库源码理解插件在备份/恢复生命周期中的真实调用位置。
一、插件架构的设计初衷:不改核心、不重编译
Velero(原 Heptio Ark)采用插件架构的核心目的,是允许用户在不修改、不重新编译 Velero 核心二进制的前提下,为备份(Backup)与恢复(Restore)流程注入自定义功能。这一设计理念由 site/content/docs/v0.10.0/plugins.md 明确阐述,并在当前仓库中得到了完整的代码级落地。
实现方式可概括为三步:
- 编写独立二进制:用户创建一个自己的二进制程序,内部实现 Velero 支持的若干种"插件类型(Plugin Kind)"(下文详述),并附带少量样板代码(boilerplate),将插件实现暴露给 Velero。
- 打包为 init container 镜像:将该二进制放入一个容器镜像中,作为 Velero Server Pod 的init container运行。
- 共享 emptyDir 卷交付:init container 将二进制复制进一个共享的
emptyDir卷,Velero Server 主容器从该卷加载并调用插件。
同一个二进制中可以同时实现多个插件、且插件类型不限,例如一个插件二进制既可以实现 Object Store,也可以实现 Backup Item Action,这为云厂商插件(如 AWS/GCP/Azure 插件)提供了高度聚合的打包形态。
需要特别说明的是:原文档中的插件类型名称(Object Store、Block Store、Backup Item Action、Restore Item Action)属于 Heptio Ark 时代(v0.10.0)的命名。当前仓库已将其演进为 Velero 的标准插件体系,其中Block Store 演变为 VolumeSnapshotter,并新增了 Delete Item Action、Item Block Action 等类型(见 pkg/plugin/framework/common/plugin_kinds.go)。下文将以当前仓库源码为准展开。
二、插件类型(Plugin Kinds)全景
原文档列出了 Ark 时代支持的四种插件类型,我们逐一对照当前仓库的实现进行解读:
| 插件类型(原文档) | 当前仓库对应 Kind | 职责 |
|---|---|---|
| Object Store | ObjectStore | 持久化与检索备份文件、备份日志、恢复日志 |
| Block Store | VolumeSnapshotter | 备份时创建卷快照,恢复时从快照还原卷 |
| Backup Item Action | BackupItemAction(含 v2) | 在单个 item 被写入备份文件之前,对其执行任意逻辑 |
| Restore Item Action | RestoreItemAction(含 v2) | 在单个 item 被恢复到集群之前,对其执行任意逻辑 |
在 pkg/plugin/framework/common/plugin_kinds.go 中,Velero 完整定义了如下插件 Kind 常量:
PluginKindObjectStore—— 对象存储插件;PluginKindVolumeSnapshotter—— 卷快照插件(即原 Block Store);PluginKindBackupItemAction/PluginKindBackupItemActionV2—— 备份 item 动作插件及其 v2 版本;PluginKindRestoreItemAction/PluginKindRestoreItemActionV2—— 恢复 item 动作插件及其 v2 版本;PluginKindDeleteItemAction—— 删除 item 动作插件;PluginKindItemBlockAction—— ItemBlock 动作插件;PluginKindPluginLister—— 插件列举器(开发者无需实现,由 Velero 与插件库代码内部处理,见 plugin_kinds.go 中AllPluginKinds()的注释说明)。
同时,源码通过PluginKindsAdaptableTo映射表(plugin_kinds.go)支持版本自适应:v1 版本的 Backup/Restore Item Action 可以被 v2 版本插件兼容,这保证了插件生态的向后兼容性。
2.1 各插件类型的职责细节
Object Store(对象存储):负责备份数据落地与读取,是所有备份/恢复流程的存储底座。它承担三类数据的持久化与检索:备份文件(backup tarball)、备份日志(backup logs)与恢复日志(restore logs)。其 gRPC 服务端实现位于 pkg/plugin/framework/object_store.go,通过proto.RegisterObjectStoreServer注册。
VolumeSnapshotter(卷快照,原 Block Store):对接云厂商卷快照能力——备份阶段创建持久卷快照,恢复阶段从快照创建新卷。对应实现见 pkg/plugin/framework/volume_snapshotter.go。
Backup Item Action(备份 item 动作):在备份过程中,对**每个 Kubernetes 资源对象(item)**在写入备份文件之前执行自定义逻辑。典型场景包括:加密/脱敏 Secret 数据、改写资源字段、注入自定义注解、处理特定资源类型的额外数据等。该插件的执行时机是"逐 item 回调",因此可以实现非常细粒度的备份前处理,对应实现见 pkg/plugin/framework/backup_item_action.go(v1)与 pkg/plugin/framework/backupitemaction/v2/backup_item_action.go(v2)。
Restore Item Action(恢复 item 动作):与 Backup Item Action 对称,在恢复流程中每个 item 被应用到集群之前执行任意逻辑。典型场景包括:修改恢复后的资源命名空间、改写存储类(StorageClass)、注入集群特定配置、调整资源配额字段等,对应实现见 pkg/plugin/framework/restore_item_action.go(v1)与 pkg/plugin/framework/restoreitemaction/v2/restore_item_action.go(v2)。
三、插件运行机制:go-plugin 与 gRPC 进程模型
当前仓库中,Velero 的插件体系建立在HashiCorp go-plugin框架之上,并采用gRPC作为进程间通信协议。这一点可以从插件接口定义中直接证实:
pkg/plugin/framework/interface.go 中,Interface接口内嵌了plugin.Plugin(来自github.com/hashicorp/go-plugin),并要求插件实现Names() []string方法——返回该插件注册的所有实现名称(例如一个 Backup Item Action 插件可同时注册针对pod、pvc等多个资源类型的实现)。
从 pkg/plugin/framework/server.go 可以看到,插件侧的Server接口提供了完整的注册能力:
RegisterBackupItemAction/RegisterBackupItemActionV2:注册备份 item 动作插件;RegisterRestoreItemAction/RegisterRestoreItemActionV2:注册恢复 item 动作插件;RegisterVolumeSnapshotter:注册卷快照插件;RegisterObjectStore:注册对象存储插件;RegisterDeleteItemAction:注册删除 item 动作插件;RegisterItemBlockAction:注册 ItemBlock 动作插件。
所有这些Register*方法均支持链式调用(返回Server自身),方便在main函数中一次性完成多个插件的注册。插件名称的合法格式为<DNS subdomain>/<non-empty name>(见 server.go 的注释说明)。
最终,Serve()方法(server.go)会将所有已注册的插件通过plugin.Serve暴露为 gRPC 服务,并额外注册一个PluginLister服务,供 Velero Server 侧枚举该插件二进制实际提供了哪些 Kind 与名称。
3.1 为什么插件必须通过独立进程运行
go-plugin 采用"进程外插件"模型:每个插件二进制是独立进程,Velero Server 通过 gRPC 与其通信。这样做的好处是:
- 插件崩溃不会拖垮 Velero Server;
- 插件可以用任何实现了 go-plugin 协议的编程语言编写,不局限于 Go;
- 插件二进制与 Velero 核心解耦,用户升级插件无需重新编译核心。
3.2 插件二进制如何被打包进 Velero Server
从 pkg/cmd/cli/plugin/add.go 的实现可以看到,Velero 为插件专门定义了一个名为plugins的 volume 与挂载路径/plugins:
- 插件镜像作为init container被注入 Velero Server Pod;
- init container 将插件二进制复制到共享的
pluginsemptyDir 卷; - Velero Server 主容器通过
MountPath: "/plugins"挂载该卷,从中发现并启动插件进程。
而在安装层面,velero install命令提供了--plugins参数用于一次性注入插件镜像,见 pkg/cmd/cli/install/install.go:
# 安装 Velero 时同时安装云厂商插件镜像 velero install --provider gcp \ --plugins velero/velero-plugin-for-gcp:v1.0.0 \ --bucket mybucket \ --secret-file ./gcp-service-account.json也可以使用velero plugin add <image>在部署后动态追加插件镜像,并使用velero plugin get查看当前 Server 上已加载的插件信息(命令定义见 pkg/cmd/cli/plugin/get.go 与velero get plugins子命令入口 pkg/cmd/cli/get/get.go)。
四、插件日志规范:结构化日志如何汇入 Velero Server
原文档特别强调:Velero(Ark)为插件提供了一套日志记录器(logger),使插件能够输出结构化日志,并使其汇入 Velero Server 主日志或按备份/恢复隔离的独立日志中。这一机制在当前仓库的实现位于 pkg/plugin/framework/logger.go,其中有几个非常关键的工程细节:
- 绝不能将日志输出到 stdout:
go-plugin使用stdout 作为客户端与服务端之间的通信协议。插件日志必须走stderr,Velero Server 侧会捕获 stderr 并将日志转发到自己的日志系统(源码注释!!!DO NOT SET THE OUTPUT TO STDOUT!!!明确标注了这一约束)。 - 使用 JSON Formatter:插件 logger 采用
logrus.JSONFormatter,并映射消息字段为@message(hclog 兼容字段),因为 Velero Server 侧的 go-plugin 会解析 stderr 上的 JSON 并生成结构化日志条目。 - 禁用时间戳:插件内不追加时间戳,由 Velero Server 在最终输出日志时统一添加,避免时间格式不一致。
- 挂载日志 Hook:通过
LogLocationHook、ErrorLocationHook、HcLogLevelHook等 Hook,分别标记日志位置、记录错误位置,并将warning级别统一调整为 go-plugin 可解析的warn表示。
对插件作者而言,这意味着:在插件代码中直接使用 Velero 提供的 logger 记录日志,即可自动获得与 Velero Server 日志体系完全兼容的结构化输出,无需自行处理日志格式化与传输问题。
五、实战:编写一个自定义插件的完整路径
结合原文档的指引与当前仓库源码,编写一个 Velero 自定义插件二进制需要经过以下步骤:
5.1 搭建插件骨架
创建一个独立的 Go 模块,引入 Velero 的插件框架代码(当前仓库中可参考的框架入口为pkg/plugin/framework与pkg/plugin/velero目录)。插件的main函数通常形如:
func main() { server := framework.NewServer() server. RegisterObjectStore(newMyObjectStore). RegisterBackupItemAction("myplugin/my-action", newMyBackupAction). RegisterRestoreItemAction("myplugin/my-restore", newMyRestoreAction) server.Serve() }其中:
framework.NewServer()(见 pkg/plugin/framework/server.go)会预置所有插件类型的处理器与 logger;Register*系列方法接收插件名(<DNS subdomain>/<name>格式)与common.HandlerInitializer类型的初始化函数;Serve()解析命令行参数、按注册内容构建PluginLister,并启动 gRPC 服务等待 Velero Server 连接。
5.2 实现插件接口
每种插件类型都定义了各自必须实现的接口。以 Backup Item Action 为例(v2 版本见 pkg/plugin/framework/backupitemaction/v2/backup_item_action.go),需要实现备份时对单个 item 的处理方法;Restore Item Action 则对应实现恢复时的处理方法。对象存储插件则需实现备份文件的读写、日志存取等能力(gRPC 服务端定义见 pkg/plugin/framework/object_store.go)。
5.3 打包、部署与验证
- 将插件二进制构建进一个容器镜像;
- 安装 Velero 时通过
velero install --plugins <你的镜像>注入,或部署后执行velero plugin add <你的镜像>; - 用
velero get plugins确认插件已被 Velero Server 发现并加载; - 执行一次备份/恢复,在 Velero Server 日志或备份/恢复专属日志中观察插件输出(使用第四节所述的 logger)。
对于希望快速上手的插件作者,原文档推荐从官方示例插件仓库(ark-plugin-example)出发——它演示了如何实例化与使用插件 logger、如何实现各插件类型的完整样板代码。在当前仓库中,可以对照 pkg/plugin/velero 目录(含各插件 Kind 的 v1/v2 接口定义与 mocks)以及 pkg/plugin/framework/examples_test.go 理解接口的精确用法。
六、小结
Velero 的插件架构以"独立二进制 + init container + emptyDir 共享卷 + go-plugin/gRPC 进程通信"为核心,将对象存储、卷快照、备份/恢复 item 动作等能力完全开放给用户,实现了:
- 核心与扩展解耦:无需修改/重编译 Velero 核心即可扩展任意备份/恢复行为;
- 多插件聚合打包:一个二进制可同时承载多种类型、多个名称的插件实现;
- 日志体系统一:插件结构化日志自动汇入 Velero Server 日志与按备份/恢复隔离的日志;
- 版本平滑演进:v1/v2 插件接口通过
PluginKindsAdaptableTo映射保持兼容。
无论是接入新对象存储后端、对接云厂商快照,还是定制备份前的数据脱敏与恢复后的资源配置,理解这套插件体系都是进行 Velero 二次开发的起点。
【免费下载链接】veleroBackup and migrate Kubernetes applications and their persistent volumes项目地址: https://gitcode.com/GitHub_Trending/ve/velero
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考