Velero 备份平台:安装、调度、筛选与恢复
值班人员在新集群里执行恢复,velero backup get 显示目标备份为 Completed,对象存储里也确实有归档。可业务仍然起不来:一个 namespace 根本没被选中,PVC 只留下了对象定义,没有可用卷数据;恢复日志还显示同名 Service 被跳过。更糟的是,团队一直把默认 Backup Storage Location 当成“全量异地备份”,却没人核对这次卷数据走的是快照、文件系统备份,还是根本没有离开原存储。
这个现场不能靠再点一次 Restore 解决。先要回答五个问题:Velero server 实际运行哪个版本;Backup 选择了哪些 API 对象;元数据写入哪个 BackupStorageLocation(BSL);卷快照由哪个 VolumeSnapshotLocation(VSL)或 CSI 路径负责;异步操作、Hook、warning 与 skipped item 是否都进入了可解释终态。只有这些证据闭合,Completed 才有业务含义。
先把五类对象放回各自位置
Velero CLI 主要是创建和查看 Kubernetes 自定义资源,真正执行的是集群内的 Velero server 与插件 controller。一次命令结束不等于数据已经传完;server 会持续协调对象归档、快照、数据移动和状态更新。
BackupStorageLocation 描述对象存储 bucket 或 prefix、provider、访问模式与凭证引用。Kubernetes 对象归档、日志、结果以及 FSB/CSI data movement 的仓库数据都依赖 BSL。VolumeSnapshotLocation 描述云 provider 原生快照所在 region 等配置。它不保存 Kubernetes 对象,也不自动把快照复制到 BSL。
Backup 是一次保护请求,保存 include/exclude、label selector、TTL、Hook、快照与数据移动选择。它的 status 是 server 对这次请求的汇总,不是业务恢复证明。Schedule 是生成 Backup 的模板与时钟。应检查每个派生 Backup 的实际 spec 和状态,不能只看 Schedule 存在。Restore 从某个 Backup 读取对象和卷恢复信息,按资源顺序、冲突策略、namespace 映射和 Hook 执行。删除 Restore 不会撤销已经创建的业务对象。
Velero 同时处理 Kubernetes API 对象和持久卷数据,但二者是两条路径。对象归档进入 BSL;卷可能进入 provider snapshot、CSI snapshot、CSI snapshot data movement 或 File System Backup。只做本地快照时,数据仍可能留在原账号、region、KMS 和存储故障域。对象关系与位置语义可对照稳定版的 BSL/VSL 文档。
在第一次安装前固定四套版本
这里以 Velero v1.18.2 和 v1.18 版本化文档为示例基线。执行前先打开 Velero Releases 与兼容矩阵,确认目标 Kubernetes minor、Velero CLI/server、对象存储或云 provider plugin、CSI driver/snapshot controller 是经过团队验证的组合。官网 main 是开发线,不能据此承诺稳定环境中的字段和行为。
安装账号通常需要创建 CRD、Deployment、可选的 DaemonSet、ClusterRole/ClusterRoleBinding、ConfigMap 和 Secret。默认安装授予 Velero cluster-admin,这意味着它可以读取包括 Secret 在内的大量集群资源。对象存储侧另需专用身份,只允许访问 Velero 专用 bucket 或 prefix;不要复用人的云管理员凭证。实验还需要:
至少一个 Linux node 运行 Velero server;Windows workload 的具体镜像与限制另查目标 release。kubectl、Helm、可销毁 namespace,以及不会包含真实客户数据的对象存储位置。若使用快照,准备 provider plugin 或支持 snapshot.storage.k8s.io/v1 的 CSI driver、snapshot controller、CRD 与可用快照类。
若使用 FSB 或内建 CSI data mover,安装 node-agent,并预留节点权限、缓存、CPU、内存和临时空间。
先记录客户端和集群事实:
velero version --client-only
kubectl version
helm version
kubectl get nodes -o custom-columns='NAME:.metadata.name,OS:.status.nodeInfo.operatingSystem,KUBELET:.status.nodeInfo.kubeletVersion'
kubectl api-resources | grep -E 'backupstoragelocation|volumesnapshot'预期 velero version --client-only 输出 v1.18.2;若目标 release 已变化,应整体更新 CLI、server 镜像、插件和文档基线,而不是只替换本机二进制。
用 CLI 安装并保留可审计参数
CLI 可以从 v1.18.2 Release下载对应平台压缩包;macOS 也可用 Homebrew,Windows 可用 Chocolatey。包管理器安装后仍要检查实际版本,避免本机自动升级领先于 server。
下面是对象存储加 provider snapshot 的安装骨架。所有尖括号都必须替换为隔离环境值;插件使用与目标 release 兼容的不可变 digest,凭证文件用短期身份生成并在安装后安全销毁。
export VELERO_VERSION=v1.18.2
export PLUGIN_IMAGE='<provider-plugin>@sha256:<digest>'
export BACKUP_BUCKET='<dedicated-bucket>'
export BACKUP_PREFIX='<velero-prefix>'
export STORAGE_REGION='<storage-region>'
export SNAPSHOT_REGION='<snapshot-region>'
velero install \
--namespace velero \
--image="velero/velero:${VELERO_VERSION}" \
--plugins="${PLUGIN_IMAGE}" \
--provider='<provider-name>' \
--bucket="${BACKUP_BUCKET}" \
--prefix="${BACKUP_PREFIX}" \
--backup-location-config="region=${STORAGE_REGION}" \
--snapshot-location-config="region=${SNAPSHOT_REGION}" \
--secret-file='./credentials-velero-lab'
kubectl -n velero rollout status deployment/velero --timeout=300s
velero version
velero backup-location get
velero snapshot-location get预期客户端和 server 都报告目标版本,Deployment 完成 rollout,默认 BSL 为 Available。VSL 只在安装了相应 provider snapshot 能力时有意义;若只使用 CSI snapshot,可不创建传统 provider VSL。安装参数以 Basic Install和目标 provider 的官方插件文档共同为准。
不要把真实凭证内容贴进终端记录或 Git。安装后检查 Secret 名称、ServiceAccount 和云身份绑定,不读取明文:
kubectl -n velero get serviceaccount,role,rolebinding,clusterrole,clusterrolebinding | grep velero
kubectl -n velero get secret -o custom-columns='NAME:.metadata.name,TYPE:.type'
kubectl -n velero get backupstoragelocation default -o yaml
kubectl -n velero logs deployment/velero --tail=100BSL 的 status.phase 不可用时,优先按日志区分 DNS/TLS、对象存储拒绝、错误 region/endpoint、KMS 拒绝和 bucket/prefix 不存在。不要通过追加全局管理员权限来掩盖具体缺失动作。
用 Helm 安装时显式覆盖应用镜像
Velero 应用版本与 Helm Chart 版本是两套序列。Chart 的 appVersion 可能落后于最新 patch,因此先查询索引和 values,再显式固定 server 镜像与 plugin digest:
helm repo add vmware-tanzu https://vmware-tanzu.github.io/helm-charts
helm repo update
helm search repo vmware-tanzu/velero --versions | head
export VELERO_CHART_VERSION='<reviewed-chart-version>'
helm show chart vmware-tanzu/velero --version "${VELERO_CHART_VERSION}"
helm show values vmware-tanzu/velero --version "${VELERO_CHART_VERSION}" > velero-values.reference.yaml从该 Chart 的 schema 生成受版本控制的 values。下面只展示关键结构;字段必须与查询到的 Chart README/values 对齐,不能把另一版 values 原样复用:
image:
repository: velero/velero
tag: v1.18.2
initContainers:
- name: velero-plugin-for-provider
image: <provider-plugin>@sha256:<digest>
volumeMounts:
- name: plugins
mountPath: /target
configuration:
backupStorageLocation:
- name: default
provider: <provider-name>
bucket: <dedicated-bucket>
prefix: <velero-prefix>
config:
region: <storage-region>
volumeSnapshotLocation:
- name: default
provider: <provider-name>
config:
region: <snapshot-region>
credentials:
useSecret: true
existingSecret: velero-cloud-credentials
deployNodeAgent: false先创建 namespace 与凭证 Secret,再安装。--atomic 只能回滚 Helm release,不会删除已经写入外部 bucket 的数据或云快照:
kubectl create namespace velero
kubectl -n velero create secret generic velero-cloud-credentials \
--from-file=cloud='./credentials-velero-lab'
helm upgrade --install velero vmware-tanzu/velero \
--namespace velero \
--version "${VELERO_CHART_VERSION}" \
--values velero-values.yaml \
--atomic \
--timeout 10m
helm -n velero get values velero -a
kubectl -n velero rollout status deployment/velero --timeout=300s
velero versionCLI 与 Helm 是两种安装入口,不是两个并行 owner。选定 Helm 后,后续配置变更应回到 values 和 release;不要再用 velero install 覆盖同一组资源。
让 BSL 与 VSL 的故障域可见
一个 BSL 至少要明确 owner、bucket/prefix、region、KMS key、访问身份、版本/保留策略和删除权限。Velero 假设自己控制该位置,因此不要与其他应用共享未隔离 prefix。BSL 可以设置 ReadWrite 或 ReadOnly;灾难恢复时常把源集群位置以只读方式挂入目标集群,防止恢复端误删正式恢复点。
velero backup-location get
kubectl -n velero get backupstoragelocations.velero.io -o custom-columns='NAME:.metadata.name,PHASE:.status.phase,MODE:.spec.accessMode,DEFAULT:.spec.default,BUCKET:.spec.objectStorage.bucket,PREFIX:.spec.objectStorage.prefix'
kubectl -n velero describe backupstoragelocation defaultVSL 的 region 必须与可快照卷和 provider 规则兼容。它只描述快照调用入口,不提供跨 provider 可移植性。若 RPO 要求源存储损坏后仍可恢复,应选择 CSI data movement 或 FSB 把卷数据写入独立 BSL,并另外验证对象存储复制与 KMS 可用性。
多 BSL 场景要显式写 --storage-location,多 VSL 场景要显式写对应 location;默认值变化会让同名 Schedule 的后续 Backup 落到不同位置。日常证据中同时保存 Backup spec 与 location 对象 resourceVersion。
正向实验:备份一个可判定 namespace
先创建只含无敏感测试数据的 namespace、ConfigMap 和 Deployment。recovery-marker 是恢复后的业务断言,不使用真实域名、Token 或客户内容:
apiVersion: v1
kind: Namespace
metadata:
name: velero-lab
---
apiVersion: v1
kind: ConfigMap
metadata:
name: recovery-marker
namespace: velero-lab
labels:
protection.example.io/tier: gold
data:
checkpoint: "ledger-000042"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: marker-reader
namespace: velero-lab
labels:
protection.example.io/tier: gold
spec:
replicas: 1
selector:
matchLabels:
app: marker-reader
template:
metadata:
labels:
app: marker-reader
protection.example.io/tier: gold
spec:
containers:
- name: reader
image: busybox:1.36
command: ["sh", "-c", "cat /evidence/checkpoint && sleep 3600"]
volumeMounts:
- name: evidence
mountPath: /evidence
volumes:
- name: evidence
configMap:
name: recovery-markerkubectl apply -f velero-lab.yaml
kubectl -n velero-lab rollout status deployment/marker-reader --timeout=180s
kubectl -n velero-lab logs deployment/marker-reader
velero backup create velero-lab-positive \
--include-namespaces velero-lab \
--selector 'protection.example.io/tier=gold' \
--storage-location default \
--snapshot-volumes=false \
--ttl 168h \
--wait这里关闭卷快照是为了先证明 API 对象筛选链,不把无 PVC 的实验伪装成卷保护。预期日志输出 ledger-000042,Backup 终态为 Completed,且资源清单包含 ConfigMap、Deployment、Pod 及其必要依赖。检查汇总、详情、日志和对象字段:
velero backup get velero-lab-positive
velero backup describe velero-lab-positive --details
velero backup logs velero-lab-positive
kubectl -n velero get backup velero-lab-positive -o yaml如果 Items backed up 为零或目标对象缺失,先核对 selector 是作用于资源自身标签,不是只看 namespace 标签。include/exclude、glob、cluster-scoped 资源和 selector 的组合规则应按 Resource Filtering验证;不要用不断扩大到 * 的方式掩盖筛选错误。
从隔离 namespace 证明 Restore
删除源 namespace 会让实验具有恢复意义,但执行前再次确认名字是 velero-lab,并保留 Backup 详情。恢复到新 namespace,避免与同名对象冲突:
kubectl delete namespace velero-lab --wait=true
velero restore create velero-lab-restore \
--from-backup velero-lab-positive \
--namespace-mappings velero-lab:velero-restore-lab \
--wait
velero restore describe velero-lab-restore --details
velero restore logs velero-lab-restore
kubectl -n velero-restore-lab get configmap recovery-marker \
-o jsonpath='{.data.checkpoint}{"\n"}'
kubectl -n velero-restore-lab rollout status deployment/marker-reader --timeout=180s
kubectl -n velero-restore-lab logs deployment/marker-reader通过条件不是 Restore 为 Completed,而是 marker 精确为 ledger-000042,Deployment Ready,日志没有未知 failed/skipped item。默认 Restore 对同名既有对象通常采取非破坏路径;admission webhook 仍会重新运行,可能拒绝或修改恢复对象。跨集群还需预建 CRD/Operator、StorageClass、CSI、镜像访问、KMS、云身份与外部依赖。
反向实验:让 Hook 稳定失败
Backup Hook 通过 pod exec 在目标容器中运行。命令数组默认不经过 shell;需要管道、变量展开或多条命令时必须显式调用容器已有的 /bin/sh。创建一个缺少放行文件的 Pod,并把 on-error 设为 Fail:
apiVersion: v1
kind: Namespace
metadata:
name: velero-hook-lab
---
apiVersion: v1
kind: Pod
metadata:
name: hook-target
namespace: velero-hook-lab
annotations:
pre.hook.backup.velero.io/container: app
pre.hook.backup.velero.io/command: '["/bin/sh","-c","test -f /tmp/allow-backup"]'
pre.hook.backup.velero.io/on-error: Fail
pre.hook.backup.velero.io/timeout: 30s
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "sleep 3600"]kubectl apply -f velero-hook-negative.yaml
kubectl -n velero-hook-lab wait --for=condition=Ready pod/hook-target --timeout=120s
velero backup create velero-hook-negative \
--include-namespaces velero-hook-lab \
--snapshot-volumes=false \
--wait
velero backup describe velero-hook-negative --details
velero backup logs velero-hook-negative预期证据是日志明确指出 pre hook 命令非零退出,Backup 不应被当作可用恢复点;具体汇总 phase 以目标 release 的实际输出为准。创建放行文件后重跑新 Backup,日志中的 hook error 应消失:
kubectl -n velero-hook-lab exec hook-target -c app -- touch /tmp/allow-backup
velero backup create velero-hook-positive \
--include-namespaces velero-hook-lab \
--snapshot-volumes=false \
--wait
velero backup describe velero-hook-positive --details数据库冻结 Hook 不能只写 pre-freeze;必须设计 post-unfreeze,并验证 pre 成功、快照失败、post 失败和 timeout 四条路径。onError: Continue 只表示继续处理,不会让数据自动一致。字段与默认超时查 Backup Hooks;应用级一致性仍要用数据库启动与业务查询证明。
把一次成功固化为 Schedule
先让一次手工 Backup 与 Restore 通过,再建立 Schedule。前面的步骤已经删除了源 velero-lab,因此这里保护恢复并验证过的 velero-restore-lab;若实际演练选择重建源 namespace,则应把参数改回重建后的真实名称,绝不能让 Schedule 长期指向不存在的 namespace。下面每小时触发,保留七天只是实验值;生产频率和 TTL 要由 RPO、恢复点增长、对象存储版本、快照成本和法规保留共同决定:
velero schedule create velero-lab-hourly \
--schedule='0 * * * *' \
--include-namespaces velero-restore-lab \
--selector 'protection.example.io/tier=gold' \
--storage-location default \
--snapshot-volumes=false \
--ttl 168h
velero schedule get
velero schedule describe velero-lab-hourly
kubectl -n velero get schedule velero-lab-hourly -o yamlSchedule 的时钟被接受不等于派生 Backup 成功。告警至少关联最近成功时间、连续失败数、Backup phase、warnings/errors、BSL 可用性和未完成异步操作。若手工触发模板对应 Backup,可使用目标 CLI 支持的 velero backup create --from-schedule 入口,并检查生成对象的最终 spec。
创建后至少等待一个派生 Backup,确认其 spec.includedNamespaces 为 velero-restore-lab,并检查对象数不是零。这样才能发现 namespace 拼写错误、对象已迁走或 selector 已失配,而不是等到灾难时才发现 Schedule 一直在生产空恢复点。
删除 Schedule 只停止后续创建,不能假设历史 Backup、provider snapshot 和仓库数据会同步消失。Schedule owner reference、Backup TTL、对象存储生命周期与不可变保留要一起演练。
从状态字段定位失败层
排障时按数据路径取证,而不是只收集 server 日志:
velero backup-location get
velero snapshot-location get
velero backup describe <backup-name> --details
velero backup logs <backup-name>
velero restore describe <restore-name> --details
velero restore logs <restore-name>
kubectl -n velero get backup,restore,schedule
kubectl -n velero get podvolumebackups,podvolumerestores
kubectl -n velero get datauploads,datadownloads
kubectl -n velero get events --sort-by=.lastTimestamp
kubectl -n velero logs deployment/velero --since=30m常见分型如下:
| 现象 | 第一证据 | 先查什么 |
|---|---|---|
BSL Unavailable | BSL condition 与 server log | DNS/TLS、region/endpoint、KMS、对象存储权限、专用 prefix |
| Backup 对象数异常少 | Backup spec 与 details | include/exclude、selector、glob、API discovery、RBAC |
Backup 长时间 InProgress/Finalizing | 异步 item operation | DataUpload、PodVolumeBackup、item timeout、node-agent |
Restore Completed 但对象缺失 | restore warnings/skipped | 同名对象、resource policy、webhook、namespace mapping |
| PVC 恢复后 Pending | PVC event、StorageClass、CSI log | driver、拓扑、配额、KMS、snapshot handle 或 data mover |
| Hook 超时 | backup/restore log 与 pod event | 容器名、命令是否存在、pod exec RBAC、冻结解冻路径 |
warnings > 0、未知 skipped item 或 PartiallyFailed 都必须有 owner 和处置结论。一次 Completed 只能说明 Velero 自身已完成它知道的工作;外部数据库、DNS、镜像、证书和下游连接仍需单独验收。
权限、凭证与多租户不能靠 namespace 想象
Velero 的 Backup、Restore 和 Schedule 通常集中在安装 namespace,由一个 server 读取集群资源。默认 cluster-admin 与可读取 Secret 的能力意味着它不是天然的普通租户自助隔离平台。官方提供收紧 RBAC 的方法,但仅把 Role 限制在业务 namespace 会遗漏 PV、CRD、cluster-scoped RBAC、VolumeSnapshotContent 等依赖。
生产收敛时按真实筛选集合生成权限清单,并分别验证备份和恢复。平台 API 只允许受控服务账号创建 Backup/Restore;租户不应直接修改 BSL、VSL、plugin、repository credential 或 namespace mapping。严格租户隔离使用独立 Velero 实例、独立 BSL/prefix、独立云身份和准入策略,再验证 cluster-scoped 对象不会串租户。
至少分开管理四种秘密:对象存储访问身份、provider snapshot 身份、Kopia repository password、目标集群恢复所需的 KMS/registry/外部服务凭证。Velero 备份了 Kubernetes Secret 对象,不表示这些外部身份在灾难后仍有效;仓库访问者也可能读取归档中的敏感配置,因此 bucket policy、传输 TLS、服务端加密、审计和跨账号副本都要独立成立。
用恢复吞吐反推容量与费用
容量预算不能只看当前 PVC 已用空间。对象归档、provider snapshot、对象版本、增量块、Kopia 索引、临时 PVC、节点 cache、日志和跨区复制都会占空间。一次 Backup 的对象数很小,也可能对应数 TB 卷数据;反过来,成千上万个小对象会增加 API listing、压缩和 restore apply 时间。
建立以下可计算项:
daily_logical_change = protected_bytes × daily_change_rate
repository_growth = daily_logical_change × retention_days × compression_dedup_factor
restore_floor = recoverable_bytes / min(object_read_throughput, network, node_write, storage_write)compression_dedup_factor 只能由目标数据压测得出,不能借用其他业务。费用至少包含对象存储容量与请求、provider snapshot、跨区/跨账号复制、下载与出口流量、KMS 请求、数据移动节点、临时卷和日志保留。把最近一次隔离恢复的实际吞吐与尾延迟纳入容量模型;Backup 调度成功并不能证明 RTO 可达。
清理实验对象,而不是误删正式恢复点
先删除恢复出的实验 namespace,再删除 Restore 记录。velero restore delete 会删除 Restore CR 及其日志/结果文件,但不会删除恢复出的对象:
kubectl delete namespace velero-restore-lab velero-hook-lab --ignore-not-found --wait=true
velero restore delete velero-lab-restore --confirm
velero backup delete velero-hook-negative velero-hook-positive --confirm
velero schedule delete velero-lab-hourly --confirmvelero backup delete 才会请求清理 Backup 及其关联 snapshot/repository 引用;直接 kubectl delete backup 只删 CR,可能留下对象存储和快照数据。受 Object Lock、版本保留或跨账号复制保护的对象不会因为 CR 消失而立即释放。FSB/Kopia 的 orphan blob 还要等待 repository maintenance,物理容量下降可能滞后。
实验用 velero-lab-positive 应先完成一次恢复证据归档,再决定删除:
velero backup describe velero-lab-positive --details > velero-lab-positive.describe.txt
velero backup logs velero-lab-positive > velero-lab-positive.log
velero backup delete velero-lab-positive --confirm
velero backup get不要把日志里可能含有的对象名称、错误正文或 Secret 数据直接上传公开工单。证据包保存版本、对象 UID、phase、计数、digest 和脱敏错误即可。
升级、回滚与退出按数据兼容处理
升级到 1.18 的官方路径以 1.17.x 为直接前置版本,要求依次处理 CLI、CRD、server、plugin 与 node-agent。跨多个 minor 时逐版阅读升级文档,不要把“Helm release 能升级”误写成“历史备份一定可恢复”。发布流程至少包含:
导出 Helm values、CRD、BSL/VSL、Schedule、RBAC、plugin digest 和仓库凭证引用。在隔离集群用当前版恢复一个旧恢复点,再用候选版恢复同一恢复点并比较业务断言。更新 CLI 与 CRD,再更新 server/plugin/node-agent;观察 API schema、repository 和异步对象。
创建候选版新 Backup,执行完整 Restore;回滚窗口内保留旧镜像、Chart、CRD 兼容评估和可读仓库。
旧 restic 路径是硬边界:Velero 1.17/1.18 只允许恢复既有 restic 备份,不再新写;1.19+ 将移除旧 restic 恢复。升级前必须盘点旧恢复点,在仍可读取的版本完成恢复演练、重备或到期处置,不能只证明新 Kopia Backup 成功。
退出 Velero 时先停止 Schedule,等待运行中 Backup/Restore/DataUpload/DataDownload 终止并归档证据,再决定外部备份的保留与迁移。按卸载文档删除集群组件,不等于删除 bucket、云快照、Kopia repository、IAM/KMS、恢复出的对象或临时 DNS/LoadBalancer。最终退出证明应同时列出集群内残留、对象存储 inventory、快照、凭证撤销、KMS grant、复制规则和仍受保留期约束的数据 owner。
当团队能从任意一个 Schedule 追到实际 Backup spec,从 Backup 追到 BSL/VSL 与卷数据路径,再从 Restore 追到应用水位和清理证据,Velero 才从“会产生绿色状态的控制器”变成了可治理的恢复平台。
