SaaS应用开发的12个要素(saas 开发)

软件开发作为一种职业以来,从传奇极客、商业化、工程化、大型化,在发展过程中涌现了许多的理论、方法。人们通常将软件工程与建筑工程相比,二者的确有一些相似支持,不同的是建筑工程的理论实践已经趋于成熟,而软件工程仍在飞速发展。

SaaS应用开发的12个要素(saas 开发)

塔科马海峡吊桥

本文为读者介绍一种被称为“12要素应用程序(12factor.net)”的实践方法论。“十二要素应用程序”是一种构建在云中运行并作为服务交付的分布式应用程序的方法。该方法是由Heroku的联合创始人Adam Wiggins于2012年提出的,Heroku是一种平台即服务,现在是Salesforce.com的一部分。Wiggin的目标是综合在Heroku上部署应用程序的最佳实践,并为刚接触云的开发人员提供一个框架来讨论原生云应用程序的挑战。

为什么12要素值得借鉴

SaaS应用开发的12个要素(saas 开发)

Heroku Logo

“12要素应用程序”的贡献者Adam Wiggins参与过数以百计的应用程序的开发和部署,并通过Heroku平台间接见证了数十万应用程序的开发,运作以及扩展的过程。这套实践经验综合了Heroku关于 SaaS应用几乎所有的经验和智慧,是开发此类应用的理想实践标准,并特别关注于应用程序如何保持良性成长,开发者之间如何进行有效的代码协作,以及如何避免软件污染。Wiggin的初衷是分享在现代软件开发过程中发现的一些系统性问题,并加深对这些问题的认识。

12要素概览

SaaS应用开发的12个要素(saas 开发)

12 Factor App Principles

软件通常会作为一种服务来交付,称为网络应用程序或软件即服务(SaaS)。“12Factor”理论为构建如下特点的SaaS应用提供了方法论:

  • 使用标准化流程自动配置,从而使新的开发者花费最少的学习成本加入这个项目;
  • 和操作系统之间尽可能地划清界限,在各个系统中提供最大的可移植性
  • 适合部署在现代的云计算平台,从而在服务器和系统管理方面节省资源
  • 开发环境和生产环境的差异降至最低,并使用持续交付实施敏捷开发;
  • 可以在工具、架构和开发流程不发生明显变化的前提下实现扩展

这套理论适用于任意语言和后端服务(数据库、消息队列、缓存等)开发的应用程序。

对于应用上云的PaaS提供商Heroku,关注的是应用服务如何在其Platform上运行的更好,因此要理解这些要素,我们先得搞清楚一个服务是怎么在Platform上跑起来的,简化的流程如下图所示:

SaaS应用开发的12个要素(saas 开发)

The design, build, release, run cycle

我们以标准的Kubernetes平台为例展开来看:一个典型的容器化后端服务,从开发到上线需要经历哪些工作。

  • 设计阶段:需求分析和领域设计、技术选型确定依赖的框架和组件、建立项目框架;
  • 开发阶段:开发、测试、代码评审,迭代到可发布的版本;
  • 镜像仓库:为服务编写Dockerfile,构建出服务的容器镜像;
  • 容器编排:Kubernetes编排文件编写,确定非产线环境部署运维阶段的各项细节;
  • 外部依赖:确认基础设施容量及第三方组件(数据库等)符合条件并初始化(ddl等);
  • 更新配置:在配置中心创建或更新配置文件,配置参数和密钥等;
  • 持续集成:创建或更新持续集成、交付系统的pipeline,并执行部署到测试环境;
  • 入口配置:配置测试环境的访问入口,如反向代理的路由、域名等;
  • 系统监控:日志、监控、告警、链路追踪等相关组件接入;
  • 集成测试:在测试环境进行完整的功能测试、性能测试,并在预生产、生产环境重复;
  • 迭代开发:重复以上过程迭代功能;
  • 灰度发布:每次迭代灰度发布,逐步放开新版本的流量;

