首页 / 资讯中心 / 文章详情

Kubernetes备份恢复实战:Velero部署与灾难恢复指南

Kubernetes备份恢复实战:Velero部署与灾难恢复指南 ★ FEATURED ARTICLE
Kubernetes 集群跑得越久我越能体会到一件事备份这件事大家都觉得重要但真正动手做的没几个。等哪天节点硬件坏了、某个 namespace 被误删、或者升级的时候配置没改对导致控制平面起不来才会在半夜一边抓头发一边问自己上次备份是什么时候来着这一章聊的就是 Kubernetes 生态里最主流的备份、恢复与灾难恢复方案——Velero。它解决的问题很直接把集群里的资源对象和持久化数据安全地备份到对象存储出事了能一键把整个应用、namespace 甚至整个集群恢复到原来的状态。这篇文章会从底层原理讲起再带着你从零部署 Velero亲手做一次备份和恢复演练最后把我在实际运维中踩过的坑和排查经验一并交代清楚。适合理清备份思路的运维、想给集群加保险的开发者以及准备做集群迁移的任何人。1. 为什么 Kubernetes 需要一套专门的备份方案1.1 先搞清楚集群里到底有什么数据需要保护很多刚接触 Kubernetes 的朋友会有一个误区以为集群数据都在 etcd 里把 etcd 备份好就万事大吉了。这想法对了一半错了一半。etcd 里存的是集群的控制面数据——Deployment、Service、ConfigMap、Secret 这些 API 对象的定义确实都在里面。但一个正在跑业务的生产集群数据远不止这些。你的应用写在 PVC 里的文件、数据库的数据文件、上传的图片和附件这些数据存在存储卷里etcd 里只有哪个 Pod 挂载了哪个卷的关联信息卷里面的内容它管不着。我用一个生活化的类比来解释这个问题etcd 相当于你手机里的通讯录存的是谁的电话号码是多少而 PVC 里的数据相当于你相册里的照片。备份通讯录能让你换手机后重新知道联系人的号码但备份不了照片本身。Velero 的厉害之处在于它同时把两边都管起来了——既备份 API 对象也能备份卷数据。1.2 传统备份脚本为什么在 Kubernetes 面前会失灵有的团队会写 shell 脚本定时执行kubectl get all --export之类的命令来做备份这种做法我理解但真心不建议。问题在于 Kubernetes API 对象的完整备份要比这复杂得多一个应用往往牵扯几十种资源Deployment 下面的 ReplicaSet、Service 的 Endpoint、Ingress 的规则、PVC 的存储类定义散落在多个资源类型里靠get all根本抓不全。直接用kubectl get导出的 YAML 不带资源版本和状态信息恢复的时候经常因为字段缺失或者 API 版本不兼容而失败。就算你把 YAML 都导出来了PV 里的数据怎么备份还得单独找方案自己做脚本整合复杂度直接起飞。所以这个场景需要一个专业的工具能识别 Kubernetes 资源的依赖关系能调用底层存储的快照能力或者文件系统备份能力能按 namespace、按 label、按资源类型做精细化选择还能把备份产物放到一个可靠的外部存储位置。Velero 正是在这些需求下诞生的项目。1.3 认清 Velero 的能力边界Velero 本身不备份 etcd这一点需要一开始就明确。它是通过调用 Kubernetes API 来读取资源对象的定义而不是直接去拷贝 etcd 数据文件。如果集群连 kubectl 都跑不了、API Server 彻底挂掉Velero 也救不了你。等 API Server 恢复后它才能完成恢复工作。所以一个严谨的生产集群备份策略至少要有三层etcd 层面的备份用于处理控制面数据损坏、配置错误导致的集群不可用。Velero 层面的备份用于处理资源误删、namespace 清理、集群迁移、应用级恢复。基础设施层面的快照比如节点磁盘快照、数据库自动备份用于兜底。Velero 是中间最关键的那一层它能让你在大多数灾难场景里快速恢复业务但别指望它做所有事情。2. Velero 的核心设计拆解它到底是怎么工作的2.1 Velero 的架构一个 Server 加一个 CLIVelero 的架构不算复杂分成两部分跑在集群里的 Velero Server以及你操作时用的 velero 命令行工具。客户端通过 kubeconfig 访问集群 API Server把操作指令发给 Velero Server真正干活的 Server 会启动一个备份进程通过 API Server 读取目标资源的数据处理完以后上传到对象存储。Velero Server 本身是一组 Deployment里面跑的是velero主进程还挂了一个叫node-agent的 DaemonSet。node-agent 的作用是处理卷的文件系统备份也就是后面要说的 Kopia/Restic 方式备份 PVC 数据。这个设计把数据采集和控制逻辑分离开了扩展性做得很好。因为 Velero 是从 CNCF 毕业的项目它设计了一套插件机制。存储插件负责对接各种对象存储比如 AWS S3、阿里云 OSS、Azure Blob卷快照插件负责对接云厂商的块存储快照 API。这种插件化的思路让 Velero 具有很强的适配性你在不同云环境甚至本地机房都能用上同一套备份操作方式。2.2 备份一个对象背后经历了什么当你执行velero backup create的时候Velero Server 内部做的事情大致是这样的。第一步它根据你指定的参数是所有 namespace 还是特定 namespace是否包含某些 label向 API Server 发起 List 请求收集匹配到的所有资源对象。这一步不是简单地把 YAML 抓下来而是会展开资源之间的关联关系。你备份一个 Deployment它会连带备份它管理的 ReplicaSet、Pod 模板、Service、PVC 等关联资源保证恢复出来的对象是完整可用的。第二步收集到的对象会经过序列化打包成一个个 item写进备份归档文件里。这个过程有点类似把一套乐高积木按图纸分类整理好装进箱子。第三步是处理卷数据。如果备份时指定了卷的处理方式Velero 会调用 VolumeSnapshotLocation 对应的云厂商接口对 PVC 的底层块存储做快照如果使用文件系统备份方式node-agent 会进入 Pod 的文件系统层把这个卷里的文件以增量形式读出来压缩后存到对象存储。最后所有这些产物——对象清单和卷数据——统一以 tar 格式上传到你配置的对象存储 bucket 里备份任务就完成了。2.3 两种卷备份方式到底该怎么选Velero 对 PVC 数据的备份提供了两种截然不同的实现路径这也是新手容易搞混的地方。第一种叫原生快照VolumeSnapshot依赖云厂商的块存储快照能力。它速度快、对应用无感知缺点是强绑定云环境而且要求底层存储类支持 CSI 快照。你在本地方案比如 NFS、Longhorn里不一定能用得上。第二种叫文件系统备份FSBackupVelero 先是用 Restic后来逐渐过渡到 Kopia。它在 Pod 的文件系统层面工作把 PVC 里的文件一个个读出来做增量备份对底层存储没有特殊要求兼容性极强。缺点是恢复速度相对慢一些备份过程中会增加 Pod 的 IO 负载。我给一个简单的选型判断云环境里你能流畅操作快照优先用 VolumeSnapshot性能和可靠性都更好本地自建的存储、没法做快照的环境、或者需要把数据迁到另一个云平台的场景用文件系统备份更省心。2.4 认识 Velero 的几个核心对象用 Velero 的时候你会频繁接触到这几个概念提前理解它们的区别能省掉很多麻烦。Backup一次备份操作的事件记录。创建时会生成一个备份对象里面记录了备份的时间、命中的资源范围、状态Completed / Failed / PartiallyFailed。Schedule定时备份计划按 Cron 表达式定义备份频率每次触发会生成一次 Backup。Restore一次恢复操作的事件记录。可以通过--from-backup指定恢复哪一次备份也支持 namespace 重命名映射。BackupStorageLocation备份文件存放位置的定义比如你的对象存储的 endpoint、bucket 名称、region 等。VolumeSnapshotLocation卷快照存放位置的定义主要给 VolumeSnapshot 方式用。拿数据库管理做个类比Backup 就是一次完整的导出Schedule 就是数据库的定时任务Restore 就是把导出的文件重新导入的过程。3. 从零部署 Velero环境准备与安装实操3.1 环境准备清单在开始装 Velero 之前先确认手头的条件。我自己测试用的环境是这样搭建的一套可以由任意方式安装的 Kubernetes 1.28 集群本机已经配好了 kubeconfig 而且有操作权限。另外为了演示我在虚拟机里装了一个 MinIO 作为对象存储后端——这是最接近生产环境又最容易在本地复现的方案。你需要准备的还有两样一台能访问集群的机器用来跑 velero 命令行以及一个对象存储的 access key 和 secret key。如果暂时没有云端的对象存储完全可以用 Docker 把 MinIO 拉起来方法和下文一致。3.2 用 Docker 快速搭一个 MinIOMinIO 是兼容 S3 协议的开源对象存储最适合做本地测试。我用 Docker 启动它的命令是这样的mkdir -p /data/minio docker run -d \ --name minio \ --restartunless-stopped \ -p 9000:9000 \ -p 9001:9001 \ -e MINIO_ROOT_USERminioadmin \ -e MINIO_ROOT_PASSWORDminioadmin123 \ -v /data/minio:/data \ minio/minio server /data --console-address :9001启动以后浏览器访问http://宿主机IP:9001用 minioadmin / minioadmin123 登录控制台手动创建一个 bucket名字就叫velero-backup。记住这个 bucket 名后面安装 Velero 会用到。3.3 安装 velero 命令行工具velero 命令行工具安装很简单去 GitHub Releases 页面下载对应平台的最新二进制包就行。Linux 环境下的操作流程是这样curl -LO https://github.com/vmware-tanzu/velero/releases/download/v1.13.2/velero-v1.13.2-linux-amd64.tar.gz tar -xzf velero-v1.13.2-linux-amd64.tar.gz sudo mv velero-v1.13.2-linux-amd64/velero /usr/local/bin/velero velero version --client-only我特别提醒一句下载后先确认版本号Velero 客户端和服务器版本要尽量保持一致差太多会出现兼容问题。3.4 准备 MinIO 访问凭证Velero 安装时需要传入一个访问对象存储的凭证文件。先创建credentials-velero[default] aws_access_key_id minioadmin aws_secret_access_key minioadmin123然后执行安装命令。注意这里有个关键点由于 MinIO 是本地部署而非云厂商的 S3我们需要额外指定--public-url和--backup-location-config参数否则 Velero 找不到 MinIO 的访问地址。velero install \ --provider aws \ --plugins velero/velero-plugin-for-aws:v1.9.0 \ --bucket velero-backup \ --secret-file ./credentials-velero \ --backup-location-config regionminio,s3ForcePathStyletrue,s3Urlhttp://192.168.1.100:9000 \ --snapshot-location-config regionminio \ --use-volume-snapshotsfalse \ --public-urlhttp://192.168.1.100:9000参数解析--provider aws因为我们用 S3 协议访问 MinIO所以 Provider 依旧填 aws只是后端地址写 MinIO。--pluginsVelero 要访问 S3 就得加载对应的插件。我用的是官方提供的 AWS 插件。--use-volume-snapshotsfalse本地 MinIO 不支持真正的块存储快照先关掉卷快照功能只做资源对象和文件系统备份。等待安装完成后检查一下命名空间里的 Pod 状态kubectl get pods -n velero看到 velero-xxxxx 和 node-agent-xxxx 都处于 Running 状态说明安装成功了。为了验证存储位置是否正常执行velero get backup-locations状态应该是 Available。3.5 安装阶段最容易出问题的三个点第一插件版本和 Velero 主版本不匹配。建议去 Velero 的官方支持矩阵页面核对一下你选用的插件版本和 Velero 主版本的兼容组合然后再执行安装。第二MinIO 的地址写错。很多次排查来排查去最后发现s3Url里少写了端口或者填了 localhost而 Velero Server 跑在集群内部根本访问不到宿主机的 localhost。这里务必填宿主机对集群可达的 IP。第三忘记了s3ForcePathStyletrue。云厂商 S3 用的是虚拟主机风格路径MinIO 默认支持 path style两个不一致会导致 Velero 访问 MinIO 时报 403 错误。加上这个参数就能解决。4. 第一次备份与恢复实战从创建到演练4.1 创建一个 Demo 应用用来做实验任何恢复演练的前提是先有一个真实的环境。我搭建一个简单的 nginx 应用并且带一个 PVC这样既能测资源对象备份也能测卷数据备份。kubectl create namespace demo kubectl create -n demo deployment nginx --imagenginx:1.27 kubectl create -n demo pvc data --storage-classlocal-path --request-storage1Gi kubectl patch -n demo deployment nginx \ --typejson \ -p[{op:add,path:/spec/template/spec/containers/0/volumeMounts,value:[{name:data,mountPath:/usr/share/nginx/html}]},{op:add,path:/spec/template/spec/volumes,value:[{name:data,persistentVolumeClaim:{claimName:data}}]}]上面的命令里我用到了local-path这个 StorageClass如果你的环境里没有可以用其他任何可用的 StorageClass 替代。给 nginx 挂好 PVC 之后在卷里写一个文件模拟用户数据kubectl exec -n demo deploy/nginx -- sh -c echo Hello Backup /usr/share/nginx/html/index.html4.2 执行第一次备份备份整个 demo 命名空间velero backup create nginx-backup-$(date %Y%m%d-%H%M%S) --include-namespaces demo这条命令的参数很简单--include-namespaces后面的逗号分隔列表就是你想备份的命名空间名。时观察备份状态velero backup get状态会依次经历 New - InProgress - Completed。等它变成 Completed再执行velero backup describe 备份名就能看到这个备份到底收集了哪些资源、备份了哪些 Pod、卷状态如何。4.3 模拟事故删掉这个命名空间既然要做恢复演练就得真的动手把环境弄坏。我直接把整个 namespace 删掉连带着里面的 Deployment、PVC、数据全部消失kubectl delete namespace demo这时候你访问kubectl get ns会发现 demo 已经没了PVC 的持久化数据也没了。心理上先体验一下这种东西真的没了的感觉。4.4 从备份中恢复恢复操作同样是一条命令velero restore create --from-backup nginx-backup-20250801-103000把备份名替换成你自己刚才生成的那个名字。恢复过程也是异步执行的用velero restore get查看状态最后标记为 Completed 即为成功。验证恢复结果kubectl get ns demo kubectl get deploy -n demo kubectl get pvc -n demo kubectl exec -n demo deploy/nginx -- cat /usr/share/nginx/html/index.html不出意外的话namespace 回来了、Deployment 回来了、PVC 回来了卷里的Hello Backup也还在。Velero 之所以能把 PVC 的数据也带回来是因为备份时用了文件系统备份模式node-agent 把卷里的文件数据也打包上传了。这一点值得多强调一句VPP 恢复和上层 YAML 恢复不同Velero 恢复 PVC 时需要重新调度 PV如果你原来的 PV 是通过 StorageClass 动态创建的它会重新创建一块新 PV如果用的是静态 PV那就要求原 PV 还能用否则卷恢复会失败。4.5 全量备份与跨集群迁移演示完了特定 namespace 的备份恢复再聊聊更实用的场景整个集群的备份和迁移。全量备份就是把整个集群的所有资源打一个包velero backup create full-cluster-backup这种备份不带过滤条件备份范围覆盖所有 namespace 以及集群级别的资源比如 ClusterRole、ClusterRoleBinding、CustomResourceDefinition。等备份完成以后你可以用同一个备份文件在另一个集群里恢复完成环境迁移。跨集群恢复的时候一般会做 namespace 重命名比如把备份里的demo恢复成目标集群的testvelero restore create --from-backup full-cluster-backup \ --namespace-mappings demo:test这样就能实现一次备份多次恢复还能顺带做环境隔离。4.6 定时备份把备份变成日常习惯手动备份用得再多也挡不住你某天忘了。Velero 提供了 Schedule 机制用 Cron 表达式控制备份频率。我自己的生产集群里配置了一个每 6 小时执行一次、保留 72 小时的备份计划velero schedule create daily-backup \ --schedule0 */6 * * * \ --ttl72h \ --include-namespacesdefault,prod,dev \ --storage-locationdefault这里--ttl指的是备份文件在对象存储中的保留时长。这样设置的好处是备份频率可控存储成本可控数据保留时长也符合常见的业务安全期限要求。如果业务要求更严格的 RPO可以把--schedule改短比如每隔 1 小时跑一次同时注意观察对象存储的存储量增长速度。5. 常见问题与排查技巧实录5.1 备份一直停留在 InProgress 状态这个问题的出现频率非常高。遇到这种情况第一步是看备份日志velero backup logs backup-name最常见的原因有三类访问对象存储超时Velero 插件和对象存储不兼容备份过程中某些资源被删除API Server 返回了 404。日志里通常能看到具体是哪个环节卡住了。有一次我排查了半天发现是 MinIO 所在的机器磁盘满了导致上传一直超时重试清理掉磁盘空间后状态立刻恢复。5.2 恢复时报资源冲突错误如果在恢复前目标 namespace 里已经有同名资源Velero 恢复时报错“resource already exists”的概率很大。Velero 默认的恢复策略是 upsert但在某些资源类型上仍然存在冲突。解决办法有两个方向恢复前先把目标 namespace 清空如果业务上不允许停服清理可以用--exclude-resources排除诸如 Service、Endpoint 这类可能被实时更新的资源等恢复完成后用别的方式处理。5.3 卷备份失败状态为 PartiallyFailed这通常是因为备份对象时把 PVC 的数据部分遗漏了。查看velero backup describe里的 Volume info 一栏能发现卷的备份状态是 Skipped 还是 Failed。如果 Skipped可能是这个 StorageClass 不支持快照如果 Failed很可能是 node-agent 对卷里的文件没有读取权限或者备份的文件中有正在被删除的文件。处理方式一是确保目标 Pod 的挂载路径对 node-agent 可读二是使用文件系统备份模式Kopia替代原生快照。5.4 备份成功但对象存储里看不到数据这个情况基本可以确定是对象存储地址或凭证配置的问题。先检查 Velero Server 的日志看有没有上传相关的报错。再手动用 S3 客户端测试访问aws --endpoint-url http://192.168.1.100:9000 s3 ls s3://velero-backup如果 S3 客户端能访问而 Velero 不行那问题大概率出在 Velero 的 BackupStorageLocation 对象配置里检查 region、s3Url、s3ForcePathStyle 这几个字段是否和 MinIO 实际环境一致。5.5 我自己的排查心得两句话总结我的排查方法论先看日志别看状态先确认网络再怀疑权限。Velero 的日志信息本身写得相当详细绝大多数问题都能在日志里直接看出端倪。另外我强烈建议你每调试完一次环境就把velero backup create --dry-run的演练跑一遍。VPP 和 Restic 在真实备份前的 dry run能省下你关键时候的一堆麻烦。我自己在刚接触 Velero 的时候总觉得备份这种低频操作不值得花太多精力直到有一次做恢复演练才发现之前配置的备份计划因为存储桶权限变更已经连续两周没有成功执行。那一刻才意识到备份系统如果没有恢复演练来验证本质上就是个心理安慰。后来我在运维团队里定了一条规矩每个季度至少做一次真实的恢复演练把从备份到恢复的完整流程跑通并且把操作步骤写进 runbook。说真的当你真的在某个深夜遇到集群故障能从容地从备份里把业务捞回来的时候你会感谢那个当初愿意花一个下午把 Velero 配置到位的自己。
阅读完成 · 觉得有帮助?
咨询建站