腾讯Gaia平台的Docker应用实践
《腾讯Gaia平台的Docker应用实践》要点: 本文由谢恒忠根据2016年1月24日@Container容器技术大会·北京站上陈纯的演讲《腾讯Gaia平台的Docker应用实践》整理而成. 大家好,我是腾讯数据平台部的陈纯,今天非常高兴为大家介绍一下腾讯Gaia平台Docker实践. 首先我介绍一下Gaia平台.Gaia平台是腾讯数据平台部资源调度和管理的系统,承载公司的离线业务、实时业务以及在线设备service业务,最大单集群达到8800台,并发资源池个数达到2000个,服务于腾讯所有的事业群. 在2014年10月份我们正式上线对Docker类型的支持,通过Docker将Gaia云平台以更好用的方式呈现给各个业务.目前,Gaia平台已经服务于公司内部游戏云、广点通以及GPU深度学习等Docker类业务. Gaia架构下面我简要介绍一下Gaia架构.如下图: Gaia架构 Gaia其实是我们基于Hadoop的YARN改造的一个Docker的调度系统. 首先它相比社区的YARN有哪些特点呢?社区的YARN可能在RM、NM都已经实现了无单点的设计,可以热升级.在此基础之上,我们自研了一个AM,负责所有Docker类作业的调度.然后我们对AM以及Docker也进行了一定的改造,让它支持无单点的一个设计.右边的图中我们可以看到每个Slave节点上除了有NM之外,还有一个Docker进程,负责拉起所有的Docker作业,我们也实现了Docker的热升级.除了对Master节点进行无单点改造之外,Gaia也为用户的APP提供了本地重试和跨机重试两种容灾方式. 第二点就是Gaia除了对CPU内存以及网络出带宽进行限制之外,还增加了对GPU和磁盘空间的隔离. 第三点是Gaia可以最大化的利用集群的所有资源,在保证用户最低资源使用量的情况下,在集群有空闲资源时还能借集群的空闲资源进行使用. 最后一点是我们自研的SFair调度器解决了调度器效率和扩展性的,目前调度器每秒最多可调度4K个Container实例. 当然我今天所演讲的主要内容并不是对Gaia进行分析,我今天演讲的内容主要是跟Docker相关的,因为我平常在组里面也是负责Docker的研发. Docker热升级功能Docker Daemon单点问题首先我们来看一下Docker Daemon单点问题,相信所有使用Docker的人都会遇到这个问题.Docker Daemon在退出时会杀掉所有的Container,这一点对于在线服务来说完全不可接受. 其次Docker的坑也比较多,比如我们用的1.6.×版本: 第一个问题是我们遇到了Docker stats.Docker stats其实是用来监控Container实际使用资源的命令,当你的Container拉起来之后,用Docker stats监听这个Container使用资源,当Container被回收,Stop之后,Docker stats命令由于它没有正确的回收内存中的一些数据结构,会导致Docker Daemon crash,如果Docker Daemon crash,它就会把这个机器的所有Container都给杀掉.这个问题是我们遇到的,自己解决了,并且反馈到社区了. 第二个问题是Docker exec,Docker exec在Docker Daemon代码里面没有很好的做到同步,会引发一个NPE的异常,导致Docker Daemon crash. 第三个问题相信很多人都遇到过,在Docker 1.9.1版本之前,由于Container的stdout和stderr都会经过Docker Daemon进行缓存,并最终写入到磁盘的一个文件中.当Container打的日志量过大,或者速度过快,Docker Daemon来不及把这个日志写到文件中的时候,就会导致Docker Daemon crash. Docker热升级功能设计除了我这里列举的一些Docker Daemon的bug会导致Docker Daemon crash之外,我们日常也有对Docker Daemon进行升级的一个需求.总而言之,Docker Daemon的单点问题是一个痛点,所以我们就对Docker Daemon的热升级功能进行了开发.要开发这样一个功能,首先搞清楚Docker Daemon为什么会在它停止的时候杀掉所有的Container,主要是受限于两点: 第一用户的进程是Docker Daemon子进程; 第二就是Container的IO流会经过Daemon缓存,如果Docker Daemon挂掉的时候它不去杀掉所有的Container,在它被重新拉起来之后,它无法将这个IO流重新以原先的方式流经Daemon,这样势必会对它的Docker attach造成影响,所以现在一直都没有支持这样一个无单点的设计. 图1 我们的做法也非常简单,就是把原先的两层进程父子关系变为三层(如图1),在Docker Daemon代码里面有一个monitor的组件,这个monitor的组件最开始是一个goroutine的方式,我们将这个goroutine的方式改为进程的方式,等待Container的运行结束.这样在Docker Daemon挂掉的时候,它没必要去杀掉Container,也没必要去杀掉monitor,它只需要自己把自己的活干完退出之后,monitor的进程它自己就会变成孤儿进程,从而托管给INIT进程,也就是进程号唯一的进程.这样在Docker Daemon重启之后,它就会从磁盘上加载所有Container的运行状态,恢复所有的Container状态. 下面讲述这样做对上层的调度系统的影响.一般调度系统拉起一个Container之后会使用Docker wait的命令去等待这个Container的运行结束,如果没有热升级功能的时候,client跟Daemon之间是通过http请求的方式通信的,那Docker Daemon挂掉之后势必会给client返回一个connection reset的response,这样的话上层的调度系统就势必会受到一些影响,对于这个client的状态就无法感知了. Docker crash不影响Docker wait图 2 我们的做法就是(如图2)将这个wait的请求转发到monitor进程,就是最开始还是client向Daemon请求说我要wait这个Container结束,那这个时候Docker Daemon发现自己已经开启热升级功能的情况下,它将这个请求返回一个 Docker热升级功能实现我接着介绍一下热升级功能的实现.首先为了兼容以前的方式,我们增加了一个开关,就是hot restart参数,并且将monitor的代码组件进行接口化设计,让它支持以前的goroutine以及外部进程两种方式. (编辑:ASP站长网) |