1 - 12因素应用概述
介绍
12-Factors 经常被翻译为12因素,符合12因素的应用则称为12因素应用。
背景
Heroku(HeroKu于2009年推出公有云PaaS)于2012年提出12因素,告诉开发者如何利用云平台提供的便利来开发更具可靠性和扩展性、更加易于维护的云原生应用。
为此还有一个专门的12因素网站: https://12factor.net/ ,以及这个网站内容的中文版本: https://12factor.net/zh_cn/ 。
援引12因素网站的介绍内容:
如今,软件通常会作为服务来交付,被称为web app,或软件即服务(SaaS)。12-Factor 为构建如下的 SaaS 应用提供了方法论:
- 使用声明式格式来搭建自动化,从而使新的开发者花费最少的学习成本加入这个项目
- 和底层操作系统保持简洁的契约,在各个系统中提供最大的可移植性
- 适合在现代的云平台上部署,避免对服务器和系统管理的额外需求
- 最小化开发和生产之间的分歧,实现持续部署以实现最大灵活性
- 可以在工具、架构和开发实践不发生重大变化的前提下实现扩展
12因素理论适用于以任意语言编写,并使用任意后端服务(数据库、消息队列、缓存等)的应用程序。
援引12因素网站的给出的12因素产生的背景:
本文的贡献者参与过数以百计的应用程序的开发和部署,并通过 Heroku 平台间接见证了数十万应用程序的开发,运作以及扩展的过程。
本文综合了我们关于 SaaS 应用几乎所有的经验和智慧,是开发此类应用的理想实践标准,并特别关注于应用程序如何保持良性成长,开发者之间如何进行有效的代码协作,以及如何 避免软件污染 。
我们的初衷是分享在现代软件开发过程中发现的一些系统性问题,并加深对这些问题的认识。我们提供了讨论这些问题时所需的共享词汇,同时使用相关术语给出一套针对这些问题的广义解决方案。
12因素的局限性
12因素创作于2012年左右,SaaS平台非常具有指导意义
12-factor为Web应用程序或SaaS平台的建立非常有用的指导原则,但它在有些地方并不适合微服务体系。
在标准的12个要素以外,还存在哪些因素, 正如Kevin Hoffmann 最近在 O’Reilly 出版的书上提到了超越 12 个因素的应用。
内容
12因素具体内容为:
Factor | 描述 |
---|---|
Codebase 基准代码 |
One codebase tracked in revision control, many deploys 一份基准代码,多份部署 |
Dependencies 依赖 |
Explicitly declare and isolate dependencies 显式声明依赖关系 |
Config 配置 |
Store config in the environment 在环境中存储配置 |
Backing services 后端服务 |
Treat backing services as attached resources 把后端服务当作附加资源 |
Build, release, run 构建,发布,运行 |
Strictly separate build and run stages 严格分离构建和运行 |
Processes 进程 |
Execute the app as one or more stateless processes 以一个或多个无状态进程运行应用 |
Port binding 端口绑定 |
Export services via port binding 通过端口绑定提供服务 |
Concurrency 并发 |
Scale out via the process model 通过进程模型进行扩展 |
Disposability 易处理 |
Maximize robustness with fast startup and graceful shutdown 快速启动和优雅终止可最大化健壮性 |
Dev/prod parity 开发环境与线上环境等价 |
Keep development, staging, and production as similar as possible 尽可能的保持开发,预发布,线上环境相同 |
Logs 日志 |
Treat logs as event streams 把日志当作事件流 |
Admin processes 管理进程 |
Run admin/management tasks as one-off processes 后台管理任务当作一次性进程运行 |
12因素在云原生架构中的体现:
2 - 基准代码
官方介绍
Codebase
One codebase tracked in revision control, many deploys
基准代码
一份基准代码(Codebase),多份部署(deploy)
12-Factor应用(译者注:应该是说一个使用本文概念来设计的应用,下同)通常会使用版本控制系统加以管理,如Git, Mercurial, Subversion。一份用来跟踪代码所有修订版本的数据库被称作 代码库(code repository, code repo, repo)。
在类似 SVN 这样的集中式版本控制系统中,基准代码 就是指控制系统中的这一份代码库;而在 Git 那样的分布式版本控制系统中,基准代码 则是指最上游的那份代码库。
基准代码和应用之间总是保持一一对应的关系:
- 一旦有多个基准代码,就不能称为一个应用,而是一个分布式系统。分布式系统中的每一个组件都是一个应用,每一个应用可以分别使用 12-Factor 进行开发。
- 多个应用共享一份基准代码是有悖于 12-Factor 原则的。解决方案是将共享的代码拆分为独立的类库,然后使用 依赖管理 策略去加载它们。
尽管每个应用只对应一份基准代码,但可以同时存在多份部署。每份 部署 相当于运行了一个应用的实例。通常会有一个生产环境,一个或多个预发布环境。此外,每个开发人员都会在自己本地环境运行一个应用实例,这些都相当于一份部署。
所有部署的基准代码相同,但每份部署可以使用其不同的版本。比如,开发人员可能有一些提交还没有同步至预发布环境;预发布环境也有一些提交没有同步至生产环境。但它们都共享一份基准代码,我们就认为它们只是相同应用的不同部署而已。
12-factor apps using Kubernetes
12 Factor App的原则1是“版本控制中的一个代码基准,许多部署”。
对于Kubernetes应用程序,这个原则实际上嵌入了容器编排本身的本质。通常,您使用源控制存储库(如git repo)创建代码,然后在Docker Hub中存储特定版本的映像。当您将要编排的容器定义为Kubernetes Pod,Deployment,DaemonSet的一部分时,您还可以指定镜像的特定版本,如:
...
spec:
containers:
- name:AcctApp
image:acctApp:v3
...
通过这种方式,您可以在不同的部署中运行多个版本的应用程序。
应用程序的行为也可能有所不同,具体取决于它们运行的配置信息。
3 - 依赖
官方介绍
Explicitly declare and isolate dependencies 显式声明依赖关系
大多数编程语言都会提供一个打包系统,用来为各个类库提供打包服务,就像 Perl 的 CPAN 或是 Ruby 的 Rubygems 。通过打包系统安装的类库可以是系统级的(称之为 “site packages”),或仅供某个应用程序使用,部署在相应的目录中(称之为 “vendoring” 或 “bunding”)。
12-Factor规则下的应用程序不会隐式依赖系统级的类库。 它一定通过 依赖清单 ,确切地声明所有依赖项。此外,在运行过程中通过 依赖隔离 工具来确保程序不会调用系统中存在但清单中未声明的依赖项。这一做法会统一应用到生产和开发环境。
例如, Ruby 的 Bundler 使用 Gemfile
作为依赖项声明清单,使用 bundle exec
来进行依赖隔离。Python 中则可分别使用两种工具 – Pip 用作依赖声明, Virtualenv 用作依赖隔离。甚至 C 语言也有类似工具, Autoconf 用作依赖声明,静态链接库用作依赖隔离。无论用什么工具,依赖声明和依赖隔离必须一起使用,否则无法满足 12-Factor 规范。
显式声明依赖的优点之一是为新进开发者简化了环境配置流程。新进开发者可以检出应用程序的基准代码,安装编程语言环境和它对应的依赖管理工具,只需通过一个 构建命令 来安装所有的依赖项,即可开始工作。例如,Ruby/Bundler 下使用 bundle install
,而 Clojure/Leiningen 则是 lein deps
。
12-Factor 应用同样不会隐式依赖某些系统工具,如 ImageMagick 或是curl
。即使这些工具存在于几乎所有系统,但终究无法保证所有未来的系统都能支持应用顺利运行,或是能够和应用兼容。如果应用必须使用到某些系统工具,那么这些工具应该被包含在应用之中。
12-factor apps using Kubernetes
12 Factor App的原则2是“明确声明和隔离依赖”。
确保满足应用程序的依赖性是实际假设的。对于12因子应用程序,其中不仅包括确保特定于应用程序的库,而且还包括不依赖于操作系统,并假设系统库(如curl)将存在。12因素应用程序必须是自包含的。
这包括确保应用程序足够孤立,以免受主机上可能安装的冲突库的影响。
幸运的是,如果应用程序确实具有任何特定或不寻常的系统要求,则容器可以轻松满足这两个要求; 容器包括应用程序所依赖的所有依赖项,并且还提供容器运行的合理隔离环境。(与普遍看法相反,容器环境并非完全孤立,但在大多数情况下,它们都足够好。)
对于模块化并依赖于其他组件的应用程序,例如HTTP服务和日志提取程序,Kubernetes提供了一种将所有这些部分组合到单个Pod中的方法,用于适当地封装这些部分的环境。
4 - 配置
Store config in the environment 在环境中存储配置
官方介绍
在环境中存储配置
通常,应用的 配置 在不同 部署 (预发布、生产环境、开发环境等等)间会有很大差异。这其中包括:
- 数据库,Memcached,以及其他 后端服务 的配置
- 第三方服务的证书,如 Amazon S3、Twitter 等
- 每份部署特有的配置,如域名等
有些应用在代码中使用常量保存配置,这与 12-Factor 所要求的代码和配置严格分离显然大相径庭。配置文件在各部署间存在大幅差异,代码却完全一致。
判断一个应用是否正确地将配置排除在代码之外,一个简单的方法是看该应用的基准代码是否可以立刻开源,而不用担心会暴露任何敏感的信息。
需要指出的是,这里定义的“配置”并不包括应用的内部配置,比如 Rails 的 config/routes.rb
,或是使用 Spring 时 代码模块间的依赖注入关系 。这类配置在不同部署间不存在差异,所以应该写入代码。
另外一个解决方法是使用配置文件,但不把它们纳入版本控制系统,就像 Rails 的 config/database.yml
。这相对于在代码中使用常量已经是长足进步,但仍然有缺点:总是会不小心将配置文件签入了代码库;配置文件的可能会分散在不同的目录,并有着不同的格式,这让找出一个地方来统一管理所有配置变的不太现实。更糟的是,这些格式通常是语言或框架特定的。
12-Factor推荐将应用的配置存储于 环境变量 中( env vars, env )。环境变量可以非常方便地在不同的部署间做修改,却不动一行代码;与配置文件不同,不小心把它们签入代码库的概率微乎其微;与一些传统的解决配置问题的机制(比如 Java 的属性配置文件)相比,环境变量与语言和系统无关。
配置管理的另一个方面是分组。有时应用会将配置按照特定部署进行分组(或叫做“环境”),例如Rails中的 development
,test
, 和 production
环境。这种方法无法轻易扩展:更多部署意味着更多新的环境,例如 staging
或 qa
。 随着项目的不断深入,开发人员可能还会添加他们自己的环境,比如 joes-staging
,这将导致各种配置组合的激增,从而给管理部署增加了很多不确定因素。
12-Factor 应用中,环境变量的粒度要足够小,且相对独立。它们永远也不会组合成一个所谓的“环境”,而是独立存在于每个部署之中。当应用程序不断扩展,需要更多种类的部署时,这种配置管理方式能够做到平滑过渡。
12-factor apps using Kubernetes
12 Factor App的原则3是“在环境中存储配置”。
这个原则背后的想法是应用程序应完全独立于其配置。换句话说,您应该能够将其移动到另一个环境而无需触摸源代码。
一些开发人员通过创建某种配置文件来实现此目标,指定详细信息,例如目录,主机名和数据库凭据。这是一种改进,但它确实存在某人将配置文件检入源控制存储库的风险。
相反,12个因素应用程序将其配置存储为环境变量; 正如宣言所说,这些“不太可能被意外地检入存储库”,并且它们与操作系统无关。
Kubernetes允许您通过Downward API在清单中指定环境变量,但是由于这些清单本身会在源代码控制中进行检查,这不是一个完整的解决方案。
相反,您可以指定环境变量应该由Kubernetes ConfigMaps或Secrets的内容填充,这些内容可以与应用程序分开。例如,您可以将Pod定义为:
apiVersion:v1
kind:Pod
元数据:
name:secret-env-pod
spec:
containers:
- name:mycontainer
image:redis
env: - name:SECRET_USERNAME valueFrom:secretKeyRef : name:mysecret key:username - name:SECRET_PASSWORD valueFrom:secretKeyRef : name:mysecret key:password - name:CONFIG_VERSION valueFrom: configMapKeyRef: name:redis-app-config key:version.id
如您所见,此Pod接收三个环境变量SECRET_USERNAME,SECRET_PASSWORD和CONFIG_VERSION,前两个来自引用的Kubernetes Secrets,第三个来自Kubernetes ConfigMap。这使您可以使它们远离配置文件。
当然,仍然存在某人错误处理用于创建这些对象的文件的风险,但是它们在一起并制定安全处理策略,而不是清除分散在部署周围的数十个配置文件。
更重要的是,社区中有些人指出,即使是环境变量也不一定是出于自身原因的安全。例如,如果应用程序崩溃,它可能会将所有环境变量保存到日志中,甚至将它们传输到另一个服务。DiogoMónica指向一个名为Keywhiz的工具,您可以将其与Kubernetes一起使用,从而创建安全的秘密存储。