腾讯第一季度的财报显示 ,腾讯游戏的收入占腾讯总营收的59.4%,很显然,腾讯游戏已经是腾讯最赚钱的部门,当然,腾讯也是国内最大的游戏发行商。游戏行业相对于其他行业来说特点非常明显,一是需要同时运营多款游戏,二是很多游戏的生命周期都很短。不管是从数量还是周期来看,游戏行业特殊的业务都为技术团队提出了更高的要求。腾讯游戏从2014年下半年开始就在生产环境中使用Docker,并取得了不错的成果。目前《我叫MT2》等多款重量级游戏都跑在容器中,且整体运行良好。在8月28日的 CNUTCon全球容器技术大会 上,腾讯游戏的高级工程师尹烨将会介绍腾讯游戏业务使用Docker的进展及收益,并从内核、网络、存储、运营等方面深入探讨腾讯游戏在实践过程中遇到的问题及解决方案,最后还会复盘反思Docker对于游戏业务的价值。本文是会前InfoQ记者对尹烨的采访。
尹烨,腾讯互娱运营部高级工程师。2011年毕业加入腾讯,现在主要负责Docker等相关技术在腾讯游戏业务的实践。主要关注Linux内核、虚拟化、存储等领域,给libcontainer/kubernetes等开源项目贡献过代码。
InfoQ:腾讯游戏是什么时候开始使用Docker的?能介绍下目前的一些应用情况吗?
尹烨:我们是从2014年6月份开始接触Docker,那时Docker在国内才刚刚开始兴起,了解的人还很少。Docker让容器的管理变得非常简单,再加上创造性的分层镜像的技术,给人眼前一亮。我们希望通过Docker,构建腾讯游戏内部的容器平台,一方面通过容器提高资源利用率,另一方面通过镜像分发技术标准化、统一化应用部署流程。
经过半年的调研、各种测试、系统设计和开发,14年底,整个系统开始上线试运行。但是对于一项全新技术的应用,大家都很谨慎,因为很多游戏业务的在线玩家很多,我们的压力很大。第一个接入我们Docker平台的是腾讯的一款游戏,叫《QQ宠物企鹅》。这款游戏的架构在容灾方面设计得很好,前端可平行扩展,所以就选它作为试金石了。跑了几个星期,运行正常,然后开始慢慢扩展到其它业务。
现在,我们的Docker平台上已经跑了数十款端游、页游和手游的各种游戏应用,特别是新上的手游业务,其中,我们代理的一款重量级手游《我叫MT2》,一个业务就使用了700多个容器。现在整个平台总共有700多台物理机,3000多个Docker容器。这个数字在业界并不算多,我们自己也没有刻意去追求数量,相对数量,我们更愿意以稳为先。目前,整个平台运行了大半年,整体运行良好。
InfoQ:与其它行业相比,游戏行业有什么特殊性?Docker在这样的业务中有什么样的优势?它可以发挥怎么样的价值?
尹烨:相比于其它业务,一是游戏业务更加复杂多样,有端游、手游和页游,有的是分区分服,有的是全区全服;另外,我们又分自研和代理游戏,更加增加了复杂性。这也给业务的运维部署带来了许多不便,尽管我们内部有很成熟的部署平台。而Docker统一的镜像分发方式,可以标准化程序的分发部署,解放运维的生产力。特别是代理游戏,如果都以image方式交付,可以极大提高效率。
另一方面,一般来说,游戏业务的生命周期长短不一,这需要弹性的资源管理和交付。所以,腾讯游戏很早就开始使用XEN/KVM等虚拟机技。相比于虚拟机,容器更加轻量,效率更高,资源的交付和销毁更快。另外,还可以通过修改cgroup参数,在线调整容器的资源配额,更加灵活弹性。
尹烨:Docker开创性的提出了『Build、Ship、Run』的哲学。总的来看,现在主要有两种使用Docker的方式。一是基于Docker搭建CI/CD平台,重点放在Build和Ship上面,一般用于开发、测试环境;另外就是将Docker容器当作轻量级虚拟机,更加关注Run的问题,大规模的用于生产环境。个人认为,这两种方式无所谓谁好谁坏,长远来看,二者会渐渐趋于统一。
腾讯内部有很成熟的开发、部署工具和流程,我们作为平台支撑部门,去推动业务开发改变原来的开发模式需要的较长的时间周期。所以,我们现在更多的是将Docker容器作为轻量级的虚拟机来使用,我们在Run上面花了很多时间和精力。同时,我们也在探索通过Image方式去标准化业务部署流程。但是,我们不太会去做CI/CD的事情,我们更关注提供一个高效的容器资源调度管理平台,然后以API的方式对外,提供给开发和运维同学使用,比如,与互娱的蓝鲸平台打通。
尹烨:我们的使用Docker的初衷是替代虚拟机,所以我们直接将Docker跑在物理机上。我们使用Docker面临的第一个问题就是操作系统内核的问题。腾讯内部一般使用自己的OS(tlinux)团队维护的内核,这个内核历史比较久,不支持Docker,我们就选择了CentOS 6.5的内核。实际上,由于CentOS的内核不像Ubuntu,演进得很慢,CentOS 6.5的内核也很老,但基本能把Docker跑起来。但在实际使用过程中遇到了一些内核方面问题,现在tlinux团队已经提供了3.10.x内核的支持,我们也在逐渐往3.10.x的内核迁移。
第二个问题就是容器集群的管理调度,那时候虽然出现一些专门针对Docker的容器管理工具,比如Fig、Shipyard等,但这些工具无法胜任生产环境大规模集群管理调度。刚好那时Google开源了Kubernetes,它是Borg的开源版本实现。源于对Google的崇敬,我们研究了一下源码,基于0.4版本,针对我们的环境做了一些定制修改,用于我们的集群管理调度。现在我们最大的单个Kubernetes集群700多台物理机、将近3000个容器,生产一个容器只需几秒钟。
容器的监控问题也花了我们很多精力。监控、告警是运营系统最核心的功能之一,腾讯内部有一套很成熟的监控告警平台,而且开发运维同学已经习惯这套平台,如果我们针对Docker容器再开发一个监控告警平台,会花费很多精力,而且没有太大的意义。所以,我们尽量去兼容公司现有的监控告警平台。每个容器内部会运行一个代理,从/proc下面获取CPU、内存、IO的信息,然后上报公司的监控告警平台。但是,默认情况下,容器内部的proc显示的是Host信息,我们需要用Host上cgroup中的统计信息来覆盖容器内部的部分proc信息。我们基于开源的lxcfs,做了一些改造实现了这个需求。
(点击放大图像)
这些解决方案都是基于开源系统来实现的,当然,我们也会把我们自己觉得有意义的修改回馈给社区,我们给Docker、Kubernetes和lxcfs等开源项目贡献了一些patch。融入社区,与社区共同发展,这是一件很有意义的事情。
InfoQ:在我的印象里,游戏还是相对较保守的行业。你们在内部推进Docker过程中遇到过哪些阻力?是如何解决的?
尹烨:首先,我们会在Docker新功能接入与业务原有习惯之间做好平衡,尽量降低业务从原来的物理机或虚拟机切换Docker的门槛,现阶段业务接入我们的Docker平台几乎是“零门槛”。正如前面所述,我们的Docker平台上已经跑了数十款端游、页游和手游。
其次,Docker相对原有的开发部署方式变化很大,与其它新事物一样,让大家全部适应这种方式是需要一些时间,但Docker本身的特性是能够在游戏运营的各环节中带来诸多便利,我们的业务主观上对新技术的应用还是欢迎的,双方共同配合,共同挖掘Docker在游戏运营的中的优势,所以Docker推广目前没有遇到太大的阻力。
InfoQ:应用过程中,哪些问题是Docker目前无法解决的?你们的解决方案是怎样的?
尹烨:我们在实践过程中遇到了很多问题,有些是内核的问题,也有些是Docker本身的问题。由于篇幅问题,这里仅举一些比较大的问题。 详细的分享留到8月底的容器技术大会吧 。
我们遇到的第一个大的挑战就是网络的问题,Docker默认使用NAT方式,这种方式性能很差,而且容器的IP对外不可见。一般来说,游戏业务对网络实时性和性能要求较高,NAT这种方式性能损失太大,根本不能用于实际业务中。另外,腾讯内部的很多程序对IP都是很敏感的,比如只有特定的IP才能拉取用户的资料,如果这些服务没有独立的IP,是无法正常运行的。
我们针对腾讯的大二层网络环境做了较大的调整,整体架构如下:
整体架构比较简单,与原来虚拟机的网络架构一致。每个容器都有一个独立可以路由的IP,网络性能大幅提高,基本能满足业务的需求。而且,每个容器都可以携带IP在同一个核心交换机下任意漂移,业务通过IP漂移可以做很多有意义的事情,比如Host故障快速恢复等。另外,我们针对一些对网络性能要求高的应用,直接使用SR-IOV,可以完全达到物理机的网络性能。
容器相对于虚拟机,有很多优势,但是也有一些劣势,比如安全性、隔离性等。由于我们是内部业务,所以安全性的问题不是那么突出,但隔离性的问题还是给我们带来了很多麻烦。性能监控我们通过lxcfs基本解决,但是还是有一些问题无法解决,比如内核参数的问题。很多内核参数没有实现namespace隔离,CentOS 6.5的内核下,在容器内部修改,会影响整个Host,我们只能在 Host上设置一个最优的值,然后告诉业务,让他们不要在容器内部修改内核参数。3.10.x的内核要好一些,对于没有实现namespace的参数,在容器内部不可见,这可以防止业务私自修改内核参数,避免对别的业务造成影响。但是,有些业务对内核参数有特殊要求,我们只能让业务选择虚拟机。
再举个例子,一些业务会将程序进行CPU绑定,这可以避免CPU切换带来的性能损失,由于程序无法获取cgroup对容器cpuset的限制,绑定会失败,这需要业务程序先获取容器的cpuset,但还是给业务带来了不便。
再比如,现在cgroup对buffer IO并不能进行throttle限制,不过内核社区已经在解决了,但离生产环境使用,可能还需要些时间。
还有Docker daemon进程升级的问题,现有Docker实现下,Docker daemon一退出,所有容器都会停止,这会给大大限制Docker本身的升级。但最近社区已经在讨论这个问题,希望这个问题在不久的将来得到解决。
尹烨:上面已经讨论了很多我们在使用Docker遇到的问题,当然还有更多,这里不再一一论述。这里再举个简单的例子吧。
Docker daemon进程在退出时,会给所有的容器的init进程发送SIGTERM信号,但是如果容器的init进程忽略了SIGTERM信号,就会导致daemon进程一直等待,不会结束。我们修改了Docker,发送SIGTERM信号后,等待一段时间,如果容器还是没有退出,就继续发送SIGKILL信号,让容器强制退出。我们将这个修改提交到了Docker社区,因为一些原因并没有被接受,不过已经有另外的PR 解决了。
尹烨:的确,相对于Docker、Kubernetes的发展速度,大半年的时间已经很长了。我们使用的Docker版本还是基于1.3.2的,Kubernetes的版本是基本0.4的,已经很老了,但是基本上是满足我们现在的要求,而且系统运行也很稳定,所以,短时间内不会做大的调整。但是,我们也看到最近Docker发布一些非常有意思的变化,比如network plugin、volume plugin,还实现了默认的overlay network。Plugin机制会让Docker更加开放,生态圈也会发展得更快。但总体来说,这些新特性还处于experimental阶段,等这些特性成熟稳定之后,我们会考虑切换我们的Docker版本。
但是,我们也不会坐着等待,也会尝试一些我们需要的东西。比如,最近我们正在实现将Docker与Ceph结合,我们已经实现了Ceph rbd graph driver,将Docker的rootfs跑在了Ceph存储集群上面,结合IP漂移,可以实现更快的故障恢复。我们也将其实现提交给了Docker社区,因为一些技术原因,并没有被社区接受。目前看来,以plugin graph driver的方式提供会更好,但是现在Docker还不支持plugin graph driver,不过Docker社区正在实现,相信不久就会支持。我们已经在GitHub上创建一个开源的plugin graph driver项目。
另外,对于Kubernetes的应用,我们也还没有完全发挥其优势。Kubernetes的负责人Brendan Burns曾说过,“Make writing BigTable a CS 101 Exercise”,Kubernetes有很多非常超前的设计思想,当然,也会改变业务原有的一些软件架构,真正应用到实际业务中还需要一些时间。
另外,我们也会继续探索与业务开发、运维的结合方式,进一步发挥Docker的优势,提高我们的运营效率,更好的支撑游戏业务的发展。