Codebase
- 一个
codebase
,代码基线,是任意多个repo共享的一个root commit, 也就是说,无论当前处于什么状态,它们在某一个时间点,“是相同的”。
- 如果一个app包含多个
codebase
,那么它不应该是一个app,而是一个分布式系统 - 分布式系统中每一个独立的组件都是一个app,每一个都独立遵循12factor理论。
- 如果说多个app都依赖于某一个
codebase
,它们不应该是多个app,而应该通过第二章节的依赖管理来把它们共用的部分变为一个依赖。
对于一个app,它当前的所有版本始终有相同的codebase
,但是可以有很多的deploy - app当前运行的实例。 比如线上环境,每个开发者自己的环境启动的app等等。
依赖管理
遵循 12factor理论的app应该从不隐式依赖任何系统工具、包。 应该显式的声明依赖,比如node项目的npm,ruby项目的rubygems等等。 同时,每一个app的依赖都应该是相互隔离的
,一个系统中对某一个包肯定有不同版本的依赖,它们不能互相影响彼此的app。
依赖声明以及依赖隔离是任何一个遵循 12factor理论的app要同时满足的。
配置
- 配置基本上每个deploy都不一样,代码可能是一样的。 需要严格区分代码以及配置。判断是否正确的区分的一个简单方法是看该应用的基准代码在任何时候都可以开源而不会暴露任何敏感信息。
- 12factor理论建议把配置放在环境变量中,这个可以非常方便的在不同系统移植
- 12factor理论建议把配置文件分组,比如简单的分为开发环境、生产环境后期可能因为开发人员的增加而增加大量的类似配置。 12factor应用中,分组的粒度应该足够小,且相互独立。 它们永远不会组合成一个所谓的“环境”,而是独立存在于每一个系统中。
后端服务
- 把后端服务当作资源。比如数据库MySQL,消息队列RabbitMQ,SMTP服务器,缓存系统redis等等,当然还有项目自身的代码以及依赖的第三方库或者系统。
- 12factor应用不会区别对待本地或第三方服务,这意味着可以在不改变代码的情况下把本地的MySQL迁移到第三方提供的数据库比如Amazon RDS或者说把本地的redis切换到阿里巴巴的redis上,仅仅需要修改配置中的资源。
- 每个后端服务是一项资源,一个MySQL数据库是一个资源,2个不同的MySQL被视为2个不同的资源。 12factor应用将这些视为附属资源,资源与它们附属的部署环境松耦合。更改资源不应该需要修改代码。
构建,发布和运行
一份基线代码转化为一个dploy需要精力个步骤:
- build,构建。 即将某次提交时的代码转化一个可执行包的过程,该过程会将会获取和打包依赖项,编译成二进制文件和资源文件。
- release,发布。 将构建过程得到的包结合当前deploy的配置组合起来,形成一个在执行环境可以立即执行包
- run,运行。 运行当前deploy
12factor应用严格遵守上述规格,无法在运行时立即更改代码并且使之生效。
每次构建都应该有一个唯一ID,无论是使用时间戳生成还是使用增长ID。 一次构建过后有任何新的改变必须重新创建一个新的构建。
发布新代码时构建应该由开发者发起,但是在运行阶段,应该能够在系统重启或者崩溃的进程重启时自动运行。 所以,运行阶段模块应该尽可能少,而构建阶段应该复杂点已使得错误可以被开发人员捕捉到。
进程
已一个或多个无状态进程运行应用。
12factor应用是无状态和不共享任何东西的。 任何需要持久化的需要存储在一个有状态的后端服务上,比如数据库。
许多打包工具使用文件系统来缓存编译过的文件, 12factor应用更倾向于在构建步骤做这个动作。
应用也不应该将某些数据保存在进程内存中 - 防止进程崩溃,比如登录信息。 而应该使用redis之类带有过期时间的缓存系统。
端口绑定
web应用偶尔会依赖容器执行,比如tomcat之类的。 12factor应用应该是可以完全自我加载的,通过使用依赖的方式解决容器的问题,比如jetty。 互联网应用通过端口绑定来提供服务,并监听该端口的请求。
当然,这个端口不一定是处理HTTP请求,Redis之类的也完全可以。同时,一个app也可以作为其他app的后端系统 - 通过提供相应的URL当作资源以备调用。
并发
12factor应用中,进程是一等公民。使用这个模型,开发者可以为应用的不同类型的工作交给不同的进程类型。比如,HTTP请求交给web进程,而常驻的后台工作则有worker进程负责。 应用程序必须可以在多台物理机上跨进程工作,这个可以解决单击的性能瓶颈等等问题。
12factor应用不需要守护进程或者写入PID文件,相反,依赖操作系统的进程管理器,比如systemmd,或者类似foreman的工具来管理输出流。
易处理(Disposability)
12factor应用是易处理的,即可以瞬间停止或者重启。这有利于快速、弹性的伸缩应用,迅速部署变化的代码或者配置,鲁棒性的部署应用。
进程应该追求最小启动时间,比如桥下命令到接收请求只须几秒时间。
进程一旦接收到SIGTERM
信号就应该优雅的停止。 比如关闭监听的端口等。 对于worker进程来说,优雅终止指将当前任务退回到队列。 比如RobbitMQ会发送NACK
信号。 有锁的系统则需要确保释放了锁。
进程能够处理意外的”死亡“,比如硬件的问题等等。一种推荐的方式是使用一个健壮的后端队列,比如Beanstalkd, 他在客户端断开连接或超时后自动退回任务。 12factor应用应该设计的能够应对意外的、突然的终止。
开发、线上环境
尽可能的缩小开发环境与线上环境的差异。这个包括以下几个方面:
- 每次部署间隔。 12factor应用建议几个小时
- 缩小人员差异。 开发人员编写代码并且还要参与部署以及线上测试
- 缩小工具差异。 包括数据库、缓存等等
日志
把日志当作事件流,日志汇总了应用程序运行中的按时间的输出流。
12factor应用本身不考虑自己的输出流。不应该试图去写或者管理日志文件。相反,每一个应用进程都会有一个STDOUT,开发环境中,开发人员可以根据此实时看到终端状态。 在预发布或者生产环境中则通过环境变量等拦截、整理日志。