
Velero Restore Hooks 设计解析让 Kubernetes 恢复流程自动执行自定义命令【免费下载链接】veleroBackup and migrate Kubernetes applications and their persistent volumes项目地址: https://gitcode.com/GitHub_Trending/ve/velero导读本文基于 Velero 仓库中的设计文档 design/Implemented/restore-hooks.md系统讲解 Restore Hooks恢复钩子的完整设计它允许用户在恢复Restore过程中像备份Backup一样执行自定义命令从而解决数据恢复后还需人工登录 Pod 手动完成数据导入的痛点。读完本文你将掌握 Restore Hooks 的两种形态Exec 恢复钩子与 InitContainer 恢复钩子、Restore spec 与 Pod 注解两种定义方式、全部注解键与参数语义、失败处理模型onError: Fail / Continue以及底层实现原理并可直接在自己的 Velero 恢复场景中落地。一、为什么需要 Restore Hooks从备份钩子到恢复钩子的缺口1.1 背景Backup Hooks 解决了备份前准备却没有恢复后处理Velero 早已支持Backup Hooks备份钩子允许用户在备份之前pre和备份之后post执行命令。设计文档中给出了一个典型的场景给 Postgres Pod 挂载一个空卷用备份钩子执行pg_dump将数据导出到该卷再把包含导出的卷备份下来。这样做的目的是在数据使用中也能做出一致性备份不必冻结正在使用的卷。但问题在于恢复过程没有任何自动化的对应机制。按照上面的配置恢复后Postgres Pod 是空的必须由人工exec进入 Pod 手动执行pg_restore才能把数据导回。这既不自动化也无法扩展到大集群规模。1.2 设计目标与非目标维度内容Goal让用户在恢复过程中运行自定义命令能力上对齐备份钩子Goal对恢复后 Pod 中执行的命令结果提供可观测性写入 Restore 日志、反映到 Restore 对象状态Non Goal不处理任何应用特定的业务场景如 postgres、mongo 等专用逻辑交由用户通过钩子命令自行实现1.3 备选方案回顾设计权衡设计文档在 Alternatives Considered 中记录了被否决的候选方案这些权衡直接解释了最终设计的形态等所有恢复的 Pod 都 Ready 后再同时执行第一批钩子再执行下一批可能引入死锁例如 API Pod 必须等 DB Pod 恢复完成才能就绪。把恢复钩子挂在 Backup spec 上作为pre、post之外的第三个生命周期事件restore会造成混乱因为pre/post出现在 Backup 日志中而restore只出现在 Restore 日志中。对每个 Pod 并行执行恢复钩子与备份钩子的既有行为不一致。等待 PodStatus 就绪Ready后再执行 post 钩子存在某些场景下 Pod 在恢复钩子执行完成之前不应上报 Ready。把注入的 initContainers 日志也写入 Restore 日志exec 钩子的 stdout/stderr 一旦不写入日志就会永久丢失而注入 initContainers 的日志随时可以通过kubectl或 Kubernetes API 获取因此不必写入 Restore 日志。二、高层设计在 Restore spec 中新增 hooks 段2.1 设计要点设计文档给出的 High-Level Design 可以归纳为以下几点Restore spec 中新增spec.hooks段结构上与 Backup spec 的 hooks 一致但只允许定义post钩子不允许pre钩子恢复没有恢复前的语义。Pod 上也可以设置与备份阶段类似的注解annotation来定义钩子。对每个被恢复的 PodVelero server 会检查是否存在适用于该 Pod 的钩子。如果存在适用钩子Velero 会等待钩子要执行的目标容器进入 Running 状态后再执行。Restore 日志会记录每个 post-restore 钩子的执行结果Restore 对象状态status也会汇总钩子执行结果。新增spec.hooks.resources.initContainers段允许向恢复的 Pod 注入 initContainers也可以用注解替代在 Restore 对象中定义。2.2 注解优先级规则与备份钩子完全一致如果 Pod 上定义了钩子注解则该 Pod 不再应用 Restore spec 中定义的任何钩子As with Backups, if an annotation is defined on a pod then no hooks from the Restore spec will be applied.。注解与 spec 二选一注解优先。三、Post-Restore 钩子详解在恢复后的容器中执行命令Post-restore 钩子Exec Restore Hook在恢复后的 Pod 容器内执行命令可通过Pod 注解或Restore spec 中的资源钩子数组两种方式定义。3.1 支持的注解Post 钩子注解键含义post.hook.restore.velero.io/container执行钩子的目标容器名post.hook.restore.velero.io/command在容器内执行的命令post.hook.restore.velero.io/on-error执行失败时的处理方式post.hook.restore.velero.io/exec-timeout命令执行开始后的超时时间post.hook.restore.velero.io/wait-timeout等待容器可用ready的超时时间实现说明当前源码在 internal/hook/item_hook_handler.go 中定义的注解键与设计文档完全对应并在此基础上增加了post.hook.restore.velero.io/wait-for-ready布尔值字符串用于要求容器进入完全 Ready 状态后才执行钩子。getPodExecRestoreHookFromAnnotations函数负责解析这些注解命令为空返回 nil跳过该 Pod、on-error只接受Fail/Continue两个合法值、exec-timeout与wait-timeout使用time.ParseDuration解析解析失败时记录警告并忽略。3.2 在 Restore spec 中定义 Post 钩子示例设计文档给出了完整的 Restore 定义示例其中包含了同时定义 post 钩子与 init 钩子的完整结构apiVersion: velero.io/v1 kind: Restore spec: ... hooks: resources: - name: my-hook includedNamespaces: - * excludedNamespaces: - some-namespace includedResources: - pods excludedResources: [] labelSelector: matchLabels: app: velero component: server post: - exec: container: postgres command: - /bin/bash - -c - rm /docker-entrypoint-initdb.d/dump.sql onError: Fail timeout: 10s readyTimeout: 60s init: timeout: 120s initContainers: - name: restore image: postgres:12 command: [/bin/bash, -c, mv /backup/dump.sql /docker-entrypoint-initdb.d/] volumeMounts: - name: backup mountPath: /backup钩子条目hooks.resources[]通过name、includedNamespaces、excludedNamespaces、includedResources、excludedResources和labelSelector组成资源选择器只有满足选择条件的 Pod 才会应用其下的post/init钩子。当前 API 类型定义见 pkg/apis/velero/v1/restore_types.goRestoreResourceHookSpec钩子本体由RestoreResourceHook承载其exec字段对应ExecRestoreHook、init字段对应InitRestoreHook。3.3 ExecRestoreHook 字段语义结合 pkg/apis/velero/v1/restore_types.go 的字段注释container命令执行的目标容器不指定时使用 Pod 的第一个容器。command要执行的命令与参数kubebuilder 校验要求至少 1 项。onErrorHookErrorMode枚举Fail/Continue。execTimeout命令开始执行后 Velero 等待其完成的最大时间超时视为失败。waitTimeout执行前等待容器变为 Ready 的最大时间。waitForReady为 true 时等待容器进入 Ready而非仅 Running再执行。3.4 执行时机与并发模型设计文档明确了执行时机Velero 会等待钩子要执行的容器进入 Running 状态再执行钩子。当前实现由 pkg/restore/restore.go 中的hooksWaitExecutor完成每个有适用钩子的 Pod 会在exec方法中启动一个 goroutine异步执行Pod 恢复后容器变为可用时执行钩子然后继续恢复其他资源goroutine 内部调用 internal/hook/wait_exec_hook_handler.go 的DefaultWaitExecHookHandler.HandleHooks通过 Informer 监听该 Pod容器进入 Running 后串行执行该容器下的所有钩子钩子执行完即从待执行 map 中删除map 清空后结束 watch钩子执行期间的失败会记录到MultiHookTracker最终反映到 Restore 状态的HookStatusHooksAttempted/HooksFailed字段见 pkg/apis/velero/v1/restore_types.go 与 config/crd/v1/bases/velero.io_restores.yaml用户可通过velero restore describe restore-name查看汇总结果输出HooksAttempted与HooksFailed两行见 pkg/cmd/util/output/restore_describer.go。3.5 失败处理模型onError: Continue钩子失败只记录到 Restore 日志不影响父 Restore 的状态。onError: Fail钩子失败会使父 Restore 状态变为PartiallyFailed并且会取消该 Restore 其他钩子的执行上下文不再执行剩余钩子。这一模型在HandleHooks中落地返回的 errors 列表会通过hooksCancelFunc取消共享的 hooks context见 pkg/restore/restore.go与设计文档中任何 hooksErrs 错误都会取消所有钩子的 context的描述一致。四、InitContainer 恢复钩子详解在应用容器启动前注入初始化容器InitContainer 恢复钩子Init Restore Hook通过向被恢复的 Pod 注入 initContainers在应用容器启动前完成必要的准备例如把备份的 dump 文件移动到应用期望的路径。4.1 支持的注解Init 钩子设计文档规定的注解键注解键含义init.hook.restore.velero.io/timeout等待 initContainers 完成的最大时间init.hook.restore.velero.io/initContainers注入的 initContainers 定义实现演进说明当前源码 internal/hook/item_hook_handler.go 将 init 钩子注解细化为init.hook.restore.velero.io/container-image、init.hook.restore.velero.io/container-name、init.hook.restore.velero.io/command、init.hook.restore.velero.io/timeout四个键。getInitContainerFromAnnotation同文件 L419-L449解析这些注解构造corev1api.Container仅提供镜像时使用镜像默认 ENTRYPOINT未提供容器名时自动生成velero-restore-init-uuid形式的名称。注意命令默认不在 shell 中执行需要 shell 时应在命令开头显式包含/bin/sh或/bin/ash等目标镜像支持的 shell。4.2 InitContainer 注入规则注入位置initContainers 被注入到 PodinitContainers列表的最前面。与 File System Backuprestic/数据移动共存如果 Pod 同时注入了restore-waitinitContainer卷数据恢复等待容器则恢复钩子的 initContainers 会被注入在restore-wait之后以保证卷数据先恢复、再由钩子 initContainer 处理数据。当前实现见 internal/hook/item_hook_handler.go若 Pod 第 0 个 initContainer 名为restorehelper.WaitInitContainer则保留它并放在注入列表之前。实现载体initContainers 的注入通过一个RestoreItemAction完成——pkg/restore/actions/init_restorehook_pod_action.go 中的InitRestoreHookPodAction其AppliesTo声明只作用于podsExecute中调用hook.GetRestoreHooksFromSpec解析 spec 钩子并交给InitContainerRestoreHookHandler.HandleRestoreHooks注入。命名空间映射选择器匹配时使用 Pod 恢复后的目标命名空间namespaceMapping相关 issue 与实现见 internal/hook/item_hook_handler.go。校验spec 中定义的 initContainer 必须包含name、image、command三个必填字段否则报错ValidateContainer同文件 L620-L631。日志与状态注入的 initContainers 的 stdout/stderr不会写入 Restore 日志可通过kubectl logs查看initContainers 失败不影响父 Restore 的状态。五、实现架构从设计到源码的落地5.1 公共钩子包的迁移设计文档明确提出将pkg/backup/item_hook_handler.go中的类型和函数迁移到新的pkg/hooks当前为 internal/hook/包并导出使备份与恢复共用。这一点已在当前仓库落地internal/hook/item_hook_handler.go 中ItemHookHandler接口含HandleHooks同时服务于备份与恢复流程备份侧调用见 pkg/backup/backup.goHookStatus统计与 pkg/backup/item_backupper.go恢复侧则额外提供了ItemRestoreHookHandler、InitContainerRestoreHookHandler、WaitExecHookHandler等恢复专用接口同文件 L89-L100。5.2 与 restic 卷恢复设计的异同设计文档指出 post-restore 钩子的实现紧密跟随 restic 卷恢复设计但有一个关键差异共同点均为异步 goroutine 模式恢复流程继续处理其他资源最后统一等待差异点restic 设计中的错误不会互相取消而钩子通道上的任何错误都会取消所有钩子的 context因为只有onError: Fail的钩子失败才会向该通道上报错误但取消钩子 goroutine不会取消 restic goroutine。实际运行中 restic goroutine 通常会先完成钩子要等 Pod ready 后才执行但存在某个 Pod 的钩子失败时另一 Pod 仍在卷恢复阶段的可能。5.3 端到端调用链从当前源码可以还原完整的调用链RestoreItemAction 注入阶段InitRestoreHookPodAction.Execute解析Restore.Spec.Hooks将 initContainers 写入 Pod specpkg/restore/actions/init_restorehook_pod_action.go钩子收集阶段hooksWaitExecutor.groupHooks调用hook.GroupRestoreExecHooks按容器名对 Pod 的 exec 钩子分组如果 Pod 存在post.hook.restore.velero.io/command注解则只使用注解钩子pkg/restore/restore.go、internal/hook/item_hook_handler.go等待与执行阶段hooksWaitExecutor.exec为每个 Pod 启动 goroutineDefaultWaitExecHookHandler.HandleHooks监听 Pod 状态容器 Running或waitForReady时 Ready后逐个执行钩子internal/hook/wait_exec_hook_handler.go结果汇总阶段钩子执行结果通过MultiHookTracker记录Restore 完成时写入status.hookStatus.hooksAttempted与status.hookStatus.hooksFailed供velero restore describe展示。5.4 钩子在 Restore 生命周期中的位置设计文档描述Restore log 将包含每个 post-restore 钩子的结果Restore 对象状态将纳入钩子结果。从当前实现看钩子 goroutine 会在恢复进入 finalizing 阶段时被等待见 pkg/restore/restore.go 注释Velero will wait for goroutine to finish in finalizing phase, using hook tracker to track the progressonError: Fail的失败最终把 Restore 置为PartiallyFailedRestorePhasePartiallyFailed见 pkg/apis/velero/v1/restore_types.go。六、Restore Hooks 的实战使用建议6.1 两种定义方式的选择场景推荐方式理由钩子与特定应用强绑定如 Postgres 恢复后执行pg_restorePod 注解在备份前kubectl annotate钩子随 Pod 走天然只作用于该 Pod注解优先级高于 spec钩子面向一批满足条件的资源如某命名空间下所有带app: velero标签的 PodRestore spec 的hooks.resources[]通过includedNamespaces/labelSelector批量选择集中管理需要在应用容器启动前准备数据如移动 dump 文件、初始化卷内容InitContainer 钩子initContainer 在应用容器前串行执行天然满足先准备后启动6.2 常见实践要点命令默认不进 shellexec 钩子与 initContainer 的 command 默认不经过 shell需要环境变量展开、管道等能力时必须在命令开头加上目标镜像支持的 shell如/bin/sh -c、/bin/bash -c例如command: [/bin/bash, -c, psql /backup/backup.sql]。wait-timeout 要留足余量等待超时从容器恢复完成开始计算包含镜像拉取与卷挂载时间还应包含同一容器中排在前面钩子的执行时间不设置则无限等待。多钩子顺序保证同一容器内的多个钩子按 spec 中定义的顺序串行执行同一 Pod 内不同容器的钩子互不并行但不同 Pod 的钩子可以并行执行。失败策略业务关键步骤如数据导入建议onError: Fail以便 Restore 呈现PartiallyFailed可被监控感知辅助性操作如写标记文件建议onError: Continue只留日志。结果观测velero restore describe restore-name输出的HooksAttempted/HooksFailed提供钩子执行汇总详细失败原因位于 Restore 的Errors区exec 钩子的 stdout/stderr 会进入 Restore 日志而 initContainer 日志需通过kubectl logs查看。6.3 安全注意事项设计文档在 Security Considerations 中明确指出Restore 日志中的 stdout/stderr 可能包含敏感信息但该风险与备份钩子早已存在并非恢复钩子引入的新问题。实践中应避免在钩子命令中打印密钥、密码等敏感数据必要时对 Restore 日志的访问做权限控制。七、进一步阅读设计文档原文design/Implemented/restore-hooks.md用户手册含更多 YAML/JSON 示例site/content/docs/main/restore-hooks.mdAPI 类型定义RestoreHooks、ExecRestoreHook、InitRestoreHook、HookStatuspkg/apis/velero/v1/restore_types.go钩子核心实现注解解析、initContainer 注入、钩子分组internal/hook/item_hook_handler.go等待-执行处理器容器 Ready 后串行执行钩子internal/hook/wait_exec_hook_handler.goRestoreItemAction 注入实现pkg/restore/actions/init_restorehook_pod_action.go恢复流程中的钩子执行器pkg/restore/restore.goCRD 中的钩子状态字段config/crd/v1/bases/velero.io_restores.yaml【免费下载链接】veleroBackup and migrate Kubernetes applications and their persistent volumes项目地址: https://gitcode.com/GitHub_Trending/ve/velero创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考