背景
- 公司内部的云平台为各个业务线提供了大量的实体机和虚拟机来运行业务的服务,经过统计发现,这些分配给业务的机器cpu, memory等资源利用并不充分;
- 如果能够充分利用这些机器上的空闲资源同时又能保证业务服务的正常运行,将会节省不少的机器资源;
选型
Mesos简介
- 先放上官方网站, 上面有很详细的说明;
- 简单来说,
Mesos
就是用于整个计算中心的操作系统,它统一管理计算中心所有机器的cpu, memory, disk, network等计算资源,按任务所需分配资源,调度任务,支持故障转移等等;
- Mesos最大特点是两级资源调度, 如下图:
- 上面架构图的简要说明如下:
- 各个
Agent
上报自已的计算资源给Master
;
-
Master
给各个二级调度框架Framework
发送resource offer
;
-
Framework
将其上等待调度的task
与收到的resource offer
作匹配,反馈给Master
;
-
Master
将相应Framework
反馈的task
和resource offer
发送到对应的Agent
;
-
Agent
使用Executor
来运行task, 并限定资源使用;
- 在Mesos上可以运行Spark, Storm, Hadoop, Marathon等多种Framework;
- Mesos系统架构:http://mesos.apache.org/documentation/latest/architecture/;
- 针对任务隔离这块, Mesos除了支持
docker
容器技术,还提供了它自己的Mesos Containerizer, 这正是我们所需要的.其实Mesos Containerizer
目前也是利用Linux Cgroup
作资源限制, 用Linux namespace
作资源隔离.
我们的多任务调度任务需要解决的几个问题
-
Mesos agent
在业务机器上需要非侵入式地部署,不能污染所部署的机器的环境;
- 实时监控和调整
Mesos Agent
所能使用的计算资源;
-
Task
的快速部署和资源隔离;
- 集群整体运行情况的监控;
多任务调度系统总体架构
- 各组件简介:
1.1 主体还是Mesos master
+ Mesos agent
;
1.2 二级调度框架使用的是Marathon;
1.3 在部署了Mesos agent
的机器上同时部署monitor
用于实时监控和调整Agent
的j可用计算资源;
- 系统运行流程,按上图中标号顺序
2.1 Monitor
实时监控组件收集所在机器上的可用资源;
2.2 Monitor
根据所在机器上的可用资源动态调整agent的保留资源;
2.3 Agent
动态实时的将自已的保留资源上报到Mesos master
;
2.4 Mesos Master
在resource offer发到Marathon
;
2.5 Marathon
根据收到的resource offer和需要运行的task
作资源分配, 将分配结果反馈给Mesos Master
;
2.6 Mesos Master
将task
分配到具体的Agent
上执行;
解决问题 1: Mesos agent
在业务机器上非侵入式部署
- 我们采用的是Mesos 1.4.1版本,用
C++11
编写,Mesos项目本身非常庞大,依赖库必然也很多,解决这些运行依赖问题首当其冲;
- 部署原则就是不改变,不污染所部署的机器环境,针对libstdc++和其他一些so, 我们不会将其安装到例如
/usr/local/lib
这样的系统目录, 而是在打包时采用动态更改可执行程序的rpath
的方法,使其运行时从我们的安装目录加载相应的so库, 具体作法就是
- 我们将mesos运行所需要的所有lib文件都集中放在libs目录下;
- 编译出来的mesos可执行文件,使用patchelf来更新rpath路径,指向我们自已的libs目录即可;
-
patchelf
这个工具可以在不影响可执行文件运行的前提下,修改其elf文件结构中的某些属性值,具体可参考:https://nixos.org/patchelf.html
- 这样部署完,
Mesos agent
只是一个单独的目录,卸载只需要停掉进程,删除目录就好;
- 说一下编译过程,虽然官网上已经介绍得比较详细,但在编译过程中还是会遇到一些问题:
- 官网编译步骤: 请参考 http://mesos.apache.org/documentation/latest/building/;
- gcc官方要求是 4.8.1+, 我用了 gcc 5.4, 自己在 centos 6.2,linux 2.6.32上重新编译的;
- 编译默认是编译java语言的binding的, 但在 编译 "Build and attach javadoc"时缺 protobuffer的jar包,没编译过, 解决方案:修改
src/java/mesos.pom.in
,先找到 ``, 在它下面的 <configuration>
下添加 <skip>true</skip>
,这样不会编译这个 javdoc, 但不影响 java binding的使用
解决问题 2:实时监控和调整Agent所能使用的计算资源
- 自行开发了Monitor程序,和
Agent
一同部署在业务机器上,周期性的监测机器上可用资源和Agent
本身所用资源;
-
Mesos
为我们提供了动态资源保留这个超实用的功能,可以限制Agent
当前针对某个Role
可以使用的计算资源:
RESERVE_RESOURCES HTTP Request (JSON):
POST /api/v1 HTTP/1.1
Host: masterhost:5050
Content-Type: application/json
Accept: application/json
{
"type": "RESERVE_RESOURCES",
"reserve_resources": {
"agent_id": {
"value": "1557de7d-547c-48db-b5d3-6bef9c9640ef-S0"
},
"resources": [
{
"type": "SCALAR",
"name": "cpus",
"reservation": {
"principal": "my-principal"
},
"role": "role",
"scalar": {
"value": 1.0
}
}
]
}
}
RESERVE_RESOURCES HTTP Response:
HTTP/1.1 202 Accepted
-
Monitor
的监控评估结果就是当前Agent
可以使用的计算资源;
- 本想着到这里这个问题就结束了,测试时发现
Agent
并不能在线实时调整这个动态资源保留,需要在配置文件时更新好当前能够使用的动态资源,然后重启Agent
;
- 重启
Agent
是我们不能忍受的,因此我们修改了源码,通过http接口在线调整动态资源保留, 这部分其实不难,mesos http接口定义十分清晰,依葫芦画瓢就好了.
解决问题 3:Task的快速部署和资源隔离
-
task
的部署目前我们采用Marathon,上手简单,功能够用; 如果需要更灵活的调整策略,可能就需要自己开采框架或基于某一框架二次开发了;
-
task
其实是有重要,紧急之分,占用资源也不尽相同。对于重要紧急任务,为了保障任务的更好运行,我们会利用Mesos attribute,在调度任务时让特定任务只跑在具有特定attributes
的agent
上, 这就需要为每个mesos agent
设置相应的attributes
;
- 遇到了同样的问题,mesos不能在线动态调整
attributes
:-(, 其实也比较简单,稍微梳理下mesos源码结构,改起来不难;
- 还有一个问题,
attributes
是动态调整的,agent
如果重启了怎么办?我们为此部署了etcd集群来管理,每台agent都是etcd上的一个node, 通过etcd
提供的http接口更新其attribrtes
, agent
会周期性的从etcd
上同步;同时各agent
上的attributes
信息也很容易从etcd
上获得;
- 直接操作etcd, 删除某台
agent
上的attribute
, 那依赖于这种attribute
部署的任务会自动别调度走,不再在这台agent
上运行;
- task隔离问题,针对cpu和memory,mesos都是通过cgroup来完成,对于cpu的限制, 我们使用
cfs
方式,前提是需要判断当前kernel是否支持.对于disk的限制,目前mesos使用的是du
命令周期性检测的方式;
- 对于cpu的限制,mesos有两种方式:
- cpu shared方式:这种方式对 cpu 没有严格限制,机器上的任何task都可以访问机器上所有cpu资源,比如你限定的cpu使用资源是2, 这种方式可能使用到4,6或更高;
- cpu CFS方式: 相当于配置了独占 cpu, 比如cpu配置为1,那么这个任务的 cpu 使用率就不会超过 100%, 相当于设定了一个hard limit;
- 在我们的大部分环境中,受限于kernel版本,mount namespace不支持,因此我们采用
rootfs + chroot
的方式来作简单的资源隔离;
- 我们定制了若干版本的
rootfs
, 任务打包就是将任务本身的依赖和相应rootfs
结合的过程, 打包好可以放到s3等存储上,供marathon部署任务时调用。
- 这里我们结合marathon的一个任务部署的json配置来讲一下, 先看一个这个配置, 我将说明直接写在里面
{
"id": "/test_task", //在marathon上的任务id
"fetch": [
{
//每个任务都会打包成一个rootfs, mesos agent在运行时会从这个uri下载,然后自动解压缩
"uri": "http://xxx.xxx.xxx:8080/rootfs.tar.gz",
"extract": true,
"executable": false,
"cache": false
}
]
//上面下载的rootfs.tar.gz被解压成rootfs目录,执行任务时先chroot到此目录, 然后运行真正的/test这个任务
"cmd": "chroot ./rootfs /bin/bash -c \"/test > /dev/null \"",
"cpus": 2, //限定的cpu资源
"mem": 1024, //限定的memory资源
"disk": 2048, //限定的dist资源
"instances": 25, //当前任务一共运行多少个实例
"constraints": [ //限定当前任务只运行在有 "job=test_task"属性的agent上
[
"job",
"CLUSTER",
"test_task"
]
],
"acceptedResourceRoles": [
"test-role"
],
"portDefinitions": [
{
"port": 10005,
"name": "default",
"protocol": "tcp"
}
],
"user": "root"
}
解决问题 4:集群整体运行情况的监控
目前存在的问题
- 打包task没有实现自动化, 我们虽然定制了若干种不同的rootfs, 比如c++11环境的, python环境的, java环境的等等, 但是想要运行的task依赖千差万别, 现在都是结合rootfs和业务的程序,手动合成专用的rootfs, 基本上每个task都要走这个手动流程,烦锁,耗时,容易出错;
- 目前只引用了marathon一种调度框架,适用于长期运行的task, 对于需要定时运行的task目前无法支持;
写在最后
- 到此我们利用Mesos构建的多任务调度系统就简单介绍完成,其中还有很多不完善的地方,有兴趣的同学可以一起讨论,互相学习~~~