基于 mesos 的容器调度框架

Posted GoCN

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于 mesos 的容器调度框架相关的知识,希望对你有一定的参考价值。

在 Gopher Meetup 2017杭州站中,来自又拍云的 黄励博 进行了题为《基于 mesos 的容器调度框架》的演讲,以下是他的演讲实录。 图1
这个框架是使用 Golang 实现的,我们把它称之为 Upone(图 1)。上图是它的基本操作界面,左边可以看到应用的一些基本信息,比如说第一个应用,它起了十个实例,显示有一些占用CPU、内存和磁盘信息。侧是对它的一些操作,比如说可以对它进行更新,可以增加或者减少实例个数,也可以对它进行创建、删除之类的操作,每一个应用点进去都可以看到这个应用每个任务的具体信息
|Upone & Mesos

基于 mesos 的容器调度框架

图 2

既然 Upone 是一个基于 mesos 的框架,mesos 是什么?mesos 官方称之为分布式系统内核(图 2),它把数据中心的CPU、内存、磁盘等抽象成一个资源池,它主要是一个两级调度系统。

基于 mesos 的容器调度框架

图 3

如图 3 可以看到,左侧的是 mesos agent,我们会在集群的每一台机器上部署一个 agent,当这个 agent 启动后,它会向 master 进行注册,携带每一台机器的一些统计资源,由 master决定给每个框架分配多少资源。这种分配算法,默认采用分级主导资源分配算法。

所谓的分级就是指各个框架是有角色的概念,每一个角色可以指定资源分配的权重,它的主导资源公平是说每个框架可能有些是以 CPU 为主,有些是以内存为主导的,诸有此类,可能还有一些其他的资源。

所谓主导资源公平就是主导资源在总的资源当中占据的比例是公平的,当然也可以自定义一个算法来完成资源分配。

最右边是部署在 mesos 上的一系列框架,当这些框架收到资源之后就会根据自己的需求调度自身的任务,所以调度器也会有自己的调度模块。


基于 mesos 的容器调度框架

图 4

图 4 是 Upone。它是为云处理服务定制的一个容器调度框架。左边是 marathon,是在 mesos 上比较著名的跑长期服务的调度框架,中间 chronos 是一个跑定时任务的框架,而 Upone 是同时支持跑长期任务和定时任务,方便统一管理,再加上最右边前面两个没有的一些功能。


基于 mesos 的容器调度框架

图 5

图 5 是 mesos 启动任务的流程图。这个流程图来自官方文档,中间绿色的是调度器,也就是我们今天的主角 Upone。可以看到第一步是 Client 开始注册,然后当任务没有到来的时候,master 会不停的给 Upone 发送一个 offer,这个 offer 就包含了一些资源的信息。当 Upone没有收到任何启动任务的时候,它会不停的拒绝这个 offer,以便 mesos master 上面还有其他的框架可以利用这个 offer。当 Upone 收到任务创建的时候,Upone 会把这个任务放到任务队列,当下一个 offer 到来的时候,它选择想要的 offer,和任务信息一并告诉 mesos master,由 master 通知 mesos agent,告诉它需要创建哪些任务,各个任务需要哪些CPU或者哪些内存,最后 agent 会通知对应的执行器。Upone 是一个容器调度的方案,它的执行器都是 docker 的执行器。这样的话,一个启动流程就完成了。

接下来的状态反馈,之后任务创建成功或者失败,都是异步的通知,需要 Upone 异步地处理相关通知。比如当它状态改变的时候,会上报到 mesos master,然后 master 会上报到 Upone 这边,上面的例子是处理完之后,通知还会上报到 Client。


基于 mesos 的容器调度框架

图 6

Upone 和 mesos master 的交互是通过 mesos 提供的 /api/v1/scheduler 方案处理的,它们的消息都是通过 Protobuf 完成序列号和反序列号的操作。Mesos 提供了 proto 文件,可以供我们编译成各种语言,这里当然是编译成了 Golang。

