找回密码
 立即注册
首页 业界区 业界 OpenStack Nova Scheduler 计算节点选择机制

OpenStack Nova Scheduler 计算节点选择机制

铜坠匍 2025-10-1 17:30:18
Nova Scheduler 的核心任务是解决“虚拟机实例在哪个计算节点上启动”的问题,它根据用户通过 flavor 提出的资源需求(如 CPU、内存、磁盘)来做出决策。其默认的调度器是 Filter Scheduler,工作流程主要分为过滤 (Filtering) 和称重 (Weighting) 两个阶段。
1、整体流程

1.1 RPC 入口与协调器

scheduler/manager.py,这是调度流程的起点和总指挥。
  1. # scheduler/manager.py
  2. class SchedulerManager(manager.Manager):
  3.     """Scheduler manager."""
  4.     def __init__(self, *args, **kwargs):
  5.         super(SchedulerManager, self).__init__(service_name='scheduler', *args, **kwargs)
  6.         self.client = scheduler_client.SchedulerClient() # 初始化客户端,用于调用Placement
  7.         self.host_manager = host_manager.HostManager()   # 初始化主机管理器
  8.     @messaging.expected_exceptions(exception.NoValidHost)
  9.     def select_destinations(self, context, request_spec, filter_properties,
  10.                            spec_obj=None, ...):
  11.         """Select target hosts for instances."""
  12.         # ... 参数检查和准备 ...
  13.         # 1. 通过Client查询Placement API,获取分配候选(alloc_reqs)和提供商摘要(provider_summaries)
  14.         alloc_reqs, provider_summaries = self.client.get_allocation_candidates(
  15.             context, spec_obj)
  16.         # 2. 如果没有候选,直接抛出异常,避免后续无用功
  17.         if not alloc_reqs:
  18.             raise exception.NoValidHost(reason="")
  19.         # 3. 调用HostManager,根据Placement返回的信息更新主机状态
  20.         host_states = self.host_manager.get_host_states_from_provider_summaries(
  21.             context, provider_summaries, ...)
  22.         # 4. 调用HostManager进行过滤和称重,这是核心决策逻辑
  23.         selections = self.host_manager.select_destinations(
  24.             context, spec_obj, host_states, ...)
  25.         # 5. 通过Client,向Placement API申领资源
  26.         self.client.claim_resources(context, spec_obj, selections, alloc_reqs)
  27.         return selections
复制代码
1.2 主机状态管理与策略执行核心

scheduler/host_manager.py,这是调度逻辑的真正核心,负责管理主机状态并执行过滤和称重。
  1. # scheduler/host_manager.py
  2. class HostManager(object):
  3.     """Manage HostStates and implements scheduling logic."""
  4.     def __init__(self):
  5.         self.filter_handler = filters.HostFilterHandler()    # 过滤器加载器
  6.         self.weight_handler = weights.HostWeightHandler()    # 称重器加载器
  7.         self.filter_classes = self.filter_handler.get_matching_classes(
  8.             CONF.scheduler_default_filters)                  # 加载配置的过滤器类
  9.         self.weight_classes = self.weight_handler.get_matching_classes(
  10.             CONF.scheduler_weight_classes)                   # 加载配置的称重器类
  11.     def get_filtered_hosts(self, host_states, spec_obj, ...):
  12.         """Filter hosts based on specified filters."""
  13.         filtered_hosts = []
  14.         for host_state in host_states:
  15.             # 对每个主机,按顺序执行所有过滤器
  16.             if self.host_passes(host_state, spec_obj):
  17.                 filtered_hosts.append(host_state)
  18.         return filtered_hosts
  19.     def host_passes(self, host_state, spec_obj):
  20.         """Check if a host passes all filters."""
  21.         for filter_cls in self.filter_classes:
  22.             filter_obj = filter_cls()
  23.             # 如果任何一个过滤器不通过,立即返回False
  24.             if not filter_obj.host_passes(host_state, spec_obj):
  25.                 LOG.debug("Host %(host)s failed filter %(filter)s",
  26.                           {'host': host_state.host, 'filter': filter_cls.__name__})
  27.                 return False
  28.         return True # 全部通过才返回True
  29.     def get_weighed_hosts(self, hosts, spec_obj, ...):
  30.         """Weigh the hosts based on specified weighers."""
  31.         weighed_hosts = []
  32.         for host in hosts:
  33.             weight = 0
  34.             for weigher_cls in self.weight_classes:
  35.                 weigher_obj = weigher_cls()
  36.                 # 计算每个称重器的权重并乘以乘数,然后累加
  37.                 weight += (weigher_obj._weigh_object(host, spec_obj) *
  38.                            weigher_obj.weight_multiplier)
  39.             weighed_hosts.append(weights.WeighedHost(host, weight))
  40.         # 根据最终权重进行排序
  41.         weighed_hosts.sort(key=lambda x: x.weight, reverse=True)
  42.         return weighed_hosts
  43.     def select_destinations(self, context, spec_obj, host_states, ...):
  44.         """The main scheduling process."""
  45.         # 调用上述方法完成调度
  46.         filtered_hosts = self.get_filtered_hosts(host_states, spec_obj)
  47.         if not filtered_hosts:
  48.             raise exception.NoValidHost(reason="")
  49.         weighed_hosts = self.get_weighed_hosts(filtered_hosts, spec_obj)
  50.         # 选择最优主机
  51.         return [weighed_hosts[0]]
