云原生架构的方法论与最佳实践。
-
基准代码:一份基准代码(Codebase),多份部署(deploy)
部署在不同环境通过修改配置的方式实现。
-
依赖:显式声明依赖关系( dependency )
应用程序不会隐式依赖系统级的类库。一定是通过依赖清单 ,确切地声明所有依赖项。在 JAVA 开发时,使用 Maven 或 Gradle 即可满足。
-
配置:在环境中存储配置
推荐将应用的配置存储于环境变量中( env vars, env )。
代码和配置严格分离,判断一个应用是否正确地将配置排除在代码之外,一个简单的方法是看该应用的基准代码是否可以立刻开源,而不用担心会暴露任何敏感的信息。
不推荐给配置分组(e.g. dev、test、pro),因为随着项目的深入,开发人员可能还会添加自己的环境,导致配置组合激增,从而给管理部署增加了不确定的因素。【个人理解是不能有“完整的配置分组”,有一份“基准配置”,像配置中心读取配置那样,按优先级读取内容,覆盖“基准配置”读取的内容,这样每个环境的配置只需要留有变动内容的参数即可】
-
后端服务:把后端服务(backing services)当作附加资源
每个不同的后端服务都是一份资源。后端服务是指程序运行所需要的通过网络调用的各种服务(e.g. DB、MQ、cache)。
12-Factor 应用不会区别对待本地或第三方服务。在符合 12-Factor 的应用部署后,都应该可以在不进行任何代码改动的情况下,通过修改配置中的资源地址替换服务,例如:将本地 MySQL 数据库换成第三方服务。
-
构建,发布,运行:严格分离构建、发布和运行
- 基准代码 转化为一份部署(非开发环境)需要以下三个阶段
- 构建:指定版本,打包,编译;
- 发布:构建内容 + 配置;
- 运行:启动应用程序进程。
不要直接修改运行状态的代码,因为这些修改很难再同步回构建步骤。任何的变动都应该产生一个新的发布版本。(保证全量发布)
- 基准代码 转化为一份部署(非开发环境)需要以下三个阶段
-
进程:以一个或多个无状态进程运行应用
12-Factor 应用的进程必须无状态且无共享。任何需要持久化的数据都要存储在后端服务内,比如数据库。利于应用横向拓展,发生意外情况也利于恢复。
-
端口绑定:通过端口绑定(Port binding)来提供服务
- 12-Factor 应用完全自我加载而不依赖于任何网络服务器就可以创建一个面向网络的服务。
【早期的应用需要用运行在服务器容器中,如 Tomcat,目前都是通过 SpringBoot 开发,使用容器技术,通过与宿主机绑定端口的方式运行应用。每个应用可以独立运行,重启,对外提供服务。符合 12-Factor 应用完全自我加载的要求。】
- 互联网应用通过端口绑定来提供服务 ,并监听发送至该端口的请求。
【在线上环境中,请求都是统一发送至一个共有域名,其后的路由器(gateway)转发至绑定了端口的网络进程】
-
并发:通过进程模型进行扩展
根据第六条(进程),12-Factor 应用的进程所具备的无共享,水平分区的特性意味着添加并发会变得简单而稳妥。可以将不同的工作分配给不同进程类型,这些进程的类型以及每个类型中进程的数量就被称作进程构成。
12-Factor 应用的进程不需要守护进程或是写入 PID 文件。应该将进程管理与日志处理交由操作系统的进程管理器(例如 systemd ,分布式的进程管理云平台,或是类似 Foreman 的工具)。
-
易处理:快速启动和优雅终止可最大化健壮性
12-Factor 应用的进程是易处理(disposable)的,意思是说它们可以瞬间开启或停止。 这有利于快速、弹性的伸缩应用,迅速部署变化的代码或配置,稳健的部署应用。
- 进程应当追求最小启动时间。便于发布和拓展,增加了健壮性。
- 应该优雅的 shut down。
- 任务都应该可重复执行,保障幂等性。
- 充分考虑应用异常死亡的情况,使用后端队列,在应用突然死亡时仍然保持健壮。
-
开发环境与线上环境等价:尽可能的保持开发,预发布,线上环境相同
12-Factor 应用想要做到持续部署就必须缩小本地与线上差异。
- 缩小时间差异:使用 Jenkins 之类的自动发布工具,缩短发布时间。
- 缩小人员差异:开发人员不只要编写代码,更应该密切参与部署过程以及代码在线上的表现。
- 缩小工具差异:尽量保证开发环境以及线上环境的一致性。
-
日志:把日志当作事件流
日志应该是事件流的汇总,将所有运行中进程和后端服务的输出流按照时间顺序收集起来。
应用本身从不考虑存储自己的输出流。在预发布或线上部署中,每个进程的输出流由运行环境截获,并将其他输出流整理在一起,一并发送至最终的处理程序(e.g. Splunk、Hadoop)。
-
管理进程:后台管理任务当作一次性进程运行
开发人员经常希望执行一些管理或维护应用的一次性任务,例如:跑脚本,运行控制台等。
一次性管理进程应该和正常的常驻进程使用同样的环境。这些管理进程和任何其他的进程一样使用相同的 代码 和 配置 ,基于某个 发布版本 运行。后台管理代码应该随其他应用程序代码一起发布,从而避免同步问题。