本文讲的是DockOne微信分享( 八十八):PPTV聚力传媒的Docker与DevOps【编者的话】DevOps是2009年前后提出的一个概念,提倡开发(Development)和运维(Operations)这两个领域的高度协同。从而在完成高频率部署的同时,提高生产环境的可靠性、稳定性、弹性和安全性。本次分享介绍了PPTV聚力传媒以Docker技术为支撑,在DevOps方面做的优化,包括:
相信对很多朋友来说DevOps已经不是陌生的词。到今天为止,可能已经有很多公司在尝试通过Docker实现DevOps。可至今也并没有一个完善的DevOps实现方案和标准。
Mesos、Marathon、Docker、Jenkins大家应该都有了解,这里就不多说了。下边主要介绍PPTV的Docker使用情况。
目前为止PPTV的测试环境基本上都已经迁移到了Docker上,生产环境的容器化还在进行中。所以后边的内容主要是与测试环境的架构相关。
图 1
图 2
创建一个使用linux bridge的docker network :
该命令中创建了一个docker bridge网络,并与Docker所在主机的br-oak网桥做桥接。该网络的使用了10.199.45.0/24这个子网,同时通过
关键参数:--aux-address "DefaultGatewayIPv4=10.199.45.1"。
使用桥接的方式,需要考虑IPAM。我们在DCOS管理平台中加入了IP池管理模块,实现全局的IP管控,并提供请求申请IP的接口。
图 3
思路是以Marathon上的App信息作为数据源,定期去调用Marathon的API更新IP列表。当我们要在Marathon上创建一个使用固定IP的容器时,首先会请求IP池管理模块的IP分配接口,请求的时候把App ID发给分配接口,管理平台根据App ID判断这个应用是否是新应用,如果是新应用则从IP池中返回一个未使用的IP,并将此IP与应用关联。
容器run起来的时候自动添加 -- net 和-- ip 参数指定容器IP。
图 4
我们的OS用的是CentOS。所以在过去的一段时间,使用Loop模式的DeviceMapper作为存储驱动,是Docker默认的,在使用过程中遇到一些问题。
图 5
如上面所示问题,不只一次出现,目前都没有完全解决,最终只能用重启Docker的方法。如果在生产环境中遇到这样的问题,应该是大家不希望看到的。
经过对比(对比文章为:《 PPTV聚力传媒DCOS Docker存储方式的研究选型 》,这里不详细说明),我们决定在Btrfs和DeviceMapper的Direct LVM中选定一个存储驱动。对于我们目前打算用Docker上线的服务,主要关注从几百KB到几MB大小的文件,只需考虑读,写的操作是采用挂载volume的方式,不会直接写在容器里。在读的方面,DeviceMapper比Btrfs性能略好。在稳定性方面的比较,由于线下的试验并不能完全模仿线上的场景,初步打算上线时一部分容器运行在DeviceMapper存储驱动的环境下,一部分容器运行在Btrfs存储驱动的环境下,进行观察、比较。
容器外部卷使用GlusterFS,以分布式存储支持容器持久化数据。比如应用日志和应用静态文件。选GlusterFS的原因,因为GlusterFS此前公司内部一直在使用,足以满足测试环境的存储需求。
容器启动的时候加上volume参数,比如容器APP1启动的时候添加 –v /home/logs/app1:/home/logs。
这种做法可以在测试环境中解决容器数据持久化的问题,但是在线上环境中不建议这样做。线上环境我们目前考虑的做法是,将容器日志目录挂载到宿主机上,再通过Logstash或其他方式,统一收集。
Label属性:
图 6
图 7
图 8
图 9
而通过Docker实现DevOps的理想流程应该是这样的:
图 10
但是实际的情况通常是:
图 11
在测试环境里测试通过的镜像,很有可能是不能直接拿到线上RUN起来的。因为测试环境和线上环境的架构不一致,或者是配置不一致。
比如上边图里的App1 -> Redis1 ,就有可能存在两个问题:
解决上边两个问题的方法就是保证线下线上配置一致。
在过去的解决办法中:
对于问题1,通常各项目各组件之间调用的时候通过域名去调用,线上环境通过DNS解析域名,线下环境一般会绑host。
对于问题2,需要在项目初期就规划好,线下和线上是否使用相同的端口。如果后期想要统一配置则修改服务端口,有可能会导致很多未知的问题产生,代价太大。
线上线下配置不一致,应该是在很多公司都存在的情况,而且这样的情况很有可能会造成线上发布的问题。以我们自己的实际情况为例。再看看上边提到我们当前的发布流程:
图 12
我们通过Jenkins编译好了测试环境的应用包,在编译的过程中将应用包里的配置文件替换成线下的配置,然后通过挂载的方式,把应用包放到容器中运行。当测试通过之后,再通过Jenkins编译线上环境的应用包,这次编译的过程中会将配置文件替换成线上的配置。最后将应用包提交给线上运维发布。也就是说我们最终发布到线上的应用包,其实是没有经过测试的。也因此出过生产事故,幸运的是有灰度发布,同时及时回退,降低了影响范围。
为了尽量接近上边的Docker实现DevOps的理想流程,我们要达到的目标:
而要达到这个目标,我们需要解决的问题:
图 13
如上图,最终达到的效果:
一套完整的集成测试环境,包括主服务、Redis、MySQL、DB、ZooKeepoer等,供其他功能环境测试调用。环境内部各项目各组件调用,都使用与线上一致的地址。比如域名,DNS负责解析测试环境内部域名。
多套功能测试环境(上图只画了一套),包含需要进行功能测试的项目,各项目在功能环境中有各自的Redis、MySQL、DB等服务,配置与集成环境相同,跨项目调用的时候,默认调用集成环境。通过dns解析区分集成测试环境和功能测试环境。
完成到这一步,我们基本上能够保证大部分项目的配置、环境一致了。同时因为在Jenkins构建的产出物就是Docker镜像,在环境一致的前提下,通过测试的镜像是可以直接在线上环境中正常运行的。当然也不能排除,有一些配置和项目,确实是无法做到完全一致,那就只能尽量减小差异化。特殊项目通过其他特殊途径处理。
简单小结一下我们将Docker与DevOps结合的主要思路:在尽可能的减小测试环境与线上环境的差异的前提下,在测试环境同时部署多套运行环境进行测试,同时借助Docker的Build、Ship、and Run特性,提高开发和IT运维之间的协同度,提高项目的交付能力与效率。
- DevOps简介
- Docker在PPTV的应用
- DevOps与Docker的结合
- 实现方案
DevOps简介
首先简单介绍一下DevOps,DevOps通常指的是2009年前后兴起的一个概念,提倡开发和IT运维之间的高度协同,主要目的在于提高用户和业务需求提高产品的交付能力与效率。相信对很多朋友来说DevOps已经不是陌生的词。到今天为止,可能已经有很多公司在尝试通过Docker实现DevOps。可至今也并没有一个完善的DevOps实现方案和标准。
Docker在PPTV的应用
PPTV基于Docker打造的DCOS平台。底层基于Mesos + Marathon为核心,结合Docker和Nginx,在此基础上开发了DCOS管理平台。包括权限管理模块、统一日志管理模块、IP池管理模块、存储管理模块、服务发现模块,并与持续集成平台jenkins集成,实现应用容器的创建、运行。Mesos、Marathon、Docker、Jenkins大家应该都有了解,这里就不多说了。下边主要介绍PPTV的Docker使用情况。
目前为止PPTV的测试环境基本上都已经迁移到了Docker上,生产环境的容器化还在进行中。所以后边的内容主要是与测试环境的架构相关。
功能框架
图 1
当前架构和发布流程
图 2
容器网络
我们采用了网络桥接的方式,基于Docker的bridge模式,将默认的docker bridge网桥替换为linux bridge,把linux bridge网段的IP加入到容器里,实现容器与传统环境应用的互通。创建一个使用linux bridge的docker network :
该命令中创建了一个docker bridge网络,并与Docker所在主机的br-oak网桥做桥接。该网络的使用了10.199.45.0/24这个子网,同时通过
--aux-address "DefaultGatewayIPv4=10.199.45.1"
这个参数将容器启动时的网关指向到10.199.45.1。
关键参数:--aux-address "DefaultGatewayIPv4=10.199.45.1"。
使用桥接的方式,需要考虑IPAM。我们在DCOS管理平台中加入了IP池管理模块,实现全局的IP管控,并提供请求申请IP的接口。
图 3
思路是以Marathon上的App信息作为数据源,定期去调用Marathon的API更新IP列表。当我们要在Marathon上创建一个使用固定IP的容器时,首先会请求IP池管理模块的IP分配接口,请求的时候把App ID发给分配接口,管理平台根据App ID判断这个应用是否是新应用,如果是新应用则从IP池中返回一个未使用的IP,并将此IP与应用关联。
容器run起来的时候自动添加 -- net 和-- ip 参数指定容器IP。
图 4
容器存储
容器存储驱动我们的OS用的是CentOS。所以在过去的一段时间,使用Loop模式的DeviceMapper作为存储驱动,是Docker默认的,在使用过程中遇到一些问题。
图 5
如上面所示问题,不只一次出现,目前都没有完全解决,最终只能用重启Docker的方法。如果在生产环境中遇到这样的问题,应该是大家不希望看到的。
经过对比(对比文章为:《 PPTV聚力传媒DCOS Docker存储方式的研究选型 》,这里不详细说明),我们决定在Btrfs和DeviceMapper的Direct LVM中选定一个存储驱动。对于我们目前打算用Docker上线的服务,主要关注从几百KB到几MB大小的文件,只需考虑读,写的操作是采用挂载volume的方式,不会直接写在容器里。在读的方面,DeviceMapper比Btrfs性能略好。在稳定性方面的比较,由于线下的试验并不能完全模仿线上的场景,初步打算上线时一部分容器运行在DeviceMapper存储驱动的环境下,一部分容器运行在Btrfs存储驱动的环境下,进行观察、比较。
容器外部卷使用GlusterFS,以分布式存储支持容器持久化数据。比如应用日志和应用静态文件。选GlusterFS的原因,因为GlusterFS此前公司内部一直在使用,足以满足测试环境的存储需求。
容器日志管理
将应用日志统一收集到GlusterFS上。在Gluster上建了一个log volume ,将此volume挂载到每一台mesos slave主机上,比如 /home/logs。容器启动的时候加上volume参数,比如容器APP1启动的时候添加 –v /home/logs/app1:/home/logs。
这种做法可以在测试环境中解决容器数据持久化的问题,但是在线上环境中不建议这样做。线上环境我们目前考虑的做法是,将容器日志目录挂载到宿主机上,再通过Logstash或其他方式,统一收集。
服务发现
服务发现模块订阅了Marathon的消息,我们对Marathon App的label属性做了约定,属性里包含了这个App的IP、服务端口、服务域名等信息。每当Marathon上有App创建、停止、删除。服务模块都能接收到相应的消息,并将获取到这些信息后处理后更新到Nginx和DNS中。Label属性:
图 6
图 7
图 8
DevOps与Docker的结合
Docker的口号是Build, Ship, and Run Any App, Anywhere
。所以使用Docker的理想状态是这样的 :
图 9
而通过Docker实现DevOps的理想流程应该是这样的:
图 10
但是实际的情况通常是:
图 11
在测试环境里测试通过的镜像,很有可能是不能直接拿到线上RUN起来的。因为测试环境和线上环境的架构不一致,或者是配置不一致。
比如上边图里的App1 -> Redis1 ,就有可能存在两个问题:
- 线上环境的配置文件里Redis1的链接地址可能是线上环境Redis IP。而线下的配置是线下的Redis IP。
- 线上的Redis有可能是Redis集群,比如端口是19000。而线下Redis可能是单节点,服务端口是6379。 除此之外可能还有其他的配置参数不一致的问题。
解决上边两个问题的方法就是保证线下线上配置一致。
在过去的解决办法中:
对于问题1,通常各项目各组件之间调用的时候通过域名去调用,线上环境通过DNS解析域名,线下环境一般会绑host。
对于问题2,需要在项目初期就规划好,线下和线上是否使用相同的端口。如果后期想要统一配置则修改服务端口,有可能会导致很多未知的问题产生,代价太大。
线上线下配置不一致,应该是在很多公司都存在的情况,而且这样的情况很有可能会造成线上发布的问题。以我们自己的实际情况为例。再看看上边提到我们当前的发布流程:
图 12
我们通过Jenkins编译好了测试环境的应用包,在编译的过程中将应用包里的配置文件替换成线下的配置,然后通过挂载的方式,把应用包放到容器中运行。当测试通过之后,再通过Jenkins编译线上环境的应用包,这次编译的过程中会将配置文件替换成线上的配置。最后将应用包提交给线上运维发布。也就是说我们最终发布到线上的应用包,其实是没有经过测试的。也因此出过生产事故,幸运的是有灰度发布,同时及时回退,降低了影响范围。
为了尽量接近上边的Docker实现DevOps的理想流程,我们要达到的目标:
- 在Jenkins构建项目的产出物为包含了应用包(代码+线上配置)和环境(例如Tomcat)的Docker 大镜像。
- 在测试环境中模拟线上架构,保证环境尽量一致。
- 测试环境下测试通过的大镜像可以直接在线上发布。在测试环境部署大镜像,执行测试任务,测试通过后,运维将大镜像发布到线上环境。
而要达到这个目标,我们需要解决的问题:
- 在Docker集群中运行DB、ZooKeeper、MySQL等需要持久化数据的组件。
- 保证线上线下配置一致,包括访问链接和服务端口统一。 3.测试环境可能同时存在多套相同应用,需要实现环境隔离,保证每套环境能使用相同的配置,并行进行不同测试。
实现方案
问题 1
在没有Docker之前,在测试环境中模拟线上环境的架构,是很不现实的,需要大量的人力和物力。而借助Docker,我们可以轻松的创建很多套环境出来,只要有相应的镜像。而对于需要持久化数据的文件,因为在测试环境中访问压力小,我们采用GlusterFS存储,同时对CPU,内存等计算资源超配,最大限度的压榨服务器资源。问题 2、3
我们希望实现的方案尽量的简单,影响尽量小。所以结合服务发现模块,我们对App的label重新做了约定,将测试环境隔离为一套集成环境、多套功能环境。同时每套环境使用各自的一套DNS。尽可能的在测试环境中模拟线上的架构。图 13
如上图,最终达到的效果:
一套完整的集成测试环境,包括主服务、Redis、MySQL、DB、ZooKeepoer等,供其他功能环境测试调用。环境内部各项目各组件调用,都使用与线上一致的地址。比如域名,DNS负责解析测试环境内部域名。
多套功能测试环境(上图只画了一套),包含需要进行功能测试的项目,各项目在功能环境中有各自的Redis、MySQL、DB等服务,配置与集成环境相同,跨项目调用的时候,默认调用集成环境。通过dns解析区分集成测试环境和功能测试环境。
完成到这一步,我们基本上能够保证大部分项目的配置、环境一致了。同时因为在Jenkins构建的产出物就是Docker镜像,在环境一致的前提下,通过测试的镜像是可以直接在线上环境中正常运行的。当然也不能排除,有一些配置和项目,确实是无法做到完全一致,那就只能尽量减小差异化。特殊项目通过其他特殊途径处理。
简单小结一下我们将Docker与DevOps结合的主要思路:在尽可能的减小测试环境与线上环境的差异的前提下,在测试环境同时部署多套运行环境进行测试,同时借助Docker的Build、Ship、and Run特性,提高开发和IT运维之间的协同度,提高项目的交付能力与效率。
Q&A
Q:线上和线下环境不一致的问题,通过配置中心,比如文件或者安全变量,容器App启动读取文件或者变量,就能过去正确的配置,这样方式pptv实践为什么不采用这个方法呢?A:我们线上环境是有配置中心的,但是因为管理上的原因,配置中心主要是管理系统级别的配置,和业务相关的配置不放在配置中心里。而且我们想要做到的效果是使用相同的配置在线下线上环境跑,通过配置中心来做的话,只是把配置项做成了变量,配置实际上还是有差异的。Q:DB变更流程怎么控制的,PPTV的业务中是否涉及到分库分表,又采用了哪些手段来针对测试库与生产库的数据变更带来对业务的影响?
A:DB变更主要通过流程去规范,比如分库分表、表结构变更,需要提前1天以上申请,由DBA评估审核,涉及数据库的更新必须避开业务高峰期,选择凌晨或者早上8点前。技术上,要求业务方在数据库变更应该有备份脚本,备份需要更新的数据。Q:如何解决发布的时候Nginx频繁reload问题?对容器的Swap有限制么?
A:Marathon上有变更的时候不会立即触发reload,首先会将变更的信息记录到DB中,然后有间隔的批量同步触发reload,Swap目前没有做配置Q:1、Redis、MySQL、DB、ZooKeeper这些如果跑在容器,数据初始化持久化的方案是什么,2、linux bridge网络方案 有没有对比过Calico Contiv等?
A:1. 挂载外部卷GlusterFS实现持久化存储 。 2.对比过Calico,可以参考之前的一篇文章《 PPTV Docker集群的网络方案选型》,Contiv没有做过对比。Q:持续组件ZooKeeper、MySQL之类的是怎么和应用集成的,是一个整体镜像还是单个镜像组合的?
A:ZooKeeper、MySQL是不同镜像,运行在不同容器中。Q:请问服务自动发现怎么搞的?新手,不知如何将Node注册进HAProxy或Nginx?
A:对Marathon的label属性进行约定,默认label属性是没有意义的,可以在程序中判断label是否符合约定条件,并触发注册、修改操作。Q:您好,请问你们的DNS是怎么做的,单点故障问题是如何解决的?
A:DNS也运行在由Marathon调度的容器中,Marathon实现故障自愈,DNS重启的同时将触发服务发现的同步操作。Q:灰度发布回退是回退images还是直接回退Docker中的应用包呢,回退前如果改过Volume中数据库话,数据库也一起回退?遇到什么问题么?
A:目前线上环境的容器化还在进行中,将来会是回退images。线上的数据库不会跑在容器中,回退的时候数据库也回退,和没有用容器的时候流程一样。Q:在生产环境,一个App ID会存在需要多个容器的情况,也就需要多个IP。按上述使用方式应该会有问题。这块是如何解决呢?
A:在生产环境中在Marathon上层封装了一套发布系统,同一个项目创建多个容器,会在Marathon上创建多个App ID,Marathon上的信息对外不可见。Q:我想问,使用bridge,10.199.45.0/24,会不会IP耗尽?还有有没有测试过bridge的效率?
A:10.199.45.0/24 只是举个例子,实际场景下会有多个IP段,或者使用一个大的网段。只做了简单的测试,bridge效率基本能达到原生网络的90%以上。Q:选择CentOS而不是Ubuntu或者CoreOS的原因是什么?DNS和IP地址池如何协调?
A:技术栈原因,公司一直在用RedHat和CentOS。DNS和IP由DCOS管理平台进行管理。Q:Mesos和Marathon结合时,关闭容器后会产生stop状态的容器而不自动清理,只能脚本清理,这个请问你们有什么方案处理么?
A:目前我们也是脚本。将来可能会和Mesos的hook做到一起,Mesos发现task结束时删除容器,或者通知管理平台,由管理平台定期批量处理。
以上内容根据2016年10月11日晚微信群分享内容整理。分享人李周,PPTV聚力传媒 DCOS技术主要负责人,专注于Docker网络解决方案,容器监控,DevOps。之前在中小创业公司长期负责客户现场实施工作,积累了大量关于网络,监控,权限认证,大数据,Oracle,中间件等经验,同时喜欢研究各种新兴技术。 DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesz,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。
原文发布时间为:2016-10-24
本文作者:李周
本文来自云栖社区合作伙伴Dockerone.io,了解相关信息可以关注Dockerone.io。
原文标题:DockOne微信分享( 八十八):PPTV聚力传媒的Docker与DevOps