复制代码
1.3 过滤器示例
  1. # scheduler/filters/ram_filter.py
  2. class RamFilter(filters.BaseHostFilter):
  3.     """Filter out hosts with insufficient RAM."""
  4.     def host_passes(self, host_state, spec_obj):
  5.         """Return True if host has sufficient RAM."""
  6.         # 从请求规格中获取所需内存
  7.         requested_ram = spec_obj.memory_mb
  8.         # 从主机状态中获取可用内存(此值已考虑 overcommit ratio)
  9.         free_ram_mb = host_state.free_ram_mb
  10.         # 核心逻辑:可用内存 >= 请求内存 ? 通过 : 不通过
  11.         return free_ram_mb >= requested_ram
复制代码
1.4 权重器示例
  1. # scheduler/weights/ram.py
  2. class RamWeigher(weights.BaseHostWeigher):
  3.     # 定义权重乘数的配置名称,在nova.conf中设置
  4.     weight_multiplier = 'ram_weight_multiplier'
  5.     def _weigh_object(self, host_state, spec_obj):
  6.         """Calculate weight based on free RAM."""
  7.         # 核心逻辑:返回节点的可用内存作为基础权重值
  8.         # 最终权重 = free_ram_mb * ram_weight_multiplier
  9.         # 如果乘数为正,则空闲内存越多,权重越高,越优先(分散)
  10.         # 如果乘数为负,则空闲内存越少,权重越高,越优先(堆叠)
  11.         return host_state.free_ram_mb
复制代码
2、过滤器

所有过滤器均继承自 BaseHostFilter,并实现 host_passes 方法(判断节点是否符合条件)。调度时,过滤器按 enabled_filters 配置顺序依次执行,所有过滤器通过的节点才会进入权重计算阶段。
2.1 配置

过滤器按nova.conf中 enabled_filters 配置顺序依次执行
  1. [scheduler]
  2. enabled_filters = ComputeFilter,RamFilter,CoreFilter,DiskFilter,AvailabilityZoneFilter,ServerGroupAntiAffinityFilter
  3. [compute]
  4. cpu_allocation_ratio = 16.0  # CPU 过量使用比例,默认16.0
  5. ram_allocation_ratio = 1.5  # 内存过量使用比例,默认1.5
  6. disk_allocation_ratio = 1.0  # 磁盘过量使用比例,默认1.0
复制代码
2.1 RamFilter:内存过滤

过滤内存不足的计算节点(考虑内存过量使用策略),并可调整内存过量比例
  1. # nova/scheduler/filters/ram.py
  2. class RamFilter(filters.BaseHostFilter):
  3.     def host_passes(self, host_state, spec_obj, filter_properties):
  4.         # 获取实例所需内存
  5.         instance_ram = spec_obj.memory_mb
  6.         # 内存过量使用比例(默认1.5,可配置)
  7.         ram_ratio = CONF.compute.ram_allocation_ratio
  8.         # 计算节点实际可用内存 = 总内存 * 过量比例 - 已用内存
  9.         usable_ram = int(host_state.total_ram_mb * ram_ratio) - host_state.used_ram_mb
  10.         return usable_ram >= instance_ram
复制代码
2.2 CoreFilter:CPU 核心过滤

过滤 vCPU 核心不足的计算节点(考虑 CPU 过量使用策略),可调整 CPU 过量比例
  1. # nova/scheduler/filters/core.py
  2. class CoreFilter(filters.BaseHostFilter):
  3.     def host_passes(self, host_state, spec_obj, filter_properties):
  4.         # 获取实例所需 vCPU
  5.         instance_vcpus = spec_obj.vcpus
  6.         # 可用 vCPU = 总 vCPU - 已用 vCPU
  7.         free_vcpus = host_state.vcpus_total - host_state.vcpus_used
  8.         return free_vcpus >= instance_vcpus
