作为容器集群管理技术竞争的大赢家,Kubernetes已经和微服务紧密联系,采用Kubernetes的企业往往都开始了微服务架构的探索。然而不同企业不同阶段的微服务实践面临的问题千差万别,注定要在技术路线上产生分叉。如何选择适合自己的技术,是每一个践行微服务的团队面临的第一个问题。网易云是Kubernetes的第一批重度用户,在不同业务场景下解决了很多挑战,在本文中,网易云首席解决方案架构师刘超梳理了基于Kubernetes构建微服务体系的进阶之路。
一个产品的发展,通常可分为冷启动阶段、高速增长阶段和成熟阶段。
产品冷启动阶段,需求是以最简单的架构验证业务。以网易考拉海购(以下简称“考拉”)为例,最初的架构设计目标就是快速启动,验证产品方向,该架构包括在线、缓存、线下和管理服务四个方面,即一般电商平台加上跨境电商必备的进销存系统,采用了Oracle数据库、OpenStack管理的虚拟机(VM),并没有诸如高并发之类的考虑。
产品高速增长阶段,业务规模逐渐扩大,产品复杂度也随着增加,企业需要解决快速迭代、高可靠和高可用等问题,一个自然的选择是服务化的拆分,把一个单体架构拆分成一些较小的模块,并遵循康威定律,用5-9个小团队来适应架构的变化。仍以考拉为例,考拉在高速增长阶段也慢慢演化出各种新的模块,比如单独的支付模块、货仓模块、第三方商家模块、推送模块等,并基于Dubbo框架打造服务发现功能来支持各模块之间的相互调用。
服务化主要解决了变更的问题。在整个架构演进的过程中,各个模块都面临爆炸性的增长,比如海淘、自营、第三方商家的供应链,Web、APP、H5的呈现,限时购、秒杀、预售的活动页,以及仓库与物流系统、支付系统的对接等,紧耦合则牵一发而动全身,工程臃肿,影响迭代速度,分别独立上线更有利于适应业务发展的需求。考拉在高速增长阶段首先按照主页、活动页、优惠券、支付等维度纵向拆分,之后又不断演进成为100多个相互关联的模块,变更频率由每天2次增长到每天1000多次,产品质量提升52%。
拆分成大量小模块之后,虚拟机与服务化架构的配合就出现了很多新的挑战,于是有了容器化的需求。
刘超解释说,拆分之前首先要解决“合”的问题,即需要保证功能还是原来的功能,代码质量还是原来的代码质量,不会引入新的bug。他认为,微服务化需要从一开始就要做好持续集成,而容器是很好的持续集成的工具,完成从代码提交到自动测试、自动发布的工作。容器化会带来开发流程的变化,把环境交付过程从运维人员提前到开发人员手上。
在架构复杂的情况下,比如100多个模块,再加上各种副本,所有环境都由一个运维团队来完成,不仅工作量繁重,而且还容易出错,但这是使用虚拟机的模式。而如果写一个Dockerflie放到代码仓库,由开发人员来考虑开发完成之后应用部署的配置环境、权限等问题,包括测试环境的部署、联调环境的部署、生产环境的部署,问题就很好解决了。这就是容器化带来的流程变化。
然而,这种转变涉及到开发人员是否愿意学习容器技术。刘超推荐的解决办法,是使用镜像分层的形式,即最内部的环境包括操作系统及系统工具的镜像由运维人员来做,中间层环境的镜像由核心开发人员完成,普通开发人员只需把jar或者war扔到相应的路径下即可,这就极大降低企业组织容器化的障碍。
第一种场景,就是用Kubernetes管理虚拟机,容器的网络、存储会面临各种各样的选型。企业如果对容器的网络、存储了解不足,可以把容器当成一个持续集成的工具,把一个容器嵌入到一个虚拟机里面,相当于用容器镜像代替脚本部署。这种做法需要解决两个问题:一是IP保持的问题,二是盘保持的问题。因为原先采用虚拟机的时候,是基于有状态的设计,认为IP、Volume都是保持不变的。当容器仅仅作为持续集成的工具,团队的这个习惯可能改不了。
一个方案是自己实现一个有状态容器的方式,实现IP的保持,当一个节点挂了,重新启动的虚拟机和容器仍然可以使用原先分配的IP,二是把Docker容器的镜像一层层地Mount到外面的Volume里面,当一个节点挂了,Docker所有的镜像和Volume其实还挂载在外面的Ceph上,数据并未丢失。这和使用VM很相似,既可以Docker化支持微服务化,也不需要改变用户习惯。使用Kubernetes压力相对比较大的团队,可以通过这种方式切入。
第二种场景,企业没有使用虚拟机,有一部分应用部署在物理机(PM)上,同时想把一部分应用迁移到容器里。此时,不管物理机是否嵌套虚拟机,直接创建一个Bridge Network,把物理网卡也打进去,当Docker的网卡和Bridge连起来的时候,整个网络就是平的,容器和容器旁边的物理机都使用同一个指定的网段。网络打平之后,使用Dubbo的团队也可以比较顺畅地把一部分物理机上部署的应用逐渐迁移到容器里,而如果没有Bridge Network,中间过负载均衡(LB)或者NAT时会很别扭,因为Kubernetes层的维护人员通常很难劝说Dubbo层开发人员改变应用开发的方式。
使用Bridge Network,Kubernetes网络配置很简单,使用CNI的方式即可。如果有定制化以适应应用层的需求,可以参考Docker run的手动配置方式,开发自己的CNI插件。大致流程是先创建网桥(如果不存在),获取Namespace,配置veth pair并放到Namespace里,然后获取IP地址,获取网络和路由。
Bridge的方式,能够满足一般的Java应用部署的需求,但一些需要更高性能的应用,需要高吞吐量、高并发、高PPS,比如电商大促情况下的缓存,这时候可以采用SR-IOV代替Bridge来解决问题,带宽比较大但PPS上不去(大包或大量小包)的情况,SR-IOV都可以解决,但是需要购买SR-IOV网卡,成本比较高。
无状态。做好持续集成之后,第一件事情应该是把应用分为有状态(Stateful)和无状态(Stateless)两个部分,并且使大部分应用是无状态的,这样可以更好地适应弹性伸缩。即便Kubernetes已经可以支持有状态应用的部署,刘超还是建议在应用层尽量实现无状态,使得有状态应用聚集在少数的集群里面。有状态最重要的是数据库和缓存,通常内存数据放在缓存,需要持久化的数据放在数据库里。
分布式数据库。数据库的高可用,网易云采用的是DDB(分布式数据库)方案,基于MySQL的多台主备及负载均衡做分库分表,网易云RDS基于自己的MySQL内核优化,能够实现主备切换不丢数据,能够很好地支持容器化,有状态容器挂掉之后,重新启动一个容器,只要做好前序重置和幂等,就不会有业务问题。所以网易云RDS的主备切换也从虚拟机向容器过渡。
缓存。高并发应用需要每一层都有缓存,把客户需求尽可能地拦在前面,吞吐量就大很多。但缓存不像数据库一样有持久化机制,其高可用、跨机房就需要做双写,因为缓存保持在内存中,挂了就没有了,修复难度很大。其他的组件,比如ZooKeeper、Kafka、消息队列、HBase,都有各自的高可用机制。所以,一个发展中的应用应当被分成很显著的两个部分,一部分是无状态的,另一部分有状态的就放到本身具有高可用机制的组件里面。
产品成熟阶段要解决的问题,主要是如何通过服务治理、系统运维自动化提升可靠性和可用性,如何高效完成大项目的复杂协作,如何梳理功能、深化用户体验。以正在进行全面服务化的考拉为例,2017年双11期间其工程数量相对平时增加了20多倍,应用、存储集群规模膨胀了5倍,挑战之大不必多说。刘超对成熟阶段架构设计强调了两点:
不可变基础设施:使用Kubernetes容器技术不能沿袭虚拟机时代的操作方式,而是应当采用不可变基础设施,即所有的改变,都应该在Git的改变里面有所体现,修改环境就是修改Dockerfile,修改配置文件也是代码层次的改变,整个环境的部署,当代码merge的时候,会触发通过容器自动部署的脚本,这能很好地保持环境的一致性。大规模节点下,如果是手动部署,出错很容易,排查却很难。所以,不可变基础设施非常重要。
IaC(基础设施即代码)部署与扩容:网易云在Kubernetes的编排之外封装了另一个编排,也是在仓库里面维护的,任何的修改,比如要升级5个应用,这5个应用的版本号都在这里面都配置好,代码commit之后就触发自动部署,如果发现问题,很容易回滚,只需把代码revert回来,后续流程会自动触发。如果依赖于写Yaml文件来做,频繁升级且版本号控制不好时,就很容易回滚失误。
成熟阶段通常使用Kubernetes+Docker+PM+Overlay Network的模式,企业一旦开始用Overlay Network,基本上都会使用物理机,否则使用虚拟机会出现两层Overlay。这时候Flannel、Calico、Romana或者Weave等很多的选型都可以,Flannel的性能已经越来越好。
网易云的方式,是Kubernetes与IaaS深度融合实现动态扩展资源,目前集群调度规模支持30000+节点。这个规模下,首先要解决的是动态资源创建优化,这样才符合资源精细利用、成本最优化的设计。同时,不论虚拟机的创建还是容器的创建,对应用都是透明的,也就是说,应用只需要明确一个模块要变成3个节点还是5个节点,不需要管Docker是不是要变成多少个节点、这些节点要放在哪里、虚拟机和物理机是否有资源之类的问题,后续的动作都是联动的。
动态资源创建的实现,网易云改造了Kubernetes创建流程,主要是监听Pod创建的事件,由Resource Controller判断有没有足够的Volume资源、Network资源,Schedule判断有没有足够的Node资源,有则直接绑定,无则动态申请之后再绑定,然后由Kubernetes下发。添加资源的时候,只有应用层和机房两层,机房只需要把物理机添加到IaaS层,不需要管上面是否有Kubernetes,虚拟机的创建全部是动态的,应用层只管应用层的事情,中间都是透明的。
其次是网络优化。网易云大部分容器是运行在虚拟机上的,同时也提供采用SR-IOV网卡的裸机容器,用于需要更高性能的缓存、分布式数据库等。大部分的应用可以横向扩展,还是在IaaS里面。但是网易云希望容器里面的网卡,让最外层虚拟机上的OVS也可以看到,即只有一层Overlay,虚拟机里面有一个Bridge,但如果不需要,也可以直接打到外面的OVS上,另外还有一个管理网络,跨租户也是同一个Kubernetes来管理。只有一层Overlay意味着没有二次的虚拟化,同时原来部署在虚拟机里面的应用迁移到容器中,虚拟机和容器的网络都是通过OVS来管理,采用Dubbo做服务发现会非常平滑,这是针对业务层压力的解决方案。其实OpenStack有一个CNI插件,也采用了类似的做法,和Neutron联动,把VIF打在外面的OVS上。
本文结合网易云服务内外部客户的Kubernetes实践经验,总结了产品高速增长期和成熟期使用Kubernetes容器技术实现微服务架构的五种应用场景,针对不同的挑战提出了易于执行的解决方案,并介绍了网易云的独门优化方法,希望对读者有所启发。