揭秘云原生:你真的了解它吗?

发表时间: 2020-07-02 14:36

什么是云原生?

大白话就是: 从开发到运维整个软件生命周期都是在云上进行。


原生就是土生土长的意思,我们在开始设计应用的时候就考虑到应用将来是运行云环境里面的,要充分利用云资源的优点,比如️云服务的弹性分布式优势。


优势就是不用考虑硬件、服务器、操作系统甚至不用考虑运维,仅仅用代码实现你的想法,然后部署即可。


软件开发发展历程

  1. 面向操作系统编程

开发完应用直接部署到操作系统,此时跨平台特别差,尤其是windows/macos开发,部署到linux,给部署和运维带来非常多的复杂性。


  1. 面向容器编程:

开发完部署到Docker/k8s,容器层面隔离了操作系统,使得跨平台成为可能,但是增加了开发流程复杂度。


  1. 面向云编程:

开发时便考虑“云”,所有资源(计算/存储/网络等)按量使用,随用随取,按量付费。


  1. 畅想未来

目前的“云原生”,还是一种混合云架构,底层是多个机房,多种物理机,多种操作系统,在多个操作系统中安装k8s等容器管理软件来达到另一种“统一的云操作系统”,显然多种操作系统还是有计算资源的浪费的。未来一定会有一款云操作系统,直接部署到物理机,可跨地区、跨机房、跨网络直接提供服务,所有用户共用一个操作系统。

另外目前的“云原生”还是在本地开发,然后部署到云环境中,未来的开发一定是云上开发,云上运行,云上测试,云上部署。用户使用的只有屏幕,没有处理器,所有操作通过网络交互完成,所有计算在服务端进行。

达到这种要求需要的基础设施:

  • 高速便宜的网络:5G提供了基本的可能性,大概6G/7G有望实现
  • 全球统一的操作系统:一定是全球多国的多个商业互联网公司合同,共建的生态系统、社区、标准。
  • 便携终端的出现:比如折叠屏幕、腕式投影等等
  • 计算能力的飞速提升:量子计算提供了可能性
  • 编程语言的与时俱进


如何搭建云原生

系统架构

跨操作系统的容器管理能力,提供:

  • 软件定义存储
  • 软件定义计算
  • 软件定义网络

技术实现可以基于Docker和k8s,不关心底层操作系统,不关心机房和硬件。所有资源按量使用。

技术实现

  • 微服务
  • 服务网格

技术上基本SpringCloud/Doubbe两者统一天下。

最佳实践和方法论

  • devops
  • 自动化发布
  • 自动化运维
  • 自动化伸缩

Jenkins等各种devops工具,甚至k8s自带的各种工具完全可以实现。


CNCF基金会


一个统一中立的开源云生态(至于是否中立嘛这里就不谈了:)。这对云原生的生态定位会是很重要的一点,也算CNCF最初成立的宗旨之一吧,打破云巨头的垄断。


云原生与十二要素

十二要素 (The Twelve Factors) 是由 Heroku 团队提出的云应用设计理念,它为构建流程标 准化和高可移植的 SaaS 应用提供了完善的方法论。遵循十二要素设计的应用具备云原生应用的 所有特征。十二要素适用于任何语言开发的后端应用服务,它提供的方法论和核心思想如下。

  • 将流程自动化和标准化,降低新员工的学习成本。
  • 划清与底层操作系统间的界限,以保证最大的可移植性。
  • 适合部署在现代云平台上,避免对服务器与操作系统进行管理。
  • 将开发环境与生产环境的差异降至最低,便于实施持续交付和敏捷开发。
  • 应用可以在不改变现有工具、架构或开发流程的情况下,方便地进行水平伸缩。

十二要素重点关注应用程序的健康成长,开发者之间的有效代码协作,以及避免软件腐蚀。 十二要素的内容如图 1-8 所示。

下面我们来逐条介绍一下十二要素。

1. 基准代码 (Codebase)

同一应用对应同一套基准代码,并能够多次部署。

部署到不同环境的同一个应用,其基准代码库应该相同,但每份部署可以包含各自环境中的不同配置。一次部署对应一个运行起来的应用程序,应用与部署的关系是一对多的,这体现了应用代码的可重用性。同一套基准代码可以重用到多次部署中去,共享的是代码,而不同的仅仅是配置。从另一个角度来说,非运行时的应用对应的是代码仓库,它的每一个运行时实例都对应一次部署,同一代码仓库可以保障应用的复原能力。

推荐使用 Git、SVN 等优秀的源代码管理工具作为基准代码库。基准代码与部署的关系如 图 1-9 所示。