还有一个是执行器,也就是说如果不是采用 docker,或者想定制一些执行器的功能,也可以调用它执行器的接口 /api/v1/executor 实现。


基于 mesos 的容器调度框架

图 7

Upone 一开始和 mesos 进行通信的时候,首先要开启一个长链接订阅 mesos 的一些事件,图 7 的表列出了订阅事件,第一个是已经订阅成功的事件,还有前面提到的 offer 事件,里面包含了机器的一些资源信息,还有通知资源不再有效的信息,因为相关资源可能会被其他的框架占用,还有一个重要的是变更事件,比如任务创建成功还是失败或者是已经丢失了。接下来是一个失败的事件,失败的事件会告诉我们整个节点已经丢失了或者这个节点已经挂了,或者执行器已经被无端的停止了,这时候通常会伴随着更新事件,因为在上面会跑着不同的任务,当一个失败的事件发生的时候,这些伴随的任务都已经丢失了。最后它是一个心跳事件。


基于 mesos 的容器调度框架

图 8

对 offer 的处理,主要有两种方式:一种是接受,之前已经讲过了,如果接受这个 offer,要把任务启动信息和所需要的资源信息都告诉 master,如果是拒绝了这个 offer,可以选择拒绝这个 offer 需要多长的时间。比如说一个常见的应用场景,是一个 offer 过来的时候,发现它的相关的机器 load 已经很高了,这时候可以选择把拒绝时间调高一点,比如说可以拒绝 1 小时,也就是说 1 小时之内,这个 offer 不用再提供到这个 master 上,当然也可以用一个消息取消之前拒绝时间。


基于 mesos 的容器调度框架

图 9

图 9 这是 mesos 的 web 界面,左边最上面是显示的 slave,mesos 1.0 版本之后它改名叫 agent 了。Actived 表明现在有 46 台机器在上面运行。右侧是它的一些任务,每个任务都可以点进去,查看任务的状态和日志信息等。用红色框起来就是一些事件的状态,比如说有 Staging 状态、 Running 状态、Killing 状态。每次状态从一个变到另一个的时候会触发 update 事件,所以任务状态的变更,可以和 Upone 产生联动。比如当一个任务状态变成 Running 的时候,Upone 可以负责更新负载均衡器。关于负载均衡器的话题,后面还会提到。比如说 Lost 状态,就是说任务已经丢失了,我们会通过 Upone 的调度器完成任务的迁移工作。


基于 mesos 的容器调度框架

图 10

还有一些其他的交互消息,比如说主动删除一个任务,或者要关闭一个执行器,还有一个比较重要的是可以校准 Upone 的任务状态,因为经过长期的运行之后,Upone 的状态和 mesos 的状态,在某些任务上可能会出现不一致的情况,这种情况 mesos 提供一个校准的接口,把消息发过去,重新修改把任务保持统一。最后是删除整个框架,包括上面所有的任务。


| Upone  长期服务相关的特性


基于 mesos 的容器调度框架

图 11

基于以上的 mesos http API,Upone  已经可以实现具备一个基本的调度框架。除了这些之外,upone 还拥有哪些特性呢?

第一、是负载均衡的功能;

第二、是零停机的更新,因为我们不希望这个应用有任何变动的时候客户可以感知到;

第三、自定义策略,这是一个 upone 调度的方案,希望什么样的资源可以运行什么样的服务,我们希望这是可控的;

第四、可高用性,Upone 可以以分布式的方式运行;

第五、是 UI+命令行,UI之前已经展示了,Upone 可以提供命令行的方式和mesos进行交互。接下来逐个看一下。


负载均衡


基于 mesos 的容器调度框架

图 12

负载均衡是又拍云公司开源了一个基于 ngx-lua 的动态负载均衡方案,它可以完成对 http 和 stream 服务的负载均衡,它可以动态修改 upstream 列表,也可以简单的运行一些 lua 脚本,而不需要重启 nginx


基于 mesos 的容器调度框架

