跳到主要内容

容灾 Velero Hook

Velero Hook 用于在备份或恢复过程中让业务容器执行自定义命令。常见场景包括数据库冻结和解冻、缓存刷新、恢复后初始化、写入恢复标记、通知业务进程重新加载配置等。

Testudo 不实现自己的 Hook 执行器。平台接收、校验、保存并投影 Velero 原生 Hook 结构,真正执行仍由 Velero 完成。

注意

Hook 会在业务 Pod 或恢复出的 Pod 中执行命令。生产环境启用前,先在测试命名空间验证命令的幂等性、超时、失败策略和数据一致性。

支持范围

场景输入字段投影目标是否执行 Hook
手工应用备份hooksAppBackup.spec.template.hooks -> Velero Backup.spec.hooksSchedule.spec.template.hooks
手工应用恢复hooksAppRestore.spec.template.hooks -> Velero Restore.spec.hooks
容灾实例数据备份spec.veleroHooks.dataBackupDataSync 创建的 AppBackup.spec.template.hooks
容灾实例数据恢复spec.veleroHooks.dataRestoreDataSync 创建的 AppRestore.spec.template.hooks是,exec post hook 会做 Trafficless 兼容处理
容灾资源同步spec.veleroHooks 存在ResourceSync 创建的 AppBackup/AppRestore不投影,不承诺执行
容灾演练数据恢复DisasterDrill.spec.veleroHooks.dataRestoreDrill Data Restore 创建的 AppRestore
容灾演练数据备份DisasterDrill.spec.veleroHooks.dataBackup不支持Server 拒绝

前置条件

使用前确认:

  • Velero 已安装并且 BackupStorageLocation 可用。
  • 业务命名空间、标签选择器和资源范围能命中需要执行 Hook 的 Pod。
  • Hook 命令所在的容器镜像中确实存在对应脚本或 shell。
  • 如果命令写入 PVC,例如 /data/hook.log,对应容器已挂载该路径。
  • 需要敏感值时,通过业务容器已有 Secret env、envFrom.secretRef 或挂载文件传入,不要把明文密码或 token 写进 Hook command。

控制台入口

应用备份 Hook

进入 备份恢复 / 应用备份,创建或编辑应用备份。在高级选项中启用备份钩子,填写 Hook 资源、标签选择器、前置钩子和后置钩子。

应用备份 Velero Hook 表单

备份 Hook 支持:

  • pre:备份该 Pod 前执行。
  • post:备份该 Pod 后执行。
  • container:执行命令的业务容器名。
  • command:Velero 原生 argv 数组。
  • timeout:单次 exec 超时。
  • onErrorFailContinue

应用恢复 Hook

进入 备份恢复 / 应用恢复,创建恢复时启用恢复钩子。恢复 Hook 主要用于恢复 Pod 创建后执行初始化命令,或通过 init hook 注入初始化容器。

应用恢复 Velero Hook 表单

恢复 Hook 支持:

  • postHooks[].exec:恢复 Pod 就绪前后执行命令。
  • postHooks[].init:向恢复 Pod 注入 initContainer。
  • waitForReady:是否等待容器 ready 后执行。
  • waitTimeout:等待 ready 超时。
  • execTimeout:命令执行超时。

容灾实例 Hook

进入 容灾管理 / 实例配置,创建或编辑容灾实例,在高级选项中启用备份钩子和恢复钩子。

容灾实例 Velero Hook 表单

容灾实例保存后,字段写入:

spec:
veleroHooks:
dataBackup:
resources: []
dataRestore:
resources: []

DataSync 会读取该字段:

  • dataBackup 投影到数据同步备份使用的 AppBackup。
  • dataRestore 投影到数据同步恢复使用的 AppRestore。
  • 更新实例 Hook 后,后续 DataSync 会在触发新备份前对齐既有 ds-* AppBackup 的 desired template。

命令输入格式

Hook command 必须是 Velero 原生 argv 数组。控制台中每个蓝色标签就是数组中的一个元素。

推荐写法:

["/bin/sh", "-c", "echo pre-backup >> /data/hook.log"]

不要把整条命令提交成一个字符串:

/bin/sh -c echo pre-backup >> /data/hook.log

这不是 Velero 期望的结构。当前控制台使用分段标签输入,输入一段参数后按空格或回车确认,最终按数组提交。

如果命令本身需要 shell 展开变量,显式把 shell 放进 argv:

["/bin/sh", "-c", "dr-hook pre-backup --mode=${DR_MODE:-quiesce}"]

这里的 ${DR_MODE} 由业务容器内的 shell 解析,平台不会渲染或替换。

选择器和多个 init hook

Hook 的 labelSelector 使用 Kubernetes 原生 LabelSelector 语义。matchLabels 中多个键是 AND 关系,不能写出同一个键的 OR 条件:

labelSelector:
matchLabels:
app: abc
tier: backend

如果要表达同一个 key 的多个 value,例如 app=abc OR app=qwe,使用 matchExpressionsIn

