技术教程

Knative 云原生应用开发指南 Version 0.11

随着 Kubernetes 的崛起和 CNCF 的壮大,云原生的概念已经越来越深入人心。几乎到了如果不和云原生扯上点儿关系似乎就不好意思和别人对话的地步。那么到底什么是云原生?要搞清楚什么是云原生,我们首先要弄清楚云到底是怎么回事儿。接下来我们就从 IaaS 的演进和服务化的演进这两个脉络来看看云的发展趋势和云原生的含义。

IaaS 的演进

应用想要运行首先需要足够的计算资源,而物理机的性能在过去的十几年里始终保持着摩尔定律的速度在增长。这就导致单个应用根本无法充分利用整个物理机的资源。所以就需要有一种技术解决资源利用率的问题。简单的想如果一个应用占不满整个物理机的资源,就多部署几个。但在同一个物理机下混合部署多个应用会有下面这些问题:

  • 应用之间的端口冲突 如果一个应用占用了 80 端口,那么所有其他的应用就都不能使用 80 端口了,在同一台物理机上面每一个端口都是唯一的,不能被重复使用
  • 应用的依赖冲突 比如 Python、Nodejs、Perl、PHP 等等这些脚本语言都需要提前安装好脚本解析器才能运行。而在同一台物理机上面如果对而写软件维护多个版本是一件非常困难的事情,一旦出错就可能导致所有应用崩溃。
  • 资源隔离 不同应用使用的出资源不能不能有效的进行隔离,就容易相互影响。如果一个应用有 BUG 占光了内存会导致其他应用出问题。
  • 运维操作相互影响 一个应用的管理员误操作可能会导致本机器上面的所有应用崩溃

虚拟机技术的出现就完美的解决了上述问题,通过虚拟机技术既可以在同一个物理机上面部署多个应用,又能保证应用之间的松耦合。

虚拟机技术的出现除了提升资源的使用效率还带来了另外一个变革:使得不可变基础设施成为了可能。 不可变基础设施(Immutable Infrastructure)是由 Chad Fowler 于 2013 年提出的一个很有前瞻性的构想:在这种模式中,任何基础设施的实例(包括服务器、容器等各种软硬件)一旦创建之后便成为一种只读状态,不可对其进行任何更改。如果需要修改或升级某些实例,唯一的方式就是创建一批新的实例以替换。 这意味着配置工作可以被低成本的重复使用,这就大大减少了配置管理工作的负担,让持续集成与持续部署过程变得更流畅。同时它也更易于应对部署环境间的差异及版本管理,包括在部署出错时可进行快速回滚 —— 只要旧版本的镜像文件还有备份,就可以快速地生成旧版本的实例进行替换。

随着虚拟机技术的普及,人们越来越发现虽然虚拟机可以提升物理机的使用效率,但是虚拟机本身也会占用巨大的开销。2013 年 Docker 的出现又在虚拟机的基础上增进了一步。Docker 首先提出了镜像的概念,通过 Docker 镜像可以很容易的创建两个环境一样的运行时、这对开发测试、软件交付已经线上升级等都带来了巨大的便利性。另外 Docker 使用了内核的隔离技术,相比于虚拟机会占用的系统资源更少,所以效率更高。下图是基于虚拟机部署应用和基于容器化部署应用的对比关系。 undefined

从架构上来看 Docker比虚拟化少了两层,取消了 hypervisor 层和 GuestOS 层,使用 Docker Engine 进行调度和隔离,所有应用共用主机操作系统,因此在体量上 Docker 较虚拟机更轻量级,在性能上优于虚拟化,接近裸机性能。

Docker 之后紧跟着 Kubernetes 就出现了,Kubernetes 之后的故事大家都知道了,现在 Kubernetes 已经成了云原生的标配了,并且现在各大云厂商都提供了 Kubernetes 服务。在 Kubernetes 模式下有一个明显的特征就是你无需关心你的应用实例(Pod) 运行在哪里,因为这都是系统自动调度的。