可以看出后端系统初次上线、后续迭代的流程已经比较复杂。如果单靠人力,单体系统勉强可以应付,单体系统即使变成"大泥球",也大多处于人力可控的范围内。但随着复杂度进一步提升,整个系统演化成微服务系统,往往包含十几个、数十个、甚至成百上千个子服务,多个服务之间还有依赖关系,这其中的技术挑战是显而易见的。

如何在复杂情况下,尽量提高效率、减少错误呢?答案就是,在设计和开发阶段去迎合云平台以及整个生态的能力,从一开始就要做一个适合在云上跑的服务

“12要素”给了我们一把衡量“是否适合上云”的标尺。如果不遵循这些“要素”,不适应云平台提供的能力,随着服务规模增大、业务复杂度进一步提高,就非常容易引发问题。这也是“12要素”出现的背景和原因。了解这些之后,其内容就更好理解了。下面我们一起来看看这12条方法论每一条分别是什么。

一、代码库(一份基准代码,多份部署)

SaaS应用开发的12个要素(saas 开发)

One codebase tracked in revision control, many deploys

每个应用程序都应具有自己的基准“代码库”,避免不同版本多个“代码库”(分支是允许的)。即对于所有部署环境,应该只有一个存储库,而不应该有多个。在微服务中,每个服务应具有自己的“代码库”。拥有独立的代码库可帮助你简化应用程序的CI/CD流程。

从“12要素”看待应用程序,部署意味着该应用程序的实例运行。通常会有生产环境、一个或多个预发布环境。此外,每个开发人员都有在其本地开发环境中运行的该应用程序的一个副本,每个副本也都具有以下条件:可部署。

所有部署的代码库相同,但每份部署可以使用其不同的版本。比如,开发人员可能有一些提交还没有同步至预发布环境;预发布环境也有一些提交没有同步至生产环境。但它们都共享一个代码库。

“12要素”主张不要在应用程序之间共享代码(比如某个公共类在不同的“代码库”中存在多个副本)。如果需要共享,则需要构建一个库并将其作为依赖项,并通过像Maven这样的软件包管理工具进行管理。

二、依赖关系(显式声明并隔离依赖关系)

SaaS应用开发的12个要素(saas 开发)

Explicitly declare and isolate dependencies

大多数编程语言都会提供一个打包系统,用来为各个类库提供打包服务,就像Java中的Maven。通过打包系统安装的类库可以是系统级的,称之为“site packages”,或仅供某个应用程序使用,部署在相应的目录中,称之为“vendoring”或“bunding”。

应用程序不应该隐式依赖系统级的类库。 它一定通过依赖清单,确切地声明所有依赖项。此外,在运行过程中通过依赖隔离工具来确保程序不会调用系统中存在但清单中未声明的依赖项。并将这一做法统一应用到生产和开发环境。

例如,Python中使用pip用作依赖管理,virtualenv用作依赖隔离。C语言Autoconf用作依赖声明,静态链接库用作依赖隔离。Java使用Gradle用作依赖管理,Dockerfile用作依赖隔离。无论用什么工具,依赖声明和依赖隔离必须一起使用(在非容器化环境中,你可以使用chef,ansible等配置管理工具来安装系统级依赖项)。

显式声明依赖的优点之一是为新加入的开发者简化了环境配置流程。新进开发者可以检出应用程序的基准代码,安装编程语言环境和它对应的依赖管理工具,只需通过一个构建命令来安装所有的依赖项,即可开始工作。

应用同样不应该隐式依赖某些系统工具,如ImageMagick或是curl。即使这些工具存在于几乎所有系统,但终究无法保证所有未来的系统都能支持应用顺利运行,或是能够和应用兼容。如果应用必须使用到某些系统工具,那么这些工具应该被包含在应用之中。

三、配置(在环境中存储配置)

SaaS应用开发的12个要素(saas 开发)

Store config in the environment

