微服务:来看看微服务化不同阶段 Kubernetes 的不同玩法
作者:刘超
原文地址:https://mp.weixin.qq.com/s/sFsdK9uAud_3ELuie6KrvQ
作为容器集群管理技术竞争的大赢家,Kubernetes 已经和微服务紧密联系,采用 Kubernetes 的企业往往都开始了微服务架构的探索。然而不同企业不同阶段的微服务实践面临的问题千差万别,注定要在技术路线上产生分叉。如何选择适合自己的技术,是每一个践行微服务团队面临的第一个问题。
网易云是 Kubernetes 的第一批重度用户,在不同业务场景下解决了很多挑战,在本文中,网易云首席解决方案架构师刘超梳理了基于 Kubernetes 构建微服务体系的进阶之路。
微服务化的必要性
一个产品的发展,通常可分为冷启动阶段、高速增长阶段和成熟阶段。
产品冷启动阶段,需求是以最简单的架构验证业务。以网易考拉海购(以下简称 “考拉”)为例,最初的架构设计目标就是快速启动,验证产品方向,该架构包括在线、缓存、线下和管理服务四个方面,即一般电商平台加上跨境电商必备的进销存系统,采用了 Oracle 数据库、OpenStack 管理的虚拟机(VM),并没有诸如高并发之类的考虑。
产品高速增长阶段,业务规模逐渐扩大,产品复杂度也随着增加,企业需要解决快速迭代、高可靠和高可用等问题,一个自然的选择是服务化的拆分,把一个单体架构拆分成一些较小的模块,并遵循康威定律,用 5-9 个小团队来适应架构的变化。仍以考拉为例,考拉在高速增长阶段也慢慢演化出各种新的模块,比如单独的支付模块、货仓模块、第三方商家模块、推送模块等,并基于 Dubbo 框架打造服务发现功能来支持各模块之间的相互调用。
服务化主要解决了变更的问题。在整个架构演进的过程中,各个模块都面临爆炸性的增长,比如海淘、自营、第三方商家的供应链,Web、APP、H5 的呈现,限时购、秒杀、预售的活动页,以及仓库与物流系统、支付系统的对接等,紧耦合则牵一发而动全身,工程臃肿,影响迭代速度,分别独立上线更有利于适应业务发展的需求。考拉在高速增长阶段首先按照主页、活动页、优惠券、支付等维度纵向拆分,之后又不断演进成为 100 多个相互关联的模块,变更频率由每天 2 次增长到每天 1000 多次,产品质量提升 52%。
容器化的优势与挑战
拆分成大量小模块之后,虚拟机与服务化架构的配合就出现了很多新的挑战,于是有了容器化的需求。
刘超解释说,拆分之前首先要解决“合”的问题,即需要保证功能还是原来的功能,代码质量还是原来的代码质量,不会引入新的 bug。他认为,微服务化需要从一开始就要做好持续集成,而容器是很好的持续集成工具,完成从代码提交到自动测试、自动发布的工作。容器化会带来开发流程的变化,把环境交付过程从运维人员提前到开发人员手上。
在架构复杂的情况下,比如 100 多个模块,再加上各种副本,所有环境都由一个运维团队来完成,不仅工作量繁重,而且还容易出错,但这是使用虚拟机的模式。而如果写一个 Dockerflie 放到代码仓库,由开发人员来考虑开发完成之后应用部署的配置环境、权限等问题,包括测试环境的部署、联调环境的部署、生产环境的部署,问题就很好解决了。这就是容器化带来的流程变化。
然而,这种转变涉及到开发人员是否愿意学习容器技术。刘超推荐的解决办法,是使用镜像分层的形式,即最内部的环境包括操作系统及系统工具的镜像由运维人员来做,中间层环境的镜像由核心开发人员完成,普通开发人员只需把 jar 或者 war 扔到相应的路径下即可,这就极大降低企业组织容器化的障碍。
场景一:Kubernetes + Docker + VM + Host Network
第一种场景,就是用 Kubernetes 管理虚拟机,容器的网络、存储会面临各种各样的选型。企业如果对容器的网络、存储了解不足,可以把容器当成一个持续集成的工具,把一个容器嵌入到一个虚拟机里面,相当于用容器镜像代替脚本部署。这种做法需要解决两个问题:一是 IP 保持的问题,二是盘保持的问题。因为原先采用虚拟机的时候,是基于有状态的设计,认为 IP、Volume 都是保持不变的。当容器仅仅作为持续集成的工具,团队的这个习惯可能改不了。
一个方案是自己实现一个有状态容器的方式,实现 IP 的保持,当一个节点挂了,重新启动的虚拟机和容器仍然可以使用原先分配的 IP,二是把 Docker 容器的镜像一层层地 Mount 到外面的 Volume 里面,当一个节点挂了,Docker 所有的镜像和 Volume 其实还挂载在外面的 Ceph 上,数据并未丢失。这和使用 VM 很相似,既可以 Docker 化支持微服务化,也不需要改变用户习惯。使用 Kubernetes 压力相对比较大的团队,可以通过这种方式切入。
场景二:Kubernetes + Docker + PM + Bridge Network
第二种场景,企业没有使用虚拟机,有一部分应用部署在物理机(PM)上,同时想把一部分应用迁移到容器里。此时,不管物理机是否嵌套虚拟机,直接创建一个 Bridge Network,把物理网卡也打进去,当 Docker 的网卡和 Bridge 连起来的时候,整个网络就是平的,容器和容器旁边的物理机都使用同一个指定的网段。网络打平之后,使用 Dubbo 的团队也可以比较顺畅地把一部分物理机上部署的应用逐渐迁移到容器里,而如果没有 Bridge Network,中间过负载均衡(LB)或者 NAT 时会很别扭,因为 Kubernetes 层的维护人员通常很难劝说 Dubbo 层开发人员改变应用开发的方式。
使用 Bridge Network,Kubernetes 网络配置很简单,使用 CNI 的方式即可。如果有定制化以适应应用层的需求,可以参考 Docker run 的手动配置方式,开发自己的 CNI 插件。大致流程是先创建网桥(如果不存在),获取 Namespace,配置 veth pair 并放到 Namespace 里,然后获取 IP 地址,获取网络和路由。
场景三:Kubernetes + Docker + PM + SR-IOV Network
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
成熟阶段通常使用Kubernetes+Docker+PM+Overlay Network 的模式,企业一旦开始用 Overlay Network,基本上都会使用物理机,否则使用虚拟机会出现两层 Overlay。这时候 Flannel、Calico、Romana 或者 Weave 等很多的选型都可以,Flannel 的性能已经越来越好。
场景五:Kubernetes 和 IaaS 层深度融合
网易云的方式,是 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 容器技术实现微服务架构的五种应用场景,针对不同的挑战提出了易于执行的解决方案,并介绍了网易云的独门优化方法,希望对读者有所启发。