DevOps是2009年前后提出的一个概念,提倡开发(Development)和运维(Operations)这两个领域的高度协同。从而在完成高频率部署的同时,提高生产环境的可靠性、稳定性、弹性和安全性。本次分享介绍了PPTV聚力传媒以Docker技术为支撑,在DevOps方面做的优化,包括:
-
DevOps简介
-
Docker在PPTV的应用
-
DevOps与Docker的结合
-
实现方案
首先简单介绍一下DevOps,DevOps通常指的是2009年前后兴起的一个概念,提倡开发和IT运维之间的高度协同,主要目的在于提高用户和业务需求提高产品的交付能力与效率。
相信对很多朋友来说DevOps已经不是陌生的词。到今天为止,可能已经有很多公司在尝试通过Docker实现DevOps。可至今也并没有一个完善的DevOps实现方案和标准。
PPTV基于Docker打造的DCOS平台。底层基于Mesos + Marathon为核心,结合Docker和Nginx,在此基础上开发了DCOS管理平台。包括权限管理模块、统一日志管理模块、IP池管理模块、存储管理模块、服务发现模块,并与持续集成平台jenkins集成,实现应用容器的创建、运行。
Mesos、Marathon、Docker、Jenkins大家应该都有了解,这里就不多说了。下边主要介绍PPTV的Docker使用情况。
目前为止PPTV的测试环境基本上都已经迁移到了Docker上,生产环境的容器化还在进行中。所以后边的内容主要是与测试环境的架构相关。
功能框架
当前架构和发布流程
容器网络
我们采用了网络桥接的方式,基于Docker的bridge模式,将默认的docker bridge网桥替换为linux bridge,把linux bridge网段的IP加入到容器里,实现容器与传统环境应用的互通。
创建一个使用linux bridge的docker network :
docker network create --gateway 10.199.45.200 --subnet 10.199.45.0/24 -o com.docker.network.bridge.name=br-oak --aux-address "DefaultGatewayIPv4=10.199.45.1" oak-net
该命令中创建了一个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的接口。
思路是以Marathon上的App信息作为数据源,定期去调用Marathon的API更新IP列表。当我们要在Marathon上创建一个使用固定IP的容器时,首先会请求IP池管理模块的IP分配接口,请求的时候把App ID发给分配接口,管理平台根据App ID判断这个应用是否是新应用,如果是新应用则从IP池中返回一个未使用的IP,并将此IP与应用关联。
容器run起来的时候自动添加 — net 和– ip 参数指定容器IP。
容器存储
容器存储驱动
我们的OS用的是CentOS。所以在过去的一段时间,使用Loop模式的DeviceMapper作为存储驱动,是Docker默认的,在使用过程中遇到一些问题。
如上面所示问题,不只一次出现,目前都没有完全解决,最终只能用重启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属性:
Docker的口号是
Build, Ship, and Run Any App, Anywhere
。所以使用Docker的理想状态是这样的 :
而通过Docker实现DevOps的理想流程应该是这样的:
但是实际的情况通常是:
在测试环境里测试通过的镜像,很有可能是不能直接拿到线上RUN起来的。因为测试环境和线上环境的架构不一致,或者是配置不一致。
比如上边图里的App1 -> Redis1 ,就有可能存在两个问题:
-
线上环境的配置文件里Redis1的链接地址可能是线上环境Redis IP。而线下的配置是线下的Redis IP。
-
线上的Redis有可能是Redis集群,比如端口是19000。而线下Redis可能是单节点,服务端口是6379。 除此之外可能还有其他的配置参数不一致的问题。
解决上边两个问题的方法就是保证线下线上配置一致。
在过去的解决办法中: