mesos容器

概述

mesos容器的支持采用插件式结构,如图:

https://github.com/apache/mesos/blob/master/docs/containerizer-internals.md

  • 在每个isolator上调用prepare
  • 使用Launcher来fork executor,fork出的子进程直到被isolated之前一直被阻塞
  • 调用isolate来隔离executor,参数是每个isolator的pid
  • 获取executor
  • 执行executor

容器镜像的支持

https://github.com/apache/mesos/blob/master/docs/container-image.md

层次关系如下:

镜像provisioner提供相关支持,其负责pulling,caching及准备容器的根文件系统。并且将运行时的配置从容器映像中提取出来传递给相应的isolator。

容器映像有几种规格:

provisioner后端

后端将一级文件系统层集合堆栈组成成一个根文件系统。当前支持以下后端:

Copy

将所有层拷贝到目标根目录以创建一个根文件系统

Bind

这个用于单层在大映像(多个GB)。对于小的映像(10-100个MB)Copy后端可能就足够了。Bind是零拷贝所有很快。

但有两个限制:

  • 只支持单层。
  • 文件系统只读。

Overlay

内核必须大于4.0,见这里。更多细节见这里

AUFS

使用mesos容器

用户如果想使用Mesos统一容器的功能,有几个Agent参数是很重要的。

  1. --isolation:主要配置当前Mesos Agent所使用的isolator,例如如果通过Mesos Containerizier来使用Docker容器的话,必须配置docker/runtime作为isolator,否 则Agent无法启动。

  2. --image_providers:配置当前Agent的镜像提供这,现在只支持APPC和DOCKER。

  3. --appc_simple_discovery_uri_prefix:配置APPC的镜像前缀,该参数在“后续工作”有介绍。

  4. --docker_registry:配置Docker的registry,现在支持Docker Hub,Local Registry和本地路径。

启动master:$ sudo sbin/mesos-master --work_dir=/tmp/mesos/master

启动agent:

$ sudo GLOG_v=1 sbin/mesos-agent \
  --master=<MASTER_IP>:5050 \
  --isolation=docker/runtime,filesystem/linux \
  --work_dir=/tmp/mesos/agent \
  --image_providers=docker \
  --executor_environment_variables="{}"

使用命令行启动docker容器,注意--shell=false是通知mesos使用docker映像中的缺省entrypoint和cmd

$ sudo bin/mesos-execute \
  --master=<MASTER_IP>:5050 \
  --name=test \
  --docker_image=library/redis \
  --shell=false

验证redis已经运行:

$ sudo docker run -ti --net=host redis redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379>

Executor与容器映像的相关

mesos的所有task是由executor启动的。对于某个框架(如aurora)中的通用executor来说,要求它所有的依赖关系在所有的容器映像中都满足可能并不容易。

为此,我们提供了一种解决方案,允许executor运行在主机文件系统(无容器映像)。另外,它可以定义一个volume,其源为一个Image。mesos容器将在volume中部署image,并将其mount在sandbox目录。executor可以执行pivot_root或chroot以进入容器根文件系统。

MesosContainerizer::create

  • 150-182:process,cgroups,disk标志已经过时,提示用户并转换标志
  • 184-194:如果启动时有network/标志,则加上network/cni
  • 199-204:创建ContainerLogger对象,若失败,则返回错误
  • 207-241:launcher设置成一个lambda函数,函数的功能是:
    • 209-219:若命令行参数标志设置了launcher,则调用LinuxLauncher::create或者PosixLauncher::create并返回
    • 222-224:这是命令行未指定launcher的情况了,若LinuxLauncher可用,则调用LinuxLauncher::create否则调用PosixLauncher::create并返回
  • 243-245:若launcher失败,则返回错误
  • 247-250:调用Provisioner::create创建Provisioner对象,失败则返回出错信息
  • 271-330:初始化一个hash映射表creators,key为标明不同isolator的字符串,value为创建isolator的函数,见isolator
  • 332-338:创建字符串向量isolations,解析命令行中指定及隐含的isolator
  • 340:声明一个Isolator向量isolators
  • 342-365:对isolations向量中定义的每一个字符串isolation:
    • 343-350:声明一个lambda函数isolator,功能是若creators映射表中包含isolation字符串,则返回creators映射表中该项对应的isolator对象创建函数;否则返回ModuleManager::create<Isolator>(isolation)
    • 352-355:确保isolator函数正确创建,否则返回错误
    • 360-361:若isolation中包含"filesystem/",将该isolator插入到isolators向量的最前面,确保它被第一个调用
    • 362-364:否则,将该isolator插入到isolators向量末尾
  • 367-374:创建并返回一个MesosContainerizer对象

