正文
所有部署中都相同的信息,例如原则 2 里讲到的依赖信息,不在本原则所讨论的范围内。一些虽然在不同的部署中有所差异,但是和业务相关的信息,例如资金结算的转换比例,也不属于本原则所讨论的配置。
我想大多数的开发者都知道如何通过使用配置文件实现配置和代码的分离,但是这种方式仍然存在一些缺点,例如:
为了避免上述问题,本原则要求将在环境中存储配置。一种典型的方式是把配置存储在环境变量中,这会使配置和代码彻底的分离,格式上也与开发语言和框架再无瓜葛,并且也不会被误提交到代码库中。还可以使用 Spring Cloud Config Server 这类配置管理服务进行配置推送,并将配置的历史版本和变更原因也一起管理起来。
这里的后端服务指的是应用运行所依赖的各种服务,例如数据库、消息代理、缓存系统等,对于云原生应用来说,往往还会有日志收集服务、对象存储服务、以及各种通过 API 访问的服务;当作附加资源指的是把这些服务作为外部的、通过网络调用的资源。
该原则有如下几层含义:
-
不要将这些服务放在应用本地:云原生应用要求应用本身无状态化,那么状态信息就应该存储在外部服务中(参见不可变服务器)。同时,微服务模式要求应用责权单一以实现可靠性和扩展性,如果在应用本地放置数据库,那么微服务平台将无法通过更换应用的故障实例实现应用的高可用性,也无法通过自动化的横向伸缩实现扩展性,因为应用实例内包含两种性质完全不同的软件(应用和数据库),无法对两者使用同一种方式进行横向扩展。另外,如果将这些服务放在应用本地,那么也无法通过充分利用云平台提供的能力简化运维工作,例如,如果在应用本地放置数据库,而不是使用云平台提供的数据库服务,那么显然无法利用数据库服务提供的自动备份、安全、和高可用等特性。
-
通过 URL 或者服务注册 / 认证中心访问这些后端服务:应用应该能够在不进行任何代码修改的情况下,在不同的目标环境中进行部署,应用不应该和后端服务的任何一种具体实现存在紧耦合关系。
-
类似“显式声明依赖关系”原则,应用最好也能够对其使用的这些后端服务进行显示声明,以方便云平台对服务资源进行自动绑定,在后端服务出现故障的时候,云平台也能够对其进行自动恢复。
在本原则中,构建、发布和运行这三个概念可能和从前有所不同,因此有必要首先对其进行明确:
-
构建指的是将应用代码转化为执行体的过程:构建时会拉取特定版本的代码和依赖项,将其编译为二进制文件(针对编译型语言),并和资源文件一起打包。
-
发布指的是将构建的结果和部署所需的配置相结合,并将其放置于运行环境之中。
-
运行指的是将发布的结果启动为运行环境中的一个或多个进程。
本原则要求构建、发布和运行这三个步骤严格区分:
-
禁止直接修改运行状态的代码或者对应用进行打补丁,因为这些修改很难再同步回构建步骤,这时运行状态的代码就成为了“孤本”。同时,也不应该在运行期间修改应用的配置,配置的修改应该仅限于发布阶段(参见不可变服务器)。
-
运行这一步骤应该非常简单,仅限于启动进程,资源文件的关联应仅限于构建阶段,配置的结合应仅限于发布阶段。
同时,每一次发布都应该对应一个唯一的发布 ID,发布的版本应当像一个只能追加的账本,一旦发布就不能修改。这么做的好处是:
-
每一份运行状态的代码都可以在对应的发布和构建阶段找到它的来源,这是实现重新发布、故障实例的自动替换、发布出错后的版本回退等机制的基础。
-
运行步骤非常简单,这样在硬件重启、实例故障和横向扩展等情况下,应用可以简单和快速的实现重启。