labelSelector:
matchExpressions:
- key: app
operator: In
values:
- abc
- qwe

如果要表达不同 key 或不同条件组之间的 OR,例如 app=abc OR component=qwe,需要拆成多个 Hook resource。平台按 Velero 原生结构透传,不额外扩展 selector 表达式。

恢复 init hook 可以配置多个。Velero 会把匹配到的 init hook 注入恢复出的 Pod;同一个 Pod 最终得到的 initContainers[].name 必须唯一。平台不会自动重命名,也不会合并同名 initContainer。多个 init hook 需要使用不同名称,例如:

postHooks:
- init:
initContainers:
- name: restore-init-db
image: registry.example.com/dr-tools:1.0
- init:
initContainers:
- name: restore-init-cache
image: registry.example.com/dr-tools:1.0

手工 AppBackup 示例

通过 API 或 CRD 配置手工备份 Hook 时,hooks 写入 AppBackup.spec.template.hooks

apiVersion: testudo.softcdata.com/v1
kind: AppBackup
metadata:
name: bookinfo-backup-hook
spec:
cluster: prod-a
schedule: ""
template:
storageLocation: minio-dr
includedNamespaces:
- demo-bookinfo
labelSelector:
matchLabels:
app: bookinfo
hooks:
resources:
- name: app-quiesce
includedNamespaces:
- demo-bookinfo
includedResources:
- pods
labelSelector:
matchLabels:
app: bookinfo
pre:
- exec:
container: app
command:
- /bin/sh
- -c
- echo pre-backup >> /data/hook.log
onError: Fail
timeout: 5m
post:
- exec:
container: app
command:
- /bin/sh
- -c
- echo post-backup >> /data/hook.log
onError: Continue
timeout: 5m

一次性备份会创建带 spec.hooks 的 Velero Backup;调度备份会创建带 spec.template.hooks 的 Velero Schedule

手工 AppRestore 示例

恢复 Hook 写入 AppRestore.spec.template.hooks,并透传到 Velero Restore.spec.hooks

apiVersion: testudo.softcdata.com/v1
kind: AppRestore
metadata:
name: bookinfo-restore-hook
spec:
cluster: prod-b
backupName: bookinfo-backup-hook
restorePVs: true
targetCluster: prod-b
template:
backupName: bak-bookinfo-backup-hook-xxxx
includedNamespaces:
- demo-bookinfo
namespaceMapping:
demo-bookinfo: demo-bookinfo-restore
hooks:
resources:
- name: app-after-restore
includedNamespaces:
- demo-bookinfo-restore
includedResources:
- pods
labelSelector:
matchLabels:
app: bookinfo
postHooks:
- exec:
container: app
command:
- /bin/sh
- -c
- echo post-restore >> /data/hook.log
onError: Continue
waitForReady: false
waitTimeout: 10m
execTimeout: 5m

如果使用 init hook,可以通过 Kubernetes initContainer 原生字段传参:

postHooks:
- init:
initContainers:
- name: restore-init
image: registry.example.com/dr-tools:1.0
command:
- /bin/sh
- -c
args:
- dr-restore-init --namespace=$POD_NAMESPACE
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
envFrom:
- secretRef:
name: restore-hook-secret
timeout: 10m

容灾实例示例

容灾实例 Hook 适用于自动 DataSync 链路:

apiVersion: testudo.softcdata.com/v1
kind: DisasterInstance
metadata:
name: bookinfo-dr
spec:
config: dc-prod
namespaces:
- demo-bookinfo
veleroHooks:
dataBackup:
resources:
- name: freeze-before-data-sync
includedResources:
- pods
labelSelector:
matchLabels:
app: bookinfo
pre:
- exec:
container: app
command:
- /bin/sh
- -c
- echo inst-pre-backup >> /data/hook.log
onError: Fail
timeout: 5m
post:
- exec:
container: app
command:
- /bin/sh
- -c
- echo inst-post-backup >> /data/hook.log
onError: Continue
timeout: 5m
dataRestore:
resources:
- name: init-after-data-restore
includedResources:
- pods
labelSelector:
matchLabels:
app: bookinfo
postHooks:
- exec:
container: app
command:
- /bin/sh
- -c
- echo inst-post-restore >> /tmp/data-restore-hook.log
onError: Continue
waitForReady: false
waitTimeout: 10m
execTimeout: 5m

DataSync dataRestore 的 Trafficless 兼容规则

DataSync 的数据恢复会创建临时 Trafficless Pod,以避免恢复出的 Pod 被 Service 导流或被 workload controller 接管。Trafficless 恢复会移除业务 labels,只保留平台隔离标签。

因此,用户输入的 dataRestore exec post hook 如果使用业务标签,例如:

labelSelector:
matchLabels:
app: bookinfo