MesosContainerizer::MesosContainerizer

378-396:代码很简单,创建新的MesosContainerizer对象,初始化,并启动它的内部进程

MesosContainerizer::MesosContainerizer(
    const Flags& flags,
    bool local,
    Fetcher* fetcher,
    const Owned<ContainerLogger>& logger,
    const Owned<Launcher>& launcher,
    const Owned<Provisioner>& provisioner,
    const vector<Owned<Isolator>>& isolators)
  : process(new MesosContainerizerProcess(
      flags,
      local,
      fetcher,
      logger,
      launcher,
      provisioner,
      isolators))
{
  spawn(process.get());
}

另外,399-404还有另一种构造函数,不知什么时候调用:

MesosContainerizer::MesosContainerizer(
    const Owned<MesosContainerizerProcess>& _process)
  : process(_process)
{
  spawn(process.get());
}

MesosContainerizer::launch

MesosContainerizer中多个函数都是通过libprocess模块中的dispatch,最终调用MesosContainerizerProcess中的对应函数,并原样传递参数

这些函数包括:

  • launch
  • update
  • usage
  • status
  • wait
  • destroy
  • containers
  • recover

MesosContainerizerProcess中定义了containers_,是hash表,containerID和Container组成。

MesosContainerizerProcess::launch

参数:

  • containerId
  • taskInfo:可能为None(),因为在Framework::launchExecutor()中调用slave->containerizer->launch时有两种情况,其中一种是没有taskInfo的
  • _executorInfo
  • directory
  • user
  • slaveId
  • slavePid
  • checkpoint

流程:

  • 754-756:确保本对象的containers_中包含了containerId
  • 758-762:若taskInfo存在,且有container但其类型不为MESOS,则返回失败
  • 766-771:得到executorInfo,若它有container但其类型不为MESOS,则返回失败
  • 775-779:。。。。。。。。
  • 785-796:创建一个新的Container对象container,并设置目录,状态为PROVISIONING,资源,ID,launchInfos
  • 805-819:若executorInfo中未设置container(对于不是执行容器的,就不会设置container),则:
    • 调用prepare
    • 成功后再调用launch,并且传递给launch的provisionInfo参数是None()
    • 完成后函数返回
  • 823-833:查看是否有容器镜像
  • 835-836:若没有容器镜像,则调用_launch(_launch中会先调用provision),并函数返回。在这里没有容器镜像很奇怪???????
  • 847-851:创建provision异步调用provisioning = provisioner->provision,并加入container->provisionInfos队列中。这里的provisioner->provision是准备容器镜像,参见provision
  • 853-864:当provisioning完成,则调用MesosContainerizerProcess::_launch

MesosContainerizerProcess::_launch

参数:

  • containerId
  • taskInfo
  • executorInfo
  • directory
  • user
  • slaveId
  • slavePid
  • checkpoint
  • provisionInfo

流程:

  • 879-898:各种正确性判断,container状态必须为PROVISIONING
  • 907-927:对于ContainerInfo::volumes定义的镜像也要做provision

    • 循环检查_executorInfo->container()中的每个volume(在ContainerInfo::volumes也可以定义image,所以这里要再处理image):
    • 必须有image volume->has_image()
    • 调用provisioner->provision(containerId, image)
  • 931-951:先做prepare,prepare返回一组ContainerLaunchInfo,完成后调用对返回的这组ContainerLaunchInfo中每一个调用__launch

