etcd 快照救的是 API server 里的数据,PV 里的数据不在其中;业务对象加 PV 数据的应用级备份,K8s 生态里的标准工具是 Velero:把集群资源对象和 PV 数据备份进对象存储桶,恢复时再从桶里拉回来。它通过 AWS 插件兼容任意 S3 后端,不过官方对 S3 兼容存储的定位要说准:兼容列表的措辞是"用户报告可用"(users have reported),Velero 团队不做常规测试。MinIO、Tencent Cloud、Ceph 都在这个列表里,社区用量大、文档齐全,但选型时要知道这层背书的分量。
这篇把 Velero 的备份后端接到自建的 RustFS 上,从装到恢复演练走一遍。当前最新版是 v1.18(官方安装文档为准),AWS 插件版本要按官方兼容表跟 Velero 版本对齐。
PV 数据走哪条路,先想清楚
Velero 备份分两层:集群资源对象(yaml 世界)直接序列化进桶;PV 数据要另选路径。云上有卷快照插件(EBS、Azure Disk 这类),自建存储通常没有对等的快照能力,所以走File System Backup(FSB):在每个节点上跑 node-agent,把 PV 数据当作文件直接复制进桶,不需要卷快照插件。装服务端时加--use-node-agent就启用它。
FSB 的官方定性是beta 质量(FSB 文档原话 “considered beta quality”),前提也先对齐:Kubernetes v1.16.0 以上,且集群要启用 MountPropagation 特性,老集群先自查这两条。实现上实际走的是 kopia 的模块(文档开头还并列提了 restic,但正文通篇 backed by kopia,官方 NOTE 明确 restic 路径在弃用流程里:v1.17 起不能再做 restic 备份,v1.19 起连恢复老 restic 备份也会关掉)。当默认路径用没问题,但几条限制要先知道:hostPath 卷不支持(local PV 支持);只能备被 Pod 挂载着的卷,不能直接从 PVC 备,没有运行中 Pod 的孤儿 PVC 备不了,社区的做法是先起一个挂着 sleep 容器的 staging pod(scale 到 0 的 StatefulSet 撞的就是这个);增量链只对 PVC 保持,emptyDir 这类非 PVC 卷在 Pod 重建后下一次是全量;单个大文件(数据库文件)的去重扫描很慢,即使实际差异很小。
还有一条安全边界值得单独记:FSB 的备份仓库用一把静态公共加密密钥,官方原话是"任何能访问你备份存储的人都能解密你的备份数据"。桶的访问控制要按"看到桶=看到全部数据"来收紧,这也是给 Velero 桶单发凭证之外,更要在意桶策略的原因。
顺带一提 node-agent 的运行环境:它靠读宿主机文件系统来拿 Pod 卷数据,官方 Pros/Cons 里写明需要 root、部分环境还要 privileged 模式(OpenShift/SELinux 这类受限环境用--privileged-node-agent安装,或自建 SCC 放开)。标准 K8s 集群一般不用管,安全基线紧的集群要先跟平台方对齐。
另一个要提前想好的点:Velero 的备份对象自带 TTL 管理:创建备份时可以给--ttl指定保留期,不设默认 30 天,到期由垃圾回收流程清理桶里的备份文件。所以桶上不该再叠 lifecycle expiration,更不能配 immutability / object lock 这类防改写保留策略:官方文档明确说 v1.11 起 Velero 备份完成时要改写桶内对象元数据,桶一旦配了不可变保留,备份删除会出问题。顺带一提桶版本控制的账:官方在 AWS S3 一节写明 object lock 只作用于版本化桶,这时删备份只是打 delete marker,对象的旧版本不会被删,空间不会真的释放。默认建议是备份桶不开版本控制;若出于防误删仍想开,就得自己补一条非当前版本的过期规则来回收旧版本空间,做法在之前 RustFS lifecycle 那篇里核过的--noncurrent-expiry-days。这个和 JuiceFS 那篇"别叠 lifecycle"的道理同源,在备份桶上代价更直接。
第一步:起 RustFS,建桶
dockerrun-d\--namerustfs\--restartunless-stopped\-p9000:9000\-p9001:9001\-vrustfs-data:/data\-eRUSTFS_ACCESS_KEY="<your-access-key>"\-eRUSTFS_SECRET_KEY="<your-secret-key>"\-eRUSTFS_ADDRESS=":9000"\-eRUSTFS_CONSOLE_ADDRESS=":9001"\-eRUSTFS_CONSOLE_ENABLE=true\rustfs/rustfs:latest\/data建一个 Velero 专用的桶,凭证单发(给备份系统单独一把 key,权限面小):
exportAWS_ACCESS_KEY_ID=<your-access-key>exportAWS_SECRET_ACCESS_KEY=<your-secret-key>aws --endpoint-url http://localhost:9000 s3 mb s3://velero第二步:凭证文件与 velero install
Velero 用 AWS 风格的凭证文件,建一个credentials-velero:
[default] aws_access_key_id = <your-access-key> aws_secret_access_key = <your-secret-key>然后装服务端(这是全文最关键的一条命令):
veleroinstall\--provideraws\--pluginsvelero/velero-plugin-for-aws:<plugin-version>\--bucketvelero\--secret-file ./credentials-velero\--use-volume-snapshots=false\--use-node-agent\--backup-location-configregion=minio,s3ForcePathStyle="true",s3Url=http://<rustfs-host>:9000\--wait几个参数的来历:
--provider aws配 AWS 插件,这是接所有 S3 兼容后端的官方姿势,RustFS 不需要专门的插件。插件版本按官方兼容表对齐主版本,对应关系简单:插件 v1.14.x ↔ Velero v1.18.x(往下 v1.13.x ↔ 1.17.x、v1.12.x ↔ 1.16.x),混着装会有兼容问题。
--use-volume-snapshots=false:自建侧没有卷快照提供方,明确关掉,避免创建用不上的 VolumeSnapshotLocation。
region=minio是官方 MinIO 示例的填法。自建服务没有真的 region 概念,照抄这个惯例值就行。
s3ForcePathStyle="true"是自建后端的生死开关。官方文档的原话值得整段读:不设或设为 false 时 AWS SDK 走 virtual-host 寻址(桶名进域名),而 MinIO 这类自建服务默认只支持 path-style 寻址(桶名进路径),不匹配的后果是 Velero能把备份传上去,但从桶里下载不了,也就是备份看起来在跑,恢复时才炸。官方给了一个真实 issue 作例子,解法有两条:设s3ForcePathStyle=true(官方推荐),或者让服务端支持 virtual-host 寻址(MinIO 配MINIO_DOMAIN环境变量)。多数自建场景选第一条更省事。BSL 的全部 config 键(含校验和、签名版本、证书这些后文会用到的)在 AWS 插件的 BackupStorageLocation 文档里有逐键说明。
s3Url指向 RustFS 的内网地址,K8s 节点要能直达。
第三步:第一个备份
验证装好了,然后拿一个带 PV 的应用开刀:
velero backup-location get velero backup create demo-backup--selectorapp=demo --default-volumes-to-fs-backup velero backup describe demo-backup--details--default-volumes-to-fs-backup让这次备份的 PV 数据全走 FSB 路径(这个参数也可以在velero install时直接带上,对所有备份生效)。describe 的输出里看两个东西:Phase 是不是 Completed,FSB 的条目有没有挂错误。桶里此刻应该已经出现了备份 tarball 和 FSB 数据对应的对象,node-agent 的日志是 FSB 排障的第一入口。
第四步:恢复演练——备份的检验标准只有这一条
备完了不算完,删掉重来才算:
kubectl delete namespace<demo-ns>velero restore create --from-backup demo-backup velero restore getSTATUS 从 InProgress 走到 Completed、WARNINGS 和 ERRORS 都是 0,才叫这套备份真的能用。有错就velero restore describe <name> --details,报错条目会指到具体资源。还有一类静默失败值得提防:官方文档(main 分支新增的 File Ownership and Permission Preservation 一节)写明,在属主和权限由挂载配置强制决定的卷上(Azure Files 的 SMB/CIFS、blobfuse、GCP FUSE 这类 mount-constant identity 文件系统),FSB 恢复时以 root 执行 chown/chmod 会返回成功但什么都没改,restore 照样 Completed 零告警,日志里也没有痕迹。自建块存储的 ext4/xfs PVC 不在此列,但演练时多抽查一两个文件的属主和权限,成本很低。
定期备份一个 cron 搞定:
velero schedule create demo-daily--schedule="0 1 * * *"--selectorapp=demo --default-volumes-to-fs-backup自建后端常见的四个坑
describe 打不开日志或下载链接失效。velero backup describe --details和velero logs会生成预签名 URL 供客户端下载,URL 默认指向 s3Url 的集群内地址,从你的笔记本当然打不开。修法是给 BackupStorageLocation 加publicUrl(值是集群外可达的地址,NodePort 或 Ingress 都行,必须带http://或https://前缀)。
自签名证书。RustFS 前面挂了 HTTPS 用自签证书时,官方给的两条路是 BSL config 里设insecureSkipTLSVerify: "true",或提交 CA 证书。官方文档自己标注了 insecureSkipTLSVerify 有中间人风险、不推荐生产,生产环境老老实实配 CA。
上传报校验类错误。AWS 插件给上传对象默认附 CRC32 校验和(BSL config 键checksumAlgorithm,默认值 CRC32),个别自建端点不支持时可以把它设为空字符串关掉。注意这个开关和 JuiceFS 的disable-checksum不是一层东西:Velero 的checksumAlgorithm控制 AWS SDK 上传请求头里的校验和,JuiceFS 那个控制的是它自身写进对象元数据的 CRC,遇到报错先分清是哪一层的校验在喊疼。
签名版本。预签名 URL 走 SigV4(默认),极少数 S3 兼容实现只认 V1,BSL config 里signatureVersion: "1"是官方留的开关,Quobyte 这类属于此列。
这套组合里对象存储的位置
Velero 把备份的调度、资源序列化、PV 数据搬运全做了,对后端只有一个要求:S3 兼容、path-style、稳定可达。RustFS 单二进制起服务,S3 全兼容,接进来就是建一个桶加三条 config 的成本。备份系统里最不该薄的其实是演练:上面的第四步每周跑一次,比任何配置优化都值钱。