复制代码
2.3 ComputeFilter:计算服务状态过滤

过滤 nova-compute 服务未运行的计算节点。
  1. # nova/scheduler/filters/compute.py
  2. class ComputeFilter(filters.BaseHostFilter):
  3.     def host_passes(self, host_state, spec_obj, filter_properties):
  4.         # 检查节点的 nova-compute 服务是否为 "up" 状态
  5.         return host_state.service_up
复制代码
2.4 AvailabilityZoneFilter:可用区过滤

过滤不在指定可用区的计算节点。实例创建时可通过 --availability-zone 指定可用区
  1. # nova/scheduler/filters/availability_zone.py
  2. class AvailabilityZoneFilter(filters.BaseHostFilter):
  3.     def host_passes(self, host_state, spec_obj, filter_properties):
  4.         # 获取实例请求的可用区列表
  5.         requested_azs = spec_obj.availability_zones
  6.         if not requested_azs:
  7.             return True  # 无指定可用区时全通过
  8.         # 检查节点的可用区是否在请求列表中
  9.         return host_state.availability_zone in requested_azs
复制代码
2.5 ServerGroupAffinityFilter:实例组亲和性过滤

确保同一实例组的实例调度到相同节点(亲和性策略),创建实例组时指定 --policy affinity
  1. # nova/scheduler/filters/server_group.py
  2. class ServerGroupAffinityFilter(filters.BaseHostFilter):
  3.     def host_passes(self, host_state, spec_obj, filter_properties):
  4.         group = filter_properties.get('group')
  5.         if not group or group.policy != 'affinity':
  6.             return True
  7.         # 检查节点是否已有同组实例
  8.         instances_on_host = self._get_group_instances(host_state, group.id)
  9.         return len(instances_on_host) > 0
复制代码
2.6 ServerGroupAntiAffinityFilter:实例组反亲和性过滤

确保同一实例组的实例调度到不同节点(反亲和性策略)。
创建实例组时指定 --policy anti-affinity
  1. # nova/scheduler/filters/server_group.py
  2. class ServerGroupAntiAffinityFilter(filters.BaseHostFilter):
  3.     def host_passes(self, host_state, spec_obj, filter_properties):
  4.         group = filter_properties.get('group')
  5.         if not group or group.policy != 'anti-affinity':
  6.             return True
  7.         # 检查节点是否已有同组实例
  8.         instances_on_host = self._get_group_instances(host_state, group.id)
  9.         return len(instances_on_host) == 0
复制代码
2.7 DiskFilter:磁盘空间过滤

过滤本地磁盘空间不足的计算节点(考虑磁盘过量使用策略),可调整磁盘过量比例
  1. # nova/scheduler/filters/disk.py
  2. class DiskFilter(filters.BaseHostFilter):
  3.     def host_passes(self, host_state, spec_obj, filter_properties):
  4.         # 获取实例所需磁盘空间
  5.         instance_disk = spec_obj.root_gb + sum(ephemeral_gb for ephemeral_gb in spec_obj.ephemeral_gbs)
  6.         # 磁盘过量使用比例(默认1.0,可配置)
  7.         disk_ratio = CONF.compute.disk_allocation_ratio
  8.         # 计算节点实际可用磁盘 = 总磁盘 * 过量比例 - 已用磁盘
  9.         usable_disk = int(host_state.local_gb * disk_ratio) - host_state.local_gb_used
  10.         return usable_disk >= instance_disk
复制代码
2.8 PciPassthroughFilter:PCI 设备过滤

过滤不支持所需 PCI 设备(如 GPU、FPGA)的计算节点。
启用后需在 nova.conf 中配置 PCI 设备白名单:
  1. [scheduler]
  2. enabled_filters = PciPassthroughFilter,...
  3. [pci]
  4. alias = {
  5.     "name": "nvidia_gpu",
  6.     "product_id": "1eb8",
  7.     "vendor_id": "10de",
  8.     "device_type": "GPU"
  9. }
复制代码
  1. # nova/scheduler/filters/pci_passthrough.py
  2. class PciPassthroughFilter(filters.BaseHostFilter):
  3.     def host_passes(self, host_state, spec_obj, filter_properties):
  4.         # 获取实例的 PCI 设备请求
  5.         pci_requests = spec_obj.pci_requests
  6.         if not pci_requests:
  7.             return True
  8.         # 检查节点是否有满足需求的 PCI 设备
  9.         return host_state.pci_stats.support_requests(pci_requests.requests)