2. 依赖 (Dependencies)

显式声明第三方依赖。

随着技术的发展,应用程序的开发已不再是一个从零开始的过程,大量的第三方类库使得工程师们可以站在巨人的肩膀上进行增量式开发。应用程序不应隐式地依赖类库,而是应该通过依赖清单明确地声明其依赖项。

显式声明依赖简化了环境配置流程,开发工程师仅需要安装编程语言环境和它对应的依赖管理工具,并从代码库中检索出代码,即可通过一个命令来构建所有的依赖项,从而轻松地开始工作。

十二要素要求应用同样不应该隐式依赖某些系统工具,比如 curl。即使这些系统工具存在 于所有的现代操作系统中,也无法保证未来的操作系统都能支持或兼容现有应用的使用方式。

现代编程语言都会提供依赖打包管理工具,如 Java 语言的 Maven、Gradle 以及早期的 Ant 等。

3. 配置 (Config)

将配置存储至环境变量。

应用的配置在不同环境中部署时也会有所差异,若应用将配置以编码的方式写入程序的常量,则会造成代码与配置混淆。十二要素强调配置应该与代码分离,不应在源码中包含任何与环境相关的敏感信息。

虽然将配置提炼到属性文件可以实现将其与代码分离,但属性文件仍然可能会被不小心地提交至源码仓库。因此,十二要素推荐将配置存储于环境变量中,这样可以非常方便地在不同的部署环境间修改,而无须改动代码。

与配置文件相比,环境变量与语言和系统无关。将配置存储在环境变量中能够方便与 Docker 等基于容器的应用配合使用,也易于与 Kubernetes 的 ConfigMap 配合使用。将配置排除在代码 之外的标准取决于,应用是否可以立刻开源且不必担心暴露任何系统的敏感信息。

十二要素并不赞同采用配置分组的方式管理配置。有些开发团队愿意将应用的配置按照特 定的环境 (如开发环境、测试环境和生产环境) 进行分组。采用配置分组方式不利于扩展,当 工程师添加他们自己的开发环境 (例如 john-dev) 时,将导致各种配置组合激增,给管理部署 增加额外的不确定性。

十二要素要求环境变量的粒度足够小且相对独立,它们不应该作为环境组合使用,而是应该独立存在于每个部署之中。当应用程序拥有更多种类的配置项或进行环境部署时,采用这种配置管理方式更容易实现平滑过渡。

需要特别指出的是,这里所指的配置并不包括应用程序的内部配置。举例来说,Spring 容 器中 Bean 的依赖注入配置,或者 Servlet 的映射配置文件 web.xml 等,它们更应该被认为是代 码的一部分。

4. 后端服务 (Backing Services)

将后端服务作为松耦合的资源。

后端服务是指应用程序所依赖的通过网络调用的远程服务,如数据库、缓存、消息中间件 以及文件系统等,不同后端服务之间的区别仅仅在于资源的 URL 不同。

十二要素要求应用程序不应该区别对待本地服务和远程服务,它们同样都属于附加资源。 应用程序可以在不改动任何代码、仅修改资源地址的情况下,将出现硬件问题的数据库切换为 备份数据库,或将本地数据库切换为云数据库。应用程序与这些附加资源应该保持松耦合的状 态。图 1-10 展示了后端服务松耦合的状态。

5. 构建、发布、运行 (Build、Release、Run)

严格分离构建阶段与运行阶段。

分离构建阶段与运行阶段的根本在于,要严格区分应用的非运行时状态和运行时状态。构建是将应用的源代码编译打包成可执行软件的过程,属于非运行时行为。将基准代码转化为一份部署一般要经过构建阶段、发布阶段和运行阶段。构建阶段是将源码从编译状态转化为可执行的二进制文件的过程 ; 发布阶段是将构建结果与当前部署所需要的配置相结合,并分发至运行环境的过程 ; 运行阶段是在执行环境中启动一系列发布完毕的应用程序进程的过程。

十二要素规定,禁止在运行阶段改动代码,这样做会导致基准代码失去同步。建议每个发 布版本对应一个唯一的发布 ID,发布版本只能追加而不能修改,除了回滚,其他变动都应该产 生新的发布版本。构建与发布的流程如图 1-11 所示。

6. 进程 (Processes)

将应用作为无状态的进程运行。

应用进程应该是无状态的。只有无状态的应用才能做到水平伸缩,从而利用云平台弹性伸缩的能力,而需要持久化的数据应该存储于后端服务中。