图 13

上图是负载均衡的图(图 13),也就是说当任务下放到 mesos,它帮你把任务创建出来之后会有一个 Running 的事件告知 Upone,这时候就可以把它更新到 Slardar 负载均衡器上。上图是一个简单的例子,这是一个 http 的请求,body 里包含了 servers,是一个 upstream 的列表,例子中只包含了一个 IP 和端口,这是最简单的情况。除此之外它还可以选择负载均衡的模式,比如 url hash 或者 ip hash 等等,当然这种情况是默认的 rr 轮询模式。


零停机更新


基于 mesos 的容器调度框架

图 14


健康检查


基于 mesos 的容器调度框架

图 15

图 15 是任务健康检查的一个状态页面。第一选项是否开启健康检查,第二选项是上次开启健康检查的时间,下面是健康服务的列表。比如现在这个状态是 OK,当它在 30 秒内出现六次以上的失败的时候,这个 status 会变成为 down,这样用户接入进来的时候就再也不会选择这台机器了。关于 fail_timeout 和 max_fails,都可以在更新 upstream 的时候一起提交上去。


基于 mesos 的容器调度框架

图 16

它还可以提供预处理的功能,也就是说在接入服务之前,可以载入相关的动态脚本执行一些预处理的功能,现在又拍云云处理服务主要是用到了两个方面的功能,一是频率限制的脚本,还有就是对每个服务有参数转换的脚本,这上面列出了一些脚本的时间、版本号和脚本名称等信息。


命令行工具


基于 mesos 的容器调度框架

图 17

图 17 是 Upone 的命令行工具,它可以让服务所有者可以通过 Upone 手动操上述负载均衡和更新部署相关的功能。第一个是 add 的命令,可以新增一个 upstream 到 slardar 上,用于测试或者其他;第二个是 delete,删除一个 upstream,list 是列出 upstream 下面多少台设备,它们运行状态是否良好,pick 是摘除指定 upstream 的指定节点。下面是 lua 是操作预处理的脚本,lua 下面还有一级目录,进去同样可以看到有一些 add、delete 和 pick 的操作。


自定义调度策略


Upone  收到 mesos 发来的 offer 之后,可以自定义策略来决定任务部署调度方案。


基于 mesos 的容器调度框架

图 18


第一个是随机调度,尽可能让同一应用不同的任务部署到不同的机器,以上图为例,有五个任务,当我们收到 mesos 的 offer 之后,可以看到它满足任务的条件有三个 Slave。所以,可以把任务随机打散到三个 Slave,比如说第一台更新了 1 和 4,第二台更新了 1 跟 0,第三个是 2 和 3。


基于 mesos 的容器调度框架

图 19

但随机调度的有一个缺点就是把所有的 Slave 认为具备相同的特质,所以 Upone 加入了对机器和任务属性的调度方案。比如说一个常用的场景是,有一些服务的所有者只希望任务启动在他拥有的机器上,而不希望部署在其他的机器上,这时候可以有一个所有者的属性,供 Upone 选择任务应该部署在哪些特定的机器上。这里用到的属性和存储配置地方,是放到了 Consul 里。

Consul 是一个配置中心和注册配置中心,后面会介绍 Consul 在 Upone 当中的应用。上图是一个 GPU 的例子,它给这个任务贴了一个 GPU 的标签,当 offer 当来的时候,Upone 可以到 Consul 中获取所有 Salve 的相关信息。比如说发现有三个 Slave,可以查到三个 Slave 的标签或者属性是什么。比如说第一个只允许 GPU,第二个只允许 CPU 任务部署在上面,第三个既允许 GPU 的任务也允许 CPU 的任务部署在上面。Upone 获知这些信息之后,就可以把任务发送到第一和第三个 slave 上。比如说第一台部署了 1 和 4,第三台部署了 0、2、3。机器的属性可以进行动态的改变,当某个节点的属性发生变更的时候,就可以去动态的修改 Consul 里的对应关系,Upone 可以及时的知道这些机器属性是否发生了变化。但是,它有一个缺点是, 它们不是实时的,所以还需要有一个实时的策略,比如说我们可以根据机器的 load 调度任务。


