DevOps的最佳实践与Docker的运维挑战

时间:2021-03-15 21:52:51

本篇文章整理自王寒6月16日在『ITA1024运维技术精英群』里的分享实录:DevOps的最佳实践与Docker的运维挑战 。


众所周知,DevOps是近来很火的一个概念,WikiPedia把它定义为在促进开发与运维的交流与合作的条件下,以自动化工具与流程为基础,更快速,高效和稳定的实现环境搭建,应用测试,软件发布的一种文化和趋势。不难看出,DevOps即为开发与运维间的一种合作模式,并最终达到应用高效迭代,发布的效果。

DevOps的最佳实践与Docker的运维挑战

 

同时,在国内各种规模的企业系统都在尝试在他们的开发运维环境中使用DevOps,想要跟上这一技术潮流。可惜,理想是丰满的,现实是骨感的,很多公司在尝试的过程中都失败了。有的是因为将DevOps使用在了错误的场合,有的是因为使用了错误的方法,还有的是因为没有做好充足的准备。这些失败终将导致系统更复杂,引来潜在风险,最终致使工程师花费很多时间和精力在解决这些问题上。

那么,DevOps真的只是一个传说吗?我们该如何做才能一步步实现DevOps呢?

DevOps的四个阶段

实现DevOps,应该经历以下四个阶段:传统简单的自动化,持续交付与静态Docker,增强与动态Docker,以及全面持续交付和自动化。

1)第一个阶段是偏向传统的,只是编写了一些脚本,或者利用一些简单的自动化工具,如Ansible,执行一些简单重复的命令,完成版本更新发布而已;

2)第二个阶段,是我认为目前大多数公司应该去实现达到的一个阶段,在这个阶段里应该拥有了自己的支持交付的工具和流程,使用了Docker但可以是静态的模式,没有调度,扩展等的自动化机制,还是需要人工决定资源的调配,系统对于动态负载的弹性较弱;

 3)第三个阶段则是对第二个阶段做全方位的加强,包括很多细节,比如不断改进持续交付以及实现自动化测试,支持更复杂的应用架构,并且可以拥有一个动态的Docker 部署和调度,利用Swarm,Kubernete等工具;

 DevOps的最佳实践与Docker的运维挑战

4)第四个阶段是一个全自动的阶段,完全实现infrastructure-as-code,整个系统和应用就是在Git中的一系列文件,包括云(底层基础设施),服务器,应用,等等等等,所有一切。当然这是非常理想化也非常困难的。

DevOps的最佳实践与Docker的运维挑战

 DevOps的四个技术基础

基于定义的这四个DevOps的阶段,下面是实现DevOps的四个技术基础,分别是:版本控制,环境抽象,全自动化测试以及完全持续部署。在没有这四个基础的准备之下,DevOps就是空中楼阁。

- 版本控制指的是代码的开发,复查,版本控制,代码集成等能够在DevOps中更加自动化; 

- 抽象环境是指开发的代码可以匹配兼容各种环境,比如在一个Docker image里,我们的代码可以根据image所处的不同环境,调取不同的环境变量如IP,域名,文件,存储,密码等;

- 一个dockerimage 满足多环境部署,配置独立且动态化;

- 全自动化测试,是指所有版本,变更与其他版本的兼容性都可以得到自动的测试;

- 完全的持续集成是指无论在什么时间,或者什么人做的变更,都会与当前的版本代码进行持续的兼容测试,以保证所有的变更都可以最终整合在一起,实现最终版本的发布并部署到各个环境中。

DevOps的实践案例

在介绍了如何一步步实现DevOps以及实现DevOps的基础技术之后,我们再通过一个DevOps的实际案例来看一下如何在实战中实现DevOps。这个案例的基本背景是此案例实践了之前提到的DevOps的第二阶段,整体环境是在AWS上,开发需求是每两周做一次版本迭代,每日推送更新3-4次。通过以下技术手段实现了四大基础:

- 使用GITLAB实现版本控制,

- Docker 实现抽象环境,

- 自动化测试目前是半自动需要人为干预,

- 然后利用Jenkins实现了持续集成。

