什么是云原生?

技术

picture.image

作者 | 李‍玉光

云原生的概念从 2018 年开始流行,而到了 2022 年,大家对云原生还是没有一个统一的概念,有人觉得应用上云就是云原生、有人觉得使用了 Kubernetes 就是云原生, 那到底什么是云原 生? 云原生都涉及到哪些技术?希望本文能够解答大家的疑惑。

云计算发展至今早已成为常态化基础设施,相比传统的本地机房有很多先进特性,比如:弹性伸缩、不可变基础设施、基础设施即代码等。云计算诞生的初期,程序是从本地机房直接移植到云上的,应用架构并没考虑云环境的特点,依然是本地机房的部署和管理方式。

随着云计算的发展和越来越多的应用上云,企业开始意识到云计算环境和传统基础设施的不同,尝试使用各种技术来更好地管理公有云上的资源、利用云计算的特性、发挥云计算的价值来构建敏捷健壮的应用,这也是后来出现的云原生概念的根本目的。

这个过程中诞生了很多技术形态。比如 Cloud Foundry 这样的早期 PaaS 项目、Docker 容器、rkt 容器、微服务以及容器编排技术 Swarm、Mesos、Kubernetes 等。最终,Kubernetes 在 2017 年脱颖而出成为容器编排的事实标准。各大公有云厂商也把 Kubernetes 作为容器编排产品的底层技术,并称其为云原生操作系统。

Kubernetes 是 CNCF 托管的的第一个项目。CNCF,全称 Cloud Native Computing Foundation(云原生计算基金会),于 2015 年由 Google 主导成立,其使命是让云原生无处不在,致力于培育和维护一个厂商中立的开源生态系统,来推广云原生技术;通过将最前沿的模式民主化,让这些创新为大众所用。

CNCF 在云原生领域拥有不可撼动的影响力。其对云原生的定义也成为云原生的权威定义,引导整个云原生行业沿着 CNCF 定义的方向发展,并逐渐形成以 Kubernetes 为基础的云原生技术生态,由此云原生技术体系有了统一的标准和规范 (全景图请见 CNCF Cloud Native Interactive Landscape )。

picture.image

CNCF 在 2018 年对云原生的概念进行了定义:

“云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式 API。

这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。”

定义中提出了云原生的代表技术: 容器、服务网格、微服务、不‍‍可变基础设施和声明式 API 。这些技术可以更好地利用云计算的能力去构建和运行可弹性扩展的应用,并且使工程师能够轻松地对系统作出频繁和可预测的重大变更。

同时定义也提到了在公有云、私有云、混合云环境中都可以落地云原生技术。最早的云计算的概念是随着公有云的诞生而出现的。随着云计算的发展,传统基础设施也在不断地采纳云计算的先进技术和理念,例如使用虚拟化和容器技术来构建本地私有云,而各个云计算厂商也提供了本地私有云的版本,进而将公有云和私有云打通形成了混合云的形态。因此云原生的外沿从公有云延伸到私有云和混合云的领域。

云原生代表技术

下 面 针对云原生代表技术以及相关技术出现的背景和意义做一些介绍:

容器

picture.image

容器是云原生技术的基石,当我们提到容器时,首先想到的应该就是 Docker ,但最早的容器形态并不是 Docker 所创造的,而是早期的 PaaS 项目 Cloud Foundry,其旨在模拟本地环境,一键把应用部署到云上,带来更好的用云体验。

它通过 Cgroups 和 Namespace 机制来为应用创建称作“沙盒”的隔离环境,也就是早期的容器形态。但是一键部署背后所需要的繁琐的打包机制一直为人诟病,而且不能从根本上保证本地和云上环境的一致。

2013 年,Docker 项目发布,虽然 Docker 也是使用 Cgroups 和 Namespace 机制来做隔离,但是其独创的镜像机制从根本上解决了打包和环境一致性的问题,开创了全新的容器时代。

Docker 实现了容器、镜像、构建、移植、运行一系列操作的标准化,一次构建,任意运行,从根本上消除环境的不一致性,可以随处移植。同时其轻量化的特性可以实现秒级启动。

虽然 Docker 是容器的典型代表,但它并不是唯一的容器运行时。2015 年,Docker 公司捐出容器运行时库 Libcontainer,将其改名为 RunC 项目,并以 RunC 为依据制定了一套容器和镜像的标准和规范,也就是 OCI(Open Container Initiative)。RunC 是 OCI 规范的一种实现。除了 RunC,它还有如下几种运行时:

  • crun :C 语言实现的容器运行时(相对而言,RunC 是使用 go 语言实现的);
  • kata-runtime :来自 Katacontainers 项目,通过轻量级虚拟机技术实现的符合 OCI 标准的容器运行时;
  • gVisor :来自 Google,其实现 OCI 标准的容器运行时称为 runsc,会创建包含独立内核的容器。

