Azkaban容器化 - 设计文档

本文翻译自Azkaban官网文档 Azkaban Containerized Executions - Design Doc

作者/关键贡献者: Arvind Pruthi , Janki Akhani , Shardool , Deepak Jaiswal , Aditya Sharma , Abhishek Nath


老架构(裸金属架构)背景/概览

Azkaban老架构图

  • 每一套Azkaban集群都包含如下组件:一个web server,一组部署在物理机上的executor server,一个mysql(用于保存job状态和历史记录)
  • 每一个executor server大约可以并行执行10个Flow(任务流),具体取决于每个Flow的消耗的资源
  • executor server定期(一般1秒)从mysql拉取处于待执行状态的Flow,mysql相当于是一个队列

Azkaban Executor Server的职责

分发任务

  • Executor Server定期从mysql拉取flows
  • 构建flow:解析所有的配置、在内存中构建一个图、下载依赖文件、分配计算资源(线程池、执行目录等)
  • 触发调度

调度

  • 为了保证任务并行执行,每一个executor server为每一个flow都维护了一个独立的线程池
  • 从hadoop name node获取hadoop tokens以便提交YARN应用
  • 每一个job都是用admin账号独立加载
  • 在内存中维护flow的状态机,并及时更新到mysql中,最后把flow产生的日志都写到mysql中

管理Flow

executor server对外暴露AJAX API,提供暂停、强制停止、恢复等操作,超过10天的flow会被强制停止。

管理日志

AJAX API支持流式日志,可以实时观察执行中的flow/job,当flow/job结束后,所有的日志都会以15MB每块的大小保存到数据库中。

部署

  • 部署executor server,先把新版代码准备好,启动前会先完成一些必要的测试,以便验证环境符合预期
  • 测试通过后,把executor server置为不可用模式,确保不会从数据库拉取flowparticular executor除外)
  • 启动新版代码,状态变为可用,恢复任务的执行

裸金属架构的问题

互相影响(无资源隔离)

Azkaban支持多种Job类型,也允许用户自己上传代码,所以很容易发生部分任务占用过多的资源(CPU、内存、磁盘),进而拖垮整个executor server,影响此server上的所有任务

弹性伸缩/维护问题(僵化的架构)

网站可靠性对弹性伸缩能力具有强烈的诉求,数十年前的架构就已经存储了。但是裸金属架构并不能从最近的新技术上获益,这在云计算领域已经司空见惯了。

缺少金丝雀发布系统

Azkaban的executor server是所有计算的门户,除了它自身的功能代码、配置、任务类型之外,还屏蔽了hadoop、安全管理器、spark等基础计算设施。当前缺少金丝雀机制以支持细粒度的新功能验证。 根据Azkaban在Linkedin的使用的经验看,在缺少合适的金丝雀系统的时候,每次滚动发布代码是多么的痛苦。

无视YARN内部的任务队列

Azkaban有自己的任务队列和分发机制,这样可以最大限度的发挥executor server的能力,但是它自己的队列与YARN的队列并不匹配,这经常导致YARN集群过载。

发布的问题

发布过程中的环境验证环节可能要持续10天,在这期间executor server都是不可用的,并且CPU和内存是闲置的。一次发布如果有问题,需要executor server带病运行的情况下,不断的尝试修复,可能导致GC暂停或者OOM,并且会污染其他的指标。

容器化的关键需求

  1. Azkaban的web server动态的为每一个flow创建一个容器,也就是为每一个flow提供一套完全独立的运行环境。
  2. 快速响应激增的资源需求(可伸缩的架构)
  3. 提供一种机制以支持Azkaban各个组件各自独立进化
    • 把发版的控制权交给对应的用户:运行平台、Azkaban本身、用户Job
    • 让用户自己选择Azkaban或者JobType的版本(在更新基础设施的时候尤其有用)
  4. 提供一套垂直的金丝雀系统以支持Azkaban/jobtypes和运行平台完全控制各自的代码更新