其系统环境的大体架构如下图所示,首先应用的请求会来通过AWS的ELB负载均衡然后分配到不同的API Gateway(这是在不同EC2 VM上的某一个Docker container中),之后API GATEWAY会通过Redis做session的缓存,通过Redis拿到中间键值然后再通过Internal ELB去联系不同VM上的应用Docker container。所有的联系都会通过指定好的IP和端口去完成,并且在VM的docker上都是没有session存在的,这样可以实现更灵活的扩张变更。 在了解了此案例的背景和架构之后,我们再来看一下是如何实现持续集成(CI)的。CI是基于Jenkins的调度与docker的灵活度来实现。可以说Jenkins与Docker是一对好搭档。在commit代码后,在build服务器上完成编译,程序代码发送到docker引擎,自动化打包至新的应用镜像,然后将docker image发送至dockerregistry完成注册。此刻的已准备好了对下一步的多环境部署动作。Docker 在这个过程中扮演了一个极为称职的对代码打包的角色。整个过程如图所示。

DevOps的最佳实践与Docker的运维挑战

上一步完成后,我们就可以开始在stage环境上部署,以便开始测试。利用Jenkins的调度发布功能,发布到本地Stage环境,同时会在基于AWS的UAT docker环境部署。实现在不同的环境下进行测试以及处理。在这个过程中,Docker可以帮助实现环境抽象化。 

那么问题来了,如果我们有50个开发人员,10个IDC环境,4个不同开发环境,20个应用,我们该如何实现环境配置的管理?在这里就需要引入etcd服务发现功能来统一管理多环境多配置的集合。一套应用的镜像可以轻松应用于多个集合环境,实现了数据和配置的解耦。 

另外Jenkins在整个过程中,起到了任务调度执行的关键作用,从而实现了整个流程的自动化。从调度打包到垮环境部署等。

讨论过DevOps案例之后,我们可以看出Docker是实现 DevOps的一个重要的技术手段,那么Docker的使用有什么优势以及挑战呢?面对这些挑战我们该如何应对呢? 

Docker的优势与挑战

Docker的优势总结起来主要有以下几点:

首先,他是高效的,没有OS这一层可以极大提高运行性能;

其次他是一个工具化的完整技术,docker中包含了许多工具去管理docker container包括环境,发现服务,存储等;

他又是极为灵活的,将应用和系统“容器化”可以更加便捷的部署应用;

他的敏捷度很高,可以快速的复原和更新;同时,他也很轻量,在一个虚拟机上可以部署很多个容器;此外,他还具有成本优势以及是目前开发这个生态环境中受广大用户欢迎支持的一种技术。 

这么棒的一种技术,受到开发者欢迎的,但对于运维工程师来说,在维护掌控这个技术的时候却面临了很多的挑战,这里我列出了如下几点在我们实践中碰到的挑战:监控,日志,排障,安全以及网络。 

在监控方面,最基本的监控是做到监控dockerservice是否在正常运行,但这是远远不够的,我们需要对docker 运行有更多的了解,比如CPU,内容,硬盘空间,I/O,网络情况。更深层次的监控可以通过三种技术手段实现:读取container里的CGROUP文件再经过分析拿到docker运行信息,通过调用docker API 直接拿去信息,或者通过命令行拿到信息但这种方式所获得的信息较少。

于是,在Cgroup和API的基础上,我们建议去使用Cgroup 直接拿到文件然后通过分析获得参数输出,因为相比API,这种手段可以获得更多有价值的信息并且对Docker的性能影响最低。此外,对于docker占用的disk问题也是个挑战,因为我们在VM上只可以看到所有containers整体占用的磁盘空间,所以为了了解到各个docker的使用情况,我们需要链接到各个container然后通过命令行调取各自占用disk的信息。

此外,监控container内部的service也是一个难点,对于service的监控,我们有两种策略:

一种是在container内部部署agent,但是这种手段需要其他的service辅助在启动container是控制启动这个agent。这里需要提醒大家的是,docker的部署标准规范是一个container中只是用一个service。

此外使用这种方法监控service的话,还有一个问题就是针对agent的更新只能在打包这个image时候完成,这样也对真个image的创建增加了复杂度。就上述多种原因,我们在这里建议使用第二种方式,即在container外部对内部service进行监控。

虽然这种方法中有一个难点是如何连接到内部的service获取数据,不过借助 Zabbix 的 Low level Discovery 技术,我们成功地可以找到内部的service 并且获取我们需要的参数信息,完成对内部服务的监控。当然,对于不同的应用,我们仍需要对我们的监控脚本做一些定制化的修改。