Kubernetes

picture.image

容器为应用的构建、移植等带来了极大的便利,但是容器本身无法发挥太大的价值,容器化应用的部署需要大量的容器协作,在一个集群中运行任务的容器之间存在着复杂的关系,需要容器的编排管理系统来对这些容器的协同工作进行处理。

在容器编排领域,先后诞生了 Docker 公司的 Docker Compose + Swarm + Machine 和 Mesosphere 公司的 Mesos + Marathon。截止目前,一些大型互联网公司仍在使用 Mesos。

2014 年 6 月,Google 开源了内部的大规模集群管理系统 Borg,正式宣告 Kubernetes 项目的诞生。Kubernetes 的出现和 Docker 一样,具有划时代的意义。

Kubernetes 并没有基于 Docker 建设,而是把它作为最底层的一个容器运行时的实现。同时 Kubernetes 还支持上文提到的 crun、kata-runtime、gVisor 等符合 OCI 标准的容器运行时。

容器本身的价值虽然大,但是如果想要让其产生商业价值或者说对云计算的价值,它就需要在编排上面占据一个有利的位置,因此几家公司在容器编排领域展开角逐。

到了 2017 年,容器编排领域的竞争格局逐渐清晰,Kubernetes 以绝对优势脱颖而出。 基于 CNCF 和 Kubernetes 的巨大影响力,Kubernetes 很快就构建起了整个云原生生态,在应用编排、数据库、存储、网络、CI/CD、监控、容器安全等领域都诞生了很多优秀的开源项目。

微服务

在具体介绍微服务架构之前,有必要先看一下与之对应的单体架构。在单体应用中,处理用户请求的所有逻辑都运行在同一个进程,通过负载均衡器对单体应用的实例集群进行流量转发。单体应用的开发、部署、测试、水平扩展比较容易,只把这一个程序代码包重新复制一份即可。

随着应用不断在云上部署,单体应用的缺点也逐渐显现出来:

  1. 因为系统的所有模块都被打包在一起,因此对系统的任何修改都需要整个系统一起重新部署/升级,很大程度上限制了程序 部署的敏捷性;
  2. 不同模块发生资源冲突时,扩展将会非常困难,有的模块需要更多的 CPU、有的模块需要大的内存、有的模块需要更高的带宽等。当对系统进行扩展时,不得不扩展整个系统,而不能仅扩展该系统中需要更多资源的那些部分;
  3. 可靠性问题,一个模块的故障会对整个系统造成影响。

picture.image

为了应对单体应用的以上问题,微服务架构由此诞生。

微服务是一种开发软件的架构和组织方法,其中,软件由通过明确定义的 API 进行通信的小型独立服务组成。这些服务由各个小型独立团队负责。微服务架构使应用程序更易于扩展和开发,从而加速创新并缩短新功能的发布时间。

微服务具有如下 优点

  • 每个服务根据功能进行划分,比如用户前端、推荐、物流、账单等;
  • 微服务之间是松耦合的,可独立部署和扩展,不需要重新部署整个应用,保证业务的敏捷性;
  • 技术选型灵活,不同的业务类型可以选择合适的技术独立实现。服务与服务之间采取与语言无关的 API 进行集成。相对单体架构,微服务架构是更面向业务创新效率的一种架构模式;
  • 因为微服务依赖的库和代码量相对较少,因此启动速度相比单体式应用要快很多 。

微服务有很多优点,然而微服务的实现也面临众多 挑战

  • 微服务应用是分布式系统,具备固有的复杂性:单体应用的所有模块之前的调用都是在本地进行的,而微服务每个模块是独立部署的,通过网络进行通信,这当中存在许多问题,比如服务注册和发现、熔断和限流、服务降级、服务超时等;
  • 不同微服务选择适合其业务特点的数据库,会造成来自分布式事务的挑战;
  • 微服务架构应用的测试也比较复杂,微服务架构模式应用的改变会波及多个服务。

传统微服务治理框架

为了应对微服务架构带来的挑战,业界出现了各种微服务治理的开发语言框架,例如 Java 语言的 Spring Cloud、Dubbo,和字节跳动的 Go 语言微服务框架 Kitex、Hertz 等。

微服务框架的本质是降低业务开发人员开发的复杂度,让业务开发人员更加聚焦于业务本身,避免不同业务团队在微服务共性问题上投入重复冗余的精力。

一个完整的微服务框架需要关注如下领域的功能实现:

高效通信

