ARTICLE DETAIL

资讯详情

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

OpenStack Cinder-scheduler调度机制解析:过滤与权重配置实战

OpenStack Cinder-scheduler调度机制解析:过滤与权重配置实战 OpenStack 里做存储这块的多少都会被 cinder-scheduler 折腾过。尤其你手上管着几十个节点、挂了 LVM 和 Ceph 多种后端的时候新创建的卷到底落在哪个后端、为什么偏偏落在那个磁盘快满的节点上这些问题不把调度逻辑吃透排查起来真的靠猜。这篇我把 cinder-scheduler 的调度机制从头到尾捋一遍从架构位置到 filter 和 weigher 的配合再到实际配置和排错适合刚接触 OpenStack 存储的运维也适合被卷一直 creating搞到头大的老哥。1. 先搞懂 cinder-scheduler 在 Cinder 里的位置1.1 Cinder 三大组件怎么分工Cinder 的逻辑结构核心就三个角色cinder-api、cinder-scheduler、cinder-volume。cinder-api 负责接收用户请求比如创建卷、删除卷、扩展卷它只做参数校验和初步处理不碰底层存储。cinder-volume 才是真正干活的人它运行在存储节点上通过不同的 driver 对接具体后端比如 LVM、Ceph RBD、NFS 这些。而 cinder-scheduler 夹在中间干的是选谁去干活的差事。这个结构跟现实里的项目分配很像。你是一个项目经理手里的开发任务下来了创建卷你不会直接把任务扔给某个开发你要先看一眼团队里有谁cinder-volume 服务列表谁手头不忙、谁会这个技术栈、谁机器还跑得动然后挑一个人把任务派下去。cinder-scheduler 就是那个项目经理只不过它做决策的依据不是人情世故是冷冰冰的容量数据和能力属性。还有一个容易忽略的点cinder-scheduler 本身不做存储操作它选完后是通知 cinder-volume 去创建卷。所以你排障的时候如果发现调度已经成功、但卷还是创建不出来问题往往在 cinder-volume 或者后端存储那边不在调度器这里。这个边界先立起来后面排查思路会清晰很多。1.2 存储后端与卷类型的关系要让调度器有的放矢你得先让 cinder-volume 把你每个后端的情况上报给它。每个 cinder-volume 服务有自己的 host 标识格式是hostbackend_name比如compute01lvm、compute02rbd。这里的 backend_name 对应 cinder.conf 中该 volume 服务的配置组名称比如[lvm]、[rbd]。每个后端在上报能力时会带上卷类型支持列表、总容量、剩余容量、存储协议等一堆属性这些属性就是后面调度器做筛选打分的原料。你可以在配置里用enabled_backends决定这个 cinder-volume 进程管理哪些后端也就是说一个节点上可以同时存在多个后端只是每个后端的驱动配置要单独写。卷类型volume type则是用户侧的概念。用户创建卷时可以不指定类型也可以指定一个卷类型比如 SSD 高性能型、普通机械盘型。卷类型通过 extra specs额外规格与后端的 capabilities 关联起来调度器看到你要求的卷类型就去匹配哪个后端能满足。这一整套逻辑全在 cinder-scheduler 这层实现。2. 调度全流程一次 create volume 请求走的路2.1 从 API 到 Scheduler 的调用链用户执行cinder create --volume-type ssd --size 10 test_vol后请求先到 cinder-api。API 层做鉴权、参数校验、创建数据库记录卷状态为 creating然后通过 RPC 异步通知 cinder-scheduler——注意这里是异步的API 不用等调度结果出来就能返回给客户端。cinder-scheduler 收到待调度卷的任务后开始干活。它会先从数据库中查出当前所有可用的 cinder-volume 服务及其上报的能力快照再用配置好的过滤器和权重器依次处理。整个调度完成后cinder-scheduler 通过 RPC 再次调用选中的 cinder-volume 服务让它在对应后端上真正创建卷。创建成功后再回写数据库把卷状态改成 available。这里有个关键点值得强调cinder-scheduler 对后端容量和能力的判断依据的是后端定期上报的缓存数据不是实时去查的。后端的 cinder-volume 服务会按照report_interval配置的间隔定期上报信息默认好像是 10 秒左右。所以你刚加了一个新后端或者后端存储出了问题调度器那边看到的可能还是几十秒前的旧数据这个时间差在实际运维中会造成一些明明有空间却不选中的假象。2.2 两阶段处理先过滤再打分调度过程分两个阶段过滤filter和打分weigh。两个阶段是串行的。过滤阶段把所有满足基本条件的后端挑出来打分工段再从中挑最优的。打个比方过滤是筛选简历——学历不符的直接淘汰打分是面试评分——剩下的人按能力、经验、匹配度排出先后谁分高谁上岗。过滤阶段用的是scheduler_default_filters配置的过滤器列表。默认一般有 AvailabilityZoneFilter、CapacityFilter、CapabilitiesFilter 几个。这些过滤器依次执行只要有一个把某个 host 淘汰了这 host 就不进入下一轮。所以过滤器的顺序是有讲究的——代价小的、淘汰率高的过滤器放前面可以提前减少后续处理的 host 数量节省性能。打分阶段用的是scheduler_default_weighers配置的权重器列表。每个权重器会给候选 host 打一个分数再乘以该权重器配置的权重multiplier最后所有分数加总得分最高的 host 胜出。注意权重器和权重是两个概念权重器是打分逻辑权重是这个逻辑结果的影响力系数。2.3 源码路径里的关键逻辑如果你要看源码重点看cinder/scheduler/filter_scheduler.py里的_schedule方法。这个方法是调度核心它把上面说的两阶段逻辑串起来先_get_filtered_hosts再_get_weighted_hosts然后调用_set_scheduler_hints之类的方法选出最终 host。还有一个容易踩坑的地方调度发生异常时比如所有候选后端都被过滤掉scheduler 会抛出NoValidHost异常卷状态会保持在 creating并且错误信息会写到卷的fault字段里。这个直接决定了你排障第一步该看什么——先看卷详情的 fault再看 cinder-scheduler 日志双管齐下。3. Filter 与 Weigher 的机制拆解3.1 那几个默认过滤器到底在干什么先看最基础的 AvailabilityZoneFilter。每个后端在 cinder.conf 里可以设置storage_availability_zone默认是 nova 的 availability zone。用户创建卷时也可以指定--availability-zone。调度器会比较请求的可用区和后端的可用区不一致的直接淘汰。这个过滤器很粗暴但也很有效——它保证了卷不会被调度到用户不想去的大区域。CapacityFilter 负责看容量够不够。它会比较请求卷的大小和后端上报的free_capacity_gb。这里有细节后端上报的剩余容量可能是字符串unknown比如某些存储驱动拿不到准确的剩余值。CapacityFilter 遇到 unknown 时默认是放行的它不会因为你报不了容量就把你淘汰掉。反而你如果某个后端没配置好报了个异常的小值那就尴尬了调度器会认为你没空间而不选你。CapabilitiesFilter 是最值得你花时间研究的过滤器因为它就是为卷类型服务的。它会把卷类型的 extra specs 和后端上报的 capabilities 做匹配。举个例子你在卷类型里定义了capabilities:storage_protocol iSCSI后端上报的storage_protocol属性就得恰好是 iSCSI 才不会被淘汰。支持的操作符包括等号、大于、小于、不等、正则等但实际上生产环境里最常用到的还是等号匹配。3.2 权重器从候选者里挑最优过滤器筛完之后可能还有多个后端满足条件。接下来的问题就是选哪个权重器就是为了回答这个问题的。CapacityWeigher 的思路很直接剩余容量占比高的 host 得分高。它的计算方式是看free_capacity_gb / total_capacity_gb这个比例比例越高分越高。这个权重器适合容量紧张的环境能把新卷倾向分配到空间更充裕的后端避免某个后端被塞满而其他后端闲着。AllocatedCapacityWeigher 则从已分配容量的角度反向打分。它看的是后端已经分配出去的容量占总容量的比例分配比例越低得分越高。默认 multiplier 是负数也就是已分配量越多的越不优先。它跟 CapacityWeigher 看着像但维度不同——CapacityWeigher 看的是自由空间AllocatedCapacityWeigher 看的是已分配比例两者可以同时启用来做平衡。生产实践中一个常见组合是同时启用这两个权重器让调度器既照顾剩余空间又避免在已重度分配的后端上继续堆积。但要注意如果你配置了多个权重器最终分数是带系数的加权和系数大小直接决定了哪个维度占主导。3.3 关键配置项与推荐值调度器相关的配置都在cinder.conf的[DEFAULT]段里。常用配置如下[DEFAULT] scheduler_driver cinder.scheduler.filter_scheduler.FilterScheduler scheduler_default_filters AvailabilityZoneFilter,CapacityFilter,CapabilitiesFilter scheduler_default_weighers AllocatedCapacityWeigher,CapacityWeigherscheduler_driver保持默认即可绝大多数场景用 FilterScheduler 就够了。ChanceScheduler 那种随机调度基本是测试环境才会用生产环境不建议。过滤器和权重器列表用逗号分隔顺序敏感。还有一个容易忽略的配置是scheduler_host_subset_size它控制在候选主机中随机取一个子集再打分。默认值是 1也就是全部候选都参与打分如果你设置成大于 1 的值调度器会先从候选里随机挑出一部分再打分这在一定程度上让调度结果不那么必然指向同一个后端多后端场景下能让负载更分散。但这个特性要看场景牺牲了最优性换来了随机性我一般不喜欢开。4. 实战配置多后端场景下的调度策略4.1 一份可用的多后端配置参考下面是一个实际可用的配置片段演示了单节点上同时配置 LVM 和 Ceph RBD 两个后端并且通过卷类型区分调度的场景。[DEFAULT] enabled_backends lvm,ceph default_volume_type lvm_type [lvm] volume_driver cinder.volume.drivers.lvm.LVMVolumeDriver volume_group cinder-volumes volume_backend_name LVM target_protocol iSCSI volume_type lvm_type,ssd_type [ceph] volume_driver cinder.volume.drivers.rbd.RBDDriver rbd_pool cinder-volumes rbd_user cinder rbd_secret_uuid xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx volume_backend_name CEPH volume_type ssd_type这里的关键是volume_type这个参数。它声明了本后端支持的卷类型多个类型用逗号分隔。调度器在过滤阶段会把请求的卷类型和后端的volume_type列表做匹配所以如果你的后端没有声明某个卷类型创建该类型的卷就永远不会落在那个后端上。4.2 卷类型与 extra specs 的关联写法卷类型可以单独创建也可以在创建时直接指定openstack volume type create ssd_type openstack volume type set ssd_type --property capabilities:storage_protocolceph创建完成后用户创建卷时带上--type ssd_typeCapabilitiesFilter 就会检查后端上报的存储协议是否等于 ceph。如果 LVM 后端的协议是 iSCSI它就会被过滤掉最终只调度到 Ceph 后端上。这种方法的好处非常明显你可以后端支持能力做精细化匹配而不仅仅是靠卷类型名字。比如你的 Ceph 后端可以进一步声明支持快照、精简配置等属性用户在卷类型里要求这些能力时调度器会自动排除不支持的后端。4.3 按可用区规划调度范围的写法假设你有两个机房分别对应 availability zone az1 和 az2。你可以给两边的存储服务设置不同的storage_availability_zone然后用户在创建卷时显式指定--availability-zone az1调度器通过 AvailabilityZoneFilter只会从 az1 的后端里选。如果用户不指定可用区卷就会被调度到默认可用区对应的后端。你可以通过default_availability_zone配置来设置默认值。这个字段虽然和在哪个节点上执行 cinder-api 无关但不同的 API 节点如果配置不同的默认可用区也会影响最终的调度范围。多区域部署时这个细节要注意。5. 调度问题排查实录与经验总结5.1 卷一直 creating从哪里下手最典型的故障就是卷创建后一直卡在 creating不急的话等几分钟急的话直接按下面顺序查。第一步看卷详情里的 fault 信息openstack volume show volume_id如果里面有NoValidHost字样说明调度器已经把卷淘汰完了问题出在调度规则不匹配。这时候去查 cinder-scheduler 日志grep NoValidHost /var/log/cinder/cinder-scheduler.log日志里会明确告诉你哪个过滤阶段淘汰了哪些 host、淘汰原因是什么。看到AvailabilityZoneFilter淘汰了所有 host就是可用区不匹配看到CapabilitiesFilter出问题就是卷类型和后端支持类型对不上。如果 fault 信息没有 NoValidHost而是超时或 RPC 异常那说明调度已经成功但卷创建请求没到达 cinder-volume 或者后端创建失败。这时候要转去查 cinder-volume 日志和存储后端状态。5.2 NoValidHost 的常见原因速查我把这些年遇到的 NoValidHost 原因整理成了一张表基本能覆盖大多数情况现象大概率原因排查方向所有后端都被淘汰可用区不匹配检查 volume_type 里的 extra specs 和卷类型声明后端容量报异常CapacityFilter 认为空间不足检查后端 free_capacity_gb 上报值卷类型匹配失败CapabilitiesFilter 属性对不上看 cinder-volume 日志里的 capabilities 上报内容总提示无有效后端enabled_backends 配置有误检查 cinder-volume 进程和 cinder.conf 的 backend 配置组其中最容易忽视的是CapabilitiesFilter的匹配细节。它默认要求请求卷类型中声明的 capability 属性在 host capabilities 里存在且值匹配而不是不存在就忽略。所以如果你在卷类型里写了一个后端完全没有的属性这个后端会被直接淘汰。排障时不要只看类型名是否一致要对比完整属性。排查 capabilities 还可以直接查 cinder-volume 上报的服务能力openstack volume service list输出的 State 列如果显示 up再去看该服务的 capabilities 有没有正常上报。也可以用cinder-manage service list做辅助诊断。正常情况下列表里每个后端都有 heartbeat 信息。5.3 权重器不生效的踩坑记录有段时间我发现新卷总往磁盘余量只有 30% 的后端上跑而同期另一个后端余量有 80% 却一直闲着。我以为是权重器配置写错了查了半天才发现问题出在report_interval和容量上报两条路一个是 free_capacity_gb 上报的值会受后端上其他卷创建、删除影响二是调度器的容量数据有一份缓存副本更新周期比你想象中长。那次之后我的做法是在切换配置或调整后端后手动触发一次容量上报cinder-manage service report_interval 0不过这只是在临时诊断时用生产环境没必要频繁手动刷新。更推荐的排查方法是直接看调度日志里打出的权重分数它会列出每个候选后端在权重器下得分是多少。日志里看到AllocatedCapacityWeigher得分为 0而CapacityWeigher得分正常那就说明后端的 allocated_capacity_gb 上报可能有问题往这个方向查基本一查一个准。5.4 日志里这些关键字要敏感cinder-scheduler 日志里值得特别关注的关键字有Scheduler successfully scheduled、NoValidHost、Filtering、Weighing。看到 successfully scheduled 说明调度正常完成后续问题在别处看到 Filtering 后跟着一长串 host 列表说明进入了过滤阶段Weighing 后跟着的分数表就是各权重器的评分明细。日志级别影响你能否看到这些信息。默认 INFO 级别基本够用但如果你想看每个候选主机被哪个过滤器淘汰的详细信息需要把cinder.conf里[DEFAULT]的log_level调到 DEBUG 级别。生产环境不建议长期开 DEBUG临时排障开个几分钟没问题。最后再分享一个我踩过的坑升级 OpenStack 版本时新版本的调度器默认过滤器列表或者权重器列表可能变化老配置里写死了的过滤器在新版本里行为不同。我遇到过升级后卷全调度到一个后端的情况排查到最后就是过滤器列表兼容性问题。所以升级前最好对比新旧版本的etc/cinder/cinder.conf.sample别盲目沿用老配置。
返回列表