统一限制¶
从 Queens 版本开始,keystone 具有存储和转发称为“限制”的信息的能力。服务可以使用限制来强制 OpenStack 中的资源配额。本节描述了限制的基本概念,服务如何使用这些信息,以及操作员如何使用限制来管理 OpenStack 中的资源配额。
什么是限制?¶
限制是资源管理的一个阈值,有助于控制资源利用率。管理限制的过程允许根据需求的变化重新分配资源。建立限制可能需要的信息包括
project_id
domain_id
API 服务类型(例如,compute、network、object-storage)
一种资源类型(例如,ram_mb、vcpus、security-groups)
一个默认限制
一个项目特定的限制,即资源限制
user_id(可选)
一个区域(可选,取决于服务)
注意
已注册限制的 默认限制 和项目限制的 资源限制 现在限制在 -1 到 2147483647(整数)之间。-1 表示无限制,2147483647 是用户定义限制的最大值。统一限制的 资源类型 的长度现在限制在 1 到 255(字符串)之间。
由于 keystone 是上述列表中几乎所有内容的真实来源,因此限制自然适合作为 keystone 资源。这种设计中存在两种不同的限制资源。第一种是已注册限制,第二种是项目限制。
已注册限制¶
已注册限制为了强制执行跨多租户、分布式系统的配额,实现了两项重要功能。首先,它建立了资源类型并将它们与服务关联。其次,它为所有项目设置了默认资源限制。第一部分将特定的资源类型映射到提供这些资源的服务。例如,已注册限制可以将 vcpus 映射到 compute 服务。第二部分设置了每个项目默认的 20 个 vcpus。这提供了服务提供的任何资源的基本配额执行所需的所有信息。
域限制¶
域限制是与特定域关联的限制,它充当已注册限制的覆盖。与已注册限制类似,域限制需要资源类型和服务。此外,在创建特定于域的覆盖之前,必须存在已注册限制。例如,假设存在 compute 服务提供的 vcpus 的已注册限制。在 compute 服务上创建 cores 的域限制是不可能的。域限制只能覆盖已经注册的限制。从一般意义上讲,已注册限制可能是在部署新服务或云时建立的。域限制被持续用于管理资源分配的流程。
域限制可能会影响域内项目的限制。在选择执行模型时,务必记住这一点,如下面所述。
项目限制¶
项目限制与域限制具有相同的属性,但特定于项目而不是域。在创建特定于项目的覆盖之前,必须注册限制。与域限制一样,相关项目之间的资源流可能因配置的执行模型而异。下面描述的受支持的执行模型描述了限制验证和执行在相关项目和域之间的行为方式。
总之,已注册限制、域限制和项目限制使部署能够默认情况下限制部署中的资源,同时又足够灵活地在项目之间自由调配资源。
限制和使用情况¶
当我们谈论配额系统时,实际上是在谈论两个系统。一个用于设置和维护限制的系统,即理论上的最大使用量,以及一个用于强制执行使用量不超过限制的系统。虽然它们是相关的,但它们是不同的。
到目前为止,我们已经确定 keystone 是维护限制信息的系统。keystone 的责任是确保对限制的任何更改与当前存储在 keystone 中的相关限制保持一致。
各个服务维护和强制执行使用情况。服务在用户请求资源时,会根据当时的当前限制检查执行情况。使用情况反映了分配给消费者的实际资源分配量。
鉴于以上所述,以下是一个可能且合法的流程
用户 Jane 位于项目 Foo 中
项目 Foo 具有默认 CPU 限制 20
用户 Jane 在项目 Foo 中分配了 18 个 CPU
管理员 Kelly 将项目 Foo CPU 限制设置为 10
用户 Jane 无法在项目 Foo 中分配实例资源,直到她(或项目中的其他人)删除至少 9 个 CPU 以低于新的限制
以下是另一种变体
用户 Jane 位于项目 Foo 中
项目 Foo 具有默认 CPU 限制 20
用户 Jane 在项目 Foo 中分配了 20 个 CPU
用户 Jane 尝试创建另一个实例,由于请求会违反基于当前 CPU 限制的使用情况,导致资源请求失败
用户 Jane 请求更多资源
管理员 Kelly 调整 Foo 项目的限制为 30 个 CPU
用户 Jane 重新发送她的实例请求,由于项目 Foo 的使用量低于项目限制 30 个 CPU,因此请求成功
这种行为允许管理员在方便时设置未来的策略,并防止这些项目创建任何超出限制的更多资源。项目成员可以通过降低项目使用量到有余量的地方来自行解决此问题。如果他们不这样做,管理员可以在某个时候更积极地删除资源。
执行模型¶
keystone 中的项目资源可以组织成分层结构,其中项目可以嵌套。因此,资源限制和使用情况应尊重该层次结构(如果存在)。可以考虑不同的情况,在这种情况下,限制或使用情况会假定不同的特征,而与项目结构无关。例如,如果项目对特定资源的的使用量尚未达到限制,子项目是否应该假定这些限制?他们不应该假定这些限制吗?这些有意见的模型被称为执行模型。本节专门用于描述已实现的不同的执行模型。
重要的是要注意,执行必须在整个部署中保持一致。将某些特征分组到一个模型中可以使对行为的引用在整个服务中保持一致。操作员应注意,在执行模型之间切换可能会导致向后不兼容的更改。如果您计划在部署中从一个模型切换到另一个模型,我们建议进行极其仔细的计划和对各种执行模型的理解。
Keystone 暴露一个 GET /limits/model 端点,该端点返回部署选择的执行模型。这使得限制信息可以被发现,并保留了具有不同执行模型的 OpenStack 部署之间的互操作性。
扁平¶
扁平执行忽略项目层次结构的各个方面。每个项目都被视为与其他所有项目的对等项目。父级、同级或子级的限制对特定项目没有影响。这种模型在项目之间行使最强的隔离,因为无论层次结构如何,限制之间都没有假设。通过 API 进行的限制验证将允许可能在其他模型中不被接受的操作。
例如,假设项目 Charlie 是项目 Beta 的子项目,而项目 Beta 是项目 Alpha 的子项目。所有项目都假定通过已注册限制默认限制为 10 个核心。下面的图表使用 limit 和 usage 的简写表示法 l 和 u
![digraph {
orientation = portrait;
node [shape=box]
Alpha [label="Alpha (u=0)"];
Beta [label=" Beta (u=0)"];
Charlie [label="Charlie (u=0)"];
}](../_images/graphviz-d58f69cf7d1773c1846ec360b25b41e5550d7703.png)
由于已注册限制,每个项目可以使用最多 10 个核心,并且没有项目具有覆盖。使用扁平执行,允许 UPDATE LIMIT on Alpha to 20
![digraph {
orientation = portrait;
node [shape=box]
Alpha [label="Alpha (l=20, u=0)", textcolor = "#00af00"];
Beta [label=" Beta (u=0)"];
Charlie [label="Charlie (u=0)"];
}](../_images/graphviz-bbfc19a1e9aaeef71528b1e3a1138f66988a8672.png)
即使 Charlie 是 Beta 和 Alpha 的子项目,也允许 UPDATE LIMIT on Charlie to 30。
![digraph {
orientation = portrait;
node [shape=box]
Alpha [label="Alpha (l=20, u=0)"];
Beta [label=" Beta (u=0)"];
Charlie [label="Charlie (l=30, u=0)", textcolor = "#00af00"];
}](../_images/graphviz-1fa0b187a1de8d141211cce32f5e3750cd15f910.png)
允许使用扁平执行,因为层次结构在限制验证期间不会被考虑在内。子项目可能具有高于父项目的限制。
相反,您可以通过手动调整项目树中的限制来模拟分层执行。例如,我们仍然假设 10 是由现有的已注册限制施加的默认限制
![digraph {
orientation = portrait;
node [shape=box]
Alpha [label="Alpha (u=0)"];
Beta [label=" Beta (u=0)"];
Charlie [label="Charlie (u=0)"];
}](../_images/graphviz-d58f69cf7d1773c1846ec360b25b41e5550d7703.png)
您可以设置项目特定的覆盖 UPDATE LIMIT on Alpha to 30
![digraph {
orientation = portrait;
node [shape=box]
Alpha [label="Alpha (l=30, u=0)", textcolor = "#00af00"];
Beta [label=" Beta (u=0)"];
Charlie [label="Charlie (u=0)"];
}](../_images/graphviz-7fd2d4f0a49185947f2e8f950fbd067e9db04554.png)
接下来,您可以 UPDATE LIMIT on Beta to 20
![digraph {
orientation = portrait;
node [shape=box]
Alpha [label="Alpha (l=30, u=0)"];
Beta [label=" Beta (l=20, u=0)", textcolor = "#00af00"];
Charlie [label="Charlie (u=0)"];
}](../_images/graphviz-2d5478dcb138ee08b5d81e9aebdab67ea0e78f17.png)
从理论上讲,由 Alpha、Beta 和 Charlie 组成的整个项目树限制为 60 个核心。如果您想确保整个层次结构中只使用 30 个核心,您可以 UPDATE LIMIT on Alpha to 0
![digraph {
orientation = portrait;
node [shape=box]
Alpha [label="Alpha (l=0, u=0)", textcolor = "#00af00"];
Beta [label=" Beta (l=20, u=0)"];
Charlie [label="Charlie (u=0)"];
}](../_images/graphviz-2478960a46fd31afb9455eb70753333ea8bd9ed5.png)
如果您
具有大于两个级别的项目层次结构
希望严格控制项目使用情况,并且不希望资源使用量在项目或域之间泄漏
优点¶
允许您建模特定的和严格的限制
适用于任何项目层次结构或深度
仅针对相关项目计算使用情况
缺点¶
不允许资源在层次结构中的项目之间流畅地流动
需要干预和验证才能在项目之间移动资源
不考虑其他项目或域对项目限制进行验证
严格的两级¶
strict_two_level 执行模型假定项目层次结构不超过两个级别。顶层可以由项目或域组成。例如,项目 Alpha 可以在此模型中拥有一个名为 Beta 的子项目。项目 Beta 不能拥有子项目。层次结构限制为两层。Alpha 也可以是一个包含项目 Beta 的域,但 Beta 不能拥有子项目。无论顶层由项目或域组成,层次结构的深度都限制为两层。
资源利用率允许在层次结构中流动,具体取决于限制。此属性比 flat 执行模型更灵活。该模型是严格的,因为操作员可以设置父项目或域上的限制,并且子项目的限制永远不能超过父项目。
例如,假设域 Alpha 包含两个项目,Beta 和 Charlie。项目 Beta 和 Charlie 是同级,因此层次结构保持两层深度。系统管理员将资源限制设置为 Alpha 为 20。项目 Beta 和 Charlie 都可以消耗资源,直到 Alpha、Beta 和 Charlie 的总使用量达到 20。此时,不应再向树中分配更多资源。系统管理员还可以直接在子项目中保留域 Alpha 的资源的一部分。使用前面的示例,项目 Beta 可以具有 12 个资源的限制,隐式地为 Charlie 留下 8 个资源来消耗。
以下图表说明了上述行为,使用了名为 Alpha、Beta、Charlie 和 Delta 的项目。假设相关资源是核心,默认注册限制为 10 个核心。此外,我们假设以下项目层次结构,其中 Alpha 的限制为 20 个核心,其使用量当前为 4
![digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
Alpha -> Charlie;
Alpha [label="Alpha (l=20, u=4)"];
Beta [label="Beta (u=0)"];
Charlie [label="Charlie (u=0)"];
}](../_images/graphviz-30a84f3f3e7137fe0fd7cb998531c913117409db.png)
从技术上讲,Beta 和 Charlie 都可以使用最多 8 个核心
![digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
Alpha -> Charlie;
Alpha [label="Alpha (l=20, u=4)"];
Beta [label="Beta (u=8)", textcolor = "#00af00"];
Charlie [label="Charlie (u=8)", textcolor = "#00af00"];
}](../_images/graphviz-2b0a2b30da83a9ca8fcadc658b1871e634b08df5.png)
如果 Alpha 尝试声明两个核心,则使用情况检查将失败,因为服务将使用 oslo.limit 从 keystone 中获取层次结构,并检查层次结构中每个项目的用法,以查看 Alpha、Beta 和 Charlie 的总使用量等于由 Alpha.limit 设置的树的限制
![digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
Alpha -> Charlie;
Alpha [label="Alpha (l=20, u=6)", textcolor = "#FF0000"];
Beta [label="Beta (u=8)"];
Charlie [label="Charlie (u=8)"];
}](../_images/graphviz-3f6f9e772a66e7c125fff1ca9f12f3fd990d7b18.png)
尽管树的使用量已达到限制,我们仍然可以向树中添加子节点
![digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
Alpha -> Charlie;
Alpha -> Delta;
Alpha [label="Alpha (l=20, u=4)"];
Beta [label="Beta (u=8)"];
Charlie [label="Charlie (u=8)"];
Delta [label="Delta (u=0)", textcolor = "#00af00"];
}](../_images/graphviz-d9f3a9b73841ad8454eeb3c4b52d0e314888414b.png)
即使项目可以创建,但树中核心的当前使用量阻止 Delta 声明任何核心
![digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
Alpha -> Charlie;
Alpha -> Delta;
Alpha [label="Alpha (l=20, u=4)"];
Beta [label="Beta (u=8)"];
Charlie [label="Charlie (u=8)"];
Delta [label="Delta (u=2)", textcolor = "#FF0000"];
}](../_images/graphviz-89f35101302dbe15ba98e857f7d260f67487615a.png)
禁止创建项目 Alpha 的孙项目,因为它违反了双层分级约束
![digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
Alpha -> Charlie;
Charlie -> Delta;
Alpha [label="Alpha (l=20, u=4)"];
Beta [label="Beta (u=8)"];
Charlie [label="Charlie (u=8)"];
Delta [label="Delta (u=0)", textcolor = "#FF0000"];
}](../_images/graphviz-5445acf6deb0fb5ed5db5b359305126f3feef72f.png)
这是此设计的一个基本约束,因为它提供了一个非常清晰的升级路径。当请求失败是因为树的限制已超过时,用户拥有他们需要的所有信息,以便在支持工单中提供有意义的上下文(例如,他们的项目 ID 和父项目 ID)。管理员应能够相应地重新分配使用量。在深度大于两级的树结构中提供此信息要困难得多,但可以使用单独的模型来实现。
可以通过赋予 Beta 针对核心的特定项目覆盖来授予 Beta 声明更多核心的能力
![digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
Alpha -> Charlie;
Alpha [label="Alpha (l=20, u=4)"];
Beta [label="Beta (l=12, u=8)", textcolor = "#00af00"];
Charlie [label="Charlie (u=8)"];
}](../_images/graphviz-a257c42f39a111910b24b25ee96f5dce30b901f8.png)
请注意,无论此更新如何,树中后续声明更多核心的请求都将被拒绝,因为使用量等于 Alpha 的限制。如果 Alpha 或 Charlie 释放核心,Beta 可以声明核心
![digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
Alpha -> Charlie;
Alpha [label="Alpha (l=20, u=2)", textcolor = "#00af00"];
Beta [label="Beta (l=12, u=8)"];
Charlie [label="Charlie (u=6)", textcolor = "#00af00"];
}](../_images/graphviz-eb8e4223900597e6ea24e6543b9c41a3f044ecae.png)
![digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
Alpha -> Charlie;
Alpha [label="Alpha (l=20, u=2)"];
Beta [label="Beta (l=12, u=12)", textcolor = "#00af00"];
Charlie [label="Charlie (u=6)"];
}](../_images/graphviz-e1d56f0fc591753e31cf37e63e7b948d3dc965a5.png)
虽然 Charlie 仍然在其默认分配的 10 个核心范围内,但它无法声明更多核心,因为树的总使用量等于 Alpha 的限制,从而阻止 Charlie 重新获得它之前拥有的核心
![digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
Alpha -> Charlie;
Alpha [label="Alpha (l=20, u=2)"];
Beta [label="Beta (l=12, u=12)"];
Charlie [label="Charlie (u=8)", textcolor = "#FF0000"];
}](../_images/graphviz-f335859bcc4dab756aab766c33b15e3f06acb573.png)
禁止创建或更新限制超过 Alpha 限制的项目。即使 Alpha 下的所有限制之和可能超过 Alpha 的限制,但总使用量上限为 Alpha.limit。允许子节点具有大于父节点限制的显式覆盖会导致奇怪的用户体验,并且会产生误导,因为树的总使用量将上限为父节点的限制
![digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
Alpha -> Charlie;
Alpha [label="Alpha (l=20, u=0)"];
Beta [label="Beta (l=30, u=0)", textcolor = "#FF0000"];
Charlie [label="Charlie (u=0)"];
}](../_images/graphviz-d8b12471e18a8e726569eade949f2cb06722a1ac.png)
![digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
Alpha -> Charlie;
Alpha -> Delta;
Alpha [label="Alpha (l=20, u=0)"];
Beta [label="Beta (u=0)"];
Charlie [label="Charlie (u=0)"];
Delta [label="Delta (l=30, u=0)", textcolor = "#FF0000"];
}](../_images/graphviz-e692fc891bafe452c04463a918ae6818f33c587d.png)
最后,我们仍然假设核心的默认注册限制为 10,但我们将创建限制为 6 个核心的项目 Alpha。
![digraph {
orientation = portrait;
node [shape=box]
Alpha;
Alpha [label="Alpha (l=6, u=0)", textcolor = "#00af00"];
}](../_images/graphviz-aa26331030e6c114c56722f3203afb9aedd520cd.png)
当我们创建项目 Beta 时,它是项目 Alpha 的子项目,限制 API 确保项目 Beta 不会假设默认值 10,尽管注册限制为 10 个核心。相反,子项目假设父项目的限制,因为任何单个子项目的限制都不应超过父项目的限制
![digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
Alpha [label="Alpha (l=6, u=0)"];
Beta [label="Beta (l=6, u=0)", textcolor = "#00af00"];
}](../_images/graphviz-94a713ccff839c6c4e76276a084fd1854e6b8113.png)
无论在项目 Alpha 下添加多少个子项目,此行为都保持一致。
![digraph {
node [shape=box]
orientation = portrait;
Alpha -> Beta;
Alpha -> Charlie;
Alpha -> Delta;
Alpha [label="Alpha (l=6, u=0)"];
Beta [label="Beta (l=6, u=0)"];
Charlie [label="Charlie (l=6, u=0)", textcolor = "#00af00"];
Delta [label="Delta (l=6, u=0)", textcolor = "#00af00"];
}](../_images/graphviz-27096be649a0e36857e95254f88fa03fb1277aaf.png)
在创建项目时创建限制覆盖似乎与注册默认的整个目的背道而驰,但通过指定其默认值低于注册默认值来限制父项目似乎也不太可能。此行为与以下要求保持一致:所有子限制之和可能超过父限制,但任何一个子限制不得超过。
如果您
希望资源在分层结构内的项目和域之间流动
项目深度不得大于两层
不关心使用量计算性能或没有很宽的项目树
优点¶
允许资源在严格的两层分级结构内的项目和域之间流动
限制在创建和更新时得到验证
缺点¶
项目深度不得超过两层
在宽而扁平的项目分层结构中,使用量计算期间性能可能会下降