从单体架构演进到微服务架构,需要进行微服务拆分。拆分后,同一业务场景,调用链变长,原本的一次 RPC 通信可能变成几次甚至几十次。如果每次 RPC 通信耗时几十毫秒,并且按照串行的方式调用的话,总耗时将达到秒级,性能损失严重。

因此,微服务框架需要实现高效的序列化、反序列化,支持并行、异步、非阻塞转换,支持多语言等。

服务治理

当下游服务数量发生变化时,如何动态通知上游服务?上游服务是否需要重新启动或者在代码中更新实例配置?这就需要微服务框架能对接注册中心实现服务的注册、发现以及实例状态的动态感知。

此外,为了保障服务高可用、降低性能瓶颈、安全访问,我们还需要支持常用策略负载均衡、服务限流、服务降级、服务容错、服务鉴定与授权等一系列服务治理能力。

容量规划

假设某服务最大承受 1000 TPS,如果超出,是排队等待,还是保证 1000 TPS 范围内请求得到正常处理而其它请求被直接拒绝、非核心服务流量洪峰导致核心服务受到影响如何解决等。 对于这些问题,微服务框架都需要给出答案。

为了应对以上问题,除了采用服务治理手段进行解决,还需要系统 SLO 约束、容量预估、流量预估、以及版本控制、洪峰策略等,最大程度保障服务可用性。

传统的微服务框架也存在一些不足,主要有以下五点:

  • 侵入性强 。如果 想要集成 SDK 的能力,除了需要添加相关的依赖,往往还需要在业务代码中增加一部分的代码、或注解、或配置;业务代码与治理层代码界限不清晰。
  • 升级成本高 。每次升级都需要业务应用修改 SDK 版本,重新进行功能回归测试,并且对每一台机器进行部署上线,而这对于业务方来说,与业务的快速迭代开发是存在冲突的。
  • 版本碎片化严重 。由于升级成本高,而中间件却不会停止向前发展的步伐,久而久之,这会导致线上不同服务引用的 SDK 版本不统一,很难统一治理。
  • 中间件演变困难 。由于版本碎片化严重,导致中间件向前演进的过程中需要在代码中兼容各种各样的老版本逻辑,带着 “枷锁” 前行,无法实现快速迭代。
  • 治理功能不全 。治理框架的功能并不是很全面,诸如协议转换支持、多重授权机制、动态请求路由、故障注入、灰度发布等高级功能治理框架并不一定能被覆盖到。而这些功能往往是企业大规模落地不可或缺的功能,因此公司往往还需要投入其它人力进行相关功能的自研或者调研其它组件作为补充。

虽然传统的微服务框架具有一定的局限性,但是它在一定时期内也为企业带来了极大的利益,满足了大部分的微服务治理的需求,并快速推进了企业的微服务化进程。

服务网格

picture.image

服务网格(Service Mesh)被称为下一代微服务架构。开发 Linkerd 的 Buoyant 公司最早提出了 Service Mesh 一词,并于 2016 年 9 月 29 日第一次公开使用了这一术语。

Service Mesh 是一个专门处理服务通讯的基础设施层。它的职责是在众多微服务的复杂拓扑结构下进行可靠的请求传送。在实践中,它是一组和应用服务部署在一起的轻量级的网络代理,并且对应用服务透明。

简单来说,Service Mesh 帮助应用程序在海量服务、复杂的架构和网络中建立稳定的通信机制,业务所有的流量都转发到 Service Mesh 的代理服务中。不仅如此,Service Mesh 还承担了微服务框架几乎所有的功能,包括服务注册发现、负载均衡、熔断限流、认证鉴权等。

不同的是,Service Mesh 强调的是通过独立的进程代理的方式运行,此外,还承担了上报日志、监控指标等信息的责任。

随着新技术的发展和人员更替,一家公司内部可能会出现不同语言、不同框架的应用和服务。为了能够统一管控这些服务,以往的做法是为每种语言、每种框架都开发一套完整的 SDK,维护成本非常之高,而且给公司的中间件团队带来了很大的挑战。

有了服务网格之后,通过将原来代码层实现的服务治理能力下沉到基础设施层,进行多语言的支持就会轻松很多。只需要提供一个非常轻量级的 SDK,甚至很多情况下都不需要一个单独的 SDK,就可以方便地实现多语言、多协议的统一流量管控、监控等需求。

尽管 Service Mesh 在微服务治理方面已经有了快速的增长,但仍然存在巨大的提升空间,而 Service Mesh 毫无疑问将成为不可或缺的基础技术。就像 TCP/IP 作为互联网的基础一样,Service Mesh 将在微服务的底层基础设施这条路上更进一步。

不可变基础设施

不可变基础设施是云原生技术中重要却并不经常被关注的概念。

picture.image