通常应用的配置在不同环境(预发布、生产、开发等)间会有很大差异。这其中包括:

  • 数据库,Redis,以及其他服务地址的配置
  • 第三方服务的验证信息,如Amazon S3、Twitter
  • 每份部署特有的配置,如域名等

有些应用在代码中使用常量保存配置,这是完全错误的。虽然配置文件在各环境间存在差异,但是代码应该完全一致

判断一个应用是否正确地将配置排除在代码之外,一个简单的方法是看该应用的代码是否可以立刻开源,而不用担心会暴露任何敏感的信息。

需要指出的是,这里定义的“配置”并不包括应用的内部配置,比如Spring代码模块间的依赖注入关系 。这类配置在不同部署间不存在差异,所以应该写入代码。

另外一个解决方法是使用配置文件,但不把它们纳入版本控制系统,如Spring的application.yml。这相对于在代码中使用常量已经有进步,但仍然有缺点:比如不小心将配置文件推送到代码库;配置文件的可能会分散在不同的目录,不同应用的配置文件有着不同的格式,这让统一管理所有配置变的不太现实。更糟的是,这些格式通常是语言或框架特定的。

正确的做法是将应用的配置存储于“环境变量”中。环境变量可以非常方便地在不同的部署间做修改,却不动一行代码;与配置文件不同,不小心把它们签入代码库的概率微乎其微;与配置文件(比如Java的属性配置文件)相比,环境变量与语言和系统无关。

配置管理的另一个方面是分组。有时应用会将配置按照特定环境进行分组,例如Spring中的applicatoin-test.yaml,applicatoin-prod.yaml。这种方法无法轻易扩展:部署环境的变化以及随着项目的不断深入,开发人员可能还会添加他们自己的环境,这将导致各种配置组合的激增,从而给管理部署增加了很多不确定因素。

此外“环境变量”的粒度要足够小,且相对独立。它们永远独立存在于每个部署之中。当应用程序不断扩展,需要更多种类的部署时,这种配置管理方式能够做到平滑过渡。

四、后端服务(把后端服务当作附加资源)

后端服务是指程序运行所需要的通过网络调用的各种服务,如数据库(MySQL,CouchDB),消息/队列系统(RabbitMQ,Beanstalkd),SMTP邮件发送服务(Postfix),以及缓存系统(Redis)等。

SaaS应用开发的12个要素(saas 开发)

Treat backing services as attached resources

类似数据库的后端服务,通常由部署应用程序的系统管理员一起管理。除了本地服务之外,应用程序有可能使用了第三方发布和管理的服务。包括 SMTP(如Postmark),数据收集服务(如New Relic或Loggly),数据存储服务(如Amazon S3),以及使用API访问的服务(如Twitter, Google Maps, Last.fm)。

应用不应该区别对待本地或第三方服务。对应用程序而言,两种都是应用的“附加资源”,通过一个URL或是配置定位来获取数据。

应用的部署,都应该可以在不进行任何代码改动的情况下,将本地MySQL数据库换成第三方服务(例如Amazon RDS)。类似的,本地SMTP服务应该也可以和第三方SMTP服务(例如Postmark)互换。

每个不同的后端服务是一份资源 。例如MySQL数据库是一个资源,两个MySQL数据库(用来数据分区)就被当作是2个不同的资源。应用将这些数据库都视作附加资源,“应用”应该和这些附加资源的部署保持松耦合。

应用程序的部署可以按需加载或卸载资源。例如,如果应用的数据库服务由于硬件问题出现异常,管理员可以从最近的备份中恢复一个数据库,卸载当前的数据库,然后加载新的数据库,整个过程都不需要修改代码。

在应用开发的过程总,通过遵循基于接口的编程,实现动态变更组件而不会影响系统。比如通过工厂模式实现不同厂商的短信接口,应用通过配置加载不同厂商的发送组件。

五、构建,发布和运行(完全独立的构建和运行阶段)

SaaS应用开发的12个要素(saas 开发)

Strictly separate build and run stages

