12要素原则是一种构建可扩展、高性能、高健壮性应用的方法论或原则。12要素原则天然地适用于微服务,并且随着微服务的发展,这些原则也变得越来越流行。
代码库:一份基准代码,多个部署
每一个应用都应该拥有自己的代码库(repo); 应该避免不同版本使用不同的代码库的情况。当然,一个代码库可以有多个分支(branch)。也就是说,同一个应用的多个部署环境应该使用一个代码库。
同时,多个应用共享一个代码库的情况也应当避免。这种情况下,应该把其它应用调整为主应用的动态链接库(shared libraries)。
这里的“部署”指的是应用运行的实例,如生产实例、预发布实例、QA实例。而且,每个开发人员都应该在自己的开发环境里运行一个应用的实例,这个开发实例也是一个部署。
当然,一个应用的不同版本(版本指的是代码变更,这个变更在一个版本里可见,在另一个版本里不可见)可以同时存在在不同的部署里。
微服务的情况下,每个服务都应该有自己的基准代码。各微服务使用独立的代码库将有助于简化应用的CI/CD流程。
另外,不建议在不同的应用之间共享代码。如果需要共享,可以考虑打包为动态链接库并且作为依赖,通过Maven的package repository进行管理。
依赖:显式声明依赖并隔离依赖关系
应该使用依赖管理工具来管理外部依赖,而不是直接把被依赖的代码添加到使用依赖的应用的代码库中。Java语言中使用Gradle作为依赖管理工具,在build.gradle文件中描述所有的依赖,然后在应用打包时打包工具会从maven库或其它的库中下载所有被提及到的依赖。
另外,我们也需要充分考虑对操作系统或执行环境的依赖。
微服务的情况下,所有应用包都通过包管理器,如sbt, maven,进行管理。如果应用不用容器封装,可以使用配置管理工具,如Chef, Ansible等,来安装系统级的依赖;如果使用容器,需要在dockerfile里声明。
配置:在环境中保存配置
同一应用的不同部署中变化的东西,都可以认为是配置。配置包括:
不应该直接把配置项作为常量写到代码中。这将直接违反12要素应用原则。
建议将这些配置项保存到环境变量中。
12要素应用原则严格分离代码和配置。同一个应用无论部署在哪里,代码都必须是相同的。
微服务的情况下,配置独立于应用之外。可以通过代码控制工具git(spring-cloud-config)来管理配置项,使用环境变量保存敏感信息,以免这些信息保存在git代码库中。
后端服务:把后端服务作为附属资源
后端服务是指当前应用正常操作时通过网络所用到的服务或应用,如数据库,消息中间件、或者当前应用与之通信的其它外部系统。
12要素应用可以在不修改代码的前提下自动切换后端服务,如把数据库服务从PostgreSQL切换到MySQL。切换后端服务不需要修改代码,只需要进行配置变更就可以了。
微服务应用的生态中,服务以外的任何事物都被当作附属资源,该资源可以在任意时点被切换,而不影响服务本身。
基于接口的编程允许你切换后端服务,而不影响系统使用;基于插件开发应用有助于应用支持多个后端服务。
构建、发布、运行:严格区分构建与运行
应用必须对构建、发布、运行的不同阶段进行严格区分:
微服务的情况下,一般采用CI/CD工具自动化构建与部署过程,容器镜像使得构建、发布与运行的过程更加高效。
进程:通过一个或多个无状态进程运行应用
应用在执行环境中作为进程来运行。一个应用可能有一个或多个实例或进程。
根据12要素应用原则,应用不应该在内存中存储任何数据,应用中的数据应当保存在持久存储中。至于应用的状态,应用应该将状态保存在数据库中,而不是临时保存在进程的内存中。
避免使用Sticky会话。如果需要保存会话系统,应用应当基于实际需求选择使用redis、memcache或其它缓存系统来保存该数据。
通过遵守该原则,应用可以在不影响系统使用的情况下进行横向扩展。
微服务的情况下,通过使用REST架构风格的无状态特性,你的应用微服务可以在不影响服务运行的情况下按需进行横向扩展。如果应用系统需要保存在状态或会话,不要直接保存在运行的应用中,而应该保存在附属资源中,如redis、memcache、datastore中。
(未完待续)