YardStrong 2019-06-20
中国开源云联盟容器工作组成立后的第一次活动——数人云对话Docker&Mesos沙龙活动圆满落下帷幕。本篇文章是活动的实录分享,介绍了CNTV容器技术的探索与实践,在以高安全、高可用和弹性伸缩为需求的前提下,容器技术具体在CNTV是如何落地的。
本文内容主要有以下五点:第一,CNTV为什么要用容器,第二,CNTV容器管理平台的选型,第三,容器在CNTV的发展与现状,第四,案例分享,第五,容器未来发展。
张航源,CNTV技术中心高级工程师
2009年加入CNTV,现任技术中心高级工程师,主要负责CNTV容器化的探索和平台搭建,存储平台的架构和运维,以及ELK平台的建设等工作。
CNTV是中国网络电视台的简称,是中国中央电视台旗下的一个网络媒体播出机构,于2009年12月28日正式开播。中国网络电视台可以理解为基于视频内容为主的一个互联网网站。在今年6月,CNTV很荣幸地加入了中国开源云联盟容器工作组,我们有更多的机会来参与容器技术的讨论,与大家一起学习。
首先来谈一谈CNTV为什么要用容器,主要是要解决哪方面的问题或者说我们的痛点在哪里。其实我们当时的想法很简单,就是为了更好的应对CNTV面临的重大活动。怎么来理解这个“重大活动”?比如像去年的“九三阅兵”,刚刚结束的“欧洲杯”,即将要开始的奥运会,“世界杯”,以及每年的春节联欢晚会,这些都是CNTV传统的一些重要保障活动。在活动重保期间,我们主要面临三个问题,即高安全、高可用和弹性伸缩。
高安全怎么理解呢?我相信每个企业都会把安全放在第一位,我们公司也同样是这样,我们把安全放在一个高强度和常态化的现象当中。在重保期间内,安全方面的调整并不是特别多。在高可用方面,我们更多的理解是系统的底层架构,它在重保期间往往也不会变动。所以,我们的重心就放在了弹性伸缩,也就是说,如何在重大活动期间更合理、更快速、更平稳地分配我们的资源,这也是我们这几年制定的奋斗目标。
这是我们私有云团队提供的一张PPT,以IaaS资源申请为例,和大家阐述目前在CNTV申请资源的工作流的情况。我们有传统的一些虚拟化工具,KVM、Xen、VMware等等,包括我们目前在大力发展的OpenStack平台。当有业务申请资源时,我们会根据业务的需求,开出一台虚机,然后开发或运维人员通过SSH登录到机器上部署程序和代码。平台工程师将把这个虚机做成模板,用来批量制作、测试上线、后期资源回收,并部署其它业务。其实,在活动的重保期,每个环节基本上是不需要过多调整的,往往就是在资源如何弹性伸缩方面,我们会做一些事情。因此,基于业务上的压力,以及领导给的一些任务指标和我们系统工程师对新技术的渴望,我们调研了容器技术。
在我们前期调研容器和搭建测试环境的时候,我们发现容器资源池一直从申请到下线,整个工作流和我们的IaaS平台没有太大的区别,工作流方向大致是一样的,都是提交代码、Build镜像、Push Pull镜像、启用容器、测试上线、删除容器、资源回收。而不同则在于,容器具有动态扩容和缩容的特性,也就是大家说的弹性伸缩。同时,容器的创建速度,以及更少的人为干预这两点,也是我们这几年一直在追求的地方,所以我们认为容器技术与我们在重大活动期间往往常态要调整的操作是非常契合的。
确定了容器这个方向,接下来就是内部讨论容器接下来要如何做,平台如何选型,我们的业务如何调整,以及需要哪些部门来配合。Mesos、Kubernetes、Swarm是大家在容器里面经常能听到的一些编排工具,因此,我们当时主要基于前三个(Mesos、Kubernetes、Swarm)来做调研,包括和业界、以及互联网的一些从业朋友进行讨论,包括与去哪儿网的徐磊同学也进行了深度的交流,他们给了我们好多中肯的建议。最终,我们选定了可以支持多种frameworks、组件众多、部署相对容易、高可用、能支持更庞大架构的Mesos,并选定了数人云作为合作厂商,和我们一起建立CNTV的容器平台。
简单介绍下我们的发展,真正启动容器项目是从2015年8月开始,Docker的版本是从1.7开始,然后到1.8.1、1.9.1,我们前两天升级到1.11.2版本;Registry版本也是从V1到V2;日志管理测试了log-driver、logspout、logging,logging是数人云的日志采集工具,把它放在我们的ELK日志分析系统上面。在容器监控方面,我们先测试了小米的open-falcon,第二个是大家最熟悉的Cadvisor+Influxdb+Grafana。目前整个业务的容器实例大概是1000+左右,物理服务器大概是30台左右。这个容器实例跟物理服务器没有比例关系,只是说我们当时给容器申请更多的资源而已。
我们主要改造的是以下三个系统,第一,央视网图文源站;第二,Time系统,用于PC端和移动端节目时间表的调用,以及网站的年月日时间显示;第三,API接口项目,所有央视网APP在回调素材时都会访问这个统一接口平台,包括央视影音、央视新闻、央视体育等APP。
最终的平台是这个样子,首先是监控平台,我们目前使用的是Cadvisor+Influxdb+Grafana。日志系统是通过log-driver模式,然后打到一台logstash,最终通过logstash入口,再入ES,这是我们自己的一套日志分析系统。编排工具是Mesos+Marathon+Docker,用数人云做底层的技术支持。镜像仓库,我们目前选用了开源的组件,带UI界面,是V2的版本。后端我们挂了一个GlusterFS,做了高可用。
从容器监控说起,在阶段一我们用了Cadvisor,第二阶段我们用了Cadvisor+Influxdb+Grafana这套架构,保证数据的持久化。到第三阶段,我们的ELK系统是在容器平台之前搭建完成的,我们把容器、物理机、虚拟机都统一放到Grafana上做统一的展现。
容器日志方面,阶段一跟大家走过的路都差不多,就是volume模式到宿主机的路径。第二个阶段,因为当时我们的经验也不是特别多,我们把logstash_agent打到了容器里面。第三个阶段,是通过log-driver、logspout或数人云的logging等模式,log-driver通过-log-opt参数到syslog,包括HTTP协议或TCP协议,IP,端口,直接就收走了。
容器日志大概的模型是这样,方式一:log-driver把日志打到syslog里面。方式二在宿主机上会有一个logspout的容器,最终入到ELK系统上面。
容器进程管理方面,我们和去哪儿的兄弟也沟通了很长时间,因为大家更倾向于一个容器里面一个应用,因为这样的使用方式对容器是最好的。但是程序往往不是一个,我们现在以两个居多,包括Nginx加php这两种情况。容器长时间运行需要前台进程,因此,我们一开始前期测试也是通过top或tail-f保持长时间运行,但是里面的Nginx服务死掉的话是感知不到的,这样应用会受影响。因此,在阶段二我们就用了supervisor,supervisor在CNTV已经使用了几年了,很多都是用supervisor来管理的,后来我们又发现了S6进程管理,supervisor每次拉服务就三次,而S6有一个好处是无限拉、无限起这个服务,而且还有一个finish,也就是说,如果容器的进程死掉的话就把容器干掉,没必要再重复的去拉里面的进程或服务,包括Nginx也好,PHP也好,直接干掉就OK了,我们经过对比认为,S6比supervisor要好一些,因此我们用S6来管理这两个前台进程。
镜像仓库用了AppHouse,我们把它的配置文件,包括数据镜像的相关数据放在GlusterFS上,前面顶了Nginx做负载均衡,暂时实现了一个高可用。后期我们也许会逐渐转到Harbor上,听说Harbor 0.3的功能还是比较好的,包括mirror等功能,所以,我们后期可能要转过去。
CI/CD方面,我们在这个架构上没有做过多的调整,我们把Dockerfile打到SVN,SVN会上传代码到Jenkins Build镜像,然后Push到镜像仓库里,包括如何与数人云或其他平台做CD。我们的Jenkins做的是单机部署,没有做集群部署,我们的应用现在还是比较少,后期包括Jenkins on Mesos或一些集群部署和一些其它类的CI工具,我们也在逐渐进行尝试。
这是一个案例分享,就是刚才前面说到的移动端回调素材的时候访问的一个统一的接口方。我们这个业务主要分为五层,第一层就是web层,主要是Nginx+PHP的应用,第二层是一个接口缓存层Memcache,第三个是Tomcat,也是一个接口层,第四层是数据库的缓存,第五层是Oracle。三个机房提供服务的支撑,我们目前已经做到了第三层,但是第三层还没有上线,我们上线的是前两层。我们打算再做一下第四层,近期一到四层全能改造完,中间是用VIP来调度或者是DNS来调度,可能我们还得进一步去讨论。
这里把API项目的Dockerfile列出来了,比较简单,就是一个基础镜像包括一个Time的时间区域,yum安装一些必要的程序,在这个位置我们把S6封装进来了,下面是我们Nginx业务的一个脚本,这是后端的一个启动脚本。我们web前端的Dockerfile大概是这样。为了更好的区别日志来自哪里,我们就在Nginx日志里面加了四个字段,即类型、地点、业务类型和Container ID。我们的Container ID封装在S6 run的文件夹里面,最后用sed给它替换掉,因为容器起来的时候,我们获取它的Hostname。真实的日志打出来就是这样,就是我们线上ELK的日志就是这样。
这里面有一个小插曲,一会儿到后面我可能有时间会提一下。
主要业务就分享完了。我们发现一个问题,以前我们用的是1.9.1的版本,我们在用log-driver的时候有一个OPT的参数,我们相关系统工程师把镜像名字打进去了,他认为看日志的人有可能想知道这个容器是从哪个镜像出来的。但是现在回头想想其实没有任何作用。升级到1.11.2这个版本的时候,我们发现通过Log-driver出来的时候,它前面会带出来一个时间、Docker和一个Container ID,它本身就会有这些属性,所以我们后期会把Nginx里面加的Container ID去掉,直接从Log-driver里面出来的Container ID用Logstash做切割匹配就OK了。
这是一个未来发展,这段时间我们跟数人云也在做进一步的沟通,包括我们的业务如何做配置中心,容器ENV化,把更多的环境变量或者参数从镜像里面提取出来,因为我们好多是完全打到镜像里面,可能有一些改动包括配置文件等等就要重新打一遍镜像,太繁琐,跟数人云也在进一步沟通这件事情。
对于我们来说,我们选用一个底层平台,包括我们是不是以后用Docker,我们还在继续探索,可能我们后期也会尝试一下其它的容器。
我大概分享就是这些内容,谢谢大家!