代码部署到运行环境需要以下三个阶段:

  • 构建阶段:将代码仓库转化为可执行包的过程。构建时会使用指定版本的代码,打包依赖项,编译成二进制文件和资源文件。
  • 发布阶段:将构建的结果和当前部署所需配置相结合,并能够立刻在运行环境中投入使用。
  • 运行阶段:指针对选定的发布版本,在执行环境中启动一系列应用程序进程。

“应用”应该严格区分构建,发布,运行这三个步骤。 举例来说,直接修改处于运行状态的代码是非常不可取的做法,因为这些修改很难再同步回构建步骤(你有没有在线上直接Debug改代码的经历呢๑> <๑)。

部署工具通常都提供了发布管理工具,最引人注目的功能是退回至较旧的发布版本。比如Capistrano将所有发布版本都存储在一个叫releases的子目录中,当前的在线版本只需映射至对应的目录即可。该工具的rollback命令可以很容易地实现回退版本的功能。

每一个发布版本必须对应一个唯一的发布ID,例如可以使用发布时的时间戳(2011-04-06-20:32:17),或是一个增长的数字(v100)。发布的版本就像一本只能追加的账本,一旦发布就不可修改,任何的变动都应该产生一个新的发布版本。

新的代码在部署之前,需要开发人员触发构建操作。但是运行阶段不一定需要人为触发,如服务器重启,或是进程管理器重启了一个崩溃的进程,可以自动进行。因此,运行阶段应该保持尽可能少的模块,这样假设半夜发生系统故障而开发人员又捉襟见肘也不会引起太大问题。构建阶段是可以相对复杂一些的,因为错误信息能够立刻展示在开发人员面前,从而得到妥善处理。

六、进程(通过一个或多个无状态进程运行应用程序)

SaaS应用开发的12个要素(saas 开发)

Execute the app as one or more stateless processes

运行环境中,应用程序通常是以一个和多个进程运行的。最简单的场景中,代码是一个独立的脚本,运行环境是开发人员自己的笔记本电脑,进程由一条命令启动(例如python my_script.py)。另一种情况是,复杂的应用可能会使用很多进程(或线程)。

应用的进程必须无状态且无共享。任何需要持久化的数据都要存储在后端服务内(如数据库)。

内存区域或磁盘空间可以作为进程在做某种事务型操作时的缓存,如下载一个很大的文件时暂存,然后将操作结果写入后端服务。应用不用考虑这些缓存的内容是不是可以保留给之后的请求来使用,此外先前保存的数据(内存或文件系统中)也会因为重启而丢失。

一些互联网系统依赖于 “粘性Session(sticky sessions)”, 指将用户session中的数据缓存至某进程的内存中,并将同一用户的后续请求路由到同一个进程。粘性session是原则上是不推荐使用的。Session中的数据应该保存在诸如Memcached或Redis这样的带有过期时间的缓存中。

很难对粘滞会话实现负载均衡,因为粘滞会话的均衡性不仅决定于负载均衡策略,还和会话本身的行为相关,例如,可能存在应用某些实例上的会话已经大量退出,而另一些实例上的会话依然处于活动状态,此时这两部分实例的负载处于不均衡状态,而负载均衡器无法将活动会话转移到空闲的应用实例。

七、端口绑定(通过端口绑定提供服务)

Export services via port binding.

你的应用程序应该独立运行,不依赖于平台提供等端口绑定功能。直接运行就会绑定到某个端口。比如SpringBoot应用通常内嵌Tomcat/undertow/jetty等Java Web容器,构建出的包直接运行就绑定了端口。

HTTP并不是唯一一个可以由端口绑定提供的服务。几乎所有服务器软件都可以通过进程绑定端口来等待请求。例如使用Redis协议的Redis。端口绑定这种方式也意味着一个应用可以成为另外一个应用的后端服务,调用方将服务方提供的相应URL当作资源存入配置以备将来调用。

