基于 Mesos 的当当作业云 Elastic Job Cloud

  • 张亮

2016 年 9 月 18 日

话题:DevOps语言 & 开发架构文化 & 方法

作业在互联网电商行业中是很常见的技术手段。当当具有成千上万的作业规模,大致分为业务类、归档类、监控类和大数据类。

当当分布式调度原方案

当当原有的作业解决方案是 Elastic Job,以下称为 Elastic Job 1.X。主要关注以下 4 个方面:

  1. 高可用。这里高可用指主从热备。主节点提供服务的同时从节点处于等待状态。一旦主节点失效,其中一个从节点通过选举成为主节点继续提供服务,原来的主节点再次生效后,作为从节点继续等待选举机会。
  2. 线性扩展。可理解为高可用的高阶功能。既然多节点参与服务提供,若从节点只负责热备,难免造成资源浪费,将主备调整为分片,则可大幅提升处理能力。中心化分布式服务基本都是提供独立的调度中心和执行节点,调度中心提供主备,执行节点可以弹性的线性扩展。Elastic Job1.X 采用无中心化架构,并无调度中心,而是各个执行节点通过 Zookeeper 选举主节点进行分片、清理等动作,作业调度由执行节点自行触发。Elastic Job 1.X 通过分片进行线性扩展,每个执行节点负责处理被分配到的分片项,应用开发者负责分片项和业务逻辑的对应关系。代码示例:
public class MyElasticJob implements SimpleJob {

    @Override
    public void process(ShardingContext context) {
        switch (context.getShardingItem()) {
            case 0: 
                // do something by sharding item 0
                break;
            case 1: 
                // do something by sharding item 1
                break;
            case 2: 
                // do something by sharding item 2
                break;
            // case n: ...
        }
    }
}
  1. 容错性。主要包括节点失效重分片、失效转移、错过任务重执行以及分布式场景下的脑裂,网络抖动容错等。
  2. 异构语言。Elastic Job 支持 Java 和 Shell,基本能做到异构语言支持。

Elastic Job 的核心组件图如下:

绿色部分是 Elastic Job 1.X 对开发者提供的 API。蓝色部分为内部实现,主要有选举模块、分片模块、调度模块、持久化模块以及分布式协调模块。持久化和协调均基于 Zookeeper。

部署架构图如下:

Elastic Job 1.X 与应用代码部署在一起,作为 lib 提供服务。Elastic Job 1.X 和运维平台通过注册中心进行交互。

Mesos 对云平台解决方案的革新

花了这些篇幅介绍背景,目的是让大家了解当当原有的分布式作业解决方案,接下来将阐述新一代的平台化作业解决方案。

随着时代发展进步,分布式操作系统、容器以及各式各样的容器编排治理方案层出不穷,站在巨人的肩膀上,可以更快速稳定的搭建云平台,基于这样的考虑,我们决定拥抱革新。我们首先将目光投在了 Docker 上,希望将 Elastic Job 容器化,然后再选择一个容器治理方案。经过调研,我们转而倾心 Mesos,因为我们的目标不是容器化,而是云化,容器是实现云的一种手段,但合理的分布式编排系统更加关键。Docker 本身有网络、日志采集等问题需要解决。而 Mesos 将 Docker 作为可选容器支持,为了最小化采用新技术带来的风险,我们决定将 Docker 容器化推迟,先将 Elastic Job 迁移至 Mesos。这也得益于 Mesos 对 Docker 的无缝支持,以后加入对 Docker 的支持非常容易。

下图是 Mesos 的核心概念:

蓝色是 Mesos 的基础组件,由 Mesos Master、Mesos Agent 和运维界面 /API 组成。Master/Agent 的形式在分布式系统中很常见,如 Hadoop map-reduce 的 JobTracker/TaskTacker。Mesos Master 节点通过 Zookeeper 实现高可用,用于分配资源至注册在 Mesos 的 Framework,Mesos Agent 用于收集宿主机资源,如可用 CPU、内存、甚至 GPU 等,并负责执行 Mesos Framework 分发的任务。Mesos Master 与 Slave 间使用内部 API 交互,通过远程执行部署在 Mesos Agent 的程序无需 Mesos Agent 所在服务器提供免登录账号。

