网上有些文章提到云原生是“Pivotal公司的Matt Stine于2013年首次提出云原生(CloudNative)的概念”。我搜索了英文“CloudNative”,阅读了首页的所有文章,里面没有一篇提到“Matt Stine首次提出云原生”,但它们每一篇都提到了“云原生计算基金会”的定义。“Matt Stine”确实写了一本书,叫《迁移到云原生架构》,他以前确实在Pivotal公司工作,但说他“首次提出云原生(CloudNative)的概念”应该是不准确的, 而且他的定义和云原生的含义是有一定偏差的。
我觉得比较接近的说法是Netflix公司首创了云原生,详见Going Cloud Native: 6 essential things you need to know。
虽然那篇文章主要是讲的Netflix如何开创了微服务,但Netflix的微服务是部署在亚马逊云上的。而当时亚马逊云也才刚起步,各方面都不成熟,Netflix是它的最大客户。是Netflix的层出不穷的需求帮助亚马逊云不断完善它的功能和性能,最终登顶云服务商。因此Netflix的微服务演进是和云计算交织在一起,共同推进的。Netflix在微服务领域的开创和领先地位是大家公认的,它的“Netflix OOS”系列工具至今仍被广泛使用,特别是Java社区,并被移植到其他语言。在这个过程中,也同时开创了云计算的先河,它的起点是2009年。详情请见Goto Berlin - Migrating to Microservices (Fast Delivery)。
但我想说的是云计算(Cloud)和云原生(Cloud Native)还是有很大区别的。Netflix是云计算的开拓者,但并不是云原生的创造者。云原生的基石是k8s,没有k8s就没有云原生, 而k8s的1.0版诞生于2015年。云原生计算基金会(CNCF)也诞生于2015年并致力于推动云原生的发展。云原生的概念是在2017才开始被广泛接受和流行,因此云原生和云计算是由本质区别的。云原生的诞生是和云原生计算基金会密切相关的。
下面就让我们看一下CNCF给出的云原生的定义:
“云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式API。这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。
云原生计算基金会(CNCF)致力于培育和维护一个厂商中立的开源生态系统,来推广云原生技术。我们通过将最前沿的模式民主化,让这些创新为大众所用。”
摘要来源:CNCF Cloud Native Definition v1.0
这个定义还是比较靠谱的,尽管它并不严谨,也并没有挖掘出云原生的本质。但考虑到每个组织的目的和立场不同,看问题的角度不同,CNCF的主要目的是培育云原生工具市场,因此它的定义带有很重的实用色彩,偏重工具方面。若是从这个角度看,这个定义还是比较贴切的。我觉得唯一不严谨的地方是把微服务列了进去,其他的都没什么问题。
让我们来分析一下定义中提到的工具。其中k8s是整个云原生的基石,也是CNCF的第一个项目。云原生的整个生态体系都是依靠k8s建立起来的。因此在k8s之前是不可能有云原生的。定义里还提到了容器(Container)、服务网格(Service Mesh)、微服务(Microservice)、不可变基础设施(immutable infrastructure)和声明式API(declarative APIs)。其中容器(Container)是k8s的底层引擎,服务网格(Service Mesh)是建立在k8s上的针对请求的扩展功能,不可变基础设施(immutable infrastructure)是现代运维的基石,声明式API(declarative APIs)是k8s的编码方式,这些无一不是和k8s紧密相关的。但微服务(Microservice)就不同了,它其实跟云原生没什么关系,它们是两个完全不同的东西并沿着各自的轨道独立向前发展。但由于认容器技术和微服务是天生的良配,它们现在的演进轨道交织在一起密不可分。
但实际上没有容器技术,微服务也可以部署在虚机上,只不过资源的利用率可能不够高。没有微服务,容器技术虽然不能大展宏图,但也能在分布式应用里找到一席之地。当然把它们放在一起确实能如虎添翼,但把微服务划归到云原生里实在是有点扩大外延,跑马圈地的意味。因为云原生的重点还是在基础设施,运维和运行环境以及软件的开发环境,而微服务是一个软件的架构,两者之间有明显的不同。
那么到底什么是“云原生(Cloud Native)”呢?它分表层含义和深层含义。表层含义从字面上理解就比较容易了,我们管母语叫“Native Language”,也就是你一生下来就说的语言。“Cloud Native”就是一开始开发的时候就是为了最终部署到云环境上的。而在云计算初创时,大部分的程序都是从本地环境移植到云上的,它们在设计是就根本没考虑云环境的问题。
那么部署到云环境和部署到本地服务器有什么不同?这个才是问题的本质。
你可能会说“是容器技术”,这个是现代云计算的不可或缺的支撑,但云计算开始的时候是基于虚拟化的,并没有容器技术,是在发展的过程中才有了容器技术。
是“自动伸缩(auto-scaling)”吗?这是云环境的一个主要优点和特性,但它只是结果,不是本质。
上面讲到了,只有一开始就是按照部署到云环境的要求来设计的应用程序才是云原生的。那么部署到云环境需要做哪些特殊设计呢?
它主要有两个部分:
第一部分是服务调用。不论是微服务之间的调用,还是微服务调用数据库或前端调用后端,调用的方式都是一样的。都需要知道IP地址,端口和协议,例如“http://127.0.0.1:80”, 其中“http”是协议,“127.0.0.1”是IP地址,“80”是端口。由于程序是部署在k8s上的,k8s会负责程序之间的寻址和调用。由于k8s会自动销毁出错的服务器,并创建新的服务器,IP地址就变成了动态的,而不是静态的。这时就只能通过服务名而不是IP地址来进行调用。也就是说k8s会给每个服务一个服务名,并通过k8s内部的DNS对服务名进行寻址。服务名是写在k8s的配置文件里的,软件设计的关键让应用程序和k8s配置文件都共享相同的调用地址。
第二部分是数据的持久存储。在程序运行时,经常要访问持久存储(硬盘)上的数据,例如日志,配置文件或临时共享数据。程序在容器中运行,一旦出现问题,容器会被摧毁,k8s会自动重新生成一个与原来一模一样的容器,并在上面重新部署应用程序。在集群环境下,用户感觉不到容器故障,因为系统已经自动修复了。但当容器被摧毁时,容器上的数据也一起被摧毁了,因此要保证程序运行的连续性,就要让持久存储不受容器故障的影响。
如果你对它的具体设计感兴趣,请参见把应用程序迁移到k8s需要修改什么?
不过云原生还有一层引申含义。当你的最终生产环境是云环境时,你的本地开发环境最好也是云环境,这虽然不是必须的,但它能保证本地环境和生产环境的一致性,减少部署时的意外,是一个很自然的选择。而要在本地使用云环境来进行开发,你需要一系列的工具来保证开发的顺利和高效。要想了解云原生的开发环境及工具,请继续阅读下一篇“ 云原生开发环境初探"。
12-Factors经常被直译为12要素,也被称为12原则,12原则由公有云PaaS的先驱Heroku于2012年提出(https://12factor.net/),目的是告诉开发者如何利用云平台提供的便利来开发更具可靠性和扩展性、更加易于维护的云原生应用。云原生应用程序的一大关键性特质在于,其需要遵循一套设计契约以最大程度实现行为的可预测性。云平台当中所使用的高自动化、容器驱动型基础设施也对软件的编写方式提出了要求。开发人员必须改变自己的编程习惯,在开发人员与基础设施之间创建出一套用于指导应用程序运行的新型“契约”。下面我们就通过“应用十二要素”中所提出的十二项基本原则来了解如何打造出一套理想的“契约”机制。这十二项因素之间存在一定交集,同时亦相互支撑。具体如下:
基准代码——立足于单一代码库向多种环境部署 – 包括生产性组件在内的单一代码库能够确保代码的单一来源,从而降低配置错误数量并提高弹性水平。
显式声明依赖关系——云平台需要引入必要的关联性声明并加以妥善管理,从而确保相关云应用程序始终具备必要的库及服务支持。
在环境中存储配置——使用保存在环境当中的配置信息 – 环境变量能够提供一套简洁、易于理解且符合标准要求的使用方式,从而为以多种编程语言编写而成的无状态应用程序提供良好的配置机制。
把后端服务当作附加资源——将每种资源都作为远程资源处理的思路成就了弹性这一概念,这不仅从编程层面考虑到了资源不可用情况,同时也最大程度发挥了微服务方案当中的固有优势。
严格分离构建、发布和运行——云原生应用程序的构建流程将大部分发布配置工作转移到了“开发”阶段,这意味着发布包当中将包含有代码本身以及运行应用程序所必需的生产配置方案。
无状态进程——云原生基础设施的速度表现与成本效益要得到切实体现,要求应用程序堆栈中的第一层拥有尽可能高的轻量化水平。
通过端口绑定提供服务——云原生应用程序当中的服务接口一般倾向于利用基于HTTP的API作为通用集成框架。
通过进程模型进行扩展——对于无状态非共享式设计思路的强调,意味着扩展工作能够依赖于底层平台——而非智能化多进程代码——来完成。
快速启动和优雅终止——假定任意给定进程都能够随时进行启动与关闭。
开发环境与线上环境等价——在开发、分段与生产环境下拥有统一运行效果 – 由于高度强调自动化机制并在各生命周期阶段使用同样的云平台,因此只要大家使用的是同一套“平台”、那么我这边能用的在你那边也同样能用。
日志作为事件流——对汇总及事件响应的标准输出结果进行记录 – 当日志记录由云平台而非应用程序内的库负责处理时,将记录机制作为功能实体则变得非常关键。
管理进程——允许临时性任务以短期进程方式运行 – 在云原生方案当中,管理任务可以单纯转化为另一种进程、而非特定工具,而且必须保证其行为方式要与使用“机密”API以及内部机制有所区别。
另外还有补充的三点:
API声明管理
认证和授权
监控与告警
距离12原则的提出已有五年多,12原则的有些细节可能已经不那么跟得上时代,也有人批评12原则的提出从一开始就有过于依赖Heroku自身特性的倾向。不过不管怎么说,12原则依旧是业界最为系统的云原生应用开发指南。
基础设施即代码是指把创建基础设施(包括服务器和网络环境)的命令像应用程序一样储存在源码库中,并进行版本管理。这样创建基础设施的过程就变成了部署软件的过程。它的最大的好处就是可重复性。以前的方法是用人工敲入命令来创建运行环境,出了问题就在原来的基础之上进行修修补补,一旦需要把整个环境重新建立,很难保证与原来的一样。 当使用基础设施即代码之后,再也没有了这个担心。
详情请见 InfrastructureAsCode
说道这里,我们不得不提“不可变基础设施(immutable infrastructure)“,它是基础设施即代码的升级版。有了基础设施即代码之后,随时都可以通过运行软件再构建出一个一模一样的服务器和其他需要的设备,并且还能预装应用程序,创建的时间还是秒级的。这时当服务器出现问题时,就没有必要去花时间查找原因了并修复了,而是直接把服务器销毁重新创建一个新的。因此这时的基础设施是不可变的,只有创建和删除,而没有修改操作。这彻底改变了运维的方式。
详情请见 What is "Immutable Infrastructure"?
声明式API也是基础设施即代码的升级版。最开始时,当用软件定义基础设施时是用的过程式描述,也就是通过运行一系列的命令来创建运行环境。后来发现更好的办法是描述最终运行环境的状态,而由系统来决定如何来创建这个环境。例如,你的描述就变成“创建一个有三个Nginx的集群”,而不是把创建Nginx的命令运行三次组成一个集群。这样的好处是当运行环境与描述不符合时,系统能检测到差异,并自动修复,这样系统就有了自动容错的功能。
上面讲了云计算环境和传统基础设施的不同,其实随着云计算的发展,传统基础设施也在不断地采纳云计算的先进技术和理念,例如虚拟化和容器技术,而各个云计算厂商也提供了本地私有云的版本。只不过在公有云上的管理功能更强大,而通常本地私有云的版本是公有云的一个简化版。
云原生包含了一组应用的模式,用于帮助企业快速,持续,可靠,规模化地交付业务软件。云原生由微服务架构,DevOps 和以容器为代表的敏捷基础架构组成。
这边引用网上关于云原生所需要的能力和特征总结,如下图。
云原生知识架构图
最近几年Docker容器化技术很火,经常在各种场合能够听到关于Docker的分享。Docker让开发工程师可以将他们的应用和依赖封装到一个可移植的容器中。Docker背后的想法是创建软件程序可移植的轻量容器,让其可以在任何安装了Docker的机器上运行,而不用关心底层操作系统。
Docker可以解决虚拟机能够解决的问题,同时也能够解决虚拟机由于资源要求过高而无法解决的问题。其优势包括:
隔离应用依赖创建应用镜像并进行复制创建容易分发的即启即用的应用允许实例简单、快速地扩展测试应用并随后销毁它们
自动化运维工具可以降低环境搭建的复杂度,但仍然不能从根本上解决环境的问题。在看似稳定而成熟的场景下,使用Docker的好处越来越多。
笔者看到Jimmy Song对云原生架构中运用服务编排的总结是:
Kubernetes——让容器应用进入大规模工业生产。
这个总结确实很贴切。编排调度的开源组件还有:Kubernetes、Mesos和Docker Swarm。
Kubernetes是目前世界上关注度最高的开源项目,它是一个出色的容器编排系统。Kubernetes出身于互联网行业的巨头Google公司,它借鉴了由上百位工程师花费十多年时间打造Borg系统的理念,通过极其简易的安装,以及灵活的网络层对接方式,提供一站式的服务。
Mesos则更善于构建一个可靠的平台,用以运行多任务关键工作负载,包括Docker容器、遗留应用程序(例如Java)和分布式数据服务(例如Spark、Kafka、Cassandra、Elastic)。Mesos采用两级调度的架构,开发人员可以很方便的结合公司业务场景自定制MesosFramework。
他们为云原生应用提供的强有力的编排和调度能力,它们是云平台上的分布式操作系统。在单机上运行容器,无法发挥它的最大效能,只有形成集群,才能最大程度发挥容器的良好隔离、资源分配与编排管理的优势,而对于容器的编排管理,Swarm、Mesos和Kubernetes的大战已经基本宣告结束,Kubernetes成为了无可争议的赢家。
传统的Web开发方式,一般被称为单体架构(Monolithic)所有的功能打包在一个WAR包里,基本没有外部依赖(除了容器),部署在一个JEE容器(Tomcat,JBoss,WebLogic)里,包含了DO/DAO,Service,UI等所有逻辑。其架构如下图所示。
传统的单体架构
单体架构进行演化升级之后,过渡到SOA架构,即面向服务架构。近几年微服务架构(Micro-Service Archeticture)是最流行的架构风格,旨在通过将功能模块分解到各个独立的子系统中以实现解耦,它并没有一成不变的规定,而是需要根据业务来做设计。微服务架构是对SOA的传承,是SOA的具体实践方法。微服务架构中,每个微服务模块只是对简单、独立、明确的任务进行处理,通过REST API返回处理结果给外部。在微服务推广实践角度来看,微服务将整个系统进行拆分,拆分成更小的粒度,保持这些服务独立运行,应用容器化技术将微服务独立运行在容器中。过去设计架构时,是在内存中以参数或对象的方式实现粒度细化。微服务使用各个子服务控制模块的思想代替总线。不同的业务要求,服务控制模块至少包含服务的发布、注册、路由、代理功能。
容器化的出现,一定程度上带动了微服务架构。架构演化从单体式应用到分布式,再从分布式架构到云原生架构,微服务在其中有着不可或缺的角色。微服务带给我们很多开发和部署上的灵活性和技术多样性,但是也增加了服务调用的开销、分布式系事务、调试与服务治理方面的难题。
Spring Cloud整体架构图
从上图Spring Cloud组件的架构可以看出在微服务架构中所必须的组件,包括:服务发现与注册、熔断机制、路由、全局锁、中心配置管理、控制总线、决策竞选、分布式会话和集群状态管理等基础组件。
Spring Cloud VS Kubernetes
Spring Cloud和Kubernetes有很大的不同,Spring Cloud和Kubernetes处理了不同范围的微服务架构技术点,而且是用了不同的方法。Spring Cloud方法是试图解决在JVM中的微服务架构要点,而Kubernetes方法是试图让问题消失,为开发者在平台层解决。Spring Cloud在JVM中非常强大,Kubernetes管理那些JVM很强大。看起来各取所长,充分利用这两者的优势是自然而然的趋势了。
关于云原生的定义,其中比较有影响力的是“云原生计算基金会”的定义,简略如下:
“云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式API。这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。“
它把微服务作为云原生的代表技术,我觉得是不妥的。云原生和微服务是两个不同维度的东西。 云原生更侧重应用程序的运行环境, 它是以k8s和容器为基础的云环境。“云原生计算基金会”致力于打造一整套工具来帮助应用程序从开发,测试,运行以及部署到云环境。
微服务是应用程序的软件架构,可以是单体式和微服务式。 微服务是基于分布式计算的。 你的应用程序即使不采用微服务架构也可以是云原生的,例如分布式的,但效果没有微服务好。 如果是单体式的,云原生就基本发挥不出什么优势。 另外微服务的程序也可以不是云原生的。它们虽然是两个不同的东西,但云原生和微服务是天生良配,相得益彰,相辅相成。 而且很多云原生的工具都是针对微服务架构设计的。
你可以说现代应用程序的趋势就是"微服务+云原生"
最后给大家分享Spring系列的学习笔记和面试题,包含spring面试题、spring cloud面试题、spring boot面试题、spring教程笔记、spring boot教程笔记、最新阿里巴巴开发手册(63页PDF总结)、2022年Java面试手册。一共整理了1184页PDF文档。私信博主(666)领取,祝大家更上一层楼!!!