平台会在创建数据恢复 AppRestore 时做兼容处理:

  1. 使用原始 selector 匹配备份对象中的 Pod。
  2. 在 Trafficless labels 覆盖之后,为恢复 Pod 增加系统 marker label,例如 testudo.softcdata.com/data-restore-hook-0=true
  3. 将对应 exec post hook 的 selector 改写为 marker selector。
  4. 保留用户输入的 commandcontaineronErrorwaitForReadywaitTimeoutexecTimeout

init restore hook 不做 marker 改写,因为 init hook 在 Pod 创建前按备份对象原始 selector 匹配。

信息

如果查看 DataSync 创建的 AppRestore,dataRestore exec hook 的 selector 可能不是你在实例里输入的业务标签。这是预期行为,用于兼容 Trafficless 恢复。判断是否成功应看 Velero status.hookStatus 和 Pod 内业务 marker,而不是只比较 Hook selector 是否原样相等。

容灾演练 Hook

演练只支持 dataRestore Hook:

  • 演练未配置 veleroHooks.dataRestore:继承实例级 dataRestore Hook。
  • 演练配置 veleroHooks.dataRestore:覆盖实例级 Hook,不合并。
  • 演练显式提交 veleroHooks: {}:清空继承,本次演练恢复不使用实例 Hook。
  • 演练提交 veleroHooks.dataBackup:Server 拒绝,因为演练不创建新的数据备份。

API 片段:

{
"name": "bookinfo-drill-hook",
"instanceName": "bookinfo-dr",
"veleroHooks": {
"dataRestore": {
"resources": [
{
"name": "drill-after-restore",
"includedResources": ["pods"],
"labelSelector": {
"matchLabels": {
"app": "bookinfo"
}
},
"postHooks": [
{
"exec": {
"container": "app",
"command": ["/bin/sh", "-c", "echo drill-post-restore >> /data/hook.log"],
"onError": "Continue",
"waitForReady": false,
"waitTimeout": "10m",
"execTimeout": "5m"
}
}
]
}
]
}
}
}

校验和安全限制

平台在 Server 入口做基础校验,避免明显错误配置进入 CRD:

规则行为
Hook 目标资源显式不包含 pods拒绝
command 为空拒绝
onError 不是 FailContinue拒绝
command 包含平台占位符,例如 ${testudo.namespace}{{ namespace }}拒绝
command、args、env value 中包含明显明文敏感参数,例如 password=plaintoken=plain拒绝,错误码 VeleroHookSensitiveParameter
Backup exec timeout > 10m拒绝
Restore exec execTimeout > 10m拒绝
Restore exec waitTimeout > 30m拒绝
Restore init timeout > 30m拒绝

敏感值建议通过 Secret 注入:

envFrom:
- secretRef:
name: app-hook-secret

或:

env:
- name: APP_TOKEN
valueFrom:
secretKeyRef:
name: app-hook-secret
key: token

状态查看

Velero 会在 Backup 或 Restore status 中写入 Hook 执行汇总:

status:
hookStatus:
hooksAttempted: 2
hooksFailed: 0

平台会回显:

  • AppBackup.status.history[].veleroStatus.hookStatus
  • AppRestore.status.restoreStatus.hookStatus
  • DataSync 同步历史中的 backupHookStatusrestoreHookStatus

常用检查命令:

kubectl get appbackup <appbackup> -n disaster-system -o yaml
kubectl get apprestore <apprestore> -n disaster-system -o yaml
kubectl --context <source-cluster> -n velero get backup <velero-backup> -o yaml
kubectl --context <target-cluster> -n velero get restore <velero-restore> -o yaml
kubectl --context <cluster> -n <app-namespace> exec deploy/<workload> -- cat /data/hook.log

如果是容灾实例链路,还可以查看 DataSync 生成的 AppBackup/AppRestore:

kubectl get datasync -n disaster-system
kubectl get appbackup -n disaster-system | grep '^ds-'
kubectl get apprestore -n disaster-system | grep '^rec-ds-'

排障

现象可能原因处理方式
hookStatus 为空selector 没有命中 Pod,或 Hook 不适用于该恢复阶段检查 namespace、includedResources、labelSelector 和备份/恢复范围
hooksAttempted > 0hooksFailed > 0命令退出非 0,或容器/路径不存在查看 Velero describe/logs 和业务容器日志
Server 返回敏感参数错误command 或 args 中出现明文 password/token/secret改用 Secret env、Secret envFrom 或挂载文件
Server 返回 timeout 错误超过平台上限调整 timeout,或把长任务拆到业务侧异步执行
DataSync dataRestore AppRestore 中 selector 变成平台 markerTrafficless 兼容改写这是预期行为,验证 hookStatus 和业务 marker
ResourceSync 没有 HookResourceSync 不投影 Hook如果需要恢复后执行命令,使用 DataSync dataRestore 或 Drill dataRestore
恢复成功但恢复出的临时 Pod 消失DataSync 清理 Trafficless 临时 Pod查看 Hook 写入的持久化路径或 Velero hookStatus,不要只依赖临时 Pod 是否仍存在