紫色是 Mesos Framework 的组成部分。Mesos 只有基础组件并不能独立使用,需要注册 Framework 接收 Mesos Master 分配的资源并决定如何执行,目前常见的 Marathon 和 Chronos 都是 Mesos Framework。Framework 的两个重要组成是 Scheduler 和 Executor。Scheduler 和 Mesos Master 通过 Scheduler API 交互,负责将分配的资源生成任务并分发。Executor 通过 Executor API 与 Mesos Agent 交互,负责执行分配的任务并回告状态。

红色是 Scheduler 中两个最重要的回调方法,resourceOffers 和 statusUpdate。

resourceOffers 用于将资源转化为任务并调用 Executor 执行。资源分配算法,任务业务逻辑都应在此方法中实现。

statusUpdate 用于处理 Executor 回传的状态。可通过 Executor 的状态传递和 Scheduler 的 statusUpdate 实现失效转等的分布式协调功能。

Executor 分为两种。

Marathon 和 Chronos 都使用 Command Executor,这也是 Mesos 提供的内置的 Executor,可以直接执行命令行脚本,也提供了基本的状态回传功能,对于普通应用使用 Command Executor 是不错的选择。

自定义 Executor 可提供更多灵活性。下面举几个例子,但不限于仅实现这些功能。

  1. 日志重定向。Command Executor 的日志会输出到 Mesos 提供的沙箱中,通过自定义 Executor 可将日志重定向到其他文件,或对接其他日志收集工具。
  2. 多任务复用。Command Executor 与任务是一对一的关系。有些初始化资源非常耗时的场景,可以复用 Executor,多线程并发处理多任务。
  3. 执行进度报告。Command Executor 仅回告状态变化,如需更加细化进度回告功能,需定制化处理。
  4. 心跳检测。可以通过 Executor 定时传递消息做心跳检测。

简单的介绍了 Mesos,是该讨论下 Mesos 能带来什么好处的时候了。Mesos 可作为部署平台和运行平台使用。

作为部署平台,Mesos 可以做到应用分发自动化。将应用上传到网络地址(或 Docker 仓库),Mesos 能够自动将应用分发至需要执行相关任务的 Mesos Agent,省去人工部署成本。由于应用自动分发,那么 CMDB 将大大简化,只需要管理 Zookeeper 服务器即可,Mesos Master、Mesos Agent 以及其他部署在 Mesos 内部的应用均可用 Mesos 界面统一管理。美中不足是 Mesos 目前缺乏管理数据类服务器的成功案例,数据库等部署在 Mesos 之外的应用仍需独立的 CMDB 管理。

作为运行平台,Mesos 可以将资源收集至统一资源池,做到硬件资源与应用一体化,按需分配资源,自动资源回收,减少资源闲置和浪费。

应用分发、资源分配和安全管理,即可形成云平台的雏形,由于当当仅搭建私有云,安全管理暂时不是重点。

现在再分析 Elastic Job 1.X 的解决方案,应用分发和资源分配部分是缺失的。

基于 Mesos 开发作业云 Elastic Job Cloud

决定基于 Mesos 搭建作业云,我们开始调研实现方案。首先想到自然是拥抱开源,不重复发明轮子。方案 A 是使用 Marathon 作为调度框架。

方案 A 的优点是可以实现应用分发和资源分配,并且是高可用的解决方案。

但缺点同样明显:

  1. 资源分配静态化,作业无论执行与否均需预分配资源;
  2. Executor 无法复用,对于 Elastic Job 这种对内部状态报告有要求的框架,需要在框架中通过第三方存储汇报状态;
  3. 跨机房等定制化功能较困难;
  4. 使用繁琐,需要使用 Mesos、Marathon 和 Elastic Job 3 个管理界面。

