"山寨"一个 AWS Batch

去年参加了 AWS re:Invent 2016, 最喜欢也最需要的新产品莫过于 AWS Batch. 回来几个月了发现中国区依旧没有上线, 然后就手痒痒"山寨"了一个, 跑着还很稳定. 记录一下"山寨"过程中的思考.

0x00 AWS Batch 拆解

什么是 AWS Batch? 官方介绍:

AWS Batch 让开发人员、科学家和工程师能够轻松高效地在 AWS 上运行成千上万项批处理计算任务。AWS Batch 可根据提交的批处理任务的数量和特定资源要求,动态预置计算资源 (CPU 或 内存优化型实例) 的最佳数量和类型。借助 AWS Batch,您无需安装和管理运行您的任务所使用的批处理计算软件或服务器群集,从而使您能够专注于分析结果和解决问题。AWS Batch 可以跨多种 AWS 计算服务和功能 (如 Amazon EC2 和竞价型实例) 计划、安排和执行您的批处理计算工作负载。

AWS Batch

简单来说就是一个 Producer-Consumer 的异步任务执行系统. 拆解下来有如下几个部分组成:

  • 任务队列, 用于存储异步消息. AWS 上有成熟的 SQS, 开源也有很多实现, 比如 Celery
  • 任务执行程序. 任务执行需要对应的 binary, 为了方便部署, AWS Batch 选择的是 Docker image, 直接指定 docker image 的 name 便可以从 registry pull 下来.
  • 任务调度 engine. 为了提高资源利用率, AWS Batch 支持指定执行程序所需 cpu memory 等, engine 根据需求充分调度, 提高系统利用率
  • Producer 端任务定义. 也就是后续任务执行程序的输入. 如果做一个通用的任务执行系统的话, Docker image name 也可以作为任务定义, 传入消费端.
  • 权限等其他配置. AWS Batch 我猜是基于 AWS ECS 做的, 因此支持指定 Docker image 使用与宿主主机不同的 IAM Role, 更细化控制权限(有关 IAM, 可以参见之前文章 AWS IAM 入门)

拆解完成后, 来看看我们的场景

0x01 业务场景

有一个业务, 每天凌晨需要执行多个批处理任务(任务耗时从几分钟到几个小时不等), 白天基本上没有任务执行. 遇到的问题就是: 想让任务尽快执行完成, 又想节省成本.

之前的办法是, 把任务挤在一个大 instance 上, 调整好并发(最多并发四个执行), 白天空闲.

那么, 如何改造呢?

0x02 可选方案

第一个想法: 上 Yarn 等资源调度

公司现有的资源调度框架, Yarn 是一个选择, 但想想要写 Yarn application 我瞬间就放弃了, 不值当.

第二个想法: autoscaling + ec2

当前在 AWS 中国区, 最简单的方式就是直接使用 ec2 作为独立执行单元调度了(没有 ECS), 既然是耗时耗资源(不是简单的函数执行, 需要几个 GB 内存的 batch 任务), 直接使用 ec2 对应的 instance type, 也不算浪费. 那么参照 AWS Batch 的拆解, 对应的模块如何实现?

  • 任务队列: 可以使用 SQS, 但为了简化系统, master 节点已经有了 MySQL, 直接使用 MySQL 作为任务队列更简单
  • 任务执行程序: 既然有 master 节点, ec2 worker 节点启动时, 直接从 master 拉取最新的执行程序即可.
  • 任务调度 engine: worker 节点启动一个 agent, 使用 polling 的方式从 master 节点获取任务即可.由于任务之间相互独立, 调度的 engine 就简单的下发任务即可
  • Producer 端任务定义: 系统仅仅执行一种任务, 下发的任务就是简单的配置, 作为参数输入到任务执行程序.
  • 权限等其他配置: 整个集群使用同一个 IAM role 和相同的 security group

系统就变成了这个样子:


系统结构

0x03 如何"无损" scale in?

由于任务是定时开始的(凌晨), 扩容使用 scheduled action; 但如何在没有任务执行的时候关闭 ec2 节点而不影响正在执行的任务?

一种方式是根据 SQS 队列中的任务数量, 可以参见 AWS 官方文档: Scaling Based on Amazon SQS. 但是我的任务是比较大的批处理任务, 队列中没有任务, 很有可能任务正在执行中, 如果直接关闭 ec2 instance 进行 scale in 的话, 会导致我的任务执行到一半就失败.

那么有没有其他办法呢? 翻了翻 AWS autoscaling 文档, 发现 AWS 的新功能: Instance Protection for Auto Scaling, 试了一下, 发现 autoscaling 会一直重试 scale-in 的操作, 直到你的 ec2 instance 把 scale-in 保护关闭. 正合我意!

0x04 最终实现

事情想清楚了, 行动就显得简单了.

首先, 给 worker 节点的 IAM 要加上对应的 autoscaling 权限

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "autoscaling:DescribeAutoScalingInstances",
                "autoscaling:SetInstanceProtection"
            ],
            "Resource": "*"
        }
    ]
}

其次, worker 代码中的关键在于:

  1. 每次从 master 获取任务前, 必须先打开 scale-in 保护
  2. 强烈建议累计 N 次获取不到任务时, 才关闭 scale-in 保护, 避免任务队列中依然有任务, 但 worker 节点在上一个任务执行完毕到获取到下一个任务的间隙, 被 autoscaling 关闭
# 记录连续没有获取到 task 的次数
task_poll_miss_count = 0
while True:
    # 打开 scale-in 保护
    protect_from_scalein()
    try:
        # 从 master 节点获取任务
        task = get_task_from_master()
        if task is not None:
            # 如果任务不为空, 则执行任务
            task_poll_miss_count = 0
            execute_task(task)
        else:
            task_poll_miss_count += 1
    finally:    
            if task_poll_miss_count >= 10:
                # 连续 10 次没有获取到任务, 应该是系统空闲, 直接关闭 scale-in 保护, 留足够时间给 autoscaling 关闭该节点
                LOG.info("task poll miss count > 10, unlock scale-in protection")
                no_protect_from_scalein()
                task_poll_miss_count = 0
                time.sleep(random.randint(50, 100))

由于任务执行的周期性, 直接使用 autoscaling 的 schedued action 提前在任务执行前启动 worker, 预估一个执行时间后直接将整个 autoscaling group 的节点数量设置成 0.

scale in 效果
  • 从下向上看, 前三个都是成功被系统 scale in, 关闭节点
  • 后续节点由于有任务执行, scale-in 会失败, 但系统会持续重试, 直到 scale-in 保护开关关闭为止.

0x05 总结

使用 autoscaling + scale-in 保护, 轻松实现山寨了一个 "AWS Batch". 虽然简陋, 但很使用, 优点就是大大提高了批处理任务并行度的同时, 还降低了成本. 但缺点也很明显:

  • 需要自己处理任务执行失败后, 失败的任务需要重新投递问题
  • 需要固定的执行时间, 以便定时扩容
  • 需要在 worker 节点初始化的时候处理执行程序分发问题, 或者获取到任务后根据任务中的参数在执行逻辑中添加 docker pull 逻辑
  • 资源利用率以单个 instance 为单位, 不支持一个 worker 并发执行多个任务

总之, 作为一个"山寨的 AWS Batch", 够用就好. 如果 AWS Batch 中国区再不发布, 我就要考虑一下, 把这个山寨货更完善一下, 毕竟批处理任务执行系统是刚需.

-- EOF --

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容