你确定真的理解 12 Factors 吗?

336 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第 1 天,点击查看活动详情

只有亲身经历,你才懂得“哲学”的意义 !

                       -- 来自张三的感叹

最近要设计一套脚手架,为团队提效之用。动手前参考了一些同类项目,虽各有千秋仍感觉稍有欠缺,某天翻到 12 Factors 才恍然大悟,原来自己缺少指导思想。哲学总是枯燥乏味的,直到你从中受益的那一刻,才知道其中的价值。

12 Factors 中文翻译

咱们闲话少说,言归正传。

1. 基准代码 (一份基准代码, 多份部署)

使用版本管理工具维护源代码,是每一位程序员的基本素养。使用 Git 做版本管理时,基准代码就是最上游的那份代码库。

一份代码是在要求,在项目层级做到代码包的复用。比如我们用脚手架创建两个项目,他们都依赖 “用户模型”,此时应当将其抽离出脚手架单独维护,而不是两个项目都维护一份源码。

多份部署比较容易理解,如常用的开发、测试、生产等环境,它们的基准代码相同,只是版本不同而已。同时此处也提醒我们,应该通过配置的方式,让一套基准代码在不同部署实例中,表现不同的功能,而不是维护两套基准代码。

2. 依赖 (显式声明依赖关系)

显式优于隐式,这是个非常重要的原则。很多开发框架在设计时,为约束项目结构,往往会隐式应用某些规范。比如:python Django 项目默认会自动加载 app 目录下的 models.py 文件,而没有给出明显的配置修改方案。所以要使用显式声明代替隐式约定

对于项目依赖关系,更应显式声明,不同部署实例的依赖,放在不同的配置文件中。

3. 配置 (在环境中存储配置)

应用的配置在不同的部署实例中,往往差异很大,而把配置存储在项目中是愚蠢的。推荐两种方案:使用配置中心或者环境变量

配置中心如 Appollo,可以方便管理环境配置,同时支持热加载,其不便之处,仅仅是需要引入对它的依赖。在云原生领域,则推荐直接使用环境变量管理配置,进而可以采用 Gitops 思想,把环境变量存储在仓库中管理。

4. 后端服务 (把后端服务当作附加资源)

后端服务指数据库,缓存,消息队列等等这些第三方服务,其实可以加以拓展,代指任何项目依赖的第三方资源。本原则皆在要求,项目对第三方资源的依赖,都应该是解耦的,可迁移的。

比如项目依赖数据库,那无论数据库部署在本地,还是部署在内网,抑或是云服务,项目都应该兼容,而不是绑定某种第三方形式。如此方可保持项目的独立性。

所以项目连接第三方时,尽量使用公共库,比如:数据库连接池,消息队列客户端等等。这样对第三方的兼容性就由公共库承担,而非项目本身。

5. 构建, 发布, 运行 (严格分离构建和运行)

构建,发布,运行,这是项目上线的标准流程,对应 CI,CD,CO 三个阶段。如果说部署实例隔离是纵向隔离,那构建、运行隔离则可以理解为横向隔离。

想要玩转持续构建,持续发布,持续运营,则需要完整的 DEVOPS 平台支持。很多企业基于 Jenkins 自研流水线,也有些企业采用开源方案搭建。但无论如何,其本质是在标准化流程,通过机制来约束程序员。照章办事,才能稳步快行。

6. 进程 (以一个或多个无状态进程运行应用)

应用最终运行,以进程的形式提供服务,而无依赖无共享的进程,被称为无状态应用。正是应用的无状态性,决定了云原生架构的灵活性。多进程应用,在云原生环境中,可以用多副本代替,所以务必遵守一个容器只运行一个进程的原则。

与无状态应用对应的,被称为有状态应用。这类应用的扩缩容,都严格遵守一定的顺序,想要实现 HPA,那更是不小的挑战。如非必要,请保持应用的有状态性

7. 端口绑定 (通过端口绑定提供服务)

绑定端口来提供对外服务,这在服务开发中很常见,绑定端口使得服务跨主机,跨系统成为可能,这也是分布式的基础。

值得注意的是,在云原生环境下,尽量使用随机端口来绑定程序。

8. 并发 (通过进程模型进行扩展)

只有无状态的应用,并发才有意义,因为有状态应用总会在某个时刻必须保持先后顺序。在单台主机中,进程是一等公民,是程序员可操作的最小单位。单主机想要提高应用性能,建议采用多进程;单应用多主机可以采用分布式协作架构;云原生环境中,直接采用多副本即可。

9. 易处理 (快速启动和优雅终止可最大化健壮性)

快速启动,最典型的方案就是容器化。当然容器化只是从运维层面解决启动加速的问题,应用本身的依赖管理也可以有效加速启动。

优雅终止在任何情况下都尤为重要,如此方可保证应用重启后,可以继续执行任务。在容器随时都有可能被杀掉的云原生环境中,优雅终止是保证扩展性和健壮性的前提

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

开发环境和生产环境等价,这是件非常难的事情。因为时间差异,数据差异,工具差异等等因素,即便是生产环境数据的保密性,就要求各环境之间的差异。这种差异,也是导致开发调试完美运行,上线试水一蹶不振的元凶。

推荐 Nocalhost,它可以直接在生产环境中复制一份部署,供开发人员在线调试,而不影响生产环境的稳定性。

11. 日志 (把日志当作事件流)

应用日志写入本地磁盘,导致设备无磁盘可用宕机,这或许是很多运维的第一个生产事件。日志应该集中管理,通过 EFK 等工具链,采集到时间序列数据库中,供后续的查询和分析。

云原生环境下,建议日志直接输出在 STDOUT 中,这样既方便数据采集,也避免了主机磁盘写满的风险。

关于日志,各团队应该制定好规范,合理打印日志,是日志服务治理的关键,同时出于安全考虑,应避免在日志中输出敏感数据。

12. 管理进程 (后台管理任务当作一次性进程运行)

一次性任务或者定时任务等等,都应该与主业务逻辑一样,通过构建,发布,运行等流程执行,而不是写个脚本,运行完就即刻销毁。

对于任务的管理,常常被忽略,这是导致线上幽灵操作的罪魁祸首。这类非常规操作,日志中也应明确指出,便于追溯异常。

总结

对于 12 Factors 原则,仁者见仁智者见智,虽有共识但无需观点一致,针对不同项目环境,其指导意义不同。如果你不甘做一位只撸代码的码农,还是要从脚手架开始,学会做架构设计。

愿每一位想要偷懒的程序员,都能梦想成真。