ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

自研OpenStack备份脚本:Cinder卷快照与数据保护实践

自研OpenStack备份脚本:Cinder卷快照与数据保护实践 简介这是一份面向 OpenStack 运维人员的 Cinder 卷备份/恢复辅助脚本基于 Python 编写主要用于解决多租户环境下备份、还原与轮换流程繁琐以及 Cinder Backup Service 能力受限的问题运行环境要求仅需 Python 2.7 与 cinderclient v1.1.1 及以上版本。脚本支持遍历所有租户批量备份/还原也可按原始卷 ID 或备份 ID 精确恢复对使用中的卷会借助临时快照创建临时卷完成一致性备份后自动销毁。它提供的备份轮换、元数据批量导出/导入、备份可见性控制、保留原始卷名称与描述等功能对日常运维和审计都很实用。资源包约 11KB共 4 个文件核心是 cinderback.py 源码另含 requirements.txt 依赖说明、README.md 使用文档和 .gitignore 工程配置结构清晰便于阅读和二次修改。已有 312 人学习浏览适合需要掌握 Cinder 备份自动化思路或维护备份流程的 OpenStack 技术人员参考下载后可直接作为脚本模板按需调整。 干OpenStack运维的早晚都会遇到一个尴尬场景某天你要把一台云主机回滚到昨天的状态结果发现控制台上根本没配定期备份Cinder卷里只有一份装系统时的祖传快照。我写cinderback这个OpenStack Cinder备份脚本助手就是被这种“裸奔”状态逼出来的。E版本时代我用过cinder-backup服务也试过在存储层做快照最后发现对于大多数中小规模私有云来说一套能自动发现卷、打快照、导备份、按策略清理的脚本反而最灵活、最好维护。这篇文章会把cinderback的设计思路、核心实现、关键代码和真实环境里踩过的坑完整捋一遍重点会聊Yoga版本里那个著名的Cinder卷分离失败bug希望给正在搭OpenStack备份体系的同行省点时间。1. 为什么我放弃原生方案自己写备份脚本1.1 原生Cinder备份的“能用”和“不好用”OpenStack官方其实提供了cinder-backup服务底层调用cinder backup-create命令可以把卷通过qemu-img转换成镜像格式再推送到后端存储NFS、Ceph、Swift都支持。这个方案能跑但实际体验分两半小规模、卷数量少的环境下它够用一旦卷多了问题就来了。首先是速度。cinder-backup的备份过程本质是“先打快照再从快照导出数据”卷有多大备份过程就要读多少数据。一个500GB的数据盘即使里面只用了50GB也得老老实实把整块卷读完。其次是粘滞快照的问题。backup-create在导出结束后理论上会自动清理临时快照但我在实际环境里多次遇到快照卡在creating或deleting状态导致卷没办法做后续操作。最要命的是失败重试逻辑很弱一个卷备份失败后如果不手动处理它会一直占用队列。1.2 选型对比脚本方案、原生备份、存储层快照怎么选我做方案对比时列了一张表方案备份粒度恢复速度维护成本失败处理适合规模cinder-backup原生服务卷级中等需要单独部署backup服务易卡死人工介入多小型测试环境存储层快照如Ceph RBD快照卷级快依赖存储平台OpenStack侧不可见相对稳定已有集中存储的场景cinderback脚本方案卷级可选快照导出中等偏慢极低控制节点定时任务即可可定制重试和告警中小规模私有云我最后选择自研脚本核心原因是“可控”。备份策略、保留份数、失败重试、告警通知全部写在代码里出了问题能一眼定位而不是去翻cinder-backup服务那一大坨日志。而且cinderback不需要单独起常驻服务一台控制节点加一条crontab就能跑起来。提示如果你的环境已经上了Ceph且所有云硬盘都是RBD后端存储层快照确实更高效。但很多单位的私有云是异构后端一部分LVM、一部分Ceph这时脚本方案才有通用性。2. cinderback的整体设计与分层思路2.1 备份的最小单元是快照不是卷这是整个脚本设计里最重要的一条原则。直接对Cinder卷做备份会带来两个问题一是备份过程中云主机还在读写磁盘数据不一致备份文件拿出去恢复时大概率出问题二是卷本身有状态属性available、in-use等直接操作卷容易影响业务。cinderback的做法是先调用cinder snapshot-create对目标卷创建快照等快照状态变成available之后再基于这个快照创建备份。快照本质是存储后端的时间点副本能保证数据一致性。对于使用LVM后端的卷快照还依赖LVM的写时复制机制几乎不占用额外性能可以放心跑。2.2 脚本的分层结构cinderback不是一个大而全的Python文件而是拆成四个层次发现层负责从OpenStack环境里捞卷清单按过滤规则筛选出需要备份的卷执行层对选中的卷依次执行打快照、导出、传存储等动作清理层根据保留策略删除过期快照和备份文件同时做垃圾回收通知层把每一步的结果发送到日志、邮件或Webhook便于接入告警平台。分层的好处是各层可以单独调试。我在测试环境里经常只跑发现层看输出结果对不对不用把整个备份流程都跑一遍。2.3 为什么用python-openstacksdk而不是cinder CLI一开始我用的是cinder --os-volume-api-version 3.x list配合shell命令但很快就放弃了。CLI方式在批量场景下问题太多比如每执行一条命令都要重新认证、解析输出文本容易出错、命令版本更新可能导致输出格式变化。用python-openstacksdk则能直接拿到结构化的对象数据分页、过滤、状态轮询都很好处理。from openstack import connection def get_conn(): return connection.Connection( cloudopenstack, region_nameRegionOne, authdict( auth_urlhttp://controller:5000/v3, usernamecinderback, passwordyour_password, project_nameservices, user_domain_iddefault, project_domain_iddefault, ), )上边的代码是初始化OpenStack连接的标准写法用clouds.yaml配合环境变量会更安全。实际部署时我建议建一个只读权限的专用账号给备份脚本用别拿admin到处跑。3. 核心实现与关键代码3.1 卷清单发现与过滤规则备份不能无脑全量做有些临时卷、正在创建的卷、或者已经处于error状态的卷压根不需要处理。cinderback通过conn.block_storage.volumes()拿到全部卷的生成器然后按项目、卷名前缀、以及卷状态三个维度过滤。def list_backup_volumes(conn, exclude_prefixes(temp-, scratch-)): result [] for vol in conn.block_storage.volumes(): if vol.status not in (available, in-use): continue if any(vol.name.startswith(p) for p in exclude_prefixes): continue result.append(vol) return result过滤规则是写在配置文件里的比如只备份project_a和project_b两个项目的卷跳过名字以ephemeral-开头的卷。为什么强调状态过滤因为creating、deleting、error_managing状态下的卷打快照大概率会失败还会在存储后端留下半成品。3.2 快照创建与状态轮询打快照的接口调用很简单复杂的是等待状态。OpenStack的API是异步的创建快照只是提交请求真正完成需要等存储后端处理。cinderback实现了一个带超时的轮询函数每隔10秒查一次快照状态超过300秒还没变available就报错退出。import time from openstack import exceptions def wait_snapshot_available(conn, snapshot_id, timeout300): elapsed 0 while elapsed timeout: snap conn.block_storage.get_snapshot(snapshot_id) if snap.status available: return snap if snap.status error: raise RuntimeError(fsnapshot {snapshot_id} failed) time.sleep(10) elapsed 10 raise TimeoutError(fsnapshot {snapshot_id} timeout)轮询不能写得太快每10秒一次是我实测比较稳的频率。太频繁会给cinder-api和底层存储带来额外压力尤其是后端是NFS这种IO能力一般的情况太快轮询反而容易出现误报。3.3 备份镜像导出与上传快照打好后接下来就是把快照导出成备份文件。推荐的方式是用openstack image create --volume 快照ID把快照注册成临时镜像再把镜像下载到本地最后传到备份存储。有些人会问直接基于卷导出行不行答案是不行因为卷在被云主机挂载时是不能导出为干净镜像的必须先打快照。openstack image create --snapshot snapshot_id --private snapshot_backup_volume_id openstack image save --file /backup/volume_id.raw snapshot_backup_volume_id openstack image delete snapshot_backup_volume_id上边这段命令是展示原理实际cinderback底层用的是SDK调用逻辑一样。下载备份文件这部分没有用增量因为Cinder本身没有公开的增量导出接口相比写复杂的增量逻辑我更倾向于“全量备份保留多版本”的方式——磁盘贵但恢复简单。3.4 保留策略与清理逻辑备份文件只进不出再大的磁盘也会被塞满。cinderback的保留策略支持按天或按数量两种模式我实际用的是“每个卷保留最近7份”。清理逻辑在备份完成后执行遍历备份目录下的文件按卷ID分组删除超出保留份数的旧文件。import os, glob def clean_old_backups(backup_dir, keep_count7): for vol_dir in glob.glob(os.path.join(backup_dir, vol-*)): files sorted(glob.glob(os.path.join(vol_dir, *.raw)), keyos.path.getmtime) while len(files) keep_count: os.remove(files.pop(0))这里有一个容易踩的坑清理旧文件时一定要同步删除对应的临时镜像和快照不然找存储后端的快照残留会越来越多。cinderback在清理本地文件之前会先尝试删除关联的在OpenStack侧注册的临时镜像确保垃圾不会留在两个地方。3.5 配置样例与定时调度cinderback运行不需要常驻服务控制节点上加一条crontab就行。我一般是凌晨1点跑全量备份因为凌晨的业务负载最低备份对生产环境的影响最小。# cinderback.yaml auth: cloud: openstack region_name: RegionOne backup: dir: /data/cinder_backup keep_count: 7 exclude_prefixes: - temp- - scratch- projects: - project_a - project_b schedule: snapshot_timeout: 300 polling_interval: 10crontab配置示例0 1 * * * cd /opt/cinderback python3 main.py --config cinderback.yaml /var/log/cinderback.log 214. 真实环境中的疑难问题与排查记录4.1 Yoga版本Cinder卷分离失败bug的前因后果这个坑印象太深了。OpenStack Yoga版本中Cinder在清理云主机挂载卷的设备映射时偶发出现Volume detach failed的错误。表现是卷在实例里已经被卸载但Cinder侧的状态一直停留在in-use云主机控制台显示卷仍处于“已挂载”状态怎么刷新都一样。这个bug在底层和后端的设备扫描有关。Cinder在分离卷时要调用os-brick去清掉宿主机上的SCSI设备映射Yoga版本在特定内核和multipath组合下会漏掉一部分设备导致Cinder认为卷还没分离。实际影响就是卷无法被其他实例挂载也无法通过API强制分离。我当时排障时试了nova volume-detach强制分离提示成功但状态依然不对。规避方案没有一键修复的办法只能分几步操作。第一在宿主机上手动扫描并清理遗留的SCSI设备映射# 在计算节点执行 iscsiadm -m session -R ls /dev/disk/by-path/ # 清理已失效的dm设备 multipath -ll | grep volume_wwn multipath -f volume_wwn第二清理完成后在控制节点上刷新Cinder的卷状态cinder reset-state --state available volume_id还需要提醒的是如果刚好有备份任务正在对这个卷打快照遇到这个bug会导致快照卡死。cinderback在探测到卷状态异常时会自动跳过并告警不会死循环重试算是提前做了防御。4.2 快照删不掉与备份文件占满磁盘另一个高频问题是快照进入error_deleting状态。如果是LVM后端大概率是快照被某个进程占用或者底层LV的依赖关系没清干净。我的习惯是先用lvs看快照LV是否还在如果确实存在但Cinder删不掉就手动lvremove再在Cinder数据库中把快照记录清理掉。这个过程要谨慎操作前务必确认快照确实没被使用。备份文件占满磁盘这个事我在测试环境发生过一次。原因是保留策略只删了原卷的备份但没处理临时镜像。后来我把清理逻辑升级为“先删OpenStack侧镜像再删本地文件最后尝试删快照”顺序不能反否则文件删了但镜像和快照还挂着磁盘空间并不会真正释放。4.3 控制节点MySQL的增量备份建议Cinder备份只是OpenStack数据安全的一部分控制节点上的MySQL数据库才是元数据的命根子。如果数据库丢了卷和快照的映射关系全乱备份文件拿回来也恢复不了。除了每天全量mysqldump我建议配置binlog做增量备份。MySQL开启binlog[mysqld] server-id1 log-binmysql-bin expire_logs_days7每天全量备份后用mysqlbinlog把当天的binlog增量保存下来mysqlbinlog --start-datetime$(date -d -1 day %F) mysql-bin.* /data/mysql_incremental/$(date %F).sql恢复时的顺序是先恢复最近一次全量备份再依次应用增量SQL文件。这个组合方案我用了很久实测在误删卷或数据库损坏时能恢复到分钟级别。4.4 常见问题速查表现象可能原因处理方法快照创建后一直creating存储后端IO繁忙或超时等待或重启cinder-volume服务卷分离失败状态卡in-useYoga版本设备清理bug手动清理SCSI设备映射reset-state备份文件下载中断网络不稳或镜像服务超时脚本内置断点重试检查glance-api超时配置备份目录被写满保留策略没生效或临时镜像残留检查目录文件数量和清理脚本执行日志备份恢复后云主机启动失败备份时卷里有脏数据确认快照在available后再导出不要对in-use卷强行导出5. 一些实操建议和扩展方向如果准备把cinderback用到生产环境我建议再打磨三个细节。第一备份文件最好推送到独立的备份存储不要放在控制节点的系统盘上否则控制节点磁盘一满整个OpenStack都跟着遭殃。第二告警一定要接入现有的监控渠道不管是Zabbix、Prometheus还是钉钉Webhook备份失败必须在第一时间通知到人不然备份等于没做。第三可以考虑给cinderback加“恢复演练模式”。这个脚本不只做备份还能从备份文件创建临时卷并挂载到一台测试云主机验证备份文件是否完整可用。我一般是每月挑一个备份做一次自动挂载检查虽然会多占点存储空间但能确保关键备份真正可恢复。备份这件事没有验证过的备份都只是心理安慰。本文还有配套的精品资源点击获取
返回列表