本文讲的是DockOne微信分享(一一零):Docker在沪江落地的实践【编者的话】容器化是很多公司技术层向往又惧怕的一项热门技术,它的高效性,封装性能给开发、运维带来许多便利,但其本身也需要较强的技术能力去控制,否则会变成一个无法落地的概念。沪江作为教育界的独角兽,随着业务的增长,在开发、测试、运维上的成本增加日益显著。经过我们一年的探索,终于使Docker技术在沪江落地,不但成功的降低了成本,并吸引了其他部门的关注与试用,取得良好的成效。
容器化技术在过去的几年甚至到现在都是一个十分红火的技术,每一个对技术有些追求的公司对容器或多或少都有些蠢蠢欲动与研究,我厂也不例外。今天我们就来谈谈Docker是如何在沪江落地的。
Docker与微服务
微服务与Docker都是着简单轻量的代言,以至于人们说起Docker便会联想起微服务。但其实两者没有本质的关系,Docker可以不依赖于任何语言、框架或系统,而微服务负责拆分业务,解耦复杂应用。由于Docker相比VM更加轻量,更加灵活,正好符合了微服务的一些原则,所以大家经常使用Docker来部署微服务。
沪江在使用Docker前,首先对业务进行了拆分,把传统服务拆分成微服务后再实践Docker部署。今天我以沪江的课件云为例,先讲解一下服务的拆分遇到的问题。
微服务的颗粒度一直是众多架构师探讨的问题之一,在众多的讨论中,我比较欣赏微服务教父Sam的一个定义:微服务是一个能够在两个星期内重构完成的小程序。所以在拆分课件云业务之初,就以两星期原则为拆分依据,分为下图这样的结构:
在图中,最底层由分布式存储与容器云组成,容器云使用Docker+Mesos+Marathon的组合,相信大家对这套组合不会陌生,之后会继续介绍为什么使用这套组合以及使用中遇到的问题。中间层便是拆分出的业务微服务,目前这个项目线上已有8个微服务。最上层是两个客户端软件。
编排工具的选择
如果只把Docker作为一个部署工具实在是太浪费了,Docker的优势完全没有发挥出来,通过Docker来编排微服务才是使用Docker的正确姿势,这里不得不说一说Mesos和Kubernetes的一些故事。
如今的docker编排呈现了三大阵营,Mesos+Marathon,Kubernetes和Docker Swarm。Swarm还处于发展阶段但势头很猛,Kubernetes和Mesos都源于Google的brog项目但侧重点不同:Kubernetes侧重于企业级集成,你能想到的方案,他都能通过自身组件集成,而Mesos专注于资源调度和管理。
在这里我们主要介绍Kubernetes和Mesos之争,由于两大阵营的激烈争夺,大的互联网企业自然选择了Kubernetes这样潜力十足又非常周到的企业级产品,虽然K8s目前网络,存储的解决方案还不完善,但互联网巨头不怕,他们有的是技术团队去修改源码,去贡献社区。这使我们这些中小型互联网企业面对了选择的难题。
我们最好的方式就是对两者都进行尝试。通过寻找各个框架配合的方案,找到最符合我们需求的方案。经历了99 81难后,我们给出了下图的比较
Mesos由于历史悠久,性能不差,组合方便成为了我们最终的选择
网络解决方案
Docker网络是我们遇到的最头疼也是最迫切的问题之一。站在巨人的肩膀上,业内成熟的解决方案有Calico,flannel,docker vxlan,weave,Macvlan等。Calico对物理网络侵入交多,weave性能过差暂时不考虑,所以矛盾集中在flannel和docker network的选择上。网上也有张著名的性能比较图。
我们针对Flannel和Docker Network也进行了性能的比较后发现,在大并发的情况下,Flannel网络CPU占用过高,这是因为Flannel基于大三层对tcp请求进行了封包与拆包导致,Docker network虽然也需要封包拆包,但其过程发生在内核中,性能要优于Flannel。具体网络拓扑见下图:
Docker Network方案是由Docker公司开发的,对Docker本身有足够的支持。在网络隔离性上也有一定的控制,比如可以控制两个Overlay网络之间互访。在效率上由于使用了内核级分包拆包,速度和资源消耗要远小于Flannel。
Docker Network的使用过程中,我们也遇到了许多的坑,例如内核的版本或Docker版本过低导致网络不稳定等,建议使用这种网络方案的同学把linux内核升到4.4,Docker版本升到1.11以上。对于将来,我们计划研究一下Calico网络,毕竟有人称Calico是产线上最好的网络解决方案。
存储解决方案
业务对于存储的需求,大致有两点:日志和共享存储。这两点恰恰反应了Docker本地存储和网络存储的解决方案。在存储上Docker和Kubernetes有一定的分歧,Docker公司比较推行Volume-driver的理念,即所有的存储都是驱动,本地存储和网络存储只是对应的驱动不同而已。
而Google的Kubernetes则认为存储本身应该和Docker甚至容器是隔离的,即任何存储都是卷(volume)。不管是本地存储还是网络存储,应该都是事先建立好的,在容器看来都只是一个卷,通过统一的驱动挂在即可。
我们属于墙头草派。结合两者的特点,首先创建两种存储卷,再通过Docker的卷驱动进行挂载。其结构如下图所示
监控解决方案
如果一个线上应用没有监控手段来保证运行的正常,结果是十分可怕的。曾经一位对容器化感兴趣但非常犹豫的伙伴对我说,我非常希望采用Docker的方案来改变我们的臃肿并且部署麻烦的应用,这看着十分方便和高效。但真上线我不敢啊,当遇到紧急情况,如何看到应用的状态?当遇到系统崩溃,docker容器已经消失,如何能回溯问题?这两个问题也引起了我们的思考,归纳起来是两点问题:监控与还原现场。
目前Docker的监控方案有很多,例如Google的cAdvisor,Datado,SoundCloud的Prometheus等,我们选择了使用Mesos自带的Metric做为我们监控的元数据。其原理是:任何使用Mesos Framework启动的任务,都能通过Mesos的Matrics API获取到。通过这一特性,Docker的CPU、内存、磁盘利用率就能监控了。
至于还原现场,很多公司都各显神通。我们对此需求不是特别强烈,所以没有研究。
扩容解决方案
对微服务进行扩容是相当麻烦的事。微服务本身部署数目众多,扩容也不会只扩容几个Instance,如果是人工运维的话,需要对上层的LB逐一的修改配置。这肯定不符合工程师们“懒才是推动技术发展动力”的观念,如何对微服务进行自动扩容成为了我们新目标。
在沪江,我们的解决方案是使用Mesos Consul、Consul、Consul Template等一些列配套工具。当扩容的容器通过Mesos启动后,Mesos Consul(Cisco开源)会自动读取分配的IP和port注册到Consul(一个专业服务注册软件)中。我们再用Consul Template(一个官方定时读取Consul内注册服务的组件)对Nginx进行改造,每次得到更新的微服务信息后更新Nginx配置。通过这样一套辅助软件,我们便可以实现自动化的扩容了。
当应用服务的压力达到一定的阈值后,自动扩容程序便会通过Mesos Metrics检测到,同时通知Marathon根据既定的策略进行扩容。
当应用服务的压力减小后,扩容程序也能进行自动缩容,只是会根据策略选择缩容方式,比如设定当应用服务压力减少到20%后,凌晨2点开始减小容器个数。
总结
通过上述方案,Docker终于在沪江落地了,那它的结果如何呢?下图是我们对使用前后做的对比图:
可以看出,使用Docker相比传统应用,可以部署更多的应用,QPS增加,资源降低,效果十分明显。
Docker与微服务虽然时常博得大众的眼球,但实际操作落地却并不容易。沪江并没有走大厂Kubernetes的技术路线,而是采用更稳定、更灵活的Mesos+Marathon编排也是结合了企业自身的技术能力和业务场景。
我们虽然根据自身需求,暂时解决了Docker容器编排、网络、存储、监控、扩容等相关问题,但这并不是终点,我们将持续对Docker容器应用进行研究。这就是我今天所有的分享,感谢大家倾听。
Q&A
Q:Ceph 集群在使用过程中有遇到过什么坑吗,能否分享一下?
A:Ceph集群在产线环境上使用的确有很多问题要注意,但这和本次分享没有什么关系,下次我可以分享一些Ceph集群我们遇到的问题及解决方案。
Q:日志如何进行搜集,出现业务故障时如何快速定位问题或者线上debug?
A:日志是通过卷挂载Host的方式放在了统一的文件夹下,由Logstash收集后上报ES后通过Kibana展现出来。如果出现故障,运维系统会出现报警,我们根据ELK stack看到问题所在。
Q:基础架构层的分布式存储是否使用的Ceph,存储集群的规模是多大?
A:的确,我们使用的是Ceph作为分布式存储。主要使用的是它的file system与Object storage。规模上,OSD有20台,存储容量在900T。
Q:存储方案为什么需要实现多种volume方案?
A:由于业务的需要,Docker不但要把日志输出到HOST的磁盘上给ELK使用,还要挂载Ceph文件存储,两者对应的驱动是不一样的,HOST磁盘使用的是Direct-lvm,而Ceph分布式文件存储使用的是ceph-fuse。
Q:Docker怎么跟现有的业务之间相互访问,网络层IP怎么解决的?
A:正如刚才我分享的,我们使用Docker Network作为我们网络的解决方案。在Docker Network中,我们创建了一个Overlay网络,容器之间有内部网络,路由表存储在zk中,容器中有两个虚拟网卡,一个对在Overlay中同网段的容器,一个对HOST。Overlay的IP范围是可以设置,建议不要设置太大,容易引起网络风暴。
Q:使用Docker Network跨主机的容器IP是否能互通?
A:能,而且必须能,否则这种方案是无法使用的。补充一点,Docker Network还能通过访问控制,来隔离各个Overlay网络的互访。
Q:网络存储是怎么和Ceph结合的呢,利用Ceph的rbd挂载到主机上吗?
A:很多公司都是这么做的。不过我们没有这么干,因为rbd是块存储设备,相当于在Docker中挂载了一块裸盘,没有文件系统无法使用。我们还是使用ceph-fuse并使用驱动的形式挂载载Docker内。
Q:转码是CPU和内存密集型操作,请问当初是基于怎样的考虑上Docker的?
A:这个问题其实挺有意思,有人大概还不了解转码,其实就是音视频的码率、编码格式转换的处理。例如把一个mp4视屏,转换成HLS切片格式。这的确是消耗CPU和内存的操作。但是,我们有Mesos这个强大的资源管家。如果一个视频过长过大,一台服务器转码速度太慢,可以切成小份让多台服务器一起工作,再合并起来。Docker的优势就体现了,我们把转码称为worker,当每个work接收到任务后,让Mesos调度资源,工作完成后立刻回收资源。这样既不浪费服务器也更能体现微服务,有兴趣的同学可以阅读我的另一片文章:Juice任务调度系统。
Q:能做到根据压力自动扩容么?
A:可以的。我们开源一个自动扩容程序,它的主要思想是:定时检查Mesos的Metrics中CPU和内存的占用,如果达到一个阈值,便给Marathon发一个扩容请求,Marathon接收到请求后便可按一定的比例扩充服务。如图所示:
Q:请教一下关于容器的资源分配和程序切分问题,我有很多服务容器,放的是Java Web Serive,每次启动就占了128m内存,4G内存的主机也就放20个服务,io wait达到 10%,希望能给一些建议,谢谢。
A:这个问题我们曾经也遇到过。不光io wait高,连Swap区占用都很厉害。后来研究发现了,其实这是Docker CGroup内存隔离的锅。当我们的Docker容器分配过少的内存后,应用程序的内存不够用,Docker会认为物理内存已经占完,会使用交换区,也就是硬盘中的Buffer作为内存,这样就会导致频繁的io交互。在高并发的状态下,io wait就高了。解决方式是,扩大Docker的内存设置。
Q:传统的Java Web型应用如何放到容器里,一个镜像都要600m左右?
A:如果使用官方的Java基础镜像,它的操作系统选用的是Debian,自然很大。我们使用的java-jre:alpine镜像,Alpine是最小的Linux集合,只有 5m,加上使用jre,基础镜像只有140多m。Java框架使用Spring Boot,再加上微服务的拆分,一个典型的Web应用也就200m左右。
Q:Mesos集群本身怎么鉴控?是否可以自动化排除集群故障,如网络异常导致的数据不一致?
A:Mesos集群由Mesos的master监控着,我们也是用Zabbix等对上面的Mesos服务做着监控。但Mesos中任何Slave有问题,对线上业务不会受到影响,这得益于Marathon这个应用程序的保姆,会自动在可用集群内迁移应用程序。至于网络异常导致数据不一致的问题,我们还真没遇到过,我们大部分的服务是无状态的服务,对有状态的服务一般采用主、从、选主三服务的经典HA方案。
以上内容根据2017年03月14日晚微信群分享内容整理。分享人黄凯,沪江Java 架构师。计算机硕士毕业。拥有10多年Java研发经验, 6年从事云计算研发和架构经验, 先后任职于 HP、IBM 等云计算部门, 对 IaaS,PaaS 和SaaS, 尤其是云存储有较深入的了解。 2015 年加入沪江, 主导的产品有:课件云存储, 任务调度系统等。现跻身于容器技术的热潮中,希望能凭一技之长,“云”化沪江。 DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesz,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。
原文发布时间为:2017-03-19
本文作者:黄凯
原文标题:DockOne微信分享(一一零):Docker在沪江落地的实践