复制代码
3、权重器

对经过过滤器筛选后的 “合格节点” 进行打分排序,得分最高的节点将被优先选择部署实例。权重器通过 “权重因子 + 乘数配置” 实现灵活的调度策略,可根据业务需求(如优先空闲内存、低 CPU 负载)调整节点优先级。
所有权重器均继承自 nova.scheduler.weights.BaseWeigher,需实现 weigh() 方法(计算单项得分)。关键属性:weight_multiplier:权重乘数(默认 1.0),可通过配置调整,乘数越大,该权重器对总得分的影响越强。
3.1 配置示例
  1. [scheduler]
  2. # 1. 启用权重器(顺序不影响得分计算,仅影响代码执行顺序)
  3. enabled_weighters = RAMWeigher,CoreWeigher,LoadWeigher
  4. # 2. 配置权重乘数(根据业务需求调整)
  5. # 内存密集型实例:提高RAMWeigher权重
  6. ram_weight_multiplier = 1.8
  7. # CPU密集型实例:提高CoreWeigher权重
  8. core_weight_multiplier = 1.2
  9. # 平衡负载:适度提高LoadWeigher权重
  10. load_weight_multiplier = 0.8
  11. # 3. 权重计算相关优化(可选)
  12. # 节点状态缓存时间(减少重复计算,默认60秒)
  13. scheduler_cache_expiry = 60
复制代码
3.1 RAMWeigher:内存权重器(默认启用)


  • 按节点可用内存比例打分,可用内存越多,得分越高,优先选择内存充裕的节点。
  • 适用于内存密集型实例(如大数据、缓存服务)。
  1. # nova/scheduler/weights/ram.py
  2. class RAMWeigher(BaseWeigher):
  3.     # 默认权重乘数(可通过配置覆盖)
  4.     weight_multiplier = 1.0
  5.     def weigh(self, host_state, spec_obj, weight_properties):
  6.         """计算内存单项得分:可用内存比例 × 权重乘数"""
  7.         # 总内存为0时返回0(避免除零错误)
  8.         if host_state.total_ram_mb == 0:
  9.             return 0.0
  10.         # 可用内存比例 = 可用内存 / 总内存
  11.         free_ram_ratio = host_state.free_ram_mb / host_state.total_ram_mb
  12.         # 单项得分 = 比例 × 权重乘数
  13.         return free_ram_ratio * self.weight_multiplier
复制代码
3.2 CoreWeigher:CPU 权重器


  • 按节点可用 CPU 核心比例打分,可用 CPU 越多,得分越高,优先选择 CPU 空闲的节点。
  • 适用于 CPU 密集型实例(如计算、渲染服务)。
  1. # nova/scheduler/weights/core.py
  2. class CoreWeigher(BaseWeigher):
  3.     weight_multiplier = 1.0
  4.     def weigh(self, host_state, spec_obj, weight_properties):
  5.         """计算CPU单项得分:可用CPU比例 × 权重乘数"""
  6.         if host_state.vcpus_total == 0:
  7.             return 0.0
  8.         # 可用CPU比例 = (总CPU - 已用CPU)/ 总CPU
  9.         free_core_ratio = (host_state.vcpus_total - host_state.vcpus_used) / host_state.vcpus_total
  10.         return free_core_ratio * self.weight_multiplier
复制代码
3.3 LoadWeigher:负载权重器


  • 按节点整体负载打分(CPU + 内存使用率),负载越低,得分越高,优先选择低负载节点。
  • 适用于追求节点负载均衡的场景,避免单节点过载。
  1. #nova/scheduler/weights/load.py
  2. class LoadWeigher(BaseWeigher):
  3.     weight_multiplier = 1.0
  4.     def weigh(self, host_state, spec_obj, weight_properties):
  5.         """计算负载单项得分:(1 - 平均负载)× 权重乘数"""
  6.         if host_state.vcpus_total == 0 or host_state.total_ram_mb == 0:
  7.             return 0.0
  8.         # CPU使用率 = 已用CPU / 总CPU
  9.         cpu_usage = host_state.vcpus_used / host_state.vcpus_total
  10.         # 内存使用率 = (总内存 - 可用内存)/ 总内存
  11.         mem_usage = (host_state.total_ram_mb - host_state.free_ram_mb) / host_state.total_ram_mb
  12.         # 平均负载 = (CPU使用率 + 内存使用率)/ 2
  13.         avg_load = (cpu_usage + mem_usage) / 2
  14.         # 负载越低,得分越高(1 - 平均负载)
  15.         return (1 - avg_load) * self.weight_multiplier
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册