MesosContainerizerProcess::__launch

参数:

  • containerId
  • taskInfo
  • executorInfo
  • directory
  • user
  • slaveId
  • slavePid
  • checkpoint
  • provisionInfo
  • launchInfos

流程:

  • 1095-1103:各种正确性判断,状态必须为PREPARING
  • 1106-1112:准备executor的环境变量
  • 1115-1118:准备executor的根文件系统
  • 1123-1161:准备环境变量、工作目录 、executor要执行的命令
  • 1170-1178:继续设置环境变量
  • 1186:对launchInfos中的每一个launchInfo:
    • 1187-1189:若launchInfo无内容,跳到下一次循环
    • 1193-1195:从launchInfo->pre_exec_commands()中每一个command,放到preExecCommands变量中
    • 1198-1203:若launchInfo有相关环境设置,也放在环境变量中
    • 1205-1207:若launchInfo有名字空间,也放在namespaces中
  • 1210-1212:设置命令:executorLaunchCommand
  • 1215-1218:若有provisionInfo,命令中加入rootfs选项(有相应的容器镜像,才有provisionInfo,见847行和844行)
  • 1220:调用logger->prepare(logger定义在containerizer.hpp第284行,类型是mesos::slave::ContainerLogger)
  • 1221-1350:完成prepare后,调用lambda函数,该函数执行以下操作:
    • 1227-1231:创建操作系统管道pipes
    • 1236:设置launch命令
    • 1238:若executor没有rootfs,意味着与主机共享文件系统
      • 1242-1247:若有工作目录,输出警告
      • 1249:设置工作目录
    • 1250-1254:否则,设置工作目录为参数指定目录或sandbox目录
    • 1269-1270:设置launchFlags的根文件系统和用户名
    • 1274-1275:设置launchFlags的读写管道
    • 1282:设置launchFlags的preExecCommands
    • 1288-1290:设置启动命令参数,程序名固定为MESOS_CONTAINERIZER,值为"mesos-containerizer",这个命令对应的源代码在src/slave/containerizer/mesos/main.cpp。argv[1]值为"launch"(launch.cpp 53行)
    • 1292-1303:调用launcher->fork创建子进程(launcher是MesosContainerizerProcess的私有变量,在初始化时设置,可以是linuxlauncher或者posixlauncher
    • 1308:得到fork子进程的pid
    • 1311-1331:若有checkpoint,则执行相关动作。。。。
    • 1335-1337:。。。。。。。。。
    • 1339-1349:执行isolate,再执行fetch,再执行exec。。。。。

MesosContainerizerProcess::prepare

参数:

  • containerId
  • taskInfo
  • executorInfo
  • directory
  • user
  • provisionInfo

流程:

  • 990-1001:判断container如果被删除,则返回失败;判断container状态必须是PROVISIONING
  • 1003:将container状态改为PREPARING
  • 1006-1025:初始化containerConfig,设置目录、executor、taskInfo、user、容器镜像等信息,尤其注意1022-1023将从docker manifest中得到的信息都拷贝到containerConfig
  • 1030-1031:构建一个异步的ContainerLaunchInfo列表
  • 1033-1040:对isolators(是MesosContainerizerProcess对象中的私有变量)中的每个isolator:
    • 1035-1039:调用_prepare,得到新的ContainerLaunchInfo列表收集到列表中
  • 1042:返回列表

_prepare

  • 971:调用isolator->prepare
  • 972:将此次的ContainerLaunchInfo放入列表最后,得到新的列表

针对docker的prepare

src/slave/containerizer/mesos/isolators/docker/runtime.cpp

DockerRuntimeIsolatorProcess::prepare

参数:

  • containerId
  • containerConfig

准备各种环境变量、要执行的命令等。

MesosContainerizerProcess::exec

  • 1400-1408:检查容器状态
  • 1412-1420:写管道,直到写成功为止,启动MesosContainerizerLaunch对象,见MesosContainerizerLaunch::execute
  • 1422:容器状态改为RUNNING

MesosContainerizerProcess::destroy

参数:

  • containerId

流程:

  • 1598-1613:有多种可能调用本函数,因此需要判断本容器ID是否还存在
  • 1615:获得容器对象container
  • 1617-1620:若容器状态是DESTROYING,则返回
  • 1624-1642:若容器状态是PROVISIONING,将状态改为DESTROYING,并等待provision完成后调用____destroy并返回
  • 1644-1663:若容器状态是PREPARING,将状态改为DESTROYING,对容器的launchInfos调用___destroy并返回
  • 1665-1677:若容器状态是ISOLATING,将状态改为DESTROYING,对容器的isolation调用_destroy并返回
  • 1680-1682:若容器状态是FETCHING,调用fetcher->kill(containerId)
  • 1684:将状态改为DESTROYING,调用_destroy并返回

其他destroy时的主要动作有:

  • MesosContainerizerProcess::____destroy中会调用provisioner->destroy(containerId)
  • MesosContainerizerProcess::_____destroy中会设置。。。。。。

一个执行使用mesos引擎执行docker镜像容器的例子

sudo mesos/mesos-1.0.1/build/src/mesos-execute      \
--master=192.168.10.34:5050          \
--name=test                          \
--docker_image=ubuntu:14.04          \
--containerizer=mesos                \
--command="dd if=/dev/zero of=yhw bs=1024 count=1"   \
--shell=true
  • slaveID 190e0cc6-594c-4bfe-b9cb-5b88dd3cfa09-S0
  • 创建了framework 190e0cc6-594c-4bfe-b9cb-5b88dd3cfa09-0000
  • 容器ID 589e83d2-c707-493a-bd7a-ff4d2c9357eb

容器镜像最上层目录: slaveworkdir/provisioner/containers/589e83d2-c707-493a-bd7a-ff4d2c9357eb/backends/aufs/rootfses/c78b3ef2-d680-40a7-84b6-4416f159ee15

slaveworkdir/provisioner/containers/ContainerID/backends/aufs/rootfses/UUID

使用AUFS选项mount目录:

dirs=/home/yhw/work/swcontainer/slaveworkdir/provisioner/containers/589e83d2-c707-493a-bd7a-ff4d2c9357eb/backends/aufs/scratch/c78b3ef2-d680-40a7-84b6-4416f159ee15/workdir:/tmp/mesos/store/docker/layers/9bc9537638433df5e03e4327bb57c9aa9f1372f9985928d7562a857e242b377d/rootfs:/tmp/mesos/store/docker/layers/3436fb2d153cec3ec1981b7f49bc69d88705b5864e95fdf17a13b316347da00b/rootfs:/tmp/mesos/store/docker/layers/2aaf3127c72c32cb81fe7085062eb83dc3727edd9d6fd2e02e5d47aba89b4cd8/rootfs:/tmp/mesos/store/docker/layers/460a0b713cac353ba041c303e786d6a068f515e5362f14efcf211cc7c243556f/rootfs:/tmp/mesos/store/docker/layers/39cdc7007d14204df14774895ef95342980fca2774808ebec5dfb6efa08c0e66/rootfs

第一层是创建的临时目录,可写;后面的以/tmp/mesos/store开头的目录是ubuntu:14.04镜像所在的本地目录,每个目录代表镜像的一层;这些目录都将mount到容器的根目录:/home/yhw/work/swcontainer/slaveworkdir/provisioner/containers/589e83d2-c707-493a-bd7a-ff4d2c9357eb/backends/aufs/rootfses/c78b3ef2-d680-40a7-84b6-4416f159ee15

所以,容器的根目录是slaveworkdir/provisioner/containers/ContainerID/backends/aufs/rootfses

看起来,只是mount了根目录,未执行容器的命令????

无法执行交互式的命令??通过容器启动redis服务是可以的。

results matching ""

    No results matching ""