在有些遗留系统的设计中,Web 应用通常会将用户会话中的数据缓存至内存,并保证将同 一用户的后续请求路由到同一个进程,这种会话被称为黏性会话。十二要素并不推荐这种做法, 会话数据应该保存至 Redis 这样的带有过期时间的缓存中,并作为后端服务提供服务。

7. 端口绑定 (Port Binding)

通过端口绑定对外发布服务。

应用本身对于发布服务的环境不应该有过多的要求,不需要依赖云平台提供应用运行容器, 只要云平台分配某个端口对外发布服务即可。通过端口绑定访问服务也意味着任何应用都可以 成为另一个应用的后端服务。

例如,可以利用 Jetty 这种内嵌的 Web 服务器或 Spring Boot 等快速开发框架来开发包含可 发布 HTTP 服务的应用。

8. 并发 (Concurrency)

能够通过水平伸缩应用程序进程来实现并发。

云平台操作系统与 UNIX 操作系统类似,运行在系统之上的不同进程彼此独立并且共享操作系统管理的硬件资源。不同的应用彼此独立、互不干扰地运行在一个云平台上,可以充分利用云平台的整体计算能力。这样的进程模型对于系统扩容非常实用。

开发人员应该将不同类型的工作分配给不同的进程,例如,将 HTTP 请求交给 Web 服务器 的进程来处理,将常驻后台进程交给 worker 进程负责,将定时任务交给 clock 进程负责。这条 原则与微服务的设计原则有异曲同工之妙,它希望应用开发者将应用的职责尽可能进行拆分。 图 1-12 清晰地展示了如何通过增加不同类型的应用进程来实现系统的水平伸缩。

9. 已处理 (Disposability)

可以快速启动和优雅关闭应用。

快速启动是为了充分利用云平台根据需要调度资源的能力,在需要的时候,以最小的延时扩展计算能力,提供服务。优雅关闭是为了保证应用逻辑的完整性,将该完成的任务正确完成并释放资源,将未能完成的任务重新交回系统由其他应用的运行实例来继续完成。随时可能有大量部署在云平台上的应用实例启动、运行和关闭,因此快速启动和优雅关闭应用对于维持系统的高性能和稳定性尤为重要。

10. 开发环境与线上环境等价 (Dev/Prod parity)

要保持开发环境与线上环境等价。

开发环境和线上环境之间存在着很多差异,主要包括代码差异和操作差异。开发人员正在编写的代码可能需要很长时间才会上线,这将导致开发环境和线上环境的代码差异很大。在开发环境中,代码一般由开发人员编写并调试,而在线上环境中,代码则由运维人员部署,不同的操作方式也可能带来环境的差异。

保持环境一致,可以提高功能测试和集成测试的有效性,避免出现开发环境测试正常但生 产环境出现问题的情况。推荐使用 Jenkins 等持续集成工具来缩短生产代码和代码库中的代码不 一致的时间,并采用自动化部署的方式避免操作差异。

11. 日志 (Logs)

使用事件流处理日志。

运行在云平台上的应用处在复杂的分布式基础设施之上,如果日志仍然写在硬盘的一个文件中,将给系统排错或通过日志挖掘信息带来很大的困难,并且当日志与应用绑定时,应用不能作为无状态进程,也就无法充分利用云平台的扩容能力了。

为了解决以上问题,我们应采取相应措施——应用将日志输出到标准输出 (STDOUT),然 后由云平台统一收集并处理。在线上环境中,进程的输出事件流由运行环境截获,运行环境会 将所有输出事件流整合在一起,然后发送给一个或多个最终的处理程序,用于查看或长期存档。

推荐使用 Flume、Filebeat 或 fluentd 等日志收集工具。日志事件流最终可以被发送到 Elasticsearch 或 Splunk 这样的日志索引及分析系统中,用于排错查询和后期分析。

12. 管理进程 (Admin Processes)

将后台管理任务当作一次性进程运行。

与用来处理应用的常规业务进程不同,工程师经常希望执行一些用于管理或维护应用的一次性任务,这类任务被称为后台管理任务,例如检查和清理环境、迁移数据等。

这些后台管理任务应该作为一次性进程,与常驻进程使用同样的环境,基于同样的代码库和配置发布运行。总而言之,一次性进程同样应该遵循前面提到的十一个要素。

遵循十二要素的应用程序环境是一次性且可复制的。由于应用程序的状态均通过后端服务持有,因此无状态的应用有助于编排系统自动化扩展。扩容时,编排系统仅须将应用程序的运行时环境数量扩充到期望位并直接启动进程即可 ; 缩容时,则需要停止应用进程并删除环境,无须进行环境状态备份。

要想了解有关十二要素的详细内容,请参见官方网站: https://www.12factor.net/。