基于 mesos 的容器调度框架

图 20

如上图(图 20)所示,我们可以从监控系统中获取所有节点的 load 的情况,还可以有其他实时指标,这里以 load 为例。当 offer 到来之后,我们需要部署五个任务,可以选择以 load 为第一优先。比如说有一个是我们是部署在 load 为 0.1 的机器上,接下来排 0.2 的机器,最后会发现有两台部署在了最低的 load 的机器上,还有两台部署 load 排名第二的机器上,只有一个部署在 load 排名比较高的机器上。


配置与服务发现


基于 mesos 的容器调度框架

图 21

之前提到了 Consul,基本上 Upone 上所有的组件都会和 Consul 发生联系,应用的所有配置信息都是从 Consul 加载的。之前提到属性的信息,Upone 会从 Consul 中拉取,还会用到 Consul 服务注册的功能,可以看到 Upone 是服务拥有者的入口,而 Slardar 服务用户的入口, 当所有的任务成功启动之后,它会往 Consul 注册它的服务,以便 Slardar 可以及时更新服务的变更。


高可用性


基于 mesos 的容器调度框架

图 22

Upone 是支持分布式的,它通过 raft 分布式一致性协议实现高可用。简单介绍一下 raft,它是比较容易理解的分布式一致性算法。首先,它的领导选举是通过心跳机制来触发,term  一般称之为任期,充当逻辑时钟的作用;raft 有一个日志复制,领导者会把每一指令附加到日志中,然后发起一个复制日志的 rpc 请求给集群中的所有跟随者, 让它们复制相关日志。当有半数以上的跟随者响应了这条日志之后,这条日志就会被提交。被提交之后,它会在所有跟随者的 raft 状态机中被执行,以保持状态的一致。同时它是强领导者方案,也就是说所有的指令只能通过 leader 发送给跟随者。


基于 mesos 的容器调度框架

图 23

这是 raft 论文选取的一张领导人选举的图。当节点启动的时候,首先它会成为跟随者,会感知周围有没有集群的 leader,如果没有集群 leader 和它进行通讯,一段超时时间之后,它会进行选举,选举的时候它会切换自己的角色变成候选人,候选人会把自己的任期+1,然后开始一轮选举。当它在一个集群中获得了半数以上的投票就成了 leader,如果在此其间发现了任期不低于它的 leader,它就会退化成这个 leader 的跟随者。当超时时间到来的时候,它依然没有成为 leader,也没有发现新的 leader,它就会开始一轮新的选举。为了防止所有的节点都不停地超时选举,这里的超时时间是一个随机的值。可以简单保证所有的节点不是同时不停地选举而导致没有一个节点可以获得大多数投票的情况。当一个节点成为 leader 之后,如果发现集群中有一个任期更高的 leader,它就会变成该 leader 的跟随者,这就是领导选举的情况。


基于 mesos 的容器调度框架

图 24

这是 raft 的日志复制(图 24),每个框都可以看成一条日志,框里的数字是它的任期。可以看到第一个任期到来的时候,三条日志都被完整复制到了所有节点,所以三条日志都是被提交的状态,也就是说三条日志都会被执行。当第二个任期到来的时候,F 这个节点提交了三个任务,都没有被任何节点所复制,也就是说它在系统中不会产生任何的效果,接着就超时挂了。等第三个任期到来的时候,它依然没有成功的把所有的五条日志复制到其他节点,接着它又超时了。等第四个任期到来的时候,它又被半数以上的节点复制了,也就是第四个任期日志又被成功提交。再比如 C 节点在第六任期时候,最后一个 6 的日志只有它一个节点所有,所以是处于未提交的状态,也不会在集群中产生任何作用。D 这个节点也是,在第七个任期提交了两条日志,只有它所有,集群中没有任何节点复制了它。


