方案对比
Etcd
优点:全量备份,包含集群所有资源;备份不依赖于任何K8s API和版本,可用于灾难恢复。
缺点:全量恢复,不可恢复部分资源;不支持数据迁移,恢复时必须保持配置统一(K8s版本、各种IP、主机名都要保持一致)。
GitOps
优点:符合GitOps规范,可以基于代码仓库进行配置控制、回滚、可审计、可重复部署
缺点:配置漂移,资源文件过多,管理复杂。
Velero
优点:灵活性高,支持按照namespace、集群等方式备份,支持定时备份、全量备份,支持备份应用数据,支持跨集群备份恢复数据,支持细粒度恢复。
缺点:需要S3存储备份数据,备份应用数据功能有限,K8s版本依赖
关于备份应用数据:
- 不推荐使用velero备份,他是崩溃一致性。数据库应用,直接用velero备份运行中的数据库的PV数据目录不可靠,是有其他事务逻辑约束的,还原会出问题。
- 怎么备份应用数据:用数据库自己的工具,比如mysqldump、pgdump等。能保证应用一致性
应用数据备份
这些约束是应用层的逻辑,文件系统级的备份工具(如 Velero)无法理解和保证,所以必须使用数据库自己的备份工具(如mysqldump、pg_dump),它们从应用层理解这些约束,才能保证备份的可靠性。
Velero介绍
专门为K8s设计的备份容灾工具,主要用于以下场景:
1. 备份、恢复、灾备K8s集群的各类资源
2. 灵活迁移K8s的数据,可以从集群A迁移到集群B,可以从ns A迁移到ns B
3. 手动及周期备份、最小范围恢复(只恢复某个ns内的某个资源)
核心组件:
- 服务端:处理备份和恢复操作,需要部署在K8s集群中,需要配置对象存储后端
- 客户端:负责与服务端交互
核心资源
-
Backup:备份资源,用于执行一次性备份任务,可以基于Schedule规则或者自定义字段创建,备份资源可以指定需要备份或排除的空间、资源类型,同时支持备份的保留时间等。
-
Schedule:周期性备份,基于Cron表达式创建的周期性备份任务,包含Backup的所有字段。
-
Restore:恢复资源,用于从某个backup中恢复资源,可以指定恢复的资源类型、空间等。
-
BackupStorageLocation:备份存储位置,用于指定备份文件的存储位置,比如aws、minio等。同时可以指定对象存储的bucket和prefix。
工作原理
备份过程
手动或自动备份 -- 创建backup资源 -- 服务端处理 -- 执行钩子 -- 收集资源(创建json文件和一些压缩包) -- 上传至对象存储 -- 返回结果 -- 更新bakcup状态
还原过程
执行还原 -- 创建Restore资源 -- 服务端处理 -- 下载备份文件 -- 创建k8s资源 -- 返回结果 -- 更新Restore状态
部署对象存储Minio
Velero会把备份文件上传至对象存储,可以使用公有云存储或者Minio作为存储后端。
本次实验采用docker部署Minio:
❌ 使用Bitnami镜像:(bitnami的minio的Arm64镜像无法拉取,dockerhub里面已经找不到任何tag,遂放弃)
mkdir -p /data/minio && chmod -R 777 /data/minio
docker run -d --name minio-server --restart=always \
--env MINIO_ROOT_USER="user" \
--env MINIO_ROOT_PASSWORD="password" \
--env MINIO_DEFAULT_BUCKETS="velerobackup" \
--publish 9000:9000 \
--publish 9001:9001 \
-v /data/minio:/bitnami/minio/data \
bitnami/minio:latest
访问宿主机IP+9001可以访问Minio UI界面。
✅ 使用官方镜像:
mkdir -p /data/minio && chmod -R 777 /data/minio
docker run -d --name minio-server --restart=always -p 9000:9000 -p 9001:9001 -e MINIO_ROOT_USER="user" -e MINIO_ROOT_PASSWORD="password" -v /data/minio:/data minio/minio:latest server /data --console-address ":9001"
注意:
1. server /data --console-address ":9001": 这是官方镜像必须的启动参数。如果不加 --console-address,Web 控制台端口可能会随机分配,导致你无法通过 9001 访问。
2. MINIO_DEFAULT_BUCKETS 是 Bitnami 镜像特有的环境变量。官方镜像不支持通过环境变量自动创建 Bucket。你需要启动后手动创建。
3. 访问宿主机IP+9001可以访问Minio UI界面,在界面左侧点击 "Buckets",然后点击 "Create Bucket" 创建名为 velerobackup 的存储桶。
部署Velero
版本确认及下载
部署 Velero 之前,需要找到适合的版本和插件的版本:
- Velero 版 本 选 择 (需要下载docker image): Velero Compatibility Matrix - GitHub
- 插 件 版 本 选 择(需要下载docker image): velero-plugin-for-aws Compatibility - GitHub
- 接下来下载客户端工具(下载安装包): Velero Releases - GitHub
K8s集群版本是1.34 -- Velero版本用1.17 -- 插件版本选1.13
安装客户端工具
客户端工具选:1.17.1 (release里面选择最新的patch版本下载):
Velero Releases - GitHub
tar xf velero-v1.17.1-linux-arm64.tar.gz
mv velero-v1.17.1-linux-arm64/velero /usr/local/bin/
chmod +x /usr/local/bin/velero
velero version
创建对象存储密码文件
cat > user-minio << EOF
[default]
aws_access_key_id=user
aws_secret_access_key=password
EOF
安装服务端
velero自动识别当前kubeconfig来往集群安装资源:
velero install \
--provider aws \
--plugins velero/velero-plugin-for-aws:v1.13.1 \
--image velero/velero:v1.17.1 \
--bucket velerobackup \
--secret-file ./user-minio \
--use-volume-snapshots=false \
--backup-location-config region=minio,s3ForcePathStyle="true",s3Url=http://172.16.35.120:9000 \
--namespace velero
注意:关于为什么对象存储用minio,provider却用aws:
- 因为 MinIO 是完全兼容 Amazon S3 协议的。
- Velero 并没有专门为 MinIO 开发一个独立的插件,而是复用了 AWS S3 的插件。对于 Velero 来说,MinIO 就是一个“运行在你本地的 AWS S3”。
- 虽然我们用了 AWS 的插件,但我们不能让它去连接亚马逊的服务器。这就是你命令中 --backup-location-config 这一行的作用,它是“偷梁换柱”的关键:
- s3Url=http://192.168.181.134:9000:
这个参数告诉 AWS 插件:“不要去连接默认的亚马逊官网地址,而是去连接我指定的这个 IP 和端口”。因为 MinIO 听得懂 AWS S3 的语言,所以插件发过去的请求,MinIO 能完美处理。
- s3ForcePathStyle="true":
AWS S3 现在的默认访问方式是域名形式(如 bucketname.s3.amazonaws.com),但本地部署的 MinIO 通常使用路径形式(如 192.168.x.x:9000/bucketname)。这个参数强制插件使用路径形式,否则会因为 DNS 解析不到域名而报错。
- region=minio:
AWS SDK 强制要求必须填一个 Region(区域)。对于本地 MinIO,这个值其实不重要(MinIO 不分区域),但为了骗过 SDK 的检查,我们通常约定俗成填 minio 或者 us-east-1。
- 因为加载的是 AWS 插件,该插件的代码里写死了去读取名为 aws_access_key_id 的变量。插件并不知道它连的是 MinIO,它只认这些标准的 AWS 变量名。所以,这里的 user 和 password 其实就是你 MinIO 的用户名和密码,只是必须套上 AWS 的“马甲”。
扩展-S3协议
- Amazon S3 是对象存储事实上的行业标准。几乎所有现代的对象存储软件(包括 MinIO、阿里云 OSS、腾讯云 COS、Ceph RGW 等)都实现了 S3 API。 这意味着:它们使用的通讯语言、认证方式、数据读写命令,和 AWS S3 是一模一样的。
安装完起了一个velero的deployment,检查pod ready,检查存储后端配置:
kubectl get BackupStorageLocation -n velero
创建测试资源
创建测试ns:kubectl create ns test
部署测试deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox-test
namespace: test
labels:
app: busybox-test
spec:
replicas: 1
selector:
matchLabels:
app: busybox-test
template:
metadata:
labels:
app: busybox-test
spec:
containers:
- name: busybox
image: busybox:1.28
command:
- "/bin/sh"
- "-c"
- "while true; do echo 'Hello from busybox'; sleep 10; done"
ports:
- containerPort: 80
resources:
requests:
cpu: "50m"
memory: "64Mi"
limits:
cpu: "100m"
memory: "128Mi"
---
apiVersion: v1
kind: Service
metadata:
name: busybox-test-svc
namespace: test
labels:
app: busybox-test
spec:
selector:
app: busybox-test
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
运行备份
备份:velero backup create test-backup
查看备份:velero backup get
当你运行 velero backup create test-backup 且不带任何其他参数时,Velero 的默认行为是:“尽可能备份集群内的所有 Kubernetes 资源对象(Metadata)”。
结合你之前提供的安装命令(特别是 --use-volume-snapshots=false),这个备份的具体内容如下:
1. 备份了什么(Yes)
它备份的是 Kubernetes 的“配置”和“状态描述”(也就是你通常用 kubectl get ... -o yaml 看到的那些东西):
- 所有命名空间 (Namespaces):包括
default、kube-system以及你自己创建的所有 namespace。 - 工作负载 (Workloads):Deployments, StatefulSets, DaemonSets, ReplicaSets, Pods, Jobs, CronJobs 等。
- 配置与密钥: ConfigMaps, Secrets。
- 网络配置: Services, Ingress, NetworkPolicies。
- 存储定义 (注意是定义): PersistentVolumeClaims (PVC), PersistentVolumes (PV), StorageClasses。
- 权限控制: ServiceAccounts, Roles, RoleBindings, ClusterRoles, ClusterRoleBindings。
- 自定义资源 (CRDs): 集群内安装的所有 CRD 定义及其 CR (Custom Resources)。
简单来说: 如果你的集群炸了,用这个备份还原,你的 K8s 会恢复出所有的 Deployment、Service 和 PVC 的定义。
2. 没备份什么(No)—— ⚠️ 重点警告
基于你之前的安装命令 velero install ... --use-volume-snapshots=false,这个备份极其重要的一点缺失是:
- ❌ 持久化数据(Persistent Volume Data)没有被备份!
这是什么意思?
假设你在集群里运行了一个 MySQL 数据库,挂载了一个 PVC 存数据。
- Velero 会备份:“这里有一个 MySQL Deployment” 和 “这里有一个 10GB 的 PVC 申请” 这两张“图纸”。
- Velero 不会备份:MySQL 数据库里存储的用户表、订单表等实际文件数据。
原因:
Velero 备份数据主要有两种方式:
- 存储卷快照 (Volume Snapshots):你显式禁用了它 (
false),而且 MinIO/本地环境通常也没有云厂商那种一键快照能力。 - 文件系统备份 (FSB / Restic / Kopia):这需要你显式启用 Node Agent,并在备份时指定(或者给 Pod 加注解)。你的
velero install命令里没有启用这个组件,backup命令里也没指定。
3. 还有哪些通常被排除?
默认情况下,Velero 还会自动排除一些不适合备份或不需要备份的数据:
- Kubernetes Events: 集群事件日志通常不备份。
- Node 信息: 因为 Node 是基础设施,还原时通常是还原到新的一组 Node 上。
- 临时数据: 挂载了
emptyDir的卷里的数据。
4. 如何验证备份了什么?
你可以运行以下命令来查看这次备份的详情:
查看备份概览:
velero backup describe test-backup --details
kubectl get backup -n velero
你会看到 Resource List,列出了它找到了多少个 Deployment、多少个 Pod 等。
查看具体的备份日志(排查是否有 Error):
velero backup logs test-backup
下载备份文件看看(硬核验证):
- 去你的 MinIO 控制台 (
http://<IP>:9001)。 - 进入
velerobackup存储桶。 - 你会看到
backups/test-backup/目录。 - 下载那个
test-backup.tar.gz文件并解压。 - 你会发现里面全是 JSON 文件,对应你集群里的每一个资源对象。
总结建议
如果你只是想备份 YAML 配置(GitOps 风格),目前的备份是够用的。
如果你想备份 数据库里的实际数据,你需要:
- 在
velero install时启用--use-node-agent。 - 在备份命令中指定对特定 PVC 使用文件系统备份,或者给 Pod 打注解
backup.velero.io/backup-volumes=my-volume-name。
测试还原
先删掉test ns:kubectl delete ns test
velero还原,仅还原test ns:
velero restore create test-restore --from-backup test-backup --include-namespaces test
查看恢复状态:
velero restore get
kubectl get restore -n velero test-restore
kubectl get restore -n velero test-restore -oyaml
Velero 企业实践
周期性备份任务
Velero支持定期备份集群,建议为每个集群创建一个每天备份的任务:
velero schedule create daily-cluster-backup \
--schedule="0 13 * * *" \
--include-namespaces="*" \
--exclude-namespaces="velero,kube-system" \
--ttl=720h0m0s
参数说明:
-
--schedule:备份周期,分时日月周,默认为UTC时钟 -
--include-namespaces:需要备份的Namespace,*为所有,逗号分割 -
--exclude-namespaces:需要排除的Namespace,逗号分割,建议排除kube-system和velero -
--ttl:备份文件保留的时间,建议保留30天以上
查看备份任务:
velero schedule get
暂停备份任务:
velero schedule pause daily-cluster-backup
恢复备份任务:
velero schedule unpause daily-cluster-backup
基于schedule立即手动创建备份任务:
velero backup create --from-schedule daily-cluster-backup
查看备份:
velero backup get
1. 为什么要排除 velero 命名空间?
关键词:避免“死循环”和“自我覆盖”
-
鸡生蛋问题:
当你的集群崩溃需要灾难恢复时,你做的第一件事通常是重新安装 Velero。
既然你已经安装了一个全新的 Velero 来执行还原操作,如果你从备份里强行还原旧的velero命名空间,就会把当前正在干活的 Velero 覆盖掉或搞挂。这就像是在做手术的时候,突然把医生的脑子换掉了。 -
数据并不在 Pod 里:
Velero 的核心数据(备份记录、元数据)其实都保存在 MinIO(对象存储)里。只要你重新安装 Velero 并连上同一个 Bucket,历史备份记录会自动同步回来。因此,备份 Velero 自身的 Pod 和 Deployment 是多余且危险的。
2. 为什么要排除 kube-system 命名空间?
关键词:避免“环境冲突”和“破坏基础设施”
-
每个集群的“指纹”不同:
kube-system里运行着 Kubernetes 的核心组件(如 CoreDNS, kube-proxy, CNI 网络插件等)。这些组件的配置通常与当前集群的具体环境(IP地址、节点信息、网络网段)深度绑定。
如果你把旧集群(或备份时的状态)的kube-system还原到新集群(或升级后的集群)里,极大概率会导致网络瘫痪、DNS 解析失效,甚至集群彻底不可用。 -
ETCD 才是正解:
如果你真的想备份集群的核心状态(Node 信息、集群证书等),应该使用 ETCD 备份,而不是用 Velero。
Velero 的设计定位是备份 “用户的工作负载”(你的应用、Service、PVC),而不是备份 “Kubernetes 自身”。
保留 NodePort 端口号
Velero恢复数据时,Service的NodePort端口会重新分配,如果用到了Service的NodePort会造成一些故障,此时可以使用--preserve-nodeports参数保留原端口(生产环境建议每次还原都使用该端口)。
也有可能要还原的集群中已经有占用原来NodePort的服务了,这时还原的时候会提示端口号冲突。建议先把新集群的这个服务的端口号改了,还原过去之后再交换。
velero也支持指定参数但是要写很多正则表达式非常麻烦。
查看当前NodePort:
kubectl get svc -n test
创建备份:
velero backup create test-backup2
删除数据(也可以只删除Service资源):
kubectl delete ns test
还原数据:
velero restore create test-restore-with-nodeport \
--from-backup test-backup2 \
--include-namespaces test \
--preserve-nodeports
查看还原的数据:
kubectl get svc -n test
误删资源恢复
假如K8s的资源被误删除,也可以通过Velero进行恢复。
模拟删除deployment:
kubectl delete deploy -n test --all
使用Velero还原deployment(建议最小化执行):
velero restore create test-ns-restore-deployments \
--from-backup test-backup \
--include-namespaces=test \
--include-resources=deployments \
--preserve-nodeports
注意:无法指定还原特定资源,比如无法指定还原某个特定的deployment,只支持还原所有的deployment。但是集群中已经存在的资源是不会受到影响的。
查看还原的数据:
kubectl get deploy -n test
重大上线变更提前备份
如果要对一个系统进行非常大的变更,建议在变更之前提前对某个系统进行全量备份,假设变更失败,可以一键回滚资源。
假如test项目今天需要进行变更上线,可以提前对test项目进行备份,命名方式建议 nsName1-nsName2-ns-backup:
velero backup create test-ns-backup --include-namespaces=test
查看备份:
velero backup get test-ns-backup
模拟变更服务,假设变更出错:
# 修改镜像为不存在的版本
kubectl edit deploy -n test krm
# image: registry.cn-beijing.aliyuncs.com/krm:xxx
# 删除服务
kubectl delete svc -n test --all
还原数据:
velero restore create test-ns-restore \
--from-backup test-ns-backup \
--include-namespaces=test \
--preserve-nodeports
查看还原信息:
velero restore get
此时可能有两个警告,可以通过describe查看详情:
velero restore describe test-ns-restore
输出示例:
Namespaces:
test: could not restore, Pod:krm-978cdf5dd-5nzkr already exists.
Warning: the in-cluster version is different than the backed-up version
could not restore, Deployment:krm already exists.
Warning: the in-cluster version is different than the backed-up version
此时Service已还原,但是Deployment并未被Velero恢复(Velero默认不还原已经存在的数据):
kubectl get svc -n test
kubectl get po -n test
为了还原已存在的数据,可以使用 --existing-resource-policy=update:
velero restore create test-ns-restore-update \
--from-backup test-ns-backup \
--include-namespaces=test \
--preserve-nodeports \
--existing-resource-policy=update
查看还原的数据:
kubectl get po -n test
kubectl get svc -n test
多集群备份
针对不同的集群备份时,不建议备份到同一个位置,可以采用如下方式进行隔离:
- 不同的Minio
- 同一个Minio,不同的Bucket 【更推荐】
- 同一个Bucket,不同的目录
假设对其它集群备份,可以参考如下步骤。
上传velero客户端(velero指定其它集群的Kubeconfig也可以):
# 上传离线包并解压
ls
# velero-v1.17.1-linux-amd64.tar.gz
tar xf velero-v1.17.1-linux-amd64.tar.gz
mv velero-v1.17.1-linux-amd64/velero /usr/local/bin/
chmod +x /usr/local/bin/velero
velero version
配置对象存储密码文件:
cat > user-minio << EOF
[default]
aws_access_key_id=user
aws_secret_access_key=password
EOF
创建新的Bucket(在Minio中创建)
安装Velero服务端:
velero install \
--provider aws \
--plugins velero/velero-plugin-for-aws:v1.13.1 \
--image velero/velero:v1.17.1 \
--bucket velerobackup-nj \
--prefix k8s-nj \
--secret-file ./user-minio \
--use-volume-snapshots=false \
--backup-location-config region=minio,s3ForcePathStyle="true",s3Url=http://172.16.35.120:9000 \
--namespace velero
如果是同一个桶,也可以用
--prefix指定不同的目录。prefix的值会在桶里创建一个顶级目录,在里面再创建backup子目录存放备份数据
检查后端存储是否正常:
kubectl get BackupStorageLocation -n velero
跨集群恢复/数据迁移
Velero支持把数据恢复到不同的集群,通常用于创建新环境、迁移和数据恢复,只需要能看到备份文件即可。
备份数据共享的方式一般有两个:
- 多集群备份至同一个位置,即同一个bucket的同一个目录(不推荐)
- 手动处理备份文件
接下来演示把bj集群的备份数据恢复到nj集群。
在源集群(bj)创建备份:
velero backup create demo-backup
复制备份文件到目标集群存储:
接下来只需要把bj的备份文件复制一份到nj的备份目录即可。可以使用mc命令,或者通过minio页面下载再上传均可。
本次采用mc命令复制数据:
docker ps
# 找到minio容器
docker exec -ti <minio容器ID> bash
通过mc命令查看备份文件:
mc ls local/
mc ls local/velerobackup/backups/demo-backup
拷贝数据:
把一个集群的bucket中的backups目录中的某个备份的子目录,拷贝到另一个集群的bucket中的backups目录中。(子目录会自动创建)
mc cp -r local/velerobackup/backups/demo-backup local/velerobackup-nj/k8s-nj/backups/
查看数据:
mc ls local/velerobackup-nj/k8s-nj/backups/
数据复制后,velero会读取该备份文件,并且会生成对应的Backup资源:
在目标集群(nj)查看备份:
velero backup get
直接还原:
velero restore create demo-restore-with-nodeport \
--from-backup demo-backup \
--include-namespaces demo \
--preserve-nodeports
查看还原状态:
velero restore get
Velero 卸载
velero uninstall -n velero --force