这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

12因素应用

云原生的12因素

1 - 12因素应用概述

云原生的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 - 基准代码

云原生12 factor之基准代码(Codebase)

官方介绍

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 - 依赖

云原生12 factor之依赖(Dependencies)

官方介绍

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 - 配置

云原生12 factor之配置(Config)

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 环境。这种方法无法轻易扩展:更多部署意味着更多新的环境,例如 stagingqa 。 随着项目的不断深入,开发人员可能还会添加他们自己的环境,比如 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一起使用,从而创建安全的秘密存储。