本文讲的是DockOne微信分享(一三四):国内某大型酒店管理集团基于Kubernetes的实践【编者的话】随着业务的增长,架构变得越来越复杂,服务器和应用数量越来越多,随之应用的管理,配置的管理,后期的运维都受到了极大的挑战。之前通过人工进行服务器配置,后期运维通过监控平台发现故障,人工恢复的模式已经无法适应业务的需求。我们借助Docker+Kubernetes打造的新的容器云平台,将之前90%以上需要人工维护部分的工作变成了自动化,一切变得so easy。
公司5年前已经开始了敏捷方法的实践,并打造DevOps的文化,应用架构也完成了SOA的转型,但是由于业务的增长,系统越来越庞大,大家发现敏捷越来越不敏捷,DevOps的实现愈发困难。
在2015年经历了几次大的项目上线后,大家意识解决这些问题的严重性,在2015年底,我们开始相关调研,正值Kubernetes发布,我们发现Docker+Kubernetes可以很好的解决我们面临的一些问题,2016年初我们开始基于Docker+Kubernetes的私有容器云的建设,当年10月完成所有应用容器化改造,并全部稳定运行在Docker+Kubernetes平台。升级后我们的运维效率得到了成倍的提升。
先介绍一下重构前的架构:
重构后的架构:
另外我们使用了ThoughtWorks推荐的一个方法,把我们的我们的应用环境设计成4个环境,分别是QA、UAT、Green、Blue其中QA环境是用于开发人员使用的测试环境另外考虑应用联调的需要,我们提供了一套便于各开发小组联调的UAT环境(数据非真实数据)。
绿、蓝环境均是生产环境,连接同一套数据库,但是除了数据库之外,所有的应用以及相关依赖均是独立的,蓝绿环境的配置,一解决了发布中线上环境的问题,我们的发布流程是:
这样设计的一个作用是为了实现任何时间可以进行发布任务,后面会详细讲。另一个作用蓝绿环境是一个互备的环境。
以下罗列了一下Docker+Kubernetes解决了我们具体的哪些痛点,我找了几个分解一下。
痛点 1
系统缺乏弹性
在面临大的项目时我们需要增加大量资源对现有应用系统进行升级,传统的方式费时费力,需要一台一台服务器部署,还需要调整整个CI/CD的脚本,调整Load balance。整个系统搭建完,再进行调整还会导致大量的人力成本,考虑到调整起来代价比较大,所以我们一般会按照可能的承受的最高负载配置资源。这样也导致了资源的利用率降低。另外还有脏环境的问题,环境配置文件的管理等问题。
当然考虑到以上的原因,为了降低管理成本,我们对服务器,应用运行环境做了一些标准。例如规定了Java运行环境是什么,JDK是什么,我们基于这样的规范做了虚拟机的模板,以降低管理成本。这样也带来了一些新的问题,对开发而言,一定程度上限制了对新技术的使用。
Docker+Kubernetes如何解决了这些问题
Docker的特性,用完即销毁,解决了脏环境的问题。
在Kubernetes上应用容器是基于同一个镜像生成,扩容简单,面临大项目上线时候只要Kubernetes集群资源充裕,应用扩副本可在很短时间内完成,无需对持续部署脚本做任何调整。除了提高效率外,在资源利用率上,我们可以做一些动态的调度,例如在闲时减少副本的数量,闲置资源可用于大数据计算,深度学习无实时要求的应用。
另外Docker技术的使用,应用程序的Runtime变成应用的一部分,集群节点都是标准的,至于平台上跑什么样的Docker已经对环境不会造成管理上的影响,所以我们的平台可以同时支持各种Web运行环境,中间件。对于开发而言,这部分不再受到限制。
痛点 2
敏捷体系下,大量迭代,带来大量的发布,原流程和工具阻碍了测试和版本上线的速度。
如何解决大量发布,而不影响线上业务? 很多公司考虑发布过程中对业务的影响一般会采用两套方法:
- 在系统访问低谷发布,例如在凌晨。
- 采用滚动发布的方法,节点逐个进行代码更新。但是前提是代码要做到向前兼容,因为请求会同时落到新老节点上。
我们为了解决这个问题,我们的生产环境则分为蓝绿环境,资源配比1比0.5 ,我们发布时可以先发绿,完成后将其切换到线上,再发蓝,蓝发布完成后再重新将蓝切换上线。这样可以确保在一天的任何时间进行发布。
技术方面,由于应用数量较多,而且有4个环境,导致应用直接互访的配置文件管理异常的麻烦,上千个配置导致及其容易出错。我们利用了Kubernetes的一些特性。我们为每个环境都建立了一个集群,并且应用在每个集群的service名称均一样,并且分区名称一致。
这样应用abc在qa集群内可以使用abc这个服务名进行调用,在UAT集群也是使用这个服务名,但是集群之间其实是隔离的。这样的设计,使得这部分配置变成了免维护的配置。极大降低了配置管理的工作量。
痛点 3
服务稳定需要大量人工保障
原平台下应用的稳定,我们是监控加人工干预,后期我们做过一些尝试,使用Zabbix监控配合一些服务重启脚本来实现应用故障后自动恢复,但是实现过程还是比较麻烦。
新的平台
我们利用了Kubernetes的liveness和readiness两个健康检查的探针技术解决了服务故障后的自动恢复。
在应用的yaml文件内可加入以下内容:
其中path部分就是应用的健康检查url。平台会根据预先设定时间,检查应用的url是否可访问,一般liveness的检查时间会略长于readiness,如果失败会触发readiness探针,外部请求将不会落在故障pod上,另外会触发liveness探针,系统会自动kill掉故障pod,并自动新拉起一个pod,由于这样的一个机制,整个服务实现了自动恢复。
这两个探针也支持端口检测,或者run一个脚本看是否返回特定值,当然我们觉得端口的方式不够准确,之前遇到过很多次端口是通的,但是应用已经挂掉的情况。所以我们使用了心跳url的方式,另外注意,心跳url的制作,要确保这个url不要依赖其他的服务。
Kubernetes平台很好的解决了我们的这些痛点,整体的效率得到了成倍的提高。服务可用性得到了极大的提升,并且原来投入在保障服务稳定性部分的人力降低到之前的5%。
参考文档:https://kubernetes.io/docs/tas ... obes/
另外关于分享两个容器化过程中的小经验。
1、如果使用Jenkins实现CI /CD,由于每次build出来的版本避免与之前的有冲突。可以使用Jenkins的流水线编号变量作为镜像的tag号,这样你的镜像的tag和pipline号可以一一对应,对于管理和排错提供方便。
2、关于配置文件,之前一直有一个说法,就是一个镜像可以实现在多个环境里运行。当时我们选择Docker也是看中了能解决配置文件的问题。
但是整个的实践中还是遇到了不少问题,例如有方案是,镜像是同一个,配置通过变量传进去;有的配置文件放在共享磁盘里;还有最新的ConfigMap方案。后来我们用了很久的时间想明白的这件事情,如果不同的环境配置通过变量的方式传进去,其实没有解决配置管理的问题,只是配置管理的地方从原来的配置文件变成了变量管理。换汤不换药,而且变量管理其实会更加麻烦,不如配置文件来的直接了当。所以这是个伪命题。
共享目录的方案,所有Docker同时挂在一个放置配置文件的目录,这样的方式,节点一多这个目录会成为一个性能瓶颈,另外这个挂载目录一旦出问题。所有的应用均会出现故障。这是在系统架构上不是一个好的设计。反而造成了应用和NFS服务强耦合。所以我们建议的方式是,将配置文件与项目的war包jar分离开,通过jenkins实现使用不同环境的配置文件编译不同的镜像。
另外配置文件可以与您的代码放在一起放在SVN或者Git上,修改个文本文件肯定比修改Jenkins上某个task的变量配置容易的多,而且还有版本管理。
Q&A
Q:容器的日志怎么管理呢?不同应用的日志怎么管理?
A:我们在容器化之前已经开始使用ELK的解决方案,所以我们整个容器化平台的改造中也整合了ELK的方案。
我们平台上的日志收集采用了2个方案:
- Docker启动的时候mount一个共享存储,日志都写在这个共享存储里,然后配置Logstach采集,最后吐到ES里,用kibana展示,缺点是挂共享存储,性能略差。优点是日志可以根据应用来分到不同的ELK上,并且日志做归档比较容易,我们有场景要查一年前的日志。
- 我们在Kubernetes节点上安装fluentd组件,采集Kubernetes node上的日志,应用部分只需要在log4j的配置里将日志输出打到标准输出上系统即可采集,缺点是这个集群的日志会全部聚合到一套ELK上。优点是应用几乎无需做什么改造。
Q:容器中需要持久化的数据怎么处理?
A:原则上我们不建议在容器化平台上的应用有持久化数据 ,如果互联网应用的背景,应用无状态化设计对于应用有横向扩展需求是更加适合的,应用在上容器云平台前要完成这部分改造。但是我们的确有做过数据持久化的研究,当时主要考虑ES集群会有持久化数据,我们的方案是Kubernetes+GlusterFS的方案。但是由于ES集群本身很稳定,也不会有发布,容器化意义并不大,所以暂时我们这部分仍保留在VM上。
Q:请教下,如果开发机在本地,如何连到测试环境(联调环境)上去,你们的网络如何打通?
A:我们不建议开发使用本地资源进行开发,我们提供整套的QA环境(都在Kubernetes上)。
如果实在要登录到Node上看一些东西,我们有堡垒机。
Q:Docker网络用的什么方案?
A:Flannel,能用,性能不太好,准备迭代掉它。这是坑!大家不要踩。
Q:服务对外暴露是用什么实现的?
A:集群内部访问之前说了,服务名的方式,集群外的访问,Kubernetes每个节点都可以提供服务。但是如果所有请求都落在一个Kubernetes节点上,这个节点就是一个单点。
所以我们提供对外服务时仍然会在node前面加一层loadbalance,LB后面将5-10个Kubernetes的节点作为backend添加上去。另外这部分节点最好不要调度任务在上面跑。
Q:你们的Docker容器在Windows上是怎么解决IP通信的?
A:我们不用Windows的Docker,Docker是进程共享的虚拟化技术,Windows和Linux共享哪门子进程?感觉不太靠谱,所以不用。(比较主观)
微软自己也在搞类似Docker+Kubernetes的架构的一套东西,大家可以关注一下,如果要上Windows的Docker那还是用MS的解决方案比较靠谱。
Q:你们的宿主机、容器、服务的监控告警系统架构是怎样的,能简单介绍下吗?
A:宿主机我们还是用Zabbix,服务的可用性我们也用Zabbix(这是容器化之前就保留下来的)但是只有当所有pod都挂掉才会报警,容器的监控我们是使用Prometheus和Grafana。
Q:四个环境的发布权限你们怎么去合理配置呢?
A:原则上用户登录所有环境都需要通过堡垒机,4个环境的发布,都是通过Jenkins来实现的,所以我们会控制Jenkins上对应的Pipline的权限。(Git->Jenkins->Kubernetes)这一段是自动化的,开发人员能做的就是触发构建任务。
QA环境 ,开发测试环境,我们给到开发进入容器,以及查看日志的权限
UAT、开发人员可以通过ELK查看日志,无其他权限。
生产、开发人员可以通过ELK查看日志,无其他权限。
如有特殊的需求,需要让运维人员操作。
Q:如果有开发人员需要上去看错误日志,假设这种错误日志是需要调试,不会被收集,是只能通过堡垒机去看吗?容器的日志怎么进行存储,如果挂到宿主机目录那好多容器怎么分文件夹呢?
A:我们方案里fluentd可以收集node上几乎所有的日志,另外关于错误调试日志,如果是应用的,那开发人员按照需求调整输出路径就好了,用ELK采集就好了。如果是系统级别的,运维人员就把它作为一个Linux主机查日志拍错就好了。
另外容器的日志,解释起来有些长,你可以参考我之前的帖子《Docker日志那点事》,容器日志的目录和格式都有写到。
Q:用的Overlay网络,那你们用的NodePort暴露的服务,然后外部LB访问NodePort?那你们现在容器一个集群内有多少容器?kube-proxy的性能有做过压力测试吗?
A:回答后面一部分,我们每个环境都是一个集群,例如QA是一个集群,UAT是一个集群,我们一个生产集群目前大概 1000+个容器。
关于压力测试,和kube-proxy的负载问题。提供给集群外的服务接口,我们前面会包一层loadbalance,所以请求进来后后会通过LB平均分配到不同node上的kube-proxy上,这部分可以通过横向扩展来解决压力的问题。
另外我们应用上线前会做压力测试,目前我们没遇到过kube-proxy被压死的情况,多是我们的压力不够(我们使用JMeter,大多时候是JMeter死掉)。
Q:各环境用的是同一个image build用env区分?还是不同build?有比较过或遇到什么坑吗?
A:开始的分享提到了,一个image build用env区分,就是坑。配置文件管理变成了env的管理。所以老老实实每个环境build一个镜像。
Q:各环境的config是怎样和什么时候打到image里的?
A:配置文件一起放在Git上,Jenkins拉完代码再把配置文件拉下来,简单点使用CP命令把对应的配置文件和war或者jar以及Dockerfile放到对应的目录下,进行build就好了。
不同环境作为不同的任务。
左边的任务就是把代码拉下来 配置文件拉下来,右面的任务就是把不同的配置放到不同的目录再进行build。大家可以参考一下这段脚本:
以上内容根据2017年07月25日晚微信群分享内容整理。分享人胡忠良,20年IT工作经验,曾在全球500强公司担任IT运维经理,现任国内最大酒店管理集团-基础架构中心运维经理,微软认证讲师,数据安全专家,ITIL专家,51CTO讲师,Devops实践者,拥抱开源技术。 DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesa,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。
原文发布时间为:2017-08-02
本文作者:胡忠良
原文标题:DockOne微信分享(一三四):国内某大型酒店管理集团基于Kubernetes的实践