DevOps的最佳实践与Docker的运维挑战

Docker的日志对于运维人员也是一个挑战,尤其是当我们想集中管理这些日志的时候。Docker 产生的日志主要为应用日志,系统日志是很少数的,而我们面临的最大的挑战则是去搞清楚某一个日志是来自哪里,是来自按一个host,哪一个host上的哪一个container。此外,但我们部署可一个新的container或者删除一个container的时候,如何相应的对其日志进行相应的增加或减少的操作。

针对上述问题,一个选择使用GELF收集docker的日志到ELK系统,GELK是docker自带的一个工具,在1.8版本中出现。但问题是它使用的UDP方式去传输日志,由于UDP的不稳定性以及GELK不会记录日志在本地,所以他很容易造成日志的丢失。

所以看上去这个策略可靠性不高,于是我们又想到另一种方法,即通过创建一个专用的container作为logstash的forwarder。这样所有的日志都会被存在这个host上并且这个container可以对log进行读取,标记,然后发到ELK日志系统。

排障曾经是docker面临的一个问题,因为我们无法SSH到container,需要在内部启动service使用console登录,并且我们很难对container进行修改和测试因为他的部署都是在image生成阶段完成的。后来docker新版本有了Bash Shell 工具可以实现对container的登录控制,但是对于及时即可的修改由于他的天性,我们仍然无法按照过去的习惯去完成故障排查。

安全和网络也是痛点。从安全角度讲,由于container的灵活性,image的安全补丁是不断地变化的。此外,隔离性也是个问题,不像VM与VM之间的用户权限是隔离,VM上的container的用户权限是与host VM一致的,这样不好完成权限管理。同时,由于docker的技术还在发展的阶段,他并没有很完善成熟的安全规则。

不过docker也带来了很多安全方面的最佳实践文档,包括建议容器间开启LINKS从而保护了应用端口的不必要暴露,建议使用客户端和服务器端证书认证方式,使用非root用户运行服务,使用SELINUX来增强容器安全等等方法。网络方面,对于动态docker 系统,container的IP管理也是个问题,我们需要一些工具去完善。

Docker的愿景

最后,我再来展望一下DevOps的未来。我希望我们可以在不远的时间内实现 DevOps中的自动伸缩,是全自动化的scalling,然后可以完善动态的docker ,而非现在还需要手动去部署和发布,以及实现Green/BlueRolling,即对于不同VM不同container的变更可以在负载均衡后可以做到无缝的链接,为使用者提供持续稳定的服务。

DevOps的最佳实践与Docker的运维挑战

  

- 自动伸缩:创建AWS的CloudWatch并设定阈值,在AWS的AutoScaling中的Launch Configuration 中集中制定各类弹性伸缩配置,包括AMI,安全策略等等,启动一个实例需要的所有信息(Launch Configuratio   n是的朋友)。一旦弹性伸缩配置完成后,阈值一旦触发即可自动扩展性能,反之也可以自动收缩性能,做到完全的自动化

- 集群化,自动化调度:通过一系列docker工具,诸如Swarm,Fleet,Kubernete, Meso等等实现

-严格执行蓝绿发布:

a  在系统架构中,设定主备负载均衡;

b  当需要新发布时,把绿色集群的状态改为’备用’. 从负载均衡的池里把这些地址去掉,这样,绿色的集群就不再回接收到来自用户的请求了.转而进入备用负载均衡的池里;

c  在绿色集群里部署新的代码,直到应用启动成功。使用备用负载均衡简单测试一下备用集群的部署情况;

d  将流量引到绿色集群,对蓝色集群做同样部署和测试操作。这里需要注意的是,会有一段时间蓝绿集群两者运行了新老版本,所以程序兼容性也是受到考验的。

总结

通过本文的介绍,我们了解到了DevOps是一种文化趋势,是通过促进开发与运维的合作从而实现对公司技术业务发展的加速;

同时,在实现DevOps的一步步过程中,希望在现阶段国内对DevOps技术感兴趣的公司都可以实现第二个阶段的DevOps;

最后,Docker作为实现DevOps的一个重要技术手段,他给运维其实带来了不小的挑战,我们应该做好相应的准备;正所谓机遇与挑战并存,也希望运维朋友们可以在这次技术浪潮中在做好为Dev服务的同时,开辟出创新的革命化的技术从而真正做到“Dev做快,Ops做巧”