OpenStack-Pike版本中的资源管理与并发调度

1.资源管理


之前的资源管理


资源以物理机(ComputeNode)为粒度统计和上报,所有的资源都从属于单个ComputeNode。这样对于一些共享的外部资源,例如 EBS、neutron的ip个数等,不能很好的管理和调度。每增加一种资源,就要相应的修改对应的ComputeNode代码进行统计和管理,不利于横向扩展。调度之间存在并发问题,每个调度服务之间的实时资源视图不一致,导致调度的目标节点有延后性,不够准确。

Pike版本的资源管理


Pike版本将资源抽象出来,增加一层资源管理层,并且区分资源提供者ResourceProvider、资源项ResourceClass、资源清单Inventory、资源分配Allocation,它们的对应关系如下:

ResourceProvider资源提供者

资源提供者表明资源的拥有者,单个物理机、EBS集群都是一个ResourceProvider。每个ResourceProvider可以提供数个ResourceClass的资源。例如ComputeNode是一个ResourceProvider,它提供多个ResourceClass,包括VCPU、MEMORY_MB、DISK_GB。EBS集群也是一个ResourceProvider,提供一个ResourceClass叫做Volume。

1
2
3
4
5
6
7
8
class ResourceProvider(API_BASE):
"""Represents a mapping to a providers of resources."""
id = Column(Integer, primary_key=True, nullable=False)
uuid = Column(String(36), nullable=False)
name = Column(Unicode(200), nullable=True)
generation = Column(Integer, default=0) # 用于解决并发
can_host = Column(Integer, default=0) # 当前资源提供者是否可以作为宿主机

ResourceClass资源项

资源项表明当前资源的类型和名称,供用户区分不同的资源。它只表明资源的名称类别,不包含具体的资源数量等信息。每个资源类型包含数量不等的具体资源Inventory,可以由多个ResourceProvider提供。例如,MEMORY_MB这个资源项,每个ComputeNode都提供该项资源。每个MEMORY_MB都有一个对应的Inventory,表明具体的资源数量、超售比、保留值等资源的配置。

1
2
3
4
class ResourceClass(API_BASE):
"""Represents the type of resource for an inventory or allocation."""
id = Column(Integer, primary_key=True, nullable=False)
name = Column(String(255), nullable=False)

Inventory资源详情

资源详情表明ResourceClass中的具体资源和一些配置。例如对于某台ComputeNode作为一个ResourceProvider,提供一项资源MEMORY_MB并且该项资源有一个详细的描述Inventory。

1
2
3
4
5
6
7
8
9
10
11
12
class Inventory(API_BASE):
"""Represents a quantity of available resource."""
id = Column(Integer, primary_key=True, nullable=False)
resource_provider_id = Column(Integer, nullable=False)
resource_class_id = Column(Integer, nullable=False)
total = Column(Integer, nullable=False)
reserved = Column(Integer, nullable=False) # 保留的资源,例如一台ComputeNode保留给系统的内存,不用于分配
min_unit = Column(Integer, nullable=False) # 分配的最小值,例如VCPUS是1, MEMORY_MB是1024等
max_unit = Column(Integer, nullable=False) # 分配的最大值,例如VCPUS是32, MEMORY_MB是128*1024
step_size = Column(Integer, nullable=False) # 分配的最小单位,例如MEMORY_MB是1024,只能按照G来分配,不能分配512MB
allocation_ratio = Column(Float, nullable=False) # 超售比 实际可分配资源=total * allocation_ratio

Allocation资源分配

资源分配表明一个实例需要的一项资源项ResourceClass的具体数值,以及该项资源分配给谁。例如一个云主机需要2GB内存,那么就用一个Allocation表示并单独记录在数据库中,等同于之前版本中的Instance中记录的一项资源的使用(memory_mb_used)。

1
2
3
4
5
6
7
class Allocation(API_BASE):
"""A use of inventory."""
id = Column(Integer, primary_key=True, nullable=False)
resource_provider_id = Column(Integer, nullable=False) # 资源提供者
consumer_id = Column(String(36), nullable=False) # 资源消耗者
resource_class_id = Column(Integer, nullable=False) # 资源名称
used = Column(Integer, nullable=False) # 具体数值

以上是资源管理的具体分工,它们的对应关系如下:

1
2
3
4
5
6
7
8
9
ResourceClass: CPU MEMORY_MB DISK_GB
↓ ↓ ↓
ComputeNode -> ResourceProvider -> Inventory(CPU) Inventory(MEMORY_MB) Inventory(DISK_GB)
↙ ↓ ↘
Instance(2core, 2g, 100disk): Allocation(MEMORY_MB: 2G) ↓ ↘
Instance(4core, 4g, 200disk): Allocation(MEMORY_MB: 4G) ↘
Instance(2core, 4g, 100Disk): Allocation(MEMORY_MB: 4G)
MEMORY_MB: 8G used

这样ComputeNode只是作为所有资源中的一个ResourceProvider,所有的资源提供者都是作为平等的ResourceProvider参与调度和资源管理,并且支持动态添加资源类型。

2.并发调度


之前的并发调度

之前的版本中多个调度节点之间没有同步机制,每个调度节点每次调度时直接从数据库获取资源视图,计算匹配的目标,这样导致各个节点之间资源视图不能及时更新,有延后性。只能通过CompudeNode中ResourceTracker的资源锁和rescheduler来保证资源的最终一致。

Pike并发调度

在新的资源管理下,调度成功后会将消耗的资源记为一个Allocation并写入数据库,在写入数据库时利用’compare and update’策略实现同步,保证只有在有资源的情况下Alloction才更新成功,选择的目标才有效。原理如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
deadlock_retry:
$ID, $GENERATION = SELECT id, generation FROM resource_providers
WHERE ( <QUERY_TO_IDENTIFY_AVAILABLE_INVENTORY> );
BEGIN TRANSACTION;
FOR $RESOURCE_CLASS, $REQUESTED_AMOUNT IN requested_resources:
INSERT INTO allocations (
resource_provider_id,
resource_class_id,
consumer_id,
used
) VALUES (
$RESOURCE_PROVIDER_ID,
$RESOURCE_CLASS,
$INSTANCE_UUID,
$REQUESTED_AMOUNT
);
$ROWS_AFFECTED = UPDATE resource_providers
SET generation = $GENERATION + 1
WHERE generation = $GENERATION;
IF $ROWS_AFFECTED == 0:
ROLLBACK TRANSACTION;
GO TO deadlock_retry;
COMMIT TRANSACTION;

通过事物保证每次Allocation的数据库操作的原子性。获取资源视图和更新资源视图时,通过generation来保证资源一致。总的调度逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
HOST = []
FOR $RESOUCE_CLASS, $REQUEST_ALLOCATION FOR REQUESTED_RESOUCES:
GET $RESOUCES_PROVIDERS, $GENERATION FOR $RESOUCE_CLASS
GET $INVENTORY AND $ALLOCATIONS FOR RESOUCE_CLASS AND RESOUCE_PROVIDER
IF $INVENTORY["total"] - sum($ALLOCATIONS["used"] >= REQUEST_ALLCOATION:
$HOST.append($RESOURCE_PROVIDER)
FILTER $HOSTS
WEIGHT $HOSTS
FOR $HOST IN $HOSTS:
IF (UPDATE ALLOCATION SUCCESS):
SELECTED $HOST; return
else:
ROLLBACK ALLOCATION;
IF NO $HOST SELCETED:
RAISE NoVildHost

(完)