未来的扩展

  1. 为多个组件开发细粒度的金丝雀系统以支持各自独立发版

架构概览

容器化架构概览

  1. Azkaban采用Disposable Container(一次性容器)的模式,这意味着每当flow被调度之前都会创建一个新的POD,并且在flow结束之后销毁。
  2. 资源隔离体现在flow级别(而非job),jobssubflows都是flow的一部分;Job级别的资源也探索过,但是最终放弃了:
  • 它会极大的破快原来的架构,为了实现job级别的资源隔离,大部分的代码都需要重写;
  • 为每一个job创建一个pod会造成过多的资源消耗,还有一种做法是在一个POD里面包含多个容器,但是这也会导致大部分flowjob相关的代码被重写;也许未来会重新考虑这个设计;
  1. 创建POD的时候使用默认的cpu、内存资源,也可以通过参数指定需要申请的资源
  2. 在这样的设计下,web server必须部署在k8s之外,这样做并不妨碍它与flowlog之间的通信,它们之间的通信通过Ingress Controller实现,这样就不需要导出Flow的POD了;
  3. 为了实现上述第三点需求,flow的pod的执行环境必须动态创建:
  • 在任务分发的环节,会提供动态选择组件版本的功能,以便提供executor server的运行环境
  • 一系列的容器初始化工作将通过众多组件的不同版本的组合来完成
  • 动态选择的功能可以用来实现组件的金丝雀发布
  • 需要一些Admin API来完成镜像的管理工作

详细设计

镜像管理

  • 使用docker镜像来创建flow的执行环境,为了实现上述第三个需求,要使用预制的container模板来创建POD,下文的分发逻辑一节会详述;
  • Azkaban的运行环境由以下依赖组成:
依赖类型 描述
平台依赖 Hadoop、Hive、Spark、Pig、Dali、Ksudo等
Azkaban核心 Azkaban代码包、配置、安全项,由Azkaban管理
JobTypes JobType开发人员开发的代码/配置,由Azkaban管理,如KafkaPushJob, SparkJob
  • Azkaban核心在基础镜像(RHEL7)上加一层,形成Azkaban基础镜像
  • 其他的平台依赖、JobTypes各自在Azkaban基础镜像之上新增独立的层。为了保持镜像体积小,节约下载时间,可以使用busybox或者alpine技术来实现
  • 有的开发者的job-types镜像需要特别定制,不依赖Azkaban。比如Kafka Push Job
FROM container-image-registry.mycorp.com/rhel7-base-image/rhel7-base-image:0.16.9

ARG KPJ_URL=https://artifactory.mycorp.com/kafka-push-job/kafka-push-job/0.2.61/kafka-push-job-0.2.61.jar

RUN curl $KPJ_URL --output ~/kafka-push-job-0.2.61.jar
  • 每个job-type都会基于一个公共的预制镜像,这个预制镜像会把所有的代码包和配置文件都放到容器的外部卷上,此外部卷也会被挂载到应用容器(基于Azkaban基础镜像
  • job-type开发者使用镜像管理API构造job-type镜像,构造出来的镜像可以作为默认的job-type镜像,flow的开发者可以使用DSL指定job-type镜像的版本
  • flow执行的过程中使用version-setversion-number来唯一标识它的依赖的组件的状态;这也有助于在测试环境复现失败的flow,便于排查问题

镜像管理API

API使用流程图

API使用流程图

数据库ER图

数据库ER图

分发逻辑

分发逻辑

状态流程图

状态流程图

kubernets的安全性

初始化容器

初始化k8s pod

运行Flow的容器

Ingress控制器

日志

状态页面

使用裸金属架构解决上述问题如何?

在kubernets上debug Azkaban

待解决事项

  1. 搁置了通过参数指定flow使用的镜像的版本
  2. 搁置了通过参数设置版本
  3. 搁置了通过参数指定flow容器需要的CPU和内存
  4. debug问题
  5. 更多关于配置的技巧
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章