- 云原生
- 集群管理
- 容器编排
- 任务调度
【免费下载链接】mesos
Apache Mesos
本篇围绕 Apache Mesos 的 Quota(配额)功能展开:在多个用户或团队共享同一个集群时,运维人员如何按 role 维度为每个角色设置资源用量上限(limit)、如何通过 v1 API 更新与查询配额、配额消耗量是如何计算的,以及配额在 master 内部(registry 持久化、allocator 生效、offer 回收)的完整执行链路。读完本文,你不仅能直接复制可用的UPDATE_QUOTA/GET_QUOTA操作命令,还能从源码层面理解限额的校验规则、force 标志的作用边界与 failover 恢复行为。
Quota 解决什么问题
当多个用户共享一个 Mesos 集群时,运维方往往希望限制"每个用户最多能用多少资源"。Quota 正是为此设计的:它允许 operator 以per-role(按角色)的粒度设置资源上限。Mesos 中的 role 天然适合作为租户边界——框架(framework)在注册时声明所订阅的 role,其分配到的资源都会计入该 role,从而形成清晰的配额归属。
配额的核心数据模型由 include/mesos/quota/quota.proto 定义,其中关键消息是:
message QuotaConfig { required string role = 1; map<string, Value.Scalar> guarantees = 2; // 已废弃的配额保证 map<string, Value.Scalar> limits = 3; // 资源上限 }在 src/master/quota.cpp 中,Quota结构体把QuotaConfig转换为内存中的guarantees(ResourceQuantities)与limits(ResourceLimits)两个集合;对旧版 API 的QuotaInfo/QuotaRequest,guarantee 会同时被当作 limit 处理(源码注释明确写着 "For legacyQuotaInfo, guarantee also acts as limit"),这正是后文"Deprecated: Quota Guarantees"一节的由来。
支持配额的资源类型
支持Quota 的资源:
cpusmemdiskgpus- 任何类型为
SCALAR的自定义资源
不支持Quota 的资源:
ports- 任何类型为
RANGES或SET的自定义资源
这一约束在请求校验逻辑中有对应实现:src/master/quota.cpp 中的validation::quotaInfo会对每个资源检查resource.type() != Value::SCALAR时直接报错 "QuotaInfo must not include non-scalar resources",并拒绝携带ReservationInfo、DiskInfo、RevocableInfo等资源字段。
设置配额:UPDATE_QUOTA 调用
默认情况下,每个 role 都没有资源限制。要为单个或多个 role 修改资源限制,使用 v1 API 的UPDATE_QUOTA调用。注意该调用是原子性的(all-or-nothing):如果请求中某一个 role 的配额更新无效或未获授权,整个请求都不会生效。
完整示例:
curl --request POST \ --url http://<master-ip>:<master-port>/api/v1/ \ --header 'Content-Type: application/json' \ --data '{ "type": "UPDATE_QUOTA", "update_quota": { "force": false, "quota_configs": [ { "role": "dev", "limits": { "cpus": { "value": 10 }, "mem": { "value": 2048 }, "disk": { "value": 4096 } } }, { "role": "test", "limits": { "cpus": { "value": 1 }, "mem": { "value": 256 }, "disk": { "value": 512 } } } ] } }'请求参数说明
| 字段 | 类型 | 说明 |
|---|---|---|
type | string | 固定为UPDATE_QUOTA |
update_quota.force | bool | 是否强制覆盖。默认false |
update_quota.quota_configs[] | array | 一批 role 的配额配置,整体原子生效 |
quota_configs[].role | string | 目标 role 名,必须存在于 role 白名单(若配置了--roles),且不支持嵌套 role(如eng/prod) |
quota_configs[].limits | map | 资源上限,键为资源名(cpus、mem、disk、gpus或自定义 SCALAR 资源),值为{ "value": <number> } |
quota_configs[].guarantees | map | 旧版保证语义,已废弃,新配置只应设置limits |
关于force标志的两点行为(与 docs/quota.md 保持一致):
- 如果 role当前已消耗的配额超过了所设 limit,请求会被拒绝;将
force设为true可以绕过该检查。 - master 会尝试回收(rescind)足够的 outstanding offers,确保该 role 不会超过其 limit。
源码中的校验链路
UPDATE_QUOTA的完整处理在 src/master/quota_handler.cpp 的Master::QuotaHandler::update中,校验顺序如下:
- role 白名单检查:
master->isWhitelistedRole(config.role()),不在白名单中的 role 直接返回 400("is not on the roles whitelist")。 - 嵌套 role 拒绝:role 名包含
/时返回 "Updating quota on nested role ... is not supported yet"。 - 配置合法性校验(src/master/quota.cpp 中的
validate(const QuotaConfig&)),包含几个容易踩坑的硬限制:- 标量值必须非负且有限;
mem、disk的上限为1 Exabyte(以 MB 为基准单位),其他资源(如 cpus、自定义资源)的上限为1 万亿(1 trillion)——这是为了避免Value::Scalar底层定点逻辑在约 9.2e15 附近发生溢出;- 若同时设置了
guarantees和limits,必须满足guarantees <= limits。
- 当前消耗检查:通过
RoleResourceBreakdown(master, role).consumedQuota()计算 role 当前消耗,若limits不包含该消耗量且未提供force,返回 400 并提示 "use 'force' flag to bypass this check"。 - 配额树(QuotaTree)层级校验与集群容量超卖(overcommit)检查:所有 role 的 guarantees 总和不能超过集群已注册 agent 的标量资源总量(同样可被
force覆盖)。 - 授权检查:逐个 config 调用 authorizer 的
UPDATE_QUOTA_WITH_CONFIG动作,任一未授权则整个请求返回 403 Forbidden——这就是"all-or-nothing"在权限层面的体现。
校验全部通过后,_update会将配额写入 registry(见下文持久化一节),更新 master 内存态master->quotas并调用master->allocator->updateQuota(...)让分配器立即生效。
配额如何持久化
配额的持久化走的是 master 的 registry 事务机制。src/master/quota.hpp 定义了UpdateQuota这个RegistryOperation,其头文件注释解释了关键设计决策:
不对 quota registry 操作施加任何约束,quota 请求是否合法由 master 与 allocator 判断;因此
perform()从不返回Error。
UpdateQuota::perform的实现(src/master/quota.cpp):
- 先把整批
QuotaConfig再校验一遍(防御性检查); - 逐个 role 在 registry 的
quota_configs中查找:如果新配额等于DEFAULT_QUOTA(即"空配置",等同于删除配额),则删除该 role 的条目;否则更新或插入; - 顺带清理旧版
quotas(QuotaInfo)字段中同 role 的遗留条目,保证新老实态不冲突; - 若 registry 中还存在任何配额,则在 registry 的
minimum_capabilities中追加QUOTA_V2能力标记,否则移除该标记——这保证了 HA 集群中旧版本 master 在 failback 时能正确拒绝不支持的配置。
查看配额
Web UI
Web UI 的Roles标签页会展示所有已知 role 的资源核算信息,其中就包括已配置的配额(quota)与配额消耗(quota consumption)。
API:GET_QUOTA 返回配额配置
$ curl --request POST \ --url http://<master-ip>:<master-port>/api/v1/ \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --data '{ "type": "GET_QUOTA" }'响应:
{ "type": "GET_QUOTA", "get_quota": { "status": { "infos": [ { "configs" : [ { "role": "dev", "limits": { "cpus": { "value": 10.0 }, "mem": { "value": 2048.0 }, "disk": { "value": 4096.0 } } }, { "role": "test", "limits": { "cpus": { "value": 1.0 }, "mem": { "value": 256.0 }, "disk": { "value": 512.0 } } } ] } ] } } }从源码看,GET_QUOTA的响应由 src/master/quota_handler.cpp 中_status组装:它先快照当前master->quotas(避免响应构建期间配额被并发修改),再对每个 role 逐个执行authorizeGetQuota授权检查(action 为GET_QUOTA),未被授权的 role 会从结果中过滤掉。返回体同时填充了旧版QuotaInfo(仅含 guarantee,用于向后兼容)和新版QuotaConfig(含 guarantees 与 limits)两个视图。
/roles端点:同时查看配额与消耗
$ curl http://<master-ip>:<master-port>/roles响应示例:
{ "roles": [ { "name": "dev", "weight": 1.0, "quota": { "role": "dev", "limit": { "cpus": 10.0, "mem": 2048.0, "disk": 4096.0 }, "consumed": { "cpus": 2.0, "mem": 1024.0, "disk": 2048.0 } }, "allocated": { "cpus": 2.0, "mem": 1024.0, "disk": 2048.0 }, "offered": {}, "reserved": { "cpus": 2.0, "mem": 1024.0, "disk": 2048.0 }, "frameworks": [] } ] }Quota Consumption:消耗量到底包含什么
文档给出的口径是:一个 role 的配额消耗 = 其 allocations + reservations。也就是说,即使 reservation 尚未被分配,它同样计入配额消耗;而被 offer 出去但尚未接受分配的资源(offered)不计入配额消耗。
这个口径在 src/master/master.cpp 的Master::RoleResourceBreakdown::consumedQuota中有直接实现:
ResourceQuantities Master::RoleResourceBreakdown::consumedQuota() const { ResourceQuantities unallocatedReservation; foreachvalue (Slave* slave, master->slaves.registered) { ResourceQuantities totalReservation = ResourceQuantities::fromResources( slave->totalResources.reservedToRoleSubtree(role)); ResourceQuantities usedReservation; foreachvalue (const Resources& r, slave->usedResources) { usedReservation += ResourceQuantities::fromResources( r.reservedToRoleSubtree(role)); } unallocatedReservation += totalReservation - usedReservation; } return allocated() + unallocatedReservation; }实现逻辑与文档描述一一对应:先对每个 agent 计算"总 reservation − 已使用的 reservation"得到未分配的部分,再加上allocated()(该 role 子树下所有 framework 的已用资源),即为消耗总量。offered()、allocated()、reserved()三个分解函数也在同一位置(src/master/master.cpp)实现,正是/roles端点输出中同名字段的来源。
此外,配额消耗还会沿 role 树的祖先节点向上聚合——src/master/allocator/mesos/hierarchical.cpp 中RoleTree每次 track/untrack 分配与 reservation 时都会调用applyToRoleAndAncestors更新每一层的消耗并同步指标。
Metrics
文档列出的配额相关指标键:
allocator/mesos/quota/roles/<role>/resources/<resource>/guaranteeallocator/mesos/quota/roles/<role>/resources/<resource>/limit- 配额消耗指标将经由 MESOS-9123 提供。
从当前仓库源码看,消耗指标实际上已经有对应实现:src/master/allocator/mesos/metrics.hpp 中定义了QuotaMetrics quotaConsumed {"/consumed"},Metrics::updateConsumed负责写入;src/master/allocator/mesos/metrics.cpp 的updateQuota还会为设置了配额的 role 注册allocator/mesos/quota/roles/<role>/resources/<name>/offered_or_allocated的 PullGauge。另外有一个实现细节值得注意:当 role 配额被重置为DEFAULT_QUOTA(即删除配额)时,updateQuota会提前返回,不再发布 offered_or_allocated 指标,以保持与旧版行为一致。
指标命名注释中也给出了官方示例:"Quota metrics should have a suffix in singular. (Example of a name: allocator/mesos/quota/roles/roleA/resources/cpu/limit.)"
Deprecated: Quota Guarantees(已废弃的配额保证)
Mesos 1.9 之前,配额 API 只暴露 "guarantees"(配额保证):保证某个 role 最少有若干资源可用,同时设置 guarantee 也会隐式地设置一个 limit。1.9 起,limit 作为独立概念直接暴露(即本文前述的limits字段),guarantees 则被废弃。
废弃的核心理由与 Mesos 向optimistic offer model(乐观 offer 模型)演进直接相关(见 JIRA MESOS-1607,其目标是改善多 role / 多 scheduler 场景下的可扩展性):
- 强制保证 quota guarantee 要求 Mesos预留(hold back)足够资源来满足所有尚未满足的 guarantee——这在传统分配模型下是可行的;
- 而乐观 offer 模型下,"扣下资源留白"的做法不再可行;limit 在这种模型下依然简单可执行,但 guarantee 将需要一套复杂的 "effective limit" 传播机制;
- 因此 "limit + 基于优先级的抢占(priority based preemption)" 的组合会是更简单、更契合未来模型的方向。
guarantee 在 Mesos 1.9 中仍然可用,但已标记为废弃;旧版 guarantee 用法可参考 Apache Mesos 1.8.0 发布版中的 docs/quota.md(本仓库文档指向的版本分支,此处不给出外链)。
从源码可以印证 guarantee 仍被完整保留的现状:QuotaConfig消息中guarantees字段与limits并列存在,QuotaTree::validate(src/master/quota_handler.cpp)至今仍校验"父 role 的 guarantee 必须包含其所有子 role guarantee 之和"的层级关系,_update流程里也会为 guarantee 单独执行一轮 offer 回收。
实现细节与边界条件
嵌套 role 暂不支持配额
对eng/prod这类嵌套 role 设置配额目前不支持,update入口会直接返回 "Updating quota on nested role ... is not supported yet"(源码中标注待 MESOS-7402 修复后移除该检查)。
Failover 期间 allocator 的暂停与恢复
Master failover 后,为了正确执行配额限制,allocator 会被暂停,直到满足以下两个条件之一才重新发放 offer:
- 至少80%的 agent 重新注册;
- 或10 分钟超时。
这两个参数在默认值层面与源码完全对应:include/mesos/allocator/allocator.hpp 中
constexpr Duration DEFAULT_ALLOCATOR_RECOVERY_TIMEOUT = Minutes(10); constexpr double DEFAULT_ALLOCATOR_AGENT_RECOVERY_FACTOR = 0.80;并且它们已通过 master 的启动 flag 暴露为可配置项(src/master/flags.cpp):
--allocator_agent_recovery_factor:领导者选举后,allocator 开始发放 offer 所需的已重新注册 agent 的最低比例;--allocator_recovery_timeout:领导者重选后开始发放 offer 前最多等待的时间。
在 src/master/allocator/mesos/hierarchical.cpp 的恢复流程中可以看到具体机制:expectedAgentCount = _expectedAgentCount * options.agentRecoveryFactor,随后pause()暂停分配并delay(options.recoveryTimeout, self(), &Self::resume)注册超时恢复;每有一个 agent 重新注册便计数一次,达到expectedAgentCount即恢复。若集群内没有任何预期 agent,则直接跳过恢复等待。
默认 role(*)现在受支持
配额已支持默认的*role(对应 JIRA MESOS-3938)。
配额的强制执行:best-effort 的 offer 回收
文档提到"master 会尝试回收足够的 offer 以确保 role 不超过 limit"。这一行为的完整实现位于 src/master/quota_handler.cpp 的_update中,值得理解其设计取舍:
- 配额先行:registry 事务提交后,先更新
master->quotas并 dispatchupdateQuota到 allocator,再执行回收。注释明确解释了顺序:若先回收再设配额,回收出来的资源可能在updateQuota生效前被重新分配出去(master 与 allocator 之间存在消息竞态)。 - 限额回收:计算该 role 的
consumedQuota() + offered(),遍历所有 framework 的 outstanding offers,对分配给该 role 子树的 offer 逐个rescindOffer,直到consumedAndOffered落回 limit 之内。 - 保证回收(针对 guarantee):悲观地假设 allocator 中看似"可用"的资源都会消失,贪婪地回收其他 role 子树之外的 offer,直到
rescinded + consumedAndOffered覆盖该 role 的全部 guarantees。
源码注释坦承这是一项best-effort工作:精确追踪 consumed/offered 的边界代价很高,且 master 与 allocator 之间存在竞态(可能多收,也可能少收);配额的意义在于收敛一个瞬时状态——资源持续流动后,配额最终会被强制执行。注释同时指出,未来若 Mesos 全面转向乐观 offer 模型(MESOS-1607),配额强制将移到准入控制(admission control)阶段完成,offer 回收将不再必要。
另外,MesosAllocator对updateQuota的调用(src/master/allocator/mesos/allocator.hpp)是通过process::dispatch异步派发到 allocator 进程处理的,HierarchicalAllocatorProcess::updateQuota会更新roleTree并同步刷新指标、记录日志(src/master/allocator/mesos/hierarchical.cpp),随后触发新一轮分配,使新的限额立即作用于后续 offer 的生成。
小结
| 能力 | 入口 | 关键点 |
|---|---|---|
| 设置/修改/删除配额 | v1 APIUPDATE_QUOTA | 原子生效;force可越过"已超消耗"与"超卖"检查;删除配额 = 提交空QuotaConfig |
| 查询配额配置 | v1 APIGET_QUOTA | 逐 role 授权过滤;同时返回旧版infos与新版configs |
| 查询配额消耗 | /rolesHTTP 端点 | consumed= allocated + 未分配 reservation;offered 不计入 |
| 监控指标 | /metrics | allocator/mesos/quota/roles/<role>/resources/<resource>/{guarantee,limit,consumed,offered_or_allocated} |
| Failover 恢复 | --allocator_agent_recovery_factor/--allocator_recovery_timeout | 默认 80% agent 重注册或 10 分钟超时后恢复发放 offer |
配合 src/master/quota.cpp、src/master/quota_handler.cpp 与 include/mesos/quota/quota.proto 这三个文件,可以完整追溯一条UPDATE_QUOTA请求从 HTTP 入口、参数校验、registry 事务到 allocator 生效的全链路,本文所有实现级结论均可在仓库中直接查证。
- 云原生
- 集群管理
- 容器编排
- 任务调度
【免费下载链接】mesos
Apache Mesos
相关推荐
Apache Mesos 配额(Quota)实战指南:基于角色(Role)的资源限额配置、查询与原理
Apache Mesos 配额(Quota)实战指南:基于角色(Role)的资源限额配置、查询与原理 当多个用户或团队共享同一个 Mesos 集群时,某个角色(
集群管理任务调度后端Apache Mesos 1.9.0 发布解读:Agent 排空、Quota 限额与 TLS 安全机制升级
Apache Mesos 1.9.0 发布解读:Agent 排空、Quota 限额与 TLS 安全机制升级 导读 本文基于 Apache Mesos 1.9.0
集群管理任务调度后端终极NES游戏模拟器配置指南:FCEUX完整使用教程
终极NES游戏模拟器配置指南:FCEUX完整使用教程 你是否怀念童年时期的红白机游戏?想要在电脑上重温《超级马里奥》、《塞尔达传说》等经典NES游戏?FCEUX
游戏开发开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考