另一种类似的方案是使用 Chronos,可以做到动态资源分配,但缺点是:

  1. 不支持 CRON 表达式,对于原 crontab 或 Quartz 的应用迁移不友好;
  2. 不适合低延迟作业,每次启动初始化资源会消耗时间,导致作业延迟,反复初始化也容易造成资源浪费;
  3. 定制功能仍然困难。

说到这里需要抛出常驻作业和瞬时作业概念了。

常驻作业是作业一旦启动,无论运行与否均占用系统资源;瞬时作业是在作业启动时占用资源,运行完成后释放资源。

常驻作业适合初始化时间长、触发间隔短、实时性要求高的作业,要求资源配备充足。

瞬时作业适合初始化时间短、触发间隔长、允许延迟的作业,一般用于资源不太充分,或作业要求的资源多,适合资源错峰使用的场景。

由于上述缺点,我们最终选择方案 B。

我们决定自行开发 Mesos Framework,项目的名称叫做 Elastic Job Cloud。优点:

  1. 包含方案 A 的全部优点;
  2. 资源分配静态与动态相结合,常驻与瞬时作业分离处理;
  3. Elastic Job 1.X 的核心理念仍可沿用,包括分片,以及之前未提及的功能,如多种作业类型,事件统计等;
  4. 易于定制化需求开发。

Elastic Job Cloud 核心组件图如下:

绿色的 API 部分变化不大;红色的内部实现部分,无论是模块本身,还是模块的内部实现均有大幅度调整。

选举模块由执行节点主节点选举变更为主 Mesos Framework 选举,仍然依赖 Zookeeper。

Sharding 模块功能未变,但从执行节点主节点分片调整为 Mesos Framework 集中分片。Elastic Job 1.X 的分片以 IP 为依据,Elastic Job Cloud 更改为以运行任务实例为依据。

调度引擎从去中心化执行节点调度转变为 Mesos Framework 的中心节点调度。

通过调度作业向任务队列中写入待执行作业。

日志输出方式变更为事件发布,可自行监听发布的事件,收集 Elastic Job Cloud 的运行状态,默认提供日志和数据库(推荐)两种事件收集方式。

通过 Executor 和 Scheduler API 配合进行分布式协调,替换通过 Zookeeper 协调的方案,有效的减少了与 Zookeeper 的连接数。

Elastic Job Cloud 作为 Mesos Framework 独立部署,将作业信息存入 Zookeeper。

虽然 Elastic Job Cloud 可以提供中心化云解决方案,但 Elastic Job 1.X 作为无中心化解决方案,仍适用于中小规模的分布式作业调度场景。我们并未放弃 Elastic Job 1.X,将其更名为 Elastic Job Lite。Elastic Job Lite 和 Elastic Job Cloud 共同组成 Elastic Job,核心组件全景图如下:

Elastic Job Lite 和 Elastic Job Cloud 使用统一的 Elastic Job API,开发者在开发时无需关心最终的部署方式。部署在 Lite 或 Cloud 环境无需修改业务作业代码,秩序调整配置和启动方式即可。Lite 和 Cloud 的差别仅在于云平台需要的应用分发和资源调度一体化。

目前 Elastic Job Cloud 已开发完成并在公司内部试用。日前对接监控系统的万余作业,全天运行峰值百万左右。

Elastic Job Cloud 的 roadmap 是:

  1. 公司内部大规模推广使用;
  2. Docker 支持;
  3. 任务优先级、依赖以及编排的支持;
  4. 跨机房部署、物理机隔离等功能开发。

回馈开源社区

目前 Elastic Job 已全部开源。项目地址,欢迎感兴趣的人踊跃尝试。

最后我分享下我们这 4 个月以来的里程碑。

本文由当当网架构专家张亮在CNUTCon 北京 2016 全球容器技术大会上的演讲整理而成。

DevOps语言 & 开发架构文化 & 方法