简介:cinderback 是一份面向 OpenStack 运维与云平台开发者的 Python 脚本工具,用于简化 Cinder 卷备份与还原工作流,并针对 Cinder Backup Service 的现有局限提供变通方案。它支持按租户或全部租户批量备份、还原卷,管理员可在完成备份后对属主租户隐藏备份,也能灵活恢复并控制原始租户的可见性;同时提供备份轮换、按原始卷 ID 或备份 ID 还原、保留卷名称与描述、借助临时快照处理使用中卷,以及自动备份元数据的一键导出与导入。资源包共 4 个文件,以 py 主脚本为核心,辅以 requirements.txt 依赖清单、README.md 说明文档和 .gitignore 忽略配置,整体约 11KB,轻量易读。运行仅需 Python 2.7 与 cinderclient v1.1.1 及以上版本,旧版本将缺失部分依赖多租户选项的功能。目前已有 312 人学习,适合需要参考备份脚本实现或排查 Cinder 备份限制的读者。
1. 从一次 Cinder 卷误删说起:cinderback 到底解决什么问题
凌晨两点,一个刚上线的业务集群报障:某台虚机挂载的数据盘空了。排查下来不是存储后端故障,而是有人执行清理脚本时把openstack volume delete打到了生产卷上。Cinder 默认只保证块设备的高可用,不保证你误删之后还能找回来——卷一旦被删,元数据进了数据库的删除队列,后端存储上的实际数据很快被回收,这时候再想恢复,基本只能靠备份。
问题就在这儿:OpenStack 原生给了cinder backup-create,也给了快照,但真到生产环境,多数团队的备份是「想起来跑一次」的状态。没有统一入口、没有保留策略、没有失败重试,备份文件散落在各个后端,恢复的时候连哪个卷对应哪个备份都要翻数据库。cinderback 这个方向要解决的,就是把 Cinder 备份这件事从「手动敲命令」变成「脚本化、可调度、可校验」的一套助手工具。它适合正在用 OpenStack(尤其是 kolla 部署的集群)做私有云、又不想上重型备份平台(比如直接堆 Velero、Commvault)的运维和平台工程师。读完你能拿到一套可复现的备份脚本骨架、参数怎么设、以及几个我踩过的坑。
2. cinderback 的定位与 Cinder 备份机制:先搞懂 backup 和 snapshot 的区别
2.1 为什么不能只靠 snapshot 当备份
很多人第一反应是「我有快照啊,要备份脚本干嘛」。这是最常见的认知翻车点。Cinder 的 snapshot 和 backup 在实现层面完全是两回事:
- snapshot依赖卷所在的后端驱动。它记录的是「某个时间点卷的状态」,但底层往往还是同一份存储。如果后端存储池损坏、或者卷和快照在同一个故障域,快照跟着一起没。而且 snapshot 通常不能跨后端迁移,删了源卷,快照的可用性也要看驱动实现。
- backup是把卷数据真正读出来,写到另一个位置——可以是 Cinder 的 backup 后端(Swift、Ceph、NFS 等)。它是独立的一份数据副本,能跨后端恢复,也能恢复到新卷。
所以 cinderback 这类脚本助手的核心价值,是围绕cinder backup这条链路做工程化,而不是去包装 snapshot。选型上先明确:你要的是「快速回滚」(snapshot 够用)还是「灾难恢复」(必须 backup)。生产环境我一般两个都留,但备份脚本只管 backup。
2.2 Cinder backup 的三种模式与增量逻辑
cinder backup-create支持几种模式,直接决定脚本怎么写、存储怎么规划:
| 模式 | 参数 | 数据量 | 适用场景 |
|---|---|---|---|
| 全量 | 默认 | 整个卷 | 首次备份、关键卷 |
| 增量 | --incremental | 仅变化块 | 频繁备份、大卷 |
| 快照式 | --snapshot | 基于临时快照 | 减少对在线业务 IO 影响 |
增量备份依赖上一次备份作为父备份(parent),所以脚本里必须维护「卷 → 备份链」的映射关系。如果你把父备份删了,后面的增量就断了,恢复会失败。这是脚本设计里最容易忽略的一点:保留策略不能简单按时间删,要按备份链删。
底层上,Cinder backup 服务(cinder-backup)通过驱动把卷数据搬运到 backup 后端。用 Ceph 做后端时,走的是 rbd 的 export/diff;用 Swift 时是分块上传对象。理解这一点,你才知道为什么备份速度受 backup 节点网络和 backup 后端吞吐双重制约。
2.3 用一条命令看清备份链路是否健康
动手写脚本前,先确认你的集群 backup 服务是活的。这是所有后续操作的前提:
# 确认 cinder-backup 服务在线,且 backup 后端已配置 openstack volume service list | grep cinder-backup # 查看当前 backup 后端能力(是否支持增量等) cinder service-list --binary cinder-backup # 列出已有备份,确认能正常读到 openstack volume backup list --all-projects --long逻辑说明:第一条命令确认服务注册状态,如果State不是up,后面所有backup-create都会卡在creating。第二条看 backup 节点的能力上报。第三条验证 API 和数据库链路通畅。参数上--all-projects在管理员视角下必须加,否则只能看到自己项目的备份,做全局备份脚本时会漏卷。
提示:如果
cinder-backup服务显示down,先别急着写脚本,去 backup 节点看cinder-backup日志,八成是 backup 后端(比如 Ceph 池或 Swift 容器)连不上。
3. 写一个能用的 cinderback 备份脚本:从取卷到落备份
3.1 脚本骨架:遍历卷、过滤、逐个备份
一个能上生产的备份脚本,结构应该是「取卷列表 → 过滤规则 → 逐个备份 → 记录结果」。下面是我常用的骨架,用 Python 调 OpenStack SDK,比纯 shell 解析输出稳得多:
import openstack import logging from datetime import datetime logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") # 连接集群,认证信息从 clouds.yaml 或环境变量读取 conn = openstack.connect(cloud="mycloud") def should_backup(volume): # 过滤:跳过 bootable 系统盘(按需)、跳过已挂载到临时测试项目的卷 if volume.name and volume.name.startswith("ephemeral-"): return False # 只备份大于 1G 的卷,避免备份一堆空盘 if volume.size < 1: return False return True def backup_volume(volume, incremental=True): name = f"bk-{volume.name}-{datetime.now():%Y%m%d%H%M}" try: bk = conn.block_storage.create_backup( volume_id=volume.id, name=name, incremental=incremental, force=True, # 卷处于 in-use 时也允许备份 container="cinder-backup" # 指定 backup 后端容器/池 ) logging.info("backup started: %s -> %s", volume.id, bk.id) return bk except Exception as e: logging.error("backup failed for %s: %s", volume.id, e) return None for vol in conn.block_storage.volumes(): if should_backup(vol): backup_volume(vol)逻辑说明:create_backup的force=True是关键参数——不加它,卷处于in-use(被虚机挂载)状态时备份会直接报错,而生产卷几乎都是 in-use。incremental=True让第二次之后的备份只传变化块,但前提是同一卷已有全量备份。container参数对应 backup 后端的容器名,Ceph 后端下通常填池名,Swift 下填容器名,填错会报Backup driver not found。
参数上还要注意name的命名规范:把卷名和时间戳拼进去,恢复时一眼能认出。别用 UUID 当名字,翻起来要命。
3.2 调度与并发:别让备份把生产 IO 打满
脚本能跑通只是第一步,真正上线要考虑调度和并发。我一般用 systemd timer 或 cron 触发,但绝不允许所有卷同时备份。原因很简单:backup 是读操作,会占用后端存储的 IO 带宽,几十个卷并发备份,业务侧延迟立刻飙升。
控制并发的做法是加一个信号量或分批:
# 用 flock 保证同一时间只有一个备份脚本实例在跑 flock -n /var/lock/cinderback.lock /usr/bin/python3 /opt/cinderback/backup.py # 或者脚本内部按批次 sleep,每批 5 个卷逻辑说明:flock -n的-n表示拿不到锁就立即退出,避免任务堆积。如果上一轮备份还没跑完,这一轮直接跳过,比排队更安全。脚本内部再配合time.sleep()做批次间隔,给存储留喘息时间。
调度频率上,我的经验是:核心业务卷每天一次全量 + 每 4 小时一次增量;普通卷每天一次增量、每周一次全量。这个节奏不是拍脑袋,是权衡了 RPO(恢复点目标)和存储成本。增量链太长会拖慢恢复速度,所以每周做一次全量「重置」备份链。
3.3 备份结果校验:别等恢复时才发现备份是坏的
备份脚本最坑的地方是「看起来成功了,其实不能用」。backup-create返回成功只代表任务提交成功,不代表数据完整。必须做校验:
# 检查备份状态,restoring 之外的异常状态要告警 openstack volume backup list --long -f value -c ID -c Status -c Size # 抽查:把最近一个备份恢复到临时卷,验证可读 openstack volume backup restore <backup-id>逻辑说明:备份状态正常应该是available。如果长期停在creating,多半是 backup 服务卡住或后端写不进去。定期做恢复演练是唯一可靠的验证手段——我一般每月抽一个非核心卷做一次真实恢复,确认数据能挂载、能读。这一步很多人省掉,结果真出事时才发现备份链断了。
注意:恢复演练要恢复到独立的临时卷,别覆盖生产卷。恢复操作本身也会占用后端资源,安排在业务低峰期。
4. 避坑与排查:cinderback 脚本上线后最容易翻车的 5 个点
4.1 备份一直卡在 creating,日志报 backend 超时
现象:openstack volume backup list里备份状态长时间creating,最后变error。原因:backup 后端(Ceph/Swift/NFS)容量满、网络不通,或cinder-backup服务与后端认证失败。解决:先看 backup 节点/var/log/cinder/cinder-backup.log,搜ERROR。Ceph 后端重点查池配额rbd du,Swift 后端查容器配额。认证问题多半是cinder.conf里 backup 后端的账号密钥过期。
4.2 增量备份报「parent backup not found」
现象:执行增量备份时报错,提示找不到父备份。原因:上一次的父备份被保留策略删掉了,或者父备份本身是error状态。解决:脚本里维护备份链映射,删除时从最老的增量往全量方向删,绝不先删全量。更稳妥的做法是保留策略按「链」为单位,一条链要么整体保留,要么整体清理。
4.3 卷 in-use 时备份直接失败
现象:对挂载中的卷执行备份,报Volume is in use。原因:没加force参数,或者加了但后端驱动不支持在线备份。解决:确认force=True;如果驱动仍不支持,只能先对卷打快照再备份快照,或者接受短暂停业务。Ceph 后端一般支持在线备份,NFS 后端要看版本。
4.4 备份把存储 IO 打满,业务抖动
现象:备份任务一跑,虚机磁盘延迟从几毫秒涨到几百毫秒。原因:并发备份太多,或备份时段撞上业务高峰。解决:用 flock 限制单实例,脚本内分批 + sleep,把备份窗口挪到凌晨。Ceph 后端还可以给 backup 流量设 QoS 限速。
4.5 恢复时发现备份大小对不上
现象:恢复出来的卷比原卷小,或数据缺块。原因:增量链中间有断裂,或备份时卷正在被大量写入导致一致性差。解决:恢复前先openstack volume backup show看size和链关系;对一致性要求高的卷,备份前先fsfreeze冻结文件系统,或对数据库类卷用应用层一致性备份。
5. 把 cinderback 做成可运维的备份体系:保留策略与恢复演练
脚本能跑、坑也避了,最后一步是让它「可持续」。我见过太多团队备份脚本写完就扔那,半年后没人敢动,因为不知道哪条链能删、哪条不能。这里给两个具体做法。
保留策略用「代数」而不是「天数」。按时间删备份在增量场景下必然出事。我的做法是给每条备份链打标签(用 backup 的metadata),记录chain_id和generation。保留规则是:保留最近 7 代增量 + 每周一个全量锚点,超过的按链整体清理。这样恢复时永远有一条完整链可用。
# 给备份打链标签,方便后续按链管理 conn.block_storage.create_backup( volume_id=vol.id, name=name, incremental=True, metadata={"chain_id": chain_id, "generation": str(gen)} )逻辑说明:metadata是 Cinder backup 自带的键值对字段,不占额外存储,查询时用openstack volume backup set或 API 过滤。有了chain_id,清理脚本就能按链分组,避免误删父备份。
恢复演练要自动化。手动演练坚持不了几个月。我一般写一个restore_drill.py,每周随机挑一个卷,把它的最新备份恢复成临时卷,挂到一个测试虚机上跑fsck或读几个关键文件,然后删掉临时卷。整个过程无人值守,结果写进监控。这样备份到底能不能用,是系统在告诉你,而不是靠人猜。
最后一个习惯:备份脚本的每一次变更都要在测试集群先跑一遍完整「备份 → 恢复」闭环,再上生产。我有次改了个过滤条件,把系统盘也纳入了备份,结果备份量翻了三倍,存储池差点写满——血泪经验就是,备份脚本的改动比业务代码更该谨慎,因为它动的是数据安全的底线。希望帮到你。
本文还有配套的精品资源,点击获取