回顾软件架构的发展历程,IT 基础设施(Infrastructure)的变迁总是会推动架构演进,使得架构能够充分发挥基础设施的特点。例如,在大型机时代,架构就是与之匹配的单体架构,随着 PC 及局域网时代的到来,客户服务器架构成为主流。
现今云计算的基础设施同样推动着相应的软件架构的演变。同时,云计算技术甚至还影响了软件的开发过程和团队的组织结构,由此催生的“云原生”的相关理念,正被越来越多的团队所认可并成功应用。(点击 QCon上海2020·云原生专题)
而谈到云原生,有些人会把它的概念和云计算混淆。
Matt Stine 在 2015 年撰写了《Migrating to Cloud Native Application Architectures》,这是非常早期的一本关于云原生的经典,里面也谈到了我们为什么要做云原生,放在第一位的就是速度,天下功夫唯快不破,软件开发更是如此。
蔡超之前在 Amazon 工作,着手的第一个项目叫做 Enable Daily Deployment,要做到每天都能高质量、高速度的发布软件,这其中涉及到 CI/CD 自动化,测试自动化等,团队每天要交付 3-5 个特征,整个公司每天要交付几千个。Google、Netflix 其实也是一样。
云原生要解决的核心问题,就是如何能够快速交付 App。云原生其实并不是在云平台上完整的构建应用这么简单,云原生是一种方法论或者理念,帮助开发团队充分利用云计算的基础架构来构建 App,也可以把它认为是一系列工具,但不是云。
2015 年,CNCF 给出了云原生的早期定义,主要强调了应用的容器化,面向微服务。
蔡超此前在 Amazon 零售部门工作 7 年,当时并未用云,而是用的微服务,之后 AWS 要求他们全面 moving to cloud。所以说,微服务早于云计算而存在。
软件架构是会受到软件的基础设施的推动,上面的简图就强调了 IT 基础设施对于软件架构的推进。
随着 PC 局域网,特别是关系型数据库的应用,基础架构发展成了两层架构;随后是广域网的发展,由单体的多层架构,出现了 SOA,EDA 架构盛行;接下来是虚拟机,再到今天的云计算基础架构,又出现了微服务,之后是 Container as a Service、Serverless 等,可以看到架构的变化都是要充分利用 IT 基础设施。
那么在云原生时代,要去做一个什么样的架构或者是什么样的选择,才能够充分利用云计算的特点呢?
这里列举了公认的主要的云计算特点:按需付费、按时按需获取、按使用付费、非确定性失效、快速弹性伸缩、资源托管。在设计架构的时候,基本上的规则是按非功能需求设计,这其中的原因是考虑了高可用性、高可维护性等因素。下面针对这些设计因素里面的挑战和重点来一一解析。
高可用
其实高可用很简单,注重两个方面,第一个是面向失效的设计,即针对已知的失效模式,在设计上进行有意识的规避。
首先是单体应用问题,当某一个功能出现问题的时候,比如说内存溢出之类的,它整体就会宕掉,所以说研发过程中要对它进行一个错误的隔离,当然微服务架构非常适用于云,因为它可以进行有效的错误隔离,它不再是单体每一个功能都运行在独立的进程里面。
其次是要预防失效的扩大,虽然说很常见,但还是有很多人犯错。Load Balancer 每一台虚拟机可以提供最大 1000 的 QPS,如果后面放两台机器,面对外部最大 1500 的 QPS 请求,也是会有单点失效的情况(其中一台机器失效后,如果另一台没有限流等保护也就会超载),它的可用性是达不到理想中的 SLA。
要预防失效扩大的问题,就需要采用限流措施,经常用的一个限流解决方案叫 Token Bucket,就是用自己产生 Token 的频率来控制请求的处理频率。
采用了微服务之后,微服务对于错误有隔离,但是微服务之间往往有依赖关系,所以此时候会有一种失效的传递效应:
而预防失效的传递效应,则采用的措施是熔断,熔断器在一定时间段内失败次数达到一定的 Threshold(阈值),就把熔断器处于开启状态,配合上服务降级,不再对失效服务进行调用。
采用一个降级的服务,当查询数据库,如果数据库有故障的时候,就只能给用户一个静态内容,或者提供停止运行前一时间的内容,保持其他功能可使用,保证系统的可用性。
除此之外,有很多失效的模式是无法预测的,那么更简单的解决方法就是“面向恢复的设计”,它主要是用来解决不可预知的失效,具体的做法是:
这里重点强调防止僵尸进程。什么情况会导致僵尸进程?其实非常多,池化资源耗尽,比如说连接池资源耗尽之后,那么新的请求过来的时候只会被阻塞在连接池上,因为它拿不到连接。这个时候服务也是不可用的。
当然,解决办法是可以考虑直接退出进程,退出进程之后重启一下,这个方法非常著名,叫“let it crash 让他崩溃”。
混沌工程
在强调云原生,强调速度的时候,混沌工程是比较适用的,尤其是规模不是很大的场景,说白了就是 Test in Production,借助于一些工具在生产环境做一个可控规模的破坏性试验。比如说随机关掉一些主机,随机注入一些延迟,随机注入一些常见的错误。
其实有的东西之间是互相关联的,水到渠成的关系。当实现了微服务之后,实际上就可以去更细微的伸缩,这在某种程度上已经为降低成本打下了一个良好的基础。
微服务架构:更精准的细粒度伸缩→更合理的计算资源→更低的成本 高可用性:更好弹性和容错能力→更具弹性的计算资源→更低的成本
容器化
CNCF 对云原生的定义里面都没有忘记容器化这件事情,那么容器化对于为什么要实践云原生有多重要呢?
容器化是未来云原生架构演化的一个重要部分。首先,速度第一。容器为开发提供的一致性,无论是在本地构建环境,还是在测试环境,都是通过以容器的方式,因为容器打包了整个依赖运行环境,带来了便利的一致性。
容器带来的高可用和低成本
上图可以看到,容器比虚拟机轻量很多的,另外,容器的启动速度极快,符合可恢复性的高可用要求,也能够快速伸缩,节省消耗,提高资源的利用率,且容器与容器之间具有很好的隔离性,这也是容错架构的关键点之一。
另外一个非常重要的就是便携性,容器化编排给开发团队带来了便携性和一致性。
架构师在做架构的时候,也需要思考什么是未来架构,什么样的架构是一个好的架构?
架构师除了去思考那些非功能性需求,还要去充分利用当前的基础设施,去更好的解决这些非功能性需求,架构其实就是一系列的选择,一系列的折中选择。今天的架构一直都非常强调关注点分离,这是设计里面特别强调的关注点分离(Concern Separation),面向对象的理论以及很多理论都是基于关注分离的。
上图是一个 App 运行栈,从 Hardware,VM,OS,Auto Scaling Cluster,Kubernetes,Container,最后到应用有很多层。传统的云计算为我们提供了一直到 Auto Scaling Cluster,称之为 IaaS,今天更多的云计算再往下走为我们提供了 Container as a Service。开发人员关注自己运行的容器,不用关注编排。
架构师的关注点在逐渐的分离,以前要关注基础设施,平台,而现在主要关注 App 层面。
这是另外一个值得关注的分离点。如果采用一些框架,要看它符不符合业务趋势,关注点分离是个大趋势,这里 Service Mesh 可以把很多非功能性相关部分,如:高可用,可恢复等都移到了一个 Sidecar 里面,因为依赖于像 Spring Cloud 这样的框架很难做到跨语言,那么通过 Sidecar 就可以用不同的 App 来实现,并且去使用一致的 Sidecar 来实现中央配置和中央管控流量分配。
另外一个趋势,抽象层次不断的升高,在设计应用的时候,也是每个架构师都要去考虑和重新思考设计模块。
最早的时候,思考安全、流量的时候,会考虑云上的 VPC,Subnet,NAT,叫做 VM 的 Network,它是连接虚拟实例的网络。进入了容器时代,进入 K8s 时代,就要去思考 Pod 和容器之间的关系,由它们之间形成的 Network,它们之间的集合 Replica Set,Service。Service Mesh 为我们带来了更高一层的抽象,叫做 Service Network,如果你了解 SDN Software Define Network 技术,你会知道正在用更高层次的抽象,通过软件来定义更高层次的网络,这是服务网络,可以用 Istio 来定义 Subnet Virtual Service。这和服务之间有很大不同。
举一个最简单的例子,gRPC 的一些服务放在 K8s 的时候,Load Balancer 会有问题,是因为 Multiplex 多路复用,因为 gRPC 是支持基于 HTTP2.0 的,那么它一条连接上可以发多个请求,那么这样的话因为 K8s 的 Load Balancer 是基于 L4 的,它没有办法知道 Level Seven 的事情。所以说它就没有办法做这样的 Load Balancer,但可以通过一些客户端的 Load Balancer 方法来解决这个问题。
其实 Service Mesh 的虚拟服务是 L7,L7 可以很好的解决 gRPC 的问题,还可以解决一些更酷的问题,比如一个服务有不同的版本,要做一个 A/B Test,或者一个金丝雀 发布,把一个版本慢慢的发布出来,其实我们都可以通过这种虚拟服务来完成,它是支持 L7 更高层次的服务网络抽象。
领取专属 10元无门槛券
私享最新 技术干货