基于 mesos 的容器调度框架

图 25

这是一个 raft 的可视化项目 RaftScope,上面是它的一个截图(图 25)。这是领导选举时候的图片,leader 会不停的发起请求,如果跟随者及时响应了它,它就保持了这个 leader 的角色。在这个网站上可以进行动态的操作,可以对其中一个节点 stop 或者 restart,可以通过时间轴加快速率或者改变超时时间等等。


| Upone 定时任务


基于 mesos 的容器调度框架

图 26

上面提到了 Upone 长期服务相关的特性。现在讲的是 Upone 的定时服务方案。之前我们采用的是 mesos/chronos 的定时方案,但发现它很多遗留问题没有解决,比如说 githup 上没有解决的 issue 有 188 个, 最近一次提交在 3 月 1 号。我们遇到的问题是 chronos 会无端占据 mesos 的资源,有一次遇到 mesos 资源利用率达到 100%,意味着 mesos 已经被 chronos 完全占用了 offer,mesos 已经不能把 offer 提供到其他调度框架了。


|定时任务特性


基于 mesos 的容器调度框架

图 27

所以 upone 也重新实现了定时任务的运行方案, 它同样支持了之前介绍长期服务的所有功能,当然定时任务不需要负载均衡。定时任务的格式和 linux 的定时任务配置格式保持了统一,可以有分钟、小时、天、月、周的配置项,还有一个可执行的命令,在 Upone 当中,是启动一个 docker 任务。


| 组件化


Upone 是一个由各个组件组合起来构成的,其中有两个组件可插拔:一个是存储组件,在 Raft 的三方库中提供了一个 key value 的数据库,这个数据库是纯 Golang 实现的,是默认可以支持的存储组件。Upone 也可以支持一些传统的数据库,比如说 mysql,把存储的信息都存储到数据库中。

还有一个是驱动组件,所有与 mesos 消息交互的都放到了这个驱动组件中,也就是一些小集群可以不依赖于 mesos 独立使用,它可以选择 master/agent 模式。比如有一个 upone master,在各台机器上部署一个 upone agent,这样它就可以对小集群进行简单的调度工作,而不需要部署 mesos 。


告警


图 28

接下来是告警,之前有其他讲师分享说他们是用 slack,我们这边告警也是用 slack 的通知,各个服务在创建任务的时候都可以指定自己的 slack Channel,当出现异常的时候会发送实时的通知,让服务的所有者可以及时的感知到。


|mesos 的问题


Upone 使用 mesos 过程中也遇到了一些问题:

第一、mesos 的孤儿任务的问题,在 mesos 上未注册的框架是没有办法关闭它的相关任务的,只能等超时时间到来,系统回收孤儿任务,或者再次注册这个框架,主动删除任务,最近遇到的问题是超时时间到了之后,依然可以在mesos 的状态中看到孤儿任务的信息。

第二、还遇到一个问题就是 mesos 的任务会一直处于Staging 状态,而没有任何消息的返回,后来才发现TCP recycle 开启之后,在请求不能保证时间戳单调递增的情况下就会出现丢包。


图 29

图上(图 29)是列出了刚刚提到的一些文档,比如和 mesos 相关的信息, raft 的论文和 raft 可视化的网站, 还有一些开源项目,包括前面提到了 consul、raft 都是基于 Golang 实现,还有一个是我们开源的负载均衡器 Slardar,如果大家感兴趣的话可以去看一下相关资料,谢谢大家!


以上是关于基于 mesos 的容器调度框架的主要内容,如果未能解决你的问题,请参考以下文章

docker mesos集群资源调度平台

在Mesos上使用Chronos运行计划任务

为什么我要选择用Yarn来做Docker容器调度引擎

吾家Swan初长成|数人云Mesos开源调度器

一言不合秀代码 | 怎样写好Mesos Framework

美国电信巨头Verizon基于Mesos和容器的实践经验