Juice-一种基于MesosFramework的任务云框架

634 阅读21分钟
原文链接: mp.weixin.qq.com

作者:徐佳

本文为原创文章,转载请注明作者及出处 

在介绍 Juice 之前,我想先聊一聊 Mesos,Mesos 被称为 2 层调度框架,是因为 Master 通过内部的 Allocator 完成 Master->Framework 的第一层调度,再由 Framework 通过调度器完成对于资源 -> 任务的分配,这个过程称为第二层调度。

About MesosFramework

先来看一看 Mesos&Framework 的整体架构图:

Mesos 的 Framework 分为 2 部分组成,分别为调度器和执行器。

调度器被称为 Scheduler,从 Mesos1.0 版本开始,官方提供了基于 HTTP 的RestAPI 供外部调用并进行二次开发。 

Scheduler 用于处理 Master 端发起的回调事件(资源列表并加载任务、任务状态通知等),进行相应处理。Agent 接收到 Master 分配的任务时,会根据任务的 container-type 进行不同的处理,当处理默认 container-type=’Mesos’ 时,先检查 Framework 所对应的 Executor 进程是否启动,如果没有启动则会先启动 Executor 进程,然后再提交任务到该 Executor 去执行,当运行一个container-type=’Docker’ 的任务时,则启动 Docker Executor 进行处理,程序的运行状态完全取决于 Docker 内部的处理及返回值。

MesosFramework 交互 API

