云原生的概念一直以来都很模糊,虽然云原生计算基金会(CNCF)给出了所谓的定义,但是并不能让大家很好的理解云原生的理念,为什么说是理念呢,因为云原生是一种思想,是一种解决方案,很抽象。云原生是一个组合词,Cloud和Native。Cloud表示应用程序位于云中,而不是传统的数据中心;Native表示应用程序从设计之初即考虑到云的环境,原生为云而设计,在云上运行,充分利用和发挥云平台的弹性和分布式优势。
随着云原生生态和边界不断的扩大,云原生自身的定义一直在变。不同的公司(Pivotal & CNCF)不同的人对它有不同的定义,同一家公司在不同的时间阶段定义也不一样。根据摩尔定律推断,未来对于云原生的定义还会不断变化。
概念
云原生的概念,由来自Pivotal的Matt Stine根据其多年的框架经验总结于2013年首次提出,被一直延续使用至今。
1989 年,Rob Mee 创立了咨询公司 Pivotal Labs;
2003 年,Rod Johnson 和同伴创建了 Spring;
2003 年,EMC 收购了 VMware 公司;
2009 年,VMware 收购了 Spring ;
2012 年,EMC 又收购了 Pivotal Labs 公司;
2013 年,EMC 、 VMware 和收购来的 Pivotal Labs 公司重新组建了新的公司 Pivotal;
2015 年,Dell又并购了 EMC;
2018年,Pivotal在纽交所正式上市;
2018 年,Dell再次独立上市。
Pivotal 公司可谓是大牛云集,公司的开源产品有:Spring 以及 Spring 衍生产品、Web 服务器 Tomcat、缓存中间件 Redis、消息中间件 RabbitMQ、平台即服务的 Cloud Foundry、Greenplum 数据引擎、还有大名鼎鼎的 GemFire(12306 系统解决方案组件之一)。
Pivotal公司的Matt Stine于2013年首次提出云原生(CloudNative)的概念;2015年,云原生刚推广时,Matt Stine在《迁移到云原生架构》一书中定义了符合云原生架构的几个特征:12因素、微服务、自敏捷架构、基于API协作、扛脆弱性;到了2017年,Matt Stine在接受InfoQ采访时又改了口风,将云原生架构归纳为模块化、可观察、可部署、可测试、可替换、可处理6特质;而Pivotal最新官网对云原生概括为4个要点:DevOps+持续交付+微服务+容器。
2015 年 CNCF 把云原生定义为:应用容器化、面向微服务、容器编排。到了 2018 年,CNCF 更新了云原生的定义,加入了声明式 API 和服务网格,这些技术能够构建容错性好,易于管理和便于观察的松耦合系统。
CNCF对云原生的定义如下:
云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式API。
这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。
云原生计算基金会(CNCF),是由开源基础设施界的翘楚 Google、RedHat 等公司共同牵头发起的一个基金会组织,其目的非常明确,就是为了对抗当时大红大紫的 Docker 公司在容器圈一家独大的局面。CNCF 通过 Kubernetes 项目在开源社区编排领域一骑绝尘,之后就扛起了云原生定义和推广的大旗,风光无限。目前,世界上主流的云服务提供商和互联网公司如AWS,AlibabaCloud,Apple,GoogleCloud,IBM,Intel,Microsoft,Oracle,RedHat等等,都加入了CNCF。
云原生核心技术
云原生核心技术包括容器,服务网格,微服务,不可变基础实施和声明式API。
下面对这些技术进行一些基本介绍。
Docker和Kubernetes
说到容器,大家首先想到的肯定是Docker。但事实上,容器的概念早在2008年就基本定型了。
这个概念最早来自于LXC(Linux Container),它是linux内核提供的虚拟化技术,能提供轻量级的虚拟化能力,隔离进程和资源。完整的LXC能力在2008年合入Linux主线。
2010年dotCloud公司基于LXC技术,使用Go语言实现了一套容器引擎,也就是现在的Docker。
那时候dotCloud还是个小公司,docker产品本身也没什么热度。直到2013年,dotCloud公司决定将Docker开源,Docker才迅速火了起来,随后dotCloud也直接将公司名称更改为Docker。
Docker的口号是:Build once,Run AnyWhere,和Java的Write Once,Run AnyWhere及其相似。
Docker可以将程序和其依赖项一起打包成镜像,在不同的环境中部署和运行。消除了环境的差异性,也避免了各种由于环境差异所带来的奇怪问题。
这一特点对于工程师来说,无疑是极具有吸引力的,也因此Docker在开源后变得异常火爆,直接变成了容器技术的事实标准。
但仅仅有了容器是不够的,在大规模集群中,需要对数量众多的容器进行编排和管理,于是Kubernetes(k8s)就产生了。Kubernetes是Google推出的开源容器编排工具,发展至今,已成为容器编排的事实标准。
事实上,Docker也有自己的容器编排产品-Swarm,只不过没有竞争过k8s。
Docker曾经和Kubernetes有过合作的机会,Kubernetes联合创始人Craig McLuckie还提出将Kubernetes捐赠给Docker,但最终双方未能达成一致。
Docker在容器编排之战败下阵后,始终没有找到合适的商业化模式。2021年,Docker宣布更改软件许可条款,对特大型公司的专业使用收费。
在2020年之前,Kubernetes原生支持Docker,但2020年Kubernetes宣布在1.20版本之后不再支持Docker作为默认的容器运行时,将在代码主干中移除dockershim。
Kubernetes自身定义了标准的容器运行时接口CRI(Container Runtime Interface),目的是能对接任何实现了CRI接口的容器运行时。
在初期,K8s就内置了对Docker的支持,通过dockershim来实现标准CRI接口对Docker接口的适配,达到的效果就好像Docker是一个与CRI兼容的运行时一样。
在1.20版本之后,K8s移除了dockershim这一适配组件,仅负责维护标准CRI。任何实现了CRI的容器运行时都可以作为k8s的容器运行时,如Docker捐献的Containerd。如下图所示:
容器运行时从docker切换到containerd之后,原来的
docker ps
之类的docker命令就不能再用了,但仍然可以下载镜像,或者使用docker build
命令构建镜像,只不过对于容器运行时和Kubernetes不可见,需要推送到镜像仓库才行。
正是Kubernetes的流行,才真正推动了云原生架构理念的普及,Kubernetes可以说就是云原生架构的核心承载平台。
K8s介绍参见博主其他文章系列-k8s指南。
服务网格
在博主另外的一篇介绍Rpc框架的文章最后一节提到过,传统的微服务之间的通信框架存在一些问题,如框架本身的管理维护,对不同语言的支持问题,项目对框架lib库的依赖和兼容问题等,都会导致开发人员需要消耗大量的精力和时间去处理。因此,才会有service mesh的出现。
Service Mesh又称为服务网格,其作用就是作为服务间通信的基础设施层。
2019年CNCF举办了第一届ServiceMeshCon,IBM高级技术人员在会上梳理了近几年服务网格的发展历程:
2015年,Netflix OSS stack发布,推出了一种基于Java/JVM的微服务通信库;
2017年5月,Google、IBM和Lyft联合发布了开源产品lstio;
2018年9月,Linkerd2.0正式发布;
2018年11月,Consul Connect和SuperGloo发布;
2019年5月,服务网格互通性规范SMI被推出;
2019年9月,Maesh和Kuma发布。
服务网格将分布式服务的通信抽象为单独一层,在这一层中实现负载均衡、服务发现、认证授权、监控追踪、流量控制等分布式系统所需的功能,作为一个和服务对等的代理服务(Sidecar),和服务部署在一起,接管服务的流量,通过代理之间的通信间接完成服务之间的通信请求。
服务网格的架构如下图所示:
图片来源:Pattern:Service Mesh
大量的Sidecar连接起来就形成了服务网格,如下所示。这也是服务网格名称的由来。
为什么要用服务网格
当软件架构演进至基于Kubernetes实现的微服务时,已经能够相当充分地享受到虚拟化技术发展的红利,例如:应用能够灵活地扩容缩容、不再畏惧单个服务的崩溃消亡、立足应用系统更高层来管理和编排各服务之间的版本和交互。
但单纯的k8s并不能解决所有的分布式技术问题,难以做到精细化的服务治理,如熔断、流控、观测等;而即使是那些可以提供支持的分布式能力,譬如通过DNS与服务来实现的服务发现与负载均衡,也只是初步解决了的分布式中如何调用服务的问题,只靠DNS难以满足根据不同的配置规则、协议层次、均衡算法等去调节负载均衡的执行过程这类高级的配置需求。
事实上,只从微服务的灵活和可控而言,基于Kubernetes的架构相比于基于Spring Cloud的架构是有所倒退的,但如果引入Service Mesh那又不同了。
在Kubernetes基础上引入服务网格,即通过中间人流量劫持的方式,以介于应用和基础设施之间的边车代理(Sidecar)来做到既让用户代码可以专注业务需求,不必关注分布式的技术,又能实现不亚于Spring Cloud的那种通过代码来解决分布式问题的可配置、安全和可观测性。而这一个目标,现在已成为了最热门的服务网格框架Istio的 Slogan:Connect, Secure, Control, And Observe Services。
企业级服务网格架构
服务网格中分为控制平面和数据平面。当前流行的两款开源服务网格Istio和Linkerd实际上都是这种架构。
控制平面特点:
- 不直接解析数据包
- 与控制平面中的代理通信,下发策略和配置
- 负责网络行为的可视化
- 通常提供API或者命令行工具用于配置版本化管理,便于持续集成和部署
数据平面特点:
- 通常是按照无状态目标设计的,但实际上是为了提高流量转发性能,需要缓存一些数据,因此无状态也是有争议的
- 直接处理入站和出站数据包,转发、路由、健康检查、负载均衡、认证、鉴权、产生监控数据等
- 对应用来说透明,即可以做到无感知部署
当前最受欢迎的两个服务网格项目是Istio和Linkerd,两者竞争非常激烈,且都在快速发展中。Istio每年发布3~4个版本,截止到目前为止,Istio在2022年8月份发布了最新的1.15版本。Linkerd号称是最轻量级和最快的service mesh,于2022年8月份发布了最新的2.12版本。Linkerd已经捐赠给了CNCF,而Istio也已经提议捐赠给CNCF。
国内公司对Istio的支持:国内的阿里云,腾讯云等公有云均提供了托管Istio,提供开箱即用的解决方案。此时如果你的业务跑在公有云中,可以考虑直接使用托管Istio。
国外gloo,tetrate等公司在Istio的基础上,提供Mesh解决方案。
结语
既然服务网格这么好,那到底用还是不用,如果用的话应该什么时候用,应该怎么用?这取决于您公司云原生技术的成熟度曲线的位置,服务的规模,业务核心和底层基础设施管理是否适应等。
技术总是在不断向前发展,容器出现后,解决的软件环境和分发的问题;但是如何管理分布式的应用呢,又出现了容器编排软件;容器编排软件解决的微服务的部署问题,但是对于微服务的治理的功能太弱,这才出现了服务网格,当然服务网格也不是万能的,下一步会走向何方呢?会是 Serverless 吗?我们拭目以待。
不可变基础设施
可变基础设施
在传统的可变服务器基础设施中,服务器会不断更新和修改。使用这类基础设施的工程师和管理员可以登录到他们的服务器,手动升级或降级软件包版本,逐个服务器调整配置文件,并直接将新代码部署到现有服务器上。
换句话说,这些服务器是可变的;它们可以在创建后进行更改。由可变服务器组成的基础设施本身可以称为可变的、传统的或手工的。
不可变基础设施
而不可变基础实施是指服务器在部署后永远不会被改变。如果需要以任何方式更新、修复或修改某些内容,就先对公共镜像进行修改,然后用镜像构建新服务器来替换旧服务器。
不可变基础设施的好处是在基础设施中有更多的一致性和可靠性,以及更简单、更可预测的部署过程。它可以缓解或完全防止可变基础设施中常见的问题,如配置漂移(configuration drift)。
实际上,可变基础设施是一个较早出现的基础设施模式;而不可变基础设施是在虚拟化和云计算等核心技术出来后才出现的,这些核心技术使不可变基础设施成为可能。
实践差异
在虚拟化和云计算成为可能和广泛使用之前,服务器基础设施是以物理服务器为中心的。创建这些物理服务器既昂贵又耗时;初始化设置可能需要数天或数周的时间,因为订购新硬件、配置机器、然后将其安装到一个数据中心或类似的位置需要很长时间。
可变基础设施更换服务器的成本非常高,所以最实际的做法是尽可能长时间地使用运行的服务器,而停机时间越短越好。这意味着需要对常规部署和更新进行大量适当的更改,也需要在出现问题时进行特别的修复、调整和打补丁。频繁手动更改会使服务器变得难以复制,每个服务器都成为整个基础设施中唯一且脆弱的组件。
虚拟化/云计算的出现代表了服务器架构的一个转折点。虚拟服务器更便宜,即使是大规模的,它们可以在几分钟内创建和销毁,而不是几天或几周。这使得新的部署工作流和服务器管理技术第一次成为可能,比如使用配置管理或云API以快速、编程和自动的方式提供新服务器。正是因为可以快速且低成本地创建新虚拟服务器,不变性原则才能够真正落地实践。
不可变基础设施的优点
可变基础设施中的服务器可能会配置漂移,即未经记录的临时更改会导致服务器的配置彼此之间以及与最初部署的配置之间的差异越来越大,这将导致服务器很难被复制和替换,发生问题时也难以排查。
而不可变基础设施则有很多的优点:
- 易知的服务器状态和更少的部署失败
- 没有配置漂移
- 一致的预发布环境和简单的水平扩缩容
- 简单的回滚和恢复处理
- 不可变的基础设施实现细节
声明式API
在说声明式API之前不得不提到指令式API,也可以说是过程式API,即通过一系列指令来达到所需目的。
在指令式API中,你需要详细说明每一步的步骤,经过多个步骤后,才能得到你想要的结果。也就是说,你需要自己控制所有的过程。
而在声明式API中,你只需要说明你想要的结果,至于实现这个结果的过程则无需关心。
声明式API是一个预期状态系统。使用者负责提供预期状态,具体怎么实现这个状态,则由API引擎来负责。
Kubernetes和Terraform就是广为人知的预期状态系统。
Kubernetes
在Kubernetes中,我们通过yaml文件的形式向系统提供集群中特定资源的预期状态,然后由Kubernetes中的控制器组件负责保证给定资源的实际状态与预期状态保持一致。如下图所示:
Terraform
Terraform是一个开源的IAAS工具,其目标是提供一个工作流来配置所有的基础架构。它允许用户用Terraform的原生语言HCL编写文件来指定基础架构的各个部分,最常见的是云提供商中的不同类型的资源以及它们的配置。
使用这些配置文件,你可以执行所谓的terraform计划,通过该计划,可以在更改实际基础结构之前检查配置的执行计划是否符合预期。
首次运行terraform时,它会创建一个名为tfstate的文件,用于存储资源的当前状态。每次想要对其进行更改时,都会去获取资源的当前状态,并将期望状态与当前状态进行比较,报告新计划将造成的变更。用户可以检查变更是否正确并应用这些变更。如下图所示:
不管是在Kubernetes还是在Terraform之中,用户输入的都是期望状态,在k8s中用yaml文件来表示,在terraform中用HCL文件表示。
参考资料
[1].https://jimmysong.io/kubernetes-handbook/
[2].https://zhuanlan.zhihu.com/p/441747471
[3].http://icyfenix.cn/
[4].https://istio.io/
[5].https://www.digitalocean.com/community/tutorials/what-is-immutable-infrastructure
[6].https://blog.csdn.net/M2l0ZgSsVc7r69eFdTj/article/details/122890922