在传统基础设施的环境中,服务的可靠性依赖于物理服务器的可靠性。对于数据中心中的硬件,我们往往要花很大的精力去维持服务器的持续运行,服务器的整个生命周期会经历无数次的发布、变更和配置修改,而且很多调整和变更都缺乏记录。

这就导致同一集群的服务器之间的配置变得越来越不一致,集群各个服务器出现的问题也不一样,这种情况被称为配置漂移。这种配置不一致导致我们调试服务器问题,或者对集群进行扩容时的效率非常低。在这种环境中,服务器就像宠物一样:独一无二、无法复制、无法容忍宕机。

早在 2013 年,Chad Fowler 就提出了“immutable infrastructure” 的概念, Martin Fowler 在 2012 年也有过类似的 Phoenix Servers 的描述。 不可变 基础设施指的是任何基础设施的实例(包括服务器、容器等)一旦创建之后便成为一种只读状态,禁止对任何运行的实例做手动的热修改。

如果需要修改或升级某些实例,唯一的方式就是创建新的实例替换原有实例。 通过只读和替换这两个特性,可以保证集群配置的一致性,并且使实例永远保持在最初的良好状态。

这种架构模式带来的好处是:无论是几十台还是几百台服务器,都可以像一台服务器一样去进行管理;服务器出现故障时,可以迅速启动新的实例进行替换。在不可变基础设施中的服务器集群就像牛群:没有一个个体是独特且不可替代的。

容器天然具有不可变基础设施的属性,容器实例一旦初始化之后就不再对其进行修改。并且每次版本发布都会启动新实例替换集群中原有的容器实例。相应地,我们也要关注 Node 层面的不可变,一个 Node 节点通过镜像或者统一管理的初始化脚本进行初始化之后就不再允许进行热修改,甚至禁止 SSH 登录。每次集群升级都通过实例替换的方式来进行。

不可变基础设施也是弹性扩缩容的基础。弹性扩缩容使业务根据每天请求的高低峰进行计算资源的动态调整。每次扩容都会拉起一个全新的并且配置一致的实例。

在传统的基础设施环境中,通过 Puppet 或者 Ansible 等配置管理工具在一定程度上可以实现不可变基础设施,但是很难从根本上避免配置漂移的出现。只有在云计算环境中,才可以通过结合配置和监控等各个方面的 API 接口实现全自动的环境配置,进而实施完整的不可变基础设施架构。

声明式 API

声明式 API 是 Kubernetes 编排能力的关键概念。所谓声明式指的是提交一个定义好的声明文件来定义 API 对象期望的状态,这个声明文件就是 Kubernetes 中的 YAML 文件。最常见的提交声明文件的方式就是 kubectl apply 命令。然后 Kubernetes 的各种 Controller 通过 List-watch 机制将资源对象变更为期望状态。

不同于 kubectl replace、kubectl set image 和 kubectl edit 等命令一次只能处理一个写请求,kubectl apply 会执行对原有 API 对象的 PATCH 操作,一次能处理多个写操作,并且具备 Merge 能力。

总结

本文对 云原生 的基本概念和代表技术做了介绍,云原生不仅仅是某一项或某几项技术,更是一种理念和引导,促进企业在上云的过程中使用相关技术来更好的发挥云计算的优势。

在云原生技术发展的过程中,Docker 技术的出现、CNCF 的成立、Kubernetes 成为容器编排的事实标准等事件都具有极其重要的意义,最终使得云原生向着更加标准化的方向发展。正是社区和开源的力量把全球开发者凝聚在一起共同推进云原生技术的进步,使其欣欣向荣的向前发展。

火山引擎云原生服务

基于这些云原生技术,我们可以构建敏捷、弹性、健壮的应用程序,不过这些技术的部署和维护是非常复杂的过程,需要专业的团队去开发和运维。 有了云计算托管云原生 PaaS 服务,一切就变得非常简单。

下图是火山引擎的云原生产品矩阵:

picture.image

2021 年 12 月,字节跳动发布云服务火山引擎,对外输出字节多年来积累的技术和经验,云原生方向也以 PaaS 的形态上线了很多服务。

  • 容器服务 :包括托管的 K8s 管理平台 VKE、弹性容器平台 VCI,和边缘容器服务以及多云混合云的产品 veStack;
  • 敏捷开发 :包括持续交付、镜像仓库等产品;
  • 服务治理 :包括服务网格、混沌工程等产品。

上述产品共同组成了云原生产品基础矩阵,火山引擎云原生服务不仅仅提供了托管应用的能力,也融入了字节跳动在多年的实践过程中沉淀的 K8s 的稳定性、可观测性、服务网格等方面的技术积累和最佳实践,方便用户快速地落地云原生技术。

picture.image

扫描二维码,下载火山引擎云原生白皮书

33
0
0
0
关于作者
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论