资讯详情 Apache Mesos 资源预留(Reservation)机制详解:静态预留、动态预留、标签与 RESERVATION_REFINEMENT 完整实践
📅 2026/10/14 15:47:03
云原生集群管理容器编排任务调度【免费下载链接】mesosApache Mesos项目地址https://gitcode.com/gh_mirrors/meso/mesos点击查看免费下载本文基于 Apache Mesos 官方预留文档并结合当前仓库源码系统讲解 Mesos 资源预留的两种形态——agent 启动时通过--resources指定的静态预留以及运行期通过Offer::Operation::Reserve/Unreserve操作与/reserve、/unreserveHTTP 端点完成的动态预留。读完后你将能够独立完成静态预留配置、框架侧预留/释放资源的 protobuf 消息构造、RESERVATION_REFINEMENT能力下的层级角色预留精炼refinement以及预留状态的校验、鉴权配置与集群内预留信息的查询。一、预留Reservation概览Mesos 提供了在特定 agent旧版本中称 slave上预留资源的机制。该概念最早在 0.14.0 版本中以静态预留static reservation形式引入允许运维人员在 agent 启动时指定预留资源随后在 0.23.0 版本中扩展为动态预留dynamic reservation允许运维人员和经过授权的框架在集群运行期间动态地预留和释放资源。无论是哪种预留资源都是为某个角色role预留的。两类预留的核心区别在于可变性特性静态预留动态预留配置时机agent 启动时agent 注册后的运行期配置入口--resources启动参数Offer::Operation::Reserve/Unreserve消息或/reserve、/unreserveHTTP 端点可否修改不可修改改配置需 drain 并重启 agent可随时预留/部分释放操作主体仅运维人员运维人员与具备授权的角色在协议层面预留状态由 mesos.proto 中定义的Resource.ReservationInfo消息描述其Type枚举区分了两种预留来源message ReservationInfo { enum Type { UNKNOWN 0; STATIC 1; // 由运维人员在命令行设置agent 不重启则不可变 DYNAMIC 2; // 由运维通过 /reserve 端点或框架通过 offer 循环 // 发送 Offer::Operation::Reserve 消息创建 } optional Type type 4; optional string role 3; // 预留目标角色 optional string principal 1; // 执行预留的框架/运维 principal // labels: 自由键值对关联任意元数据见下文预留标签一节 }Resource消息中同时存在旧格式字段role与reservation成对使用和新格式字段reservations列表mesos.proto 的 Resource Format 注释明确说明框架根据是否启用RESERVATION_REFINEMENT能力收到不同格式的资源 offer——未启用时使用Resource.roleResource.reservation描述预留状态启用后则仅使用Resource.reservations字段。二、静态预留Static Reservation运维人员可以通过--resources启动参数为 agent 配置指定角色的预留资源。以一台拥有 12 个 CPU 和 6144 MB 内存的 agent 为例若希望为ads角色预留 8 个 CPU 和 4096 MB 内存启动方式如下$ mesos-slave \ --masterip:port \ --resourcescpus:4;mem:2048;cpus(ads):8;mem(ads):4096启动后该 agent 上即有 8 个 CPU 和 4096 MB 内存被预留给了ads角色剩余的 4 CPU / 2048 MB 内存作为非预留资源参与调度。注意事项CAVEAT要修改静态预留运维人员必须先 drain 该 agent再以新的--resources配置重启它。从源码看--resources参数在 agent 标志定义 中的官方说明支持两种格式分号分隔的键值对列表name(role):value;name:value...role部分可选JSON 格式可直接传字符串或通过--resourcesfilepath以file:///path/to/file方式指定文件例如[ { name: cpus, type: SCALAR, scalar: { value: 24 } }, { name: mem, type: SCALAR, scalar: { value: 24576 } } ]实践中更常见的做法是把 agent 的全部资源以非预留形式通过--resources指定再动态管理预留。不过静态预留为运维提供了一种更确定性的控制手段——在 agent 暴露给 master 和框架之前就可以固定角色、数量与 principal 的预留关系典型用例包括把整台 agent 专属地划拨给某个角色。静态预留的鉴权默认情况下agent 注册时可以为任意角色携带静态预留资源。启用鉴权后master 可以使用reserve_resourcesACL 校验 agent 的principal是否被允许为特定角色做静态预留。相关 ACL 定义见 acls.protomessage ReserveResources { // 主体动态预留时为框架 principal 或运维用户名 // 静态预留时为 agent principal。 required Entity principals 1; // 客体该 principal 可以为这些角色预留资源。 required Entity roles 2; }仓库中提供的 ACL 模板 acls_template.json 默认放通所有预留操作ANY生产环境应按需收窄reserve_resources: [ { principals: { type: ANY }, roles: { type: ANY } } ], unreserve_resources: [ { principals: { type: ANY }, reserver_principals: { type: ANY } } ]三、动态预留Dynamic Reservation静态预留一经配置便不可再转预留到其他角色、也不可释放。动态预留则允许运维人员和授权框架在 agent 启动之后继续预留与释放资源主要有两条接口路径框架侧Offer::Operation::Reserve和Offer::Operation::Unreserve消息通过acceptOffersAPI 作为资源 offer 的响应发送运维侧/reserve和/unreserveHTTP 端点经由 master 管理动态预留。合并与部分释放若同一 agent 上针对同一角色且 labels 相同时存在两次动态预留两个预留会合并各次请求预留的资源相加最终在 agent 上形成一条预留记录部分释放partial unreserve也是允许的一次 unreserve 操作可以只释放某角色预留资源的一部分剩余部分继续保留。释放限制如果预留资源仍被运行中的任务使用或者基于该预留创建了持久卷则该预留不能被释放。后者必须先销毁卷再释放资源。鉴权Authorization默认情况下框架和运维人员可以为任意角色预留资源并释放任意动态预留。启用鉴权后这一行为可以被限制为只能为特定角色预留、只能释放特定主体的预留。前提框架或运维操作时需要提供principal用于身份识别master 需配置相应的 ACL即上文reserve_resources与unreserve_resources两类。其中UnreserveResourcesACL 的客体是reserver_principals执行预留时的 principal即谁释放谁预留的资源需要被授权这解释了下文/unreserve示例中区分reserver_principal与operator_principal的原因。四、预留标签Reservation Labels动态预留可以携带任意键值对形式的labels对应ReservationInfo.labels字段。其用途是为一笔预留关联任意元数据例如框架可以用 labels 标识某 agent 上一部分预留资源的预期用途。一个关键行为labels 不同的两次预留即使在同一 agent、同一角色上也不会合并它们会保持为两条独立的预留记录。因此labels 实质上也是预留记录的隔离键之一。五、预留精炼Reservation Refinement层级化角色hierarchical roles如eng/backend支持沿层级向下委派资源而预留精炼就是把预留本身沿层级向下委派delegation的机制对eng的预留静态或动态可以被精炼为eng/backend精炼后的预留被释放时资源会回退到上一级所有者——本例中回到eng精炼可以跳级eng可以直接精炼为eng/backend/db释放时同样回退给上一级所有者eng。注意框架必须声明RESERVATION_REFINEMENT能力才能收到精炼格式的 offer也才能创建精炼预留。该能力在 mesos.proto 中标注为实验性EXPERIMENTAL。仓库中的示例框架 dynamic_reservation_framework.cpp 展示了声明方式framework.add_capabilities()-set_type( FrameworkInfo::Capability::MULTI_ROLE); framework.add_capabilities()-set_type( FrameworkInfo::Capability::RESERVATION_REFINEMENT);启用该能力后资源在两种格式间的转换由 resources_utils.cpp 中的upgradeResource/convertResourceFormatPOST_RESERVATION_REFINEMENT格式完成master 在处理/reserve请求前也会先执行validateAndUpgradeResources见 http.cpp把旧格式的role/reservation字段升级到reservations新格式因此运维侧的 HTTP 请求无需感知能力开关。六、框架 Scheduler APIReserve / Unreserve 示例6.1Offer::Operation::Reserve未启用RESERVATION_REFINEMENT框架可以在资源 offer 循环中预留资源且预留角色必须与 offer 的分配角色allocation role一致。假设收到一个 offer12 个 CPU 与 6144 MB 内存未被预留分配给角色engineering{ allocation_info: { role: engineering }, id: offer_id, framework_id: framework_id, slave_id: slave_id, hostname: hostname, resources: [ { allocation_info: { role: engineering }, name: cpus, type: SCALAR, scalar: { value: 12 }, role: * }, { allocation_info: { role: engineering }, name: mem, type: SCALAR, scalar: { value: 6144 }, role: * } ] }要预留 8 个 CPU 和 4096 MB 内存发送如下Offer::Operation消息。Offer::Operation::Reserve有resources字段需要把要预留的资源填入并显式地将资源的role字段设为 offer 的分配角色。principal字段的取值取决于框架注册时是否提供了 principal若提供了资源的principal必须等于框架 principal若未提供则该字段可任意取值或留空。注意即使关闭了认证只要启用鉴权principal字段就决定reserver principal用于后续 unreserve 的授权判定{ type: Offer::Operation::RESERVE, reserve: { resources: [ { allocation_info: { role: engineering }, name: cpus, type: SCALAR, scalar: { value: 8 }, role: engineering, reservation: { principal: framework_principal } }, { allocation_info: { role: engineering }, name: mem, type: SCALAR, scalar: { value: 4096 }, role: engineering, reservation: { principal: framework_principal } } ] } }预留成功后后续的资源 offer 中会包含如下已预留资源注意role不再是*而是engineering且带上了reservation.principal{ allocation_info: { role: engineering }, id: offer_id, framework_id: framework_id, slave_id: slave_id, hostname: hostname, resources: [ { allocation_info: { role: engineering }, name: cpus, type: SCALAR, scalar: { value: 8 }, role: engineering, reservation: { principal: framework_principal } }, { allocation_info: { role: engineering }, name: mem, type: SCALAR, scalar: { value: 4096 }, role: engineering, reservation: { principal: framework_principal } } ] }6.2Offer::Operation::Unreserve未启用RESERVATION_REFINEMENT框架同样可以在 offer 循环中释放资源。假设上一步我们为订阅角色例如engineering在某 agent 上预留了 8 个 CPU 和 4096 MB 内存master 会持续只把这些预留资源 offer 给预留角色。若要释放它们先收到如下资源 offer即 6.1 节末尾的 offer然后发送{ type: Offer::Operation::UNRESERVE, unreserve: { resources: [ { allocation_info: { role: engineering }, name: cpus, type: SCALAR, scalar: { value: 8 }, role: engineering, reservation: { principal: framework_principal } }, { allocation_info: { role: engineering }, name: mem, type: SCALAR, scalar: { value: 4096 }, role: engineering, reservation: { principal: framework_principal } } ] } }释放后这些资源恢复为非预留状态可以被 offer 给其他框架。6.3Offer::Operation::Reserve启用RESERVATION_REFINEMENT想要创建精炼预留的框架需要启用RESERVATION_REFINEMENT能力启用后即可使用Resource消息的reservations字段来**推入push**一条精炼预留。机制要点为某角色预留的资源会被 offer 给该角色所有子角色因此可能出现资源为engineering预留、却被分配给engineering/backend的 offer此时就可以将其精炼为对engineering/backend的预留。精炼预留的角色必须与 offer 的分配角色一致。假设收到这样的 offer12 个 CPU 和 6144 MB 内存为engineering预留分配给角色engineering/backend{ allocation_info: { role: engineering/backend }, id: offer_id, framework_id: framework_id, slave_id: slave_id, hostname: hostname, resources: [ { allocation_info: { role: engineering/backend }, name: cpus, type: SCALAR, scalar: { value: 12 }, reservations: [ { type: DYNAMIC, role: engineering, principal: principal } ] }, { allocation_info: { role: engineering/backend }, name: mem, type: SCALAR, scalar: { value: 6144 }, reservations: [ { type: DYNAMIC, role: engineering, principal: principal } ] } ] }注意此时role和reservation字段未设置取而代之的是新的reservations字段来表达预留状态。启用RESERVATION_REFINEMENT后框架收到的就是这种仅使用reservations字段的新格式而非旧格式的role/reservation字段对。将 8 个 CPU 和 4096 MB 内存精炼预留到engineering/backend发送如下消息需要向reservations列表尾部推入一条新的ReservationInfo并显式地将该预留的role设为 offer 的分配角色。principal的规则与 6.1 相同注册时提供了 principal 则必须一致否则可任意或留空且该字段决定鉴权场景下的 reserver principal{ type: Offer::Operation::RESERVE, reserve: { resources: [ { allocation_info: { role: engineering/backend }, name: cpus, type: SCALAR, scalar: { value: 8 }, reservations: [ { type: DYNAMIC, role: engineering, principal: principal }, { type: DYNAMIC, role: engineering/backend, principal: framework_principal } ] }, { allocation_info: { role: engineering/backend }, name: mem, type: SCALAR, scalar: { value: 4096 }, reservations: [ { type: DYNAMIC, role: engineering, principal: principal }, { type: DYNAMIC, role: engineering/backend, principal: framework_principal } ] } ] } }注意第二个ReservationInfo精炼了第一个——mesos.proto 注释说明reservations列表中的每一条ReservationInfo都表示对前一条预留的精炼。预留成功后后续 offer 中该资源即带有两条ReservationInfoengineering→engineering/backend。6.4Offer::Operation::Unreserve启用RESERVATION_REFINEMENT框架释放精炼预留时效果是把reservations列表中最后一条ReservationInfo弹出pop资源回退到上一个所有者。以上文为例资源原先为engineering预留被精炼到engineering/backend释放后即回到engineering。首先收到如下 offer即 6.3 节末尾带两条ReservationInfo的资源{ allocation_info: { role: engineering/backend }, id: offer_id, framework_id: framework_id, slave_id: slave_id, hostname: hostname, resources: [ { allocation_info: { role: engineering/backend }, name: cpus, type: SCALAR, scalar: { value: 8 }, reservations: [ { type: DYNAMIC, role: engineering, principal: principal }, { type: DYNAMIC, role: engineering/backend, principal: framework_principal } ] }, { allocation_info: { role: engineering/backend }, name: mem, type: SCALAR, scalar: { value: 4096 }, reservations: [ { type: DYNAMIC, role: engineering, principal: principal }, { type: DYNAMIC, role: engineering/backend, principal: framework_principal } ] } ] }发送以下Offer::Operation消息即可释放 8 个 CPU 和 4096 MB 内存{ type: Offer::Operation::UNRESERVE, unreserve: { resources: [ { allocation_info: { role: engineering/backend }, name: cpus, type: SCALAR, scalar: { value: 8 }, reservations: [ { type: DYNAMIC, role: engineering, principal: principal }, { type: DYNAMIC, role: engineering/backend, principal: framework_principal } ] }, { allocation_info: { role: engineering/backend }, name: mem, type: SCALAR, scalar: { value: 4096 }, reservations: [ { type: DYNAMIC, role: engineering, principal: principal }, { type: DYNAMIC, role: engineering/backend, principal: framework_principal } ] } ] } }释放后资源重新回到为engineering预留的状态此后可以被 offer 给engineering本身或其下属的其他角色。七、运维 HTTP 端点/reserve与/unreserve动态预留除了由框架在 offer 循环中创建外还可以通过向 master 的/reserve和/unreserve端点发送 HTTP 请求来创建和删除这条路径面向运维人员和管理工具。7.1/reserve端点0.25.0 起提供假设要在 id 为slave_id的 agent 上为ads角色预留 8 个 CPU 和 4096 MB 内存。注意 agent ID 需要用户自行确定例如通过/slaves端点查询若该 agent 上没有足够的非预留资源请求会失败。reservation字段中的 principal 取值取决于 master 上 HTTP 认证的状态认证开启reservation 中的 principal 必须与请求 HTTP 头中认证出的 principal 一致认证关闭该字段可任意取值或留空无论认证是否开启只要启用鉴权该字段就决定reserver principal。向 master 的/reserve端点发送 POST 请求$ curl -i \ -u operator_principal:password \ -d slaveIdslave_id \ -d resources[ { name: cpus, type: SCALAR, scalar: { value: 8 }, reservations: [ { type: DYNAMIC, role: ads, principal: operator_principal, } ] }, { name: mem, type: SCALAR, scalar: { value: 4096 }, reservations: [ { type: DYNAMIC, role: ads, principal: operator_principal, } ] } ] \ -X POST http://ip:port/master/reserve用户会收到以下之一状态码含义202 Accepted请求已被接受见下文说明400 BadRequest参数无效例如缺少参数401 Unauthorized未认证请求403 Forbidden未授权请求409 Conflict资源不足无法满足 reserve 操作202 的语义是已校验、异步下发master 验证成功后立即返回 202 Accepted随后把操作异步转发到预留资源所在的 agent。该异步消息可能丢失或 agent 端预留失败此时资源不会被预留。要确认操作是否真正生效应查看对应 agent 的状态例如通过 agent 的/state端点见第八节。从源码实现看该端点在 master.cpp 中注册为读写认证域READWRITE_HTTP_AUTHENTICATION_REALM请求处理函数 Master::Http::reserve 的流程为非 leader 的 master 会将请求重定向到 leader强制要求 POST 方法并从请求体解析slaveId与resourcesresources为 JSON 数组逐项反序列化为Resource_reserve 中构造Offer::Operation类型RESERVE先执行validateAndUpgradeResources做格式升级校验再做validation::operation::validate业务校验随后调用master-authorize(principal, ActionObject::reserve(...))走reserve_resourcesACL 鉴权未通过则返回 403鉴权通过后进入 _operationmaster 会悲观地先回收rescind该 agent 上可能冲突的在途 offer 以腾出资源然后master-apply(slave, operation)下发操作——成功返回Accepted202失败例如资源不足返回Conflict409这与上表的状态码语义一一对应。同样的校验-授权-应用路径也服务于 master 的 protobuf 接口reserveResourcesCall::RESERVE_RESOURCES两者共用_reserve实现。7.2/unreserve端点0.25.0 起提供若要释放上面动态预留的资源向 master 的/unreserve端点发送 POST 请求$ curl -i \ -u operator_principal:password \ -d slaveIdslave_id \ -d resources[ { name: cpus, type: SCALAR, scalar: { value: 8 }, reservations: [ { type: DYNAMIC, role: ads, principal: reserver_principal, } ] }, { name: mem, type: SCALAR, scalar: { value: 4096 }, reservations: [ { type: DYNAMIC, role: ads, principal: reserver_principal, } ] } ] \ -X POST http://ip:port/master/unreserve这里有两个 principal 需要区分reserver_principal当初执行预留时使用的 principal写入reservation字段用于标识这是谁的预留operator_principal当前尝试执行释放操作的 principal即-u提供的认证身份。两者在某些场景下相同但不必相同。启用鉴权后operator_principal必须被授权可以释放reserver_principal所做的预留——这正对应 acls.proto 中UnreserveResourcesACL 的双主体结构principals→reserver_principals。返回状态码与/reserve相同202 Accepted/400 BadRequest/401 Unauthorized/403 Forbidden/409 Conflict资源不足以满足 unreserve 操作例如释放量超过实际预留量。同样地202 仅表示 master 校验通过并异步下发最终是否生效需通过 agent 的/state端点确认。八、查看集群中的预留状态集群中每个 agent 的预留资源信息可以通过查询 master 的/slaves端点获得位于reserved_resources_full键下。同样的信息也可以在 agent 的/state端点中查到同样位于reserved_resources_full键下。agent 端点在排查问题上尤其有用可以确认某次预留是否已真正传播到 agent——在网络分区或 master/agent 重启的情况下这一传播可能失败。源码层面master 侧的reserved_resources_full由 readonly_handler.cpp 生成它遍历该 agent 的totalResources.reservations()按角色组织的预留资源并受VIEW_ROLEACL 约束过滤输出前会通过convertResourceFormat统一转换成端点格式agent 侧对应实现见 slave/http.cpp。集成测试 reservation_endpoints_tests.cpp 中对/state端点的reserved_resources_full与unreserved_resources_full键做了断言校验可作为预期输出结构的参考。九、实践要点小结选型需要确定性、agent 专属化时用静态预留--resources中name(role):value语法需要运行期灵活调整时用动态预留框架操作或 HTTP 端点。principal 纪律无论走框架 offer 还是 HTTP 端点principal字段都决定鉴权场景下的 reserver principal释放他人预留时必须提前配好unreserve_resourcesACL。格式版本新框架建议启用RESERVATION_REFINEMENT统一使用reservations新格式并享受层级角色下的预留委派能力旧格式role/reservation仍被 master 兼容HTTP 端点会自动升级格式。验证闭环/reserve、/unreserve返回 202 不等于操作完成务必回查 agent/state端点的reserved_resources_full确认最终状态。边界约束被运行中任务占用或被持久卷占用的预留不可释放labels 不同的预留不会合并精炼预留释放时回退到上一级所有者而非直接变为非预留。赞分享云原生集群管理容器编排任务调度【免费下载链接】mesosApache Mesos项目地址https://gitcode.com/gh_mirrors/meso/mesos点击查看免费下载相关推荐Apache Mesos 资源预留Reservation完全指南静态预留、动态预留与预留精炼实战Apache Mesos 资源预留Reservation完全指南静态预留、动态预留与预留精炼实战 Apache Mesos 通过 资源预留Reserva集群管理任务调度后端革命性C内存管理库gcpp彻底解决所有权循环和内存泄漏问题革命性C内存管理库gcpp彻底解决所有权循环和内存泄漏问题 gcppGitHub加速计划是一个实验性的C延迟和无序销毁库旨在通过创新的内存管理机Aptabase核心功能全解析为什么它是开发者必备的轻量级分析工具Aptabase核心功能全解析为什么它是开发者必备的轻量级分析工具 Aptabase是一款开源的应用分析工具专为移动、桌面和Web应用打造以隐私优先、简单创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考