交互分为 2 部分 API,分别 为SchedulerAPI (http://mesos.apache.org/documentation/latest/scheduler-http-api/) 与ExecutorAPI(http://mesos.apache.org/documentation/latest/Executor-http-api/), 每个 API 都会以 TYPE 来区分,具体的处理流程如下:

  1. Scheduler 提交一个请求(type=’SUBSCRIBE’)到 Master(http://master-ip:5050/api/v1/scheduler), 并需要设置 ’subscribe.framework_info.id’,该 ID由 Scheduler 生成,在一个 Mesos 集群中必须保证唯一,Mesos 以此FrameworkID 来区分各个 Framework 所提交的任务,发送完毕后,Scheduler端等待 Master 的 ’SUBSCRIBE’ 回调事件,Master 的返回事件被定义在 event对象中,event.type 为 ’SUBSCRIBE’ (注意:’SUBSCRIBE’ 请求发起后,Scheduler 与 Master 端会保持会话连接(keep-alive),Master 端主动发起的事件回调都会通过该连接通知到 Scheduler)。(scheduler-http-api 中接口 ’SUBSCRIBE’)

  2. Master 主动发起 ’OFFERS’ 事件回调,通知 Scheduler 目前集群可分配使用资源,事件的 event.type 为 ’OFFERS’。(scheduler-http-api 中接口’OFFERS’)

  3. Scheduler 调用 resourcesOffer 为 Offers 安排 Tasks。当完成任务分配后,主动发起 ’ACCEPT’ 事件请求到 Master 端告知 Offers-Tasks 列表。(scheduler-http-api 中接口 ’ACCEPT’)

  4. Master 接收到 Scheduler 的任务请求后,将任务发送到 OfferId 对应的Agent 中去执行任务。

  5. Agent 接收到任务,检查任务对应的 Executor 是否启动,如启动,则调用该 Executor 执行任务,如未启动,则调用 lauchExecutor() 创建 Executor对象并执行 initialize() 初始化 Executor,Executor 初始化过程中会调用RegisterExecutorMessage 在 Agent 上注册,之后便接受任务开始执行。(Executor-http-api 中接口 ’LAUNCH’)

  6. Executor 执行完毕或错误时通知 Agent 任务的 task_status。(Executor-http-api 中接口 ’UPDATE’)

  7. Agent 再同步 task_status 给 Master,Master 则调用 ’UPDATE’ 事件回调,通知 Scheduler 更新任务状态。(scheduler-http-api 中接口 ’UPDATE’)

  8. Scheduler 确认后发送 ’ACKNOWLEDGE’ 请求告知 Master 任务状态已确认。(scheduler-http-api 中接口 ’ACKNOWLEDGE’)

任务状态标示及Agent宕机处理

对于一个任务的运行状态,Mesos 定义了 13 种 TASK_STATUS 来标示,常用的有以下几种:

TASK_STAGING-任务准备状态,该任务已有Master分配给Slave,但Slave还未运行时的状态。
TASK_RUNNING-任务已在Agent上运行。
TASK_FINISHED-任务已运行完毕。
TASK_KILLED-任务被主动终止,调用scheduler-http-api中'KILL'接口。
TASK_FAILED-任务执行失败。
TASK_LOST-任务丢失,通常发生在Slave宕机。

当 Agent 宕机导致 TASK_LOST 时,Mesos 又是怎么来处理的呢?

在 Master 和 Agent 之间,一般都是由 Master 主动向每一个 Agent 发送 Ping消息,如果在设定时间内(flag.slave_ping_timeout,默认 15s)没有收到Agent 的回复,并且达到一定次数(flag.max_slave_ping_timeouts,默认次数为 5),那么 Master 会操作以下几个步骤:

  1. 将该 Agent 从 Master 中删除,此时该 Agent 的资源将不会再分配给Scheduler。

  2. 遍历该 Agent 上运行的所有任务,向对应的 Framework 发送任务的Task_Lost 状态更新,同时把这些任务从 Master 中删除。

  3. 遍历该 Agent 上的所有 Executor,并删除。

  4. 触发 Recind Offer,把这个 Agent 上已经分配给 Scheduler 的 Offer 撤销。

  5. 把这个 Agent 从 master 的 Replicated log 中删除(Mesos Master 依赖Replicated log 中的部分持久化集群配置信息进行 failer over/recovery)。

使用 Marathon 可以方便的发布及部署应用

目前有很多基于 MesosFramework 的开源框架,例如 Marathon。我们在生产环境中已经使用了 Marathon 框架,一般用它来运行 long-run service/application,依靠 marathon 来管理应用服务,它支持应用服务自动/手动起停、水平扩展、健康检查等。我们依靠 jenkins+docker+marathon 完成服务的自动化发布及部署。

Why Juice

下面来讲下我基于 MesosFramework 所开发的一套框架-Juice。(开源地址:https://github.com/HujiangTechnology/Juice.git

在开发 Juice 之前,我公司所有的音视频转码切片任务都是基于一个叫TaskCenter 的队列分配框架,该框架并不具备分布式调度的功能(资源分配),所以集群的资源利用率一直是个问题。因此,我们想开发一套基于以下三点的新框架来替代老的 TaskCenter。

  • 一个任务调度型的框架,需要对资源(硬件)尽可能的做到最大的利用率。

  •  框架必须可运行各种类型的任务。 

  • 平台必须是稳定的。

凭借对 Marathon 的使用经验,以及对于 Mesos 相关文档的查阅,我们决定基于 MesosFramework 来开发一套任务调度型的框架,Mesos 与 Framework 的特性刚才已经说过了,而我们将所需要执行的任务封在 Docker 中去执行,那么对于框架本身来说,就不用关心任务的类型了。

这样业务的边界和框架的边界就变得很清晰,对于 Framework 来说,运行一个 Docker 任务也很方便,刚才说过 Mesos 内置了 DockerExecutor 可以完美的启动 Docker 任务,这样,我们的框架在 Agent 端所需要的开发就非常的少。

Juice 框架在这样的背景下开始了开发的历程,我们对于它的定位是一套分布式任务云系统,这里为什么要称为任务云系统呢?

因为对于调用者来说,使用 Juice,只要做 2 件事情:把要做的任务打成 Docker 镜像并 push 到 docke r仓库中,然后向 Juice 提交一个 Docker 类型的任务。其它的,交给 Juice 去完成就可以了,调用者不用关心任务会在哪台物理机上被执行,只需要关心任务本身的执行状况。

Juice 架构

除此,Juice 有以下一些特点,Juice 框架分为 Juice-Rest(Juice 交互 API 层,可以完成外界对于 Juice Task 的 CRUD 操作)和 Juice-Service(Juice 核心层,负责与 MesosMaster 之间的交互,资源分配、任务提交、任务状态更新等),在一套基于 Juice 框架的应用系统中,通常部署 1-N 个 Juice-Rest(取决于系统的 TPS),以及 N 个 Juice-Service(Juice-Service 分主从模式,为 1 主多从,by zookeeper),对于同一个 Mesos 集群来说,可以部署 1-N 套Juice 框架,以 FrameworkID 来区分,需要部署多套的话在 Juice-Service 的配置文件中设置 mesos.framework.tag 为不同的值即可。

Juice-Rest参数设置

Juice-Rest 采用 Spring-Boot 编写(Juice-API 接口参见:https://github.com/HujiangTechnology/Juice/blob/master/doc/api_document.md), 处理外界发起的对任务 CURD 操作,当提交一个任务到 Juice-Rest 时,需要设置一些参数,比如:

example to run docker:
{
 "callbackUrl":"http://www.XXXXXXXX.com/v5/tasks/callback",
 "taskName":"demo-task",
 "env":{"name":"environment","value":"dev"},
 "args":["this is a test"],
   "container":{
     "docker":{
       "image":"dockerhub.XXXX.com/demo-slice"
   },
     "type":"DOCKER"
 }
}

其中 Container 中的 type 目前仅支持 ’Docker’,我们没有加入 ’Mesos’ 类型的Container 模式是因为目前项目组内部的服务已经都基于 Docker 化,但是预留了 ’Mesos’ 类型,在未来可以支持 ’Mesos’ 类型的任务。

Commands 模式支持运行 Linux 命令行命令和 Shell 脚本,比如:

"commands":"/home/app/entrypoint.sh"

这里支持 Commands 模式的原因有 2 点:

  1. 有时调用方可能只是想在某台制定的 Agent 上运行一个脚本。

  2. 公司内部其他有些项目组还在使用 Jar 包启动的模式,预留一个 Shell 脚本的入口可以对这些项目产生支持。

env 设置示例,设置运行的任务环境为 dev:

"env":{"name":"environment","value":"dev"}

args 设置示例,设置文件路径:

"args":["/tid/res/test.mp4"]

PS:使用 Commands 模式时不支持 args 选项。

此外,Juice-Rest 支持用户自定义资源大小(目前版本仅支持自定义 CPU、内存),如需要指定资源,需在请求接口中配置 resources 对象,否则,将会使用默认的资源大小运行任务。Juice-Rest 支持资源约束(constrains),即满足在特定 Host 或 Rack_id 标签的 Agent 上运行某任务,设置接口中 constrains 对象字段即可。

Juice 所使用的中间件(MQ、DB等)

下面讲一下 Rest 层的处理模型,当外界发起一个任务请求时,Juice-Rest 接收到任务后,并不是直接提交到 Juice-Service 层,而是做了以下 2 件事情:

  1. 将任务放入 MQ 中。(目前 Juice 使用 Redis-List 来作为默认的 Queue,采用 LPUSH、RPOP 的模式,先进先出。)

    为什么选择使用 Redis 中的 List 作为 Queue 而没有选择其他诸如 rabbitmq、kafka 这些呢?

    首先,Redis 相对来说是一个比较轻量级的中间件,而且 HA 方案比较成熟。同时,在我看来,队列中的最佳任务 wait 数量是应该 <10000 的,否则,任务的执行周期将会被拉得很长,以我公司的Juice系统来举例,由于处理的都是耗时的音视频转码切片任务,通常情况下 10000 个任务的排队等候时间会在几个小时以上,所以当任务数量很大时,考虑扩大集群的处理能力而不是把过多的任务积压在队列中。

    基于此,选择 Redis-List 相对其他的传统MQ来说没有什么劣势。考虑到一些特殊情况,Juice 也允许用户实现 CacheUtils 接口使用其他 MQ 替换 Redis-List)。

  2. 纪录 Tasks 信息到 Juice-Tasks 表中,相当于数据落地。后续版本会基于此实现任务重试机制(目前的 1.1.0 内部开发版本已实现),或者在 failover切换后完成任务恢复,此功能在后续 1.2.0 版本中考虑加入。(目前数据库使用 MySql)。

当 Juice-Rest 接受并完成任务提交后会返回给调用方一个 Long 型 18 位数字(JuiceID,全局唯一)作为凭证号。当任务完成后,Juice-Rest 会主动发起回调请求,通知调用方该任务的运行结果(以此 JuiceID 作为业务凭证),前提是调用方必须设置 callbackUrl。同时,调用方可以使用该 JuiceID 对进行任务查询、终止等操作。

另外,在 Juice-Rest 层单独维护一个线程池来处理由 Juice-service 端返回的任务状态信息 Task_status。

Juice-Service内部处理流程

Juice-Service 可以看作是一个 MesosFramework,与 Master 之间通讯协议采用 ProtoBuf,每一种事件请求都通过对应类型的 Call 产生,这里 Juice-Service启动时会发出 Subscribe 请求,由 SubscribeCall() 方法产生 requestBody,采用 OKHTTP 发送,并维持与 Master 之间的长连接

private void connecting() throws Exception {
       InputStream stream = null;
       Response res = null;

       try {
           Protos.Call call = subscribeCall();
           res = Restty.create(getUrl())
                   .addAccept(protocol.mediaType())
                   .addMediaType(protocol.mediaType())
                   .addKeepAlive()
                   .requestBody(protocol.getSendBytes(call))
                   .post();

           streamId = res.header(STREAM_ID);
           stream = res.body().byteStream();
           log.info("send subscribe, frameworkId : " + frameworkId + " , url " + getUrl() + ", streamId : " + streamId);
           log.debug("subscribe call : " + call);
           if (null == stream) {
               log.warn("stream is null");
               throw new DriverException("stream is null");
           }
           while (true) {
               int size = SendUtils.readChunkSize(stream);
               byte[] event = SendUtils.readChunk(stream, size);

               onEvent(event);
           }
       } catch (Exception e) {
           log.error("service handle error, due to : " + e);
           throw e;
       } finally {
           if (null != stream) {
               stream.close();
           }
           if (null != res) {
               res.close();
           }
           streamId = null;
       }
}

之后便进入 while 循环,当 Master 端的通知事件发生时,调用 onEvent() 方法执行。

Mesos 的回调事件中,需要特别处理的主要事件由以下几种:

  1. SUBSCRIBED:Juice 框架在接收到此事件后将注册到 Master 中的FrameworkID 纪录到数据库 Juice_framework 表中。

  2. OFFERS:当 Juice-Service 接收到该类型事件时,便会进入资源/任务分配环节,分配任务资源并提交到 MesosMaster。

  3. UPDATE:当 Agent 处理完任务时,任务会由 Executor->Agent->Master->Juice-Service 来完成任务的状态通知。Juice-Service 会将结果塞入 result-list 中。

  4. ERROR:框架产生问题,通常这样的问题分两种,一种是比较严重的,例如 Juice-Service 使用了一个已经被 Master 端移除的 FrameworkID,则Master 会返回 ”framework has been removed” 的错误信息,Juice-Service此时会抛出 UnrecoverException 错误:

    throw new UnrecoverException(message, true)

Juice-Service 在处理 UnrecoverException 类的错误时会 Reset 服务,当第二个参数为 True 时,会重新生成一个新的 FrameworkID。

而当其他类型的错误,比如 Master 和 Juice-Service 之间的长链接中断,仅仅Reset 服务。

下面我想详细来说说第二步,我们先来看下 ’OFFERS’ 请求处理代码段:

private void onEvent(byte[] bytes) {
 ....
 switch (event.getType()) {
           ...
           case OFFERS:
               try {
                   event.getOffers().getOffersList().stream()
                           .filter(of -> {
                               if (SchedulerService.filterAndAddAttrSys(of, attrMap)) {
                                   return true;
                               }
                               declines.add(of.getId());
                               return false;
                           })
                           .forEach(
                                   of -> {
                                       List<TaskInfo> tasks = newArrayList();
                                       String offerId = of.getId().getValue();
                                       try {
                                           SchedulerService.handleOffers(killMap, support, of, attrMap.get(offerId), declines, tasks);
                                       } catch (Exception e) {
                                           declines.add(of.getId());
                                           tasks.forEach(
                                                   t -> {
                                                       AuxiliaryService.getTaskErrors()
                                                               .push(new TaskResult(com.hujiang.juice.common.model.Task.splitTaskNameId(t.getTaskId().getValue())
                                                                       , ERROR, "task failed due to exception!"));
                                                   }
                                           );
                                           tasks.clear();
                                       }
                                       if (tasks.size() > 0) {
                                           AuxiliaryService.acceptOffer(protocol, streamId, of.getId(), frameworkId, tasks, getUrl());
                                       }
                                   }
                           );

                   if (declines.size() > 0) {
                       AuxiliaryService.declineOffer(protocol, streamId, frameworkId, SchedulerCalls.decline(frameworkId, declines), getUrl());
                   }
                   long end = System.currentTimeMillis();
               } finally {
                   declines.clear();
                   attrMap.clear();
               }
               break;
           ...    
   }  
}

该段代码是分配 Offer-tasks 的核心代码,来看几个方法:

  1. SchedulerService.filterAndAddAttrSys(),该方法作用是过滤不符合的 OFFER,我们知道在 Mesos 的 Agent 中是可以通过配置 Attr 来使一些机器跑特殊的任务,而这里的过滤正是基于该特性,比如我们设置了该 Juice-Service 只使用包含以下 Attr 属性的资源时(在配置文件application.properties 中)

    mesos.framework.attr=lms,qa,mid|big

    经过了 SchedulerService.filterAndAddAttrSys() 方法的过滤,符合以上 attr的资源会被选取执行任务。同时不符合的 Offer 会加入 declines List,通过AuxiliaryServic.declineOffer() 一次性发送给 Master 告知忽略。Agent 的 attr 设置通过 /etc/mesos-slave/attributes 来设置。这个文件通常为这样的:

    cat /etc/mesos-slave/attributes

    bz:xx;
    env:xx;
    size:xx;
    rack_id:xx;
    dc:xx
  2. SchedulerService.handleOffers(),该方法实现了原先 MesosFramework 中的 resourceOffer 的功能,对 Offer 进行 Tasks 分配,最后产生 TaskInfo List,由 AuxiliaryService.acceptOffer() 发送给 Master 通知处理任务。

    注意:Master 在发送完 Offer 事件通知后会一直处于 wait 状态,直到Framework 端调用 Accept call(AuxiliaryService.acceptOffer()) 或 Decline call(AuxiliaryServic.declineOffer()) 来告知 Master 资源是否使用后才会通知下一个 Framework 去分配资源。(默认 Master 会一直等待,如果没有通知,则 Mesos 集群中的资源利用率将可能达到 100%,可以通过在 Master端设置 Timeout 来避免这个问题。)

    在 Juice-Service 内部,当 SchedulerDriver 与 Master 产生交互后,Juice-Service 的处理逻辑由 SchedulerService 以及 AuxiliaryService 来实现。SchedulerService 处理 Juice 的主要逻辑,比如资源分配算法、任务优先级算法,所有 Master 回调事件处理方法都定义在 SchedulerService 中。

    AuxiliaryService 维护几组线程池,完成各自任务,刚才看到的AuxiliaryService.acceptOffer() 和 AuxiliaryServic.declineOffer(),都是通过调用 AuxiliaryServic 中的 send-pool 去完成 call 的发送,另外还有一些管理类的任务(比如实时查询任务状态、终止正在运行的任务等等)通过auxiliary-pool 去完成。所以,AuxiliaryServic 的调用都是异步的。

Juice 中各种队列的功能介绍

刚才介绍了 Juice 的任务在 JuiceRest 提交时是被放入了一个 MQ 中,这个 MQ 在 Juice-Service 中被称为 juice.task.queue。除此之外,还有另外几个MQ,分别是 juice.task.retry.queue、juice.task.result.queue、juice.management.queue。

下面来分别说说这些 Queue 的用处:

  • juice.task.retry.queue:Juice-Service 在取任务时是按照每一个 Offer 轮询分配的,当一个 Offer 在分配资源时,假如从 MQ 中 R-POP 出来的任务不满足该 Offer 时(比如 need-resources 大于该 Offer 的 max offer value时,或者存在 constrains,当前的 offer 和指定执行任务的 offer 不 match时),这时,Juice-Service 的做法是将当前任务放入 juice.task.retry.queue中,等待下一次 Offer 分配时,优先从 juice.task.retry.queue 获取任务并分配。

    这里涉及到 Juice 内部获取任务 Queue 的优先级,我用了一个比较简单的方式,即每次分配一个新的 Offer 资源时,先从 juice.task.retry.queue 中取出一定数目的任务(CACHE_TRIES = 5),当还有剩余资源时,则从juice.task.queue 中取任务,直到撑满这个 Offer。

    另外,处于 juice.task.retry.queue 会有淘汰机制,目前的任务淘汰机制遵循2 点,当先触发以下某一项时,则该任务会认为失败,任务的 Task_status被设置为 Task_Failed,放入 juice.task.result.queue,任务的淘汰算法如下:

    1.过期时间淘汰制,任务处于juice.task.result.queue的时长>TASK_RETRY_EXPIRE_TIME,则淘汰(DEFAULT_TASK_RETRY_EXPIRE_TIME = 86400秒)。
    2.大于最大检索次数,任务被取出检索但没有被执行达到最大检索次数>MAX_RESERVED,则淘汰(DEFAULT_MAX_RESERVED = 1024)。
  • juice.task.result.queue:任务结果队列,Juice-Service 在得到一个任务的状态后(不一定是最终状态),将任务的 TaskResult 对象放入juice.task.result.queue,Juice-Rest 端从该队列取出 TaskResult,如果已经是任务的最终状态,比如 Task_Finished 或者 Task_Failed,则通过外部在提交任务时所填写的 callbackUrl 回调调用方告知任务状态。

  • juice.management.queue:管理类队列,支持放入 Reconcile 类或 Kill 类的任务,由 AuxiliaryService 发起任务的查询同步或 Kill 一个正在执行的任务。

通过SDK提交一个任务

目前开源的 Juice 版本,已经提供了完整的 SDK 来完成对于 Juice-Rest 之间的交互,以下是提交一个 Docker 任务的示例:

@Test
   public void submitsDocker() {
       Submits submitsDocker = Submits.create()
               .setDockerImage("dockerhub.XXXX.com/demo-slice")
               .setTaskName("demo-slice")
               .addArgs("/10002/res/L2.mp4")
               .addEnv("environment", "dev")
               .addResources(2.0, 2048.0);

       Long taskId = JuiceClient.create("http://your-juice-rest-host/v1/tasks", "your-system-id-in-string")
               .setOperations(submitsDocker)
               .handle();

       if(null != taskId) {
           System.out.println("submitsDocker, taskId --> " + taskId);
       }
   }

总结及未来

目前 Juice 1.1.0 开源版本已经处于测试阶段,新版本除修复一些 Bug 之外,还增加了 2 个新功能:

  1. 增加了任务插队功能,可以通过在传入参数中设置 priority=1 来提高一个任务的执行优先级,该任务会被置于处理队列的最前端。

  2. 任务失败自动重试功能,设置传入参数 retry=1,任务失败会自动重试,最多重试 3 次。

面对复杂的业务需求,Juice 目前的版本还有一些特性/功能不支持,对于此,最好的方式是请大家 Fork 这个项目的 Git,或直接联系本人,大家一起来把Juice 做好。

Q&A:

Q:Juice 与 elastic job 有哪些差异?

A:我本身对于 elastic job 并不算太熟悉,就随便说几点,如果有错还请各位纠正:

首先 Juice 与 elastic-job-cloud 都基于 mesos,资源-任务分配这块 elastic-job用了 Fenzo(netflix),而 Juice 是自己开发的调度算法。

Juice 在作业调用时不需要作业注册,只要上传任务的镜像(Docker)到仓库及任务触发。而 elastic-job 需要注册作业。

Juice 在 Rest-Api 接口上近乎完全和 marathon 一致,方便一些使用惯 marathon 部署 service 的用户。

Juice 目前版本并不支持作业分片。

Q:能详细介绍下任务资源分配这一块的算法吗?

A:之前已经简单介绍过了,通过接收 ’OFFERS’ 事件触发相关任务-资源分配的代码块。

由于得到的 Offer 对象实际为一个列表,处理逻辑会循环为每一个 Offer 分配具体的任务,而每个 Offer 的任务列表总资源(CPU,Memory等)必需小于 Offer resources * RESOURCES_USE_THRESHOLD(资源使用阈值,可通过配置文件 resources.use.threshold 设置,默认 0.8),每分配完一个 Offer 的task_infos 后,便生成 Accept Call 由发送线程池进行发送处理,整个过程都是异步非阻塞的。

Q:所有的任务都存档在 Docker 里面对于一些临时的任务如何处理?

A:临时的任务确实会产生一些垃圾的镜像,需要定期对 Docker 仓库进行清理,一般设置清理周期为 1 个月。

Q:任务系统是是否有帮助用户完成 Docker 封装的操作?

A:目前没有,所以使用者必需会一些 Docker 的基本操作,至少要会打镜像,提交镜像等。当然,像一些 Docker 的设置,比如挂载 volume,网络(bridge、host)等可以在提交任务时通过参数设置。

Q:Mesos 和 kubernetes 的优劣势是什么?

A:其实我主要使用 Mesos,Mesos 相对 K8S 应该是一套更重的系统,Mesos更像是个分布式操作系统,而 K8S 在容器编排方面更有优势(Pod之类)。

End

推荐阅读

沪江OCS分布式任务调度系统Juice终于开源了!

沪江任务调度系统的演化

OCS富文本的解决方案

沪江搜索平台化之路