- 集群管理
- 任务调度
- 后端
【免费下载链接】mesos
Apache Mesos
本文聚焦 Apache Mesos 的 Mesos Containerizer 中的docker/runtimeisolator,讲解它如何把 Docker 镜像中声明的Entrypoint、Cmd、Env、WorkingDir等运行时配置应用到容器,以及用户通过CommandInfo覆盖镜像默认启动命令的完整合并规则。读完本文,你将掌握docker/runtimeisolator 的启用方式、--isolation与--image_providers的强绑定关系、启动命令九宫格判定逻辑,以及其底层源码实现与测试验证方法。
1. Docker Runtime Isolator 是什么
Docker Runtime isolator 是 Mesos Containerizer 的一个运行时(runtime)类 isolator,职责是从 Docker 镜像中读取运行时配置(例如Entrypoint/Cmd、Env、WorkingDir等),并将其合并到 Mesos 容器的启动信息中。它的类定义位于 runtime.hpp,注释明确说明:
The docker runtime isolator is responsible for preparing mesos container by merging runtime configuration specified by user and docker image default configuration.
即在启动容器之前,将"用户通过框架指定的运行时配置"与"Docker 镜像默认的运行时配置"二者合并,产出一个最终的启动命令、环境变量和工作目录。
该 isolator 与--image_providers=docker强绑定:
- 如果
--image_providers中包含docker,则docker/runtimeisolator必须被启用; - 否则 Agent 会拒绝启动(详见下文第 3 节的启动校验逻辑)。
2. 启用方式:--isolation与--image_providers
要启用 Docker Runtime isolator,只需在启动 Agent 时把docker/runtime追加到--isolation标志中:
mesos-agent \ --isolation=docker/runtime,filesystem/linux \ --image_providers=docker \ --docker_registry=https://registry-1.docker.io \ --work_dir=/var/lib/mesos/agent实践中,Mesos Containerizer 要支持容器镜像,还需要满足以下前提(对应 containerizer.cpp 中的校验逻辑):
- 必须启用
filesystem/linuxisolator(否则报错The 'filesystem/linux' isolator must be enabled for container image support); - 必须使用
linuxlauncher(否则报错The 'linux' launcher must be used for container image support); - Linux 下启用
filesystem/linux时,容器器还会自动补上volume/image、volume/host_path、volume/sandbox_path等卷 isolator(见 containerizer.cpp)。
docker/runtime在 isolator 注册表中与appc/runtime相邻,注册于 containerizer.cpp:
{"appc/runtime", &AppcRuntimeIsolatorProcess::create}, {"docker/runtime", &DockerRuntimeIsolatorProcess::create},其中DockerRuntimeIsolatorProcess::create负责实例化 isolator(runtime.cpp),并实现了supportsNesting()与supportsStandalone()均返回true,意味着它既支持嵌套容器,也支持 standalone 容器。
2.1 与--image_providers=docker的强绑定校验
在创建镜像存储(image store)时,Store::create会做一次硬校验。如果image_providers中包含DOCKER而--isolation中没有docker/runtime,Agent 将直接EXIT(EXIT_FAILURE)退出(store.cpp):
if (stores.contains(Image::DOCKER) && !strings::contains(flags.isolation, "docker/runtime")) { EXIT(EXIT_FAILURE) << "Docker runtime isolator has to be specified if 'DOCKER' is included" << " in 'image_providers'. Please add 'docker/runtime' to '--isolation'" << " flags"; }这正是原文档中"Otherwise, the agent will refuse to start"(否则 Agent 拒绝启动)这一表述的源码出处。因此,只要 Agent 使用 Docker 镜像,就必须同时挂载docker/runtimeisolator。
2.2 相关 Agent 标志速查
| 标志 | 说明 | 默认值(依据 flags.cpp) |
|---|---|---|
--image_providers | 逗号分隔的受支持镜像提供者列表,如APPC,DOCKER(flags.cpp) | 未设置 |
--docker_registry | Docker 镜像仓库:可以是 registry 服务器 URL(如https://registry-1.docker.io),也可以是docker save产出的镜像归档目录路径或 HDFS URI(flags.cpp) | https://registry-1.docker.io |
--docker_store_dir | Docker 镜像的存储目录(flags.cpp) | $TMPDIR/mesos/store/docker |
--docker_ignore_runtime | 设为true时忽略 Docker 镜像中的运行时配置(WORKDIR、ENV、CMD等),Mesos Containerizer 不再向容器传播这些规格(flags.cpp) | false |
其中--docker_ignore_runtime直接作用于 isolator 的prepare()入口:若该标志为真,isolator 直接返回None(),不做任何运行时合并(runtime.cpp)。
3. 支持的镜像运行时配置
目前docker/runtimeisolator 支持从 Docker 镜像中解析以下四类运行时配置:
Entrypoint:镜像默认可执行入口;Cmd:镜像默认命令/参数;Env:镜像声明的环境变量;WorkingDir:镜像声明的工作目录。
这些字段对应 Docker v1 镜像清单中的Config消息。在 include/mesos/docker/v1.proto 中可以看到:
message Config { optional string Hostname = 1; repeated string Entrypoint = 2; repeated string Env = 3; optional string User = 4; repeated string Cmd = 5; optional string WorkingDir = 6; ... }用户可以在TaskInfo(命令任务)或ExecutorInfo(自定义执行器)中携带CommandInfo,用来覆盖镜像默认的Entrypoint和Cmd。
3.1 环境变量(Env)的处理
getLaunchEnvironment会遍历镜像Config.Env中的每个字符串(格式为KEY=VALUE),并使用find_first_of('=')只按第一个=进行切分,从而正确应对值中含有多个=的情况;无=的非法条目会被跳过并记录VLOG(1)日志(runtime.cpp)。重复的环境变量条目不会在此过滤,而是在后续 Mesos Containerizer 合并阶段被覆盖(代码注释:If there exists environment variable duplicate cases, it will be overwritten in mesos containerizer)。
3.2 工作目录(WorkingDir)的处理
getWorkingDirectory会读取Config.workingdir,注意一个细节:Docker 清单中如果镜像没有工作目录,WorkingDir会被序列化为空字符串"",因此只有字段存在且非空时才返回该目录(runtime.cpp)。
3.3 容器用户(User)的现状
getContainerUser能读取镜像Config.user,但当前版本尚不真正支持容器用户切换:当镜像声明了User时,isolator 只打印一条WARNING日志(Container user ... is not supported yet ...),容器仍以 ROOT 身份运行(runtime.cpp)。源码中留有 TODO,计划在容器 capabilities 落地后再支持user|uid[:group|gid]形式的解析。
4. 核心机制:启动命令的判定逻辑
这是docker/runtimeisolator 最核心的部分:如何根据用户提供的CommandInfo(shell、value、arguments三个字段)与镜像默认的Entrypoint/Cmd,决定容器最终执行的命令。
规则先概括为三条(源码注释,见 runtime.cpp):
- 若
shell=true,完全忽略镜像的Entrypoint和Cmd(表格第 5–8 行); - 若
shell=false且value已设置,同样忽略镜像的Entrypoint和Cmd(表格第 3–4 行); - 若
shell=false且value未设置,则使用镜像运行时配置:- 若
Entrypoint已设置,把Entrypoint[0]当作可执行文件,Cmd依次追加为参数; - 若
Entrypoint未设置,把Cmd[0]当作可执行文件,其余Cmd作为参数。
- 若
4.1 启动命令判定总表(原文档核心表格,完整保留)
下表完全继承自 docs/isolators/docker-runtime.md。表中0表示"未指定",1表示"已指定";首行表示 Docker 镜像中Entrypoint与Cmd的定义情况;除标为Error的单元格外,其余单元第一个元素(即Entrypoint[0]或Cmd[0])作为可执行文件,其余元素依次作为追加参数。
CommandInfo | Entrypoint=0 Cmd=0 | Entrypoint=0 Cmd=1 | Entrypoint=1 Cmd=0 | Entrypoint=1 Cmd=1 |
|---|---|---|---|---|
| sh=0 value=0 argv=0 | Error | /Cmd[0]Cmd[1].. | /Entrypt[0]Entrypt[1].. | /Entrypt[0]Entrypt[1]..Cmd.. |
| sh=0 value=0 argv=1 | Error | /Cmd[0]argv | /Entrypt[0]Entrypt[1]..argv | /Entrypt[0]Entrypt[1]..argv |
| sh=0 value=1 argv=0 | /value | /value | /value | /value |
| sh=0 value=1 argv=1 | /valueargv | /valueargv | /valueargv | /valueargv |
| sh=1 value=0 argv=0 | Error | Error | Error | Error |
| sh=1 value=0 argv=1 | Error | Error | Error | Error |
| sh=1 value=1 argv=0 | /bin/sh -cvalue | /bin/sh -cvalue | /bin/sh -cvalue | /bin/sh -cvalue |
| sh=1 value=1 argv=1 | /bin/sh -cvalue | /bin/sh -cvalue | /bin/sh -cvalue | /bin/sh -cvalue |
读表要点:
sh=1(shell 模式):无论镜像如何定义,最终命令都是/bin/sh -c value,前提是必须提供value;sh=1而value=0的四种组合全部是Error。sh=0且value=1(直接执行用户值):用户提供的value直接作为可执行文件(若带arguments则追加),镜像的Entrypoint/Cmd被整体忽略。sh=0且value=0(借助镜像默认):这是唯一会"借用"镜像配置的组合;此时按第 3 条规则把Entrypoint[0]或Cmd[0]作为可执行文件,其余元素和用户arguments作为参数。若镜像既无Entrypoint也无Cmd,则报Error(对应源码中的No executable is found)。
4.2 源码实现:getLaunchCommand的合并逻辑
getLaunchCommand(runtime.cpp)首先按场景取出基准CommandInfo:
- 命令任务(command task):
ContainerConfig中携带TaskInfo,取TaskInfo.command; - 自定义执行器 / 默认执行器 / 嵌套容器:取
ContainerConfig.command_info。
随后按以下分支处理:
if (command.shell()) { // sh=1 if (!command.has_value()) { return Error("Shell specified but no command value provided"); } return None(); // 行 7-8,无需改写命令 } if (command.has_value()) { // sh=0, value=1 return None(); // 行 3-4,无需改写命令 }只有走到sh=0且无value的分支时才真正执行合并:
- 镜像有
Entrypoint:把Entrypoint[0]写入command.value;随后清空原有arguments,依次写入Entrypoint[0]、Entrypoint[1..],再追加用户传入的arguments;若用户没有传任何参数(arguments_size == entrypoint_size),则把镜像的Cmd全部追加为默认参数(runtime.cpp)。 - 镜像无
Entrypoint但有Cmd:把Cmd[0]写入command.value,同理拼接用户参数;用户未传参数时把Cmd[1..]追加进去(runtime.cpp)。 - 两者皆无:返回
Error("No executable is found")。
注意源码注释特别强调:用户提供的arguments会被保留("We keep the arguments of commandInfo while it does not have a value, so that arguments can be specified by user, which is running with default image Entrypoint")。这正是表格中sh=0, value=0, argv=1各行"镜像可执行文件 + 用户 argv"的由来。
5. 合并结果的传递路径:命令任务 vs 自定义执行器
prepare()会把合并后的环境、工作目录与命令写入ContainerLaunchInfo,但命令任务(command task)与自定义执行器/嵌套容器的传递方式完全不同(runtime.cpp):
命令任务场景(containerConfig.has_task_info()):
- 镜像
Env→launchInfo.task_environment; - 镜像
WorkingDir→ 以--working_directory=<dir>参数追加到 command executor 的启动命令; - 合并后的任务命令 → 以
--task_command=<JSON>参数传递,由命令执行器(command executor)加载。
自定义执行器 / 默认执行器 / 嵌套容器场景:
- 镜像
Env→launchInfo.environment; - 镜像
WorkingDir→launchInfo.working_directory; - 合并后的命令 →
launchInfo.command,直接由 Containerizer 使用。
命令执行器端对应的标志定义在 src/launcher/executor.cpp:--working_directory(容器内任务的工作目录)、--task_command(覆盖 TaskInfo 中命令的启动命令)、--task_environment(JSON 化的Environmentprotobuf)。也就是说,命令任务的"镜像运行时配置"最终是通过这些标志注入命令执行器进程的。
在容器器主流程中,各 isolator 返回的ContainerLaunchInfo会被合并:若多个 isolator 都设置了command或working_directory,容器器会记录合并日志并做最终MergeFrom,最终若无任何 isolator 提供命令,则回退到容器配置自带的command_info(containerizer.cpp)。
6. 测试验证:runtime isolator 的行为矩阵
仓库为docker/runtimeisolator 提供了专门的集成测试文件 src/tests/containerizer/runtime_isolator_tests.cpp,测试类DockerRuntimeIsolatorTest直接继承MesosTest(第 81 行),用真实 Master/Agent 集群验证行为:
| 测试用例 | 验证场景 | 与判定表对应的单元格 |
|---|---|---|
ROOT_DockerDefaultCmdImageTarPuller(第 87 行) | 镜像 alpine 默认Cmd=["sh"],用户shell=false且只传arguments=["-c","echo 'hello world'"],期望最终执行sh -c "echo 'hello world'" | sh=0, value=0, argv=1, entrypoint=0, cmd=1 |
ROOT_DockerDefaultEntryptImageTarPuller(第 181 行) | 镜像默认Entrypoint=["echo"]、Cmd为空,用户传arguments=["hello world"],期望输出echo hello world | sh=0, value=0, argv=1, entrypoint=1, cmd=0 |
ROOT_INTERNET_CURL_NestedSimpleCommand(第 352 行) | 嵌套容器场景,用户以非 shell 命令/bin/ls ls -al /直接覆盖镜像默认命令 | sh=0, value=1(用户 value 覆盖) |
ROOT_NestedDockerDefaultCmdImageTarPuller(第 459 行) | 嵌套容器中镜像默认Cmd执行、用户 argv 正确追加 | sh=0, value=0, argv=1, entrypoint=0, cmd=1 |
测试中还展示了标准的启动姿势:Agent 上设置flags.isolation = "docker/runtime,filesystem/linux"、flags.image_providers = "docker",任务侧在TaskInfo.command中设置shell=false与arguments,并在ContainerInfo.mesos.image中指定 Docker 镜像名(如alpine)。其中嵌套容器的ROOT_NestedDockerDefaultCmdImageTarPuller被标记为 MESOS-6852 的回归测试,用于防止"用户 argv 在嵌套容器中被错误覆盖"的问题再次出现。
这些用例直接验证了第 4 节判定表中多行关键组合,可作为实现正确性的对照基准。
7. 使用建议与注意事项
- 强绑定关系不可破坏:只要
--image_providers里有docker,就必须启用docker/runtime,否则 Agent 启动即退出(store.cpp)。 - 覆盖规则要牢记:想让用户命令完全取代镜像命令,使用
shell=false并设置value,或者使用shell=true并提供value;想让镜像Entrypoint/Cmd生效,则不要设置value,只通过arguments追加参数。 docker_ignore_runtime的取舍:若希望完全忽略镜像中的WORKDIR/ENV/CMD等运行时规格,可设--docker_ignore_runtime=true;此时 isolator 的prepare()直接返回None(),不注入任何镜像运行时配置(runtime.cpp)。- 镜像
User暂未生效:目前镜像User只会产生告警日志,容器仍以 ROOT 运行(runtime.cpp),设计时不要依赖镜像内用户切换。 - 命令任务的环境注入方式:命令任务场景下镜像
Env通过task_environment注入命令执行器,最终以--task_environment标志生效(src/launcher/executor.cpp);若需要批量修改任务环境,应关注ContainerLaunchInfo的合并语义。
8. 关联文档与进一步阅读
- 本主题的官方文档:docs/isolators/docker-runtime.md
- Mesos Containerizer 总览:docs/mesos-containerizer.md(第 42 行即链接到本文档)
- isolator 核心实现:runtime.hpp 与 runtime.cpp
- isolator 注册表与启动校验:containerizer.cpp、store.cpp
- Agent 标志定义:src/slave/flags.cpp
- Docker v1 镜像清单 Config 定义:include/mesos/docker/v1.proto
- 集成测试:src/tests/containerizer/runtime_isolator_tests.cpp
- 集群管理
- 任务调度
- 后端
【免费下载链接】mesos
Apache Mesos
相关推荐
Apache Mesos Docker Runtime Isolator:在 Mesos Containerizer 中继承与覆盖 Docker 镜像运行时配置
Apache Mesos Docker Runtime Isolator:在 Mesos Containerizer 中继承与覆盖 Docker 镜像运行时配置
云原生集群管理容器编排任务调度Apache Mesos Mesos Containerizer 的 PID Namespace Isolator 完整指南:隔离、共享与安全配置
Apache Mesos Mesos Containerizer 的 PID Namespace Isolator 完整指南:隔离、共享与安全配置 导读 nam
集群管理任务调度后端Apache Mesos Docker Containerizer 完全指南:配置、API 与底层原理
Apache Mesos Docker Containerizer 完全指南:配置、API 与底层原理 本指南以仓库中的官方文档 docs/docker con
集群管理任务调度后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考