从物理机到 VM 就已经无需关心真正的物理机了,应用程序只需要面向逻辑的虚拟机进行管理。到了容器时代还是有很多人直接在 VM 里面使用容器的。比如现在的公有云厂售卖的 VM 都可以跑 Docker。再次 Docker 到 Kubernetes 会发现又对底层的计算资源做了一层抽象。到了 Kubernetes 这一代就完全是根据需要自动分配资源,不需要提前占用物理机资源、或者 VM 资源,都是按需申请、按需调度的。

从 IaaS 虚拟化的这个维度我们发现云的发展趋势是在这两个维度上进行演化:

  • 按需分配计算资源 对应用程序运行所在的节点越来越弱化,从虚拟化开始就都是逻辑层面的。到了人 Kubernetes 时代完全是按需申请和按需分配的。
  • 不可变基础设施 应用程序只需要先设置好运行的基础环境,做一个镜像,然后用这个镜像到处运行。

服务化的演进

在单体应用时代,每一个单体应用都是一个大而全的功能集合。每个服务器运行的都是这个应用的完整服务。但随着业务的增长单体应用面对的问题也越来越多,例如

  • 开发效率变低
  • 部署影响变大
  • 可扩展性较差
  • 技术选型成本高

所以出现了服务的拆分,也就是微服务。微服务可以实现:

  • 每个微服务易于开发与维护,便于沟通与协作,很适合小团队敏捷开发与持续交付
  • 每个微服务职责单一,高内聚、低耦合。
  • 每个微服务能够独立开发、独立运行、独立部署
  • 每个微服务之间是独立的,如果某个服务部署或者宕机,只会影响到当前服务,而不会对整个业务系统产生影响
  • 每个微服务可以随着系统规模的不断扩大,面对海量用户和高并发,独立做水平扩展与垂直扩展

微服务虽好,但也不是银弹。微服务提升效率的同时也对设计和运维难度提出了更高的要求,同时也带来了技术的复杂度。我们必须要思考与解决分布式的复杂性、数据的一致性、服务的管理与运维、服务的自动化部署等解决方案。 我们知道应用交互的复杂性不会消失,只会换一种方式存在。这个原理也同样适用于软件架构。引入微服务并不会减少原来业务的开发的复杂性,只是把复杂性换一个方式存在而已。事实上,微服务通过拆分单体应用使其成为多个体积更小的服务来降低单个服务的复杂性,但从整体来看,这种方式有造成了存在大量的服务,而服务之间的相互调用也会增多,从而导致整个系统的复杂性增加不少。 繁多的微服务之间的关系需要自动化的方式维护:

  • 服务注册和发现
  • 限流、熔断
  • 失败重试、超时时间等
  • 负载均衡
  • trace 等

Dubbo、Spring Cloud 这些微服务框架的出现很好的解决了微服务调用之间的复杂度问题。微服务框架能够解决服务通信和服务治理的复杂性,比如服务发现、熔断、限流、全链路追踪等挑战。 因为复杂性不不会消失,只可能换一种形式存在。所以现代软件架构的核心任务之一就是定义基础设施与应用的边界,合理切分复杂性,尽量减少应用开发者直接面对的复杂性。也就是让开发者专注在核心价值创新上,而把一些与核心业务不相关的问题交给更合适的人或系统来解决。而这些人或系统就是中间件团队、IaaS 团队和运维团队等。随着微服务框架和基础平台自动化能力的提升,业务团队越来越容易的面向微服务框架编程,基于微服务框架管理自己的业务应用,这也是 DevOps 出现的前奏。

服务通信和服务治理本质是横向的系统级关系,是与业务逻辑正交的。但微服务框架如 HSF/Dubbo 或 Spring Cloud 以代码库的方式来封装这些能力。这些代码库被构建在应用程序本身中,随着应用一起发布和维护,其实现方式和生命周期与业务逻辑耦合在一起的。微服务框架的升级会导致整个服务应用的重新构建和部署。所以不同的微服务框架是不能相互结合的。