反模式的例子:提供出去部署的包的是放到Tomcat的WAR、放到IIS的dll,自己本身没有描述通信协议,也没有指定绑定的端口,完全依赖Tomcat/IIS的配置。在这种模式下,一个应用出错会对同一个应用服务器上的其他应用造成影响,也无法针对单一应用做横向扩展。

八、并发(通过进程模型进行扩展)

SaaS应用开发的12个要素(saas 开发)

Scale out via the process model

任何计算机程序一旦启动就会生成一个或多个进程。互联网应用采用多种进程运行方式。例如,PHP进程作为Apache的子进程存在,随请求按需启动。Java进程则采取了相反的方式,在程序启动之初JVM就提供了一个超级进程储备了大量的系统资源(CPU 和内存),并通过多线程实现内部的并发管理。上述2个例子中,进程是开发人员可以操作的最小单位。

进程应该是一等公民。借鉴于Unix守护进程模型,开发人员可以运用这个模型去设计应用架构,将不同的工作分配给不同的进程类型。如HTTP请求可以交给Web进程来处理,而常驻的后台工作则交由Worker进程负责。

这并不包括个别较为特殊的进程,例如通过虚拟机的线程处理并发的内部运算,或是使用如EventMachine、Twisted、Node.js的异步/事件触发模型。一台独立的虚拟机的扩展有瓶颈(垂直扩展),所以应用程序必须可以在多台物理机器间跨进程工作。

上述进程模型会在系统急需扩展时大放异彩。由于应用的进程所具备的无共享,水平扩展特性,意味着添加并发会变得简单而稳妥。这些进程的类型以及每个类型中进程的数量被称作Process Formation

应用程序的进程不需要守护进程或是写入PID文件。而应该借助操作系统的进程管理器(systemd、分布式的进程管理云平台或是类似Foreman的工具)来管理输出流,响应崩溃的进程以及处理用户触发的重启和关闭进程的请求。

九、易处理(快速启动和优雅终止可最大程度提高健壮性)

Maximize robustness with fast startup and graceful shutdown.

应用的进程应该是易处理的,意思是说它们可以瞬间开启或停止。这有利于快速、弹性的伸缩应用,迅速部署变化的代码或配置。

进程应当追求最小启动时间。理想状态下,进程从敲下命令到真正启动并等待请求的时间应该只需很短的时间。更少的启动时间提供了更敏捷的发布以及扩展过程,此外还增加了健壮性,因为进程管理器可以轻松的将进程搬到新的物理机器上。

进程一旦接收终止信号(SIGTERM)就会优雅的终止。就网络进程而言,优雅终止是指停止监听服务的端口,即拒绝所有新的请求,并继续执行当前已接收的请求,然后退出。此类型的进程所隐含的要求是HTTP请求大多都很短(不会超过几秒钟),而在长轮询中,客户端在丢失连接后应该马上尝试重连。

对于Worker进程来说,优雅终止是指将当前任务退回队列。例如RabbitMQ 中,Worker可以发送一个NACK信号。Beanstalkd中任务终止并退回队列会在Worker断开时自动触发。有锁机制的系统诸如Delayed Job则需要确定释放了系统资源。此类型的进程所隐含的要求是“任务都应该可重复执行”,这主要由将结果包装进事务或是使重复操作幂等来实现。

进程还应当在面对突然终止时保持健壮,例如底层硬件故障。虽然这种情况比起优雅终止来说少之又少,但终究有可能发生。一种推荐的方式是使用一个健壮的后端队列,例如Beanstalkd,它可以在客户端断开或超时后自动退回任务。无论如何,应用都应该设计成能够应对意外的和不优雅终结。“Crash-only design”将这种概念转化为了合乎逻辑的理论。

更少的启动和停止时间意味着调度时间花费会更少, 可以提升调度性能。这对于任何调度系统都是一样的. 比如地铁调度系统, 如果每辆车的进站和出站时间越短, 那么单条线路上能容纳的最大车辆数就会提升。同样, 如果进程启动和退出的越迅速,Kubernetes调度也会越迅速, 调度能力也会更高, 达到调度目标的耗时也会更短 (比如系统遇到了流量洪峰, 需要紧急扩容1000容器, 在10秒内启动1000容器和在10分钟内启动1000容器, 这种差距能直接决定现有业务会不会被流量压垮)。

