
Velero 自定义插件开发指南插件类型、命名规则、配置与日志机制详解【免费下载链接】veleroBackup and migrate Kubernetes applications and their persistent volumes项目地址: https://gitcode.com/GitHub_Trending/ve/veleroVelero 提供了插件架构让用户在不修改、不重编译 Velero 核心二进制的情况下为备份与恢复流程扩展自定义功能。本文以官方文档site/content/docs/v1.10/custom-plugins.md的骨架为主线结合当前仓库源码pkg/plugin/framework、pkg/features、pkg/install等深入讲解如何给插件命名与注册、Velero 支持哪些插件类型Plugin Kind、插件如何获取 ConfigMap 配置、如何使用 Velero 提供的日志器、如何消费 Feature Flags 与LD_LIBRARY_PATH环境变量以及插件二进制如何以 init 容器 共享 emptyDir 卷的方式被 Velero Server 加载。读完本文你将具备从零开发并接入一个 Velero 自定义插件所需的完整知识。1. 插件架构总览init 容器 共享卷的加载机制Velero 的插件架构核心思想是插件是一个独立的 Go 二进制其中包含一个或多个 Velero 插件类型的实现外加少量样板代码boilerplate把这些实现暴露给 Velero。文档明确了其部署形态插件二进制被打进一个容器镜像该镜像作为 Velero Server Pod 的init 容器运行init 容器把二进制拷贝到共享的 emptyDir 卷中供 Velero Server 访问同一个二进制中可以同时实现多个插件类型也不限。这一机制在仓库中有直接印证。pkg/install/deployment.go构建 Velero Deployment 时主容器挂载了两个卷VolumeMounts: []corev1api.VolumeMount{ {Name: plugins, MountPath: /plugins}, {Name: scratch, MountPath: /scratch}, }, // ... Volumes: []corev1api.Volume{ {Name: plugins, VolumeSource: corev1api.VolumeSource{ EmptyDir: corev1api.EmptyDirVolumeSource{}}}, // ... }即/plugins目录就是插件二进制最终落地的位置init 容器只负责把镜像内的插件二进制写入该卷之后 Velero Server 启动时从/plugins目录扫描、拉起各个插件进程。仓库中同时提供了pkg/plugin/framework/doc.go的包注释点明该包是任何插件客户端包括插件作者与 Velero 核心本身都需要导入的公共包即插件作者与 Velero 核心共用同一套框架代码gRPC 握手、客户端/服务端桩保证了版本间协议兼容。2. 插件命名规则Plugin Naming插件由前缀 名称prefix name共同标识。文档给出了一条重要约定不要对非 Velero 团队支持的插件使用velero.io作为前缀。前缀应当能帮助用户识别插件的开发方请使用能代表你自己的前缀。当你定义 Backup Storage LocationBSL或 Volume Snapshot LocationVSL时这个完整插件名就是provider字段的取值。例如oracle.io/oracleapiVersion: velero.io/v1 kind: BackupStorageLocation spec: provider: oracle.io/oracleapiVersion: velero.io/v1 kind: VolumeSnapshotLocation spec: provider: oracle.io/oracle完整名称必须满足以下规则由两部分组成前缀 名称用/分隔两部分均不能为空前缀必须是一个合法的 DNS 子域名相同前缀 名称的插件不能已经存在。合法的示例- example.io/azure - 1.2.3.4/5678 - example-with-dash.io/azure注册入口RegisterX 系列函数文档要求插件作者在注册时传入完整名称对应pkg/plugin/framework/server.go中的RegisterX函数族。从当前仓库源码看注册接口Server接口提供了每种插件类型单数与复数两套注册方法插件类型单注册批量注册Backup Item Actionv1/v2RegisterBackupItemAction/RegisterBackupItemActionV2RegisterBackupItemActions/RegisterBackupItemActionsV2Volume SnapshotterRegisterVolumeSnapshotterRegisterVolumeSnapshottersObject StoreRegisterObjectStoreRegisterObjectStoresRestore Item Actionv1/v2RegisterRestoreItemAction/RegisterRestoreItemActionV2RegisterRestoreItemActions/RegisterRestoreItemActionsV2Delete Item ActionRegisterDeleteItemActionRegisterDeleteItemActionsItem Block ActionRegisterItemBlockActionRegisterItemBlockActions这些方法均以流式fluent风格返回Server支持链式调用例如server : framework.NewServer(). RegisterBackupItemAction(example.io/add-annotation, newAddAnnotationAction). RegisterRestoreItemAction(example.io/change-image, newChangeImageAction) server.BindFlags(pflag.CommandLine).Serve()每个注册函数接收一个common.HandlerInitializer回调Velero 在收到某类请求时才会调用该回调来构造具体的 handler实现按需初始化。所有Register*方法的注释都明确写道Accepted format for the plugin name is DNS subdomain/non-empty name与命名规则一一对应。3. 插件类型Plugin Kinds全景文档列出了 Velero 支持的插件类型Object Store持久化和取回备份文件、备份日志与恢复日志Volume Snapshotter备份时创建卷快照恢复时从快照恢复卷Backup Item Action在某个资源被写入备份文件之前针对单个资源执行任意逻辑Restore Item Action在某个资源被恢复进集群之前针对单个资源执行任意逻辑Delete Item Action在删除备份之前基于备份中的单个资源执行任意逻辑。源码级补充还有 v2 与 ItemBlockAction 两种类型结合 plugin_kinds.go 可以看到当前仓库实际支持的PluginKind常量比文档列表更完整PluginKindObjectStore PluginKind ObjectStore PluginKindVolumeSnapshotter PluginKind VolumeSnapshotter PluginKindBackupItemAction PluginKind BackupItemAction PluginKindBackupItemActionV2 PluginKind BackupItemActionV2 PluginKindRestoreItemAction PluginKind RestoreItemAction PluginKindRestoreItemActionV2 PluginKind RestoreItemActionV2 PluginKindDeleteItemAction PluginKind DeleteItemAction PluginKindItemBlockAction PluginKind ItemBlockAction PluginKindPluginLister PluginKind PluginLister几点值得注意v1/v2 兼容机制PluginKindsAdaptableTo映射表plugin_kinds.go#L60-L63声明了BackupItemAction可适配为BackupItemActionV2、RestoreItemAction可适配为RestoreItemActionV2。从源码结构看这意味着即使插件只注册了旧版 v1 action框架也能将其包装成 v2 语义供调用方使用实现了新旧 API 的平滑过渡PluginLister 不是开发者需要实现的类型AllPluginKinds()的注释明确说明 PluginLister 由 Velero 与插件框架代码自动处理在 server.go 的 Serve() 中NewPluginListerPlugin(pluginLister)被自动注册进 gRPC 插件映射Velero Server 正是通过它枚举一个二进制里到底暴露了哪些插件名Server.Serve()中把所有注册进来的插件以 gRPC 方式plugin.DefaultGRPCServer统一挂载底层通信协议来自github.com/hashicorp/go-plugin。4. 插件日志Plugin LoggingVelero 为插件提供了一个专用日志器插件可以用它向主 Velero Server 日志或按备份/恢复维度的日志中输出结构化信息。文档还强调Velero 会向每个插件进程透传--log-level参数其值来自主 Velero 进程的同一参数。因此当 Velero Server 以--log-leveldebug运行时插件也会输出 debug 级别日志。仓库中的实现细节在 logger.go有几个关键设计func newLogger() *logrus.Logger { logger : logrus.New() /* !!!DO NOT SET THE OUTPUT TO STDOUT!!! go-plugin uses stdout for a communications protocol between client and server. stderr is used for log messages from server to client. */ logger.Formatter logrus.JSONFormatter{ FieldMap: logrus.FieldMap{ logrus.FieldKeyMsg: message, // hclog-compatible message field }, DisableTimestamp: true, } // ... 位置钩子、错误位置钩子、HcLogLevelHook return logger }要点严禁插件向日志器输出到 stdout——stdout 被 go-plugin 用作客户端/服务端通信协议通道插件日志走stderr以 JSON 格式message字段兼容 hclog发送Velero Server 解析后写入标准日志流不重复打时间戳——Velero Server 输出日志时已加时间戳插件侧因此关闭附带LogLocationHook、ErrorLocationHook、HcLogLevelHook三个钩子分别用于标记日志发生在插件钩子内、记录错误位置以及把WarnLevel的字符串形态调整成 go-plugin 可解析的形式。另外--log-level参数在框架侧的处理可以在 server.go 的 Serve() 中看到解析 flags 后立即执行s.log.Level s.config.LogLevel.Parse()即日志级别是插件进程启动时就确定的。该 flag 的合法取值由pkg/cmd/server/config/config.go中的LogLevel.AllowedValues()提供。5. 插件配置基于 ConfigMap 的约定如果插件需要在运行时接收配置Velero 采用ConfigMap 约定。按照文档创建一个如下 ConfigMapapiVersion: v1 kind: ConfigMap metadata: # 名称可以是任意值Velero 通过下面的 labels 识别而不是名称 name: my-plugin-config # 必须位于 Velero Deployment 所在的命名空间 namespace: velero labels: # 这个无值 label 标识该 ConfigMap 是某个插件的配置 # 这里以内置的 change-storageclass restore item action 插件为例 velero.io/plugin-config: # 添加一个 key 为完全限定插件名如 mydomain.io/my-plugin-name、 # value 为插件类型BackupItemAction、RestoreItemAction、 # ObjectStore 或 VolumeSnapshotter的 label mydomain.io/my-plugin-name: RestoreItemAction data: # 在这里以 key-value 形式添加你的配置数据插件在自己的实现代码中读取该 ConfigMap 即可获得配置。源码印证Velero 如何找到这份 ConfigMapplugin_config.go 中的GetPluginConfig揭示了查找逻辑func PluginConfigLabelSelector(kind PluginKind, name string) string { return fmt.Sprintf(velero.io/plugin-config,%s%s, name, kind) }即 Velero 以velero.io/plugin-config完整插件名插件类型作为label selector去 List ConfigMap找不到 → 返回nil插件无配置属正常情况找到多个→ 直接报错found more than one ConfigMap matching label selector ...。因此同一个插件名 类型只允许存在一份配置 ConfigMap否则备份/恢复流程会失败label selector 是精确匹配这也解释了文档中value 必须与插件类型完全一致的硬性要求。ObjectStore 与 VolumeSnapshotter 的配置键校验对于 Object Store 和 Volume Snapshotter 这两类需要config键值对的插件仓库提供了白名单校验工具 validation.goValidateObjectStoreConfigKeys(config, validKeys...)每个configkey 必须在合法键列表中且bucket、prefix、caCert三个键始终被视为合法前两者由 Velero 自动注入到所有 object store 配置中ValidateVolumeSnapshotterConfigKeys(config, validKeys...)同样机制但没有自动追加键。出现非法键时会返回config has invalid keys ...; valid keys are ...错误。插件作者应复用这两个函数对用户传入的 BSL/VSLconfig做前置校验尽早暴露配置错误。6. Feature Flags 透传Velero 会把所有已知的 feature flags 以逗号分隔的字符串列表形式传入插件二进制的--features参数。文档建议解析成[]string后用NewFeatureFlagSet函数注册之后用features.Enabled(featureName)查询。对应实现位于 feature_flags.go当前仓库导出的 API 为// NewFeatureFlagSet 初始化并填充一个新的 FeatureFlagSet。 // 必须先调用它才能正确初始化 flag 追踪集合 // 测试中也有选择性地控制 flag 的用途。 func NewFeatureFlagSet(flags ...string) func IsEnabled(name string) bool { return featureFlags.set.Has(name) } func Enable(names ...string) func Disable(names ...string) func All() []string func Serialize() string // 逗号拼接可回传给子进程插件的典型用法// 从 --features 解析出的字符串切分为 []string 后 features.NewFeatureFlagSet(featureSlice...) if features.IsEnabled(NodeAgent) { // 走新特性代码路径 }注意文档措辞为features.Enabled而当前仓库实际函数名是IsEnabled——以仓库源码为准二者语义一致查询某个 flag 是否启用。Serialize()的存在也说明该包可用于把父进程的 flags 原样再序列化传给下游进程。7. 环境变量LD_LIBRARY_PATH文档指出Velero 会把LD_LIBRARY_PATH加入环境变量列表为那些运行时依赖 C 库/扩展的插件提供便利。在 deployment.go 中可以看到这一约定的落地方式——主容器显式设置Env: []corev1api.EnvVar{ {Name: VELERO_SCRATCH_DIR, Value: /scratch}, {Name: VELERO_NAMESPACE, ValueFrom: corev1api.EnvVarSource{...}}, {Name: LD_LIBRARY_PATH, Value: /plugins}, },即插件二进制被 init 容器拷贝进/plugins卷后主容器以LD_LIBRARY_PATH/plugins运行。这样插件二进制所依赖的.so动态库只要与二进制一起放进/plugins目录就能在进程启动时被动态链接器找到——这对封装了 CGO 代码或非 Go 实现的插件例如依赖厂商私有库的 Object Store 插件非常关键。此外还可以看到两个相关环境变量VELERO_SCRATCH_DIR/scratch临时文件目录与VELERO_NAMESPACE通过 fieldRef 注入自身命名空间。8. 插件开发落地步骤小结综合文档约定与仓库实现开发并接入一个 Velero 自定义插件的完整路径是编写插件二进制基于 Velero 提供的示例插件仓库官方 sample plugin repository可作为起点文档中以velero-io/velero-plugin-example引用编写实现导入pkg/plugin/framework包通过framework.NewServer()RegisterX系列函数注册全部插件实现BindFlags后调用Serve()遵守命名规则使用你的域名/插件名形式前缀勿用velero.io确保集群内唯一构建容器镜像把二进制打进镜像配置为 Velero Server Pod 的 init 容器将二进制拷贝到共享 emptyDir 卷对应 Deployment 中挂载到/plugins的卷可选提供配置在 Velero 所在命名空间创建带velero.io/plugin-config标签的 ConfigMaplabel key 为完整插件名、value 为插件类型ObjectStore、VolumeSnapshotter、BackupItemAction、RestoreItemAction等可选消费 feature flags 与日志解析--features后NewFeatureFlagSet初始化日志统一走框架提供的 loggerstderr JSON日志级别自动跟随主进程--log-level。相关源码索引内容仓库路径插件框架公共文档客户端/服务端共用包doc.goServer 接口与全部 RegisterX 注册函数server.gogRPC 服务挂载Serve 方法server.go插件类型常量与 v1→v2 适配表plugin_kinds.goConfigMap 配置查找label selectorplugin_config.goObjectStore/VolumeSnapshotter 配置键校验validation.go插件日志器stderr JSON 协议logger.goFeature Flags 管理feature_flags.goServer Pod 的/plugins卷与LD_LIBRARY_PATHdeployment.go【免费下载链接】veleroBackup and migrate Kubernetes applications and their persistent volumes项目地址: https://gitcode.com/GitHub_Trending/ve/velero创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考