如果是在一家公司内部还可以通过行政命令强制公司内使用一样的技术栈、使用一样的微服务框架进行编程。但如果提供的是云服务就不可能强制用户必须使用同一个微服务框架。更何况很多语言其实是没有微服务框架的。即便是那些有微服务框架的语言之间不同的微服务框架的设计也是完全不一样的。所以微服务框架在公有云面向大众提供服务的时候就显得很受限制。此时微服务框架代码级别内置集成的方式就显得很累赘了, 因此我们需要一个框架无关、语言无关甚至是协议无关的通用的微服务解决方案。 为了解决上述挑战,社区提出了Service Mesh(服务网格)架构。它重新将服务治理能力下沉到基础设施,在服务的消费者和提供者两侧以独立进程的方式部署。这样既达到了去中心化的目的,保障了系统的可伸缩性;也实现了服务治理和业务逻辑的解耦,二者可以独立演进不相互干扰,提升了整体架构演进的灵活性;同时服务网格架构减少了对业务逻辑的侵入性,降低了多语言支持的复杂性。

Istio 通过 Sidecar 的方式把微服务治理的能力下沉到业务进程之外,从而解除了微服务治理和业务进程生命周期的强耦合关系。更重要的是 ServiceMesh 这一层抽象除了一套通用的操作标准,不会因不同的语言、不同的微服务框架而不同。

服务化的发展历程,是伴随着业务一起发展起来的,随着业务本身越来越复杂导致软件架构的复杂。而服务化解决这个问题的方法就是让复杂性下沉,通过层层抽象把复杂性下沉到底层系统。始终都要保持业务开发尽量聚焦在业务本身。

微服务发展历程的背后力量

  • 业务需求越来越多,业务复杂度不断增加
  • 业务需求越来越多,人员角色越来越多,每一个角色都需要有自己独立决策的空间
  • 激烈的竞争,需要业务更快速的迭代。也希望更尽可能的复用已有的模块

从应用服务化发展的这个维度我们发现云的发展有这样一个趋势:业务开发的复杂度在不断的向基础平台下沉。

Serverless

IaaS 的演进路线一个明显的趋势就是无服务器,这其实也是 Serverless 这个单词可以看到的字面含义。无服务器是 Serverless 的一个维度。

从服务化的演进路线可以看到一个明显的趋势是业务开发的复杂度在不断的向基础平台下沉。并且下沉的同时业务和基础平台的交互界面是越来越清晰、越来越标准了。在微服务框架的模式中各种微服务框架是各自为政的,但到了 ServiceMesh 的时代就都统一到 ServiceMesh 的标准。业务复杂度向基础平台下沉这是 Serverless 的另一个维度。

undefined 借用 CNCF Serverless 白皮书里面的一张图来说明一下这个关系。 纵轴表示业务应用越来越关注自己业务逻辑,业务应用只需要使用基础平台提供的能力就可以了,至于这些能力是怎么实现的不应该让业务应用关系细节。这是业务复杂度在不断的向基础平台下沉的过程,业务开发和基础平台的界面也在不断的上涨。 横轴表示业务应用对基础平台的资源占有模式的变化,从最初的独占模式到现在的按需分配。这是无服务器的发展过程,业务系统只需要在需要的时候拿到资源使用即可,占有资源不是业务系统的核心诉求。

到这里我们看到云服务的三个趋势,这三个演进方向也就是云原生释放云红利的方式。

  • 无服务器的趋势
  • 业务逻辑不断的向基础平台下沉的趋势

Knative 作为一个 Serverless 编排框架其 Autoscaler 的自动扩缩容能力可以让应用在没有服务的时候缩容到零,完美的展示了无服务器的威力。Knative 是建立在 Istio 之上的,基于 Istio 可以提供完整的 ServiceMesh 能力。可以看到 Knative 完美的结合无服务器和服务复杂度下沉这两个维度。

Knative 作为最通用的 Severlesss Framework,其中一个核心能力就是其简洁、高效的应用托管服务(MicroPaaS),这也是其支撑 Serverless 能力的基础。Knative 提供的应用托管服务(MicroPaaS) 可以大大降低直接操作 Kubernetes 资源的复杂度和风险,提升应用的迭代和服务交付效率。当然作为 Severlesss Framework 就离不开按需分配资源的能力,阿里云容器服务 Knative 可以根据您应用的请求量在高峰时期自动扩容实例数,当请求量减少以后自动缩容实例数,可以非常自动化的帮助您节省成本。