十、开发环境与线上环境等价(尽可能的保持开发、预发布、线上环境相同)

Keep development, staging, and production as similar as possible

从以往经验来看,开发环境和线上环境之间存在着很多差异,这些差异表现在以下三个方面:

  • 时间差异:开发人员正在编写的代码可能需要几天,几周,甚至几个月才会上线;
  • 人员差异:开发人员编写代码,运维人员部署代码;
  • 工具差异:开发人员或许使用Nginx、SQLite、OS X,而线上环境使用Apache,MySQL 以及Linux;

应用想要做到持续部署就必须缩小本地与线上差异。再回头看上面所描述的三个差异:

  • 缩小时间差异:开发人员可以几小时,甚至几分钟就部署代码;
  • 缩小人员差异:开发人员不只要编写代码,更应参与部署过程以及代码在线上的表现;
  • 缩小工具差异:尽量保证开发环境以及线上环境的一致性;

将上述总结变为一个表格如下:

传统应用

12要素应用

每次部署间隔

数周

几小时

开发人员 vs 运维人员

不同的人

相同的人

开发环境 vs 线上环境

不同

尽量接近

“后端服务”是保持开发与线上等价的重要部分,例如数据库,队列系统,以及缓存。开发人员有时会觉得在本地环境中使用轻量的后端服务具有很强的吸引力,而那些更重量级的后端服务应该使用在生产环境。例如本地使用SQLite线上使用PostgreSQL;又如本地缓存在进程内存中而线上存入Memcached。

我们应该反对在不同环境间使用不同的后端服务,即使驱动适配器已经可以几乎消除使用上的差异。这是因为不同的后端服务意味着会突然出现不兼容,从而导致测试、预发布都正常的代码在线上出现问题。这些错误会给持续部署带来阻力。从应用程序的生命周期来看消除这种阻力需要花费很大的代价。

与此同时借助于Homebrew,apt-get等现代的打包系统,诸如Memcached、PostgreSQL、RabbitMQ等后端服务的安装与运行也并不复杂。此外使用类似Chef和Puppet的声明式配置工具,结合像Vagrant这样轻量的虚拟环境就可以使得开发人员的本地环境与线上环境无限接近。与同步环境和持续部署所带来的益处相比,安装这些系统显然是值得的。

十一、日志(把日志当作事件流)

Treat logs as event streams

日志使得应用程序运行的过程可被追踪。在基于服务器的环境中,日志通常被写在硬盘的一个文件里。日志应该是事件流的汇总,将所有运行中进程和后端服务的输出流按照时间顺序收集起来。尽管在回溯问题时可能需要看很多行,但日志最原始的格式确实是一个事件一行。日志没有确定开始和结束,但随着应用的运行会持续的增加。

应用本身不应该考虑存储自己的输出流。不应该试图去写或者管理日志文件。相反,每一个运行的进程都会直接使用标准输出(stdout)。开发环境中,开发人员可以通过这些数据流,实时在终端看到应用的活动。

在预发布或线上部署中,每个进程的输出流由运行环境截获,并将其他输出流整理在一起,然后一并发送给一个或多个最终的处理程序,用于查看或是长期存档。这些存档路径对于应用来说不可见也不可配置,而是完全交给程序的运行环境管理。类似Logplex和Fluentd的开源工具可以达到这个目的。

这些事件流可以输出至文件,或者在终端实时观察。最重要的,输出流可以发送到Splunk这样的日志索引及分析系统,或Hadoop/Hive这样的通用数据存储系统。这些系统为查看应用的历史活动提供了强大而灵活的功能,包括:

  • 找出过去一段时间特殊的事件。
  • 图形化一个大规模的趋势,比如每分钟的请求量。
  • 根据用户定义的条件实时触发警报,比如每分钟的报错超过某个警戒线。

适合伸缩的平台是为了海量流量的大型业务而准备的, 而大型业务的日志自然也是海量的。试想一下线上有几万实例的业务, 现在想要寻找特定的日志, 这无异于大海捞针。而统一收集并处理业务, 就可以利用现有的大数据组件, 索引系统进行日志检索和处理。而更先进系统与日志的融合, 让日志自动化分析, 阈值处理与自动化调度都变为了可能。量变产生了质变,传统的日志处理模式已经完全不适合云服务了,只有这样组织的日志系统才能继续提供服务。

十二、管理进程(后台管理任务作为一次性进程运行)

Run admin/management tasks as one-off processes

除了应用用来处理常规业务(比如处理Web请求)所需的进程,开发人员经常希望执行一些管理或维护应用的一次性任务,这就是后台管理任务,例如:

  • 数据归档作业(manage.py migrate, rake db:migrate);
  • 运行一个控制台(REPL shell)来执行一些代码或是针对线上数据库做检查。大多数语言都通过解释器提供了一个REPL工具或是其他命令;
  • 运行代码仓库中的一次性脚本;

一次性管理进程应该和正常的常驻进程使用同样的环境。这些管理进程和任何其他的进程一样使用相同的代码和配置,基于某个发布版本运行。后台管理代码应该随其他应用程序代码一起发布,从而避免同步问题。

所有进程类型应该使用同样的依赖隔离技术。如果一个Python程序使用了Virtualenv,则需要在运行Tornado Web服务器和任何脚本进程时引入bin/python。

应该尽量选择那些提供了REPL shell的语言,因为那会让运行一次性脚本变得简单。在本地部署中,开发人员直接在命令行使用shell 命令调用一次性管理进程。在线上部署中,开发人员依旧可以使用ssh或是运行环境提供的其他机制来运行这样的进程。

我见过一个最有趣的例子是, 有一个同学需要清洗一批线上数据, 于是他编写了一个脚本, 然后将脚本扔到了线上在 terminal 中直接运行. 过了一会, 他下班了于是直接关闭了显示器, 让机器继续保持 session 继而脚本继续执行。结果第二天上班. 发现昨晚物业停电电脑关机了,数据非但没清洗完毕, 反而变成了只清洗了一半的更脏了的状态。也许你会说他应该用screen命令,但我想说的是, 如果这个一次性任务时长很长呢? 比如需要一周才能完成,这期间的进程管理该怎么办? 这就是这条守则存在的意义。更广义的来说, 一些定时任务也归为此列 (比如定期给用户发送邮件的业务), 定时任务同样需要纳入正常的流程来管理。这样才能保证这些业务具有与线上业务一致的可靠性与可管理性。

本文总结

  1. Codebase:基线代码库
  2. Dependencies:显式和隔离的依赖
  3. Configuration:配置分离存储到环境中
  4. Backing services:分离基础的后端组件
  5. Build, release, run:严格分离构建、发布、运行
  6. Processes:无状态的服务进程
  7. Port binding:自带端口绑定
  8. Concurrency:通过进程的水平扩展增大并发能力
  9. Disposability:易处置 – 快速启动和优雅退出
  10. Dev/prod parity:环境对等
  11. Log:日志作为事件流
  12. Admin processes:分离管理类任务

参考资料

首图为“塔科马海峡吊桥”,该大桥的倒塌是桥梁建筑史上的重要事件;

Read-Eval-Print Loop,简称REPL;https://en.wikipedia.org/wiki/JShell ;

Beyond the Twelve-Factor App

https://12factor.net/

https://www.oreilly.com/library/view/beyond-the-twelve-factor/9781492042631/

https://github.com/cncf/toc/blob/master/DEFINITION.md

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

(0)
上一篇 2024年5月19日 下午4:57
下一篇 2024年5月19日 下午5:09

相关推荐