很早就知道 12-factors,却一直没怎么重视。最近在自定义 Django 开发框架,趁着这个机会进行了研究。夜以继日,呕血三升,飞哥这些经验之谈来之不易,诸位请收藏加关注,从此开发不迷路!文末还有惊喜哟!
00 什么是 12 factors
———————————
软件通常作为一种服务被交付,也就是常说的软件即服务(SaaS)。12-factors 为构建 SaaS 应用提供方法论。直白的说,就是 SaaS 应用开发过程中需要注意的十二个原则,这套理论适用于任何语言开发的应用程序。
对于 12-factors,开发者们仁者见仁智者见智,褒贬不一。但在任何时候,都应该学会摒弃他人的意见,结合自身实践经验,得出自己的结论。
01 基础代码 - code base
—————————————
代码开发离不开版本控制软件,如 Git, Svn 等。基础代码就是指版本控制软件中的那份代码。通常一份基础代码可以有多个部署,每个部署相当于应用的一个运行实例。
一个应用对应一份基础代码,但有时多个应用会同时依赖某段代码,此时应将这份共享代码独立维护,通过依赖管理策略加载它们。
比如 Django 应用的用户模型,权限模型等,任何 django 项目都会依赖这些代码,所以官方将其发布为独立应用,其他项目只需管理依赖即可。
# 你的应用中是否有公用代码呢?能否独立维护呢?
02 显式声明依赖关系
———————————
应用必须显式声明依赖关系,同一应用的不同部署,需要用维护不同的依赖清单。这对于运维和新进开发者来说是友好的,从应用安全的角度看,只有这样才能保障应用不会调用那些系统中存在但不在应用中依赖清单中的程序。
python 使用 pip 声明依赖,使用 virtualenv 隔离依赖,使用 pip freeze 生成依赖清单。
03 环境变量中存储配置
———————————
判断一个应用是否正确地将配置排除在代码之外,一个简单的方法是看该应用的基础代码是否可以立即开源,而不用担心会暴露任何敏感信息。
应用配置大体有三个位置可以存放:本地配置文件,统一配置中心,环境变量。它们各有优劣,但没有好坏之分,主要看使用场景来决定管理方案。
本地配置文件最为常见,如 django 的 settings.py 文件,在应用实例的生命周期中,仅在应用启动时加载一次,无法动态修改应用配置。
为实现应用运行时修改配置且不重启应用生效配置,就有了统一配置中心,如美团开源的 Apollo 等,同时还实现了应用配置的集中管理,版本管理及权限管理。SaaS 应用需要引入配置中心的 SDK 依赖,姑且算作不足之处吧!
随着云原生概念的推广,越来越多的应用运行在容器中,通过环境变量加载应用配置,就炙手可热了起来。
12-factors 推荐将应用的配置存储在应用的环境变量中。针对 django 应用飞哥开发了环境变量管理工具,请关注公众号回复 “env”
04 后端服务当做附加资源
—————————————
后端服务是指应用需要通过网络调用的各种服务,比如数据库,消息队列等。
类似数据库服务,通常由部署应用的系统管理员一起管理。除本地服务之外,应用程序还有可能使用了第三方发布和管理的服务。
12-factors 不区分本地服务和第三方服务。对于应用而言,它们都是附加资源。应用需要在不进行任何代码改动的情况下,实现本地服务和第三方服务的切换。
所有对后端服务的配置,都应该通过应用配置独立于基础代码之外管理。
05 严格区分构建和运行
————————————
通常情况下,应用发布需要如下两个步骤:应用构建和应用发布运行。12-factors 要求严格区分这两个步骤。
针对 python 应用,不想 C/Java 等语言,无需语言层面的编译过程,所以天然少了 build 这个过程。但容器化部署时,另一种意义的 build 必不可少。
大飞哥开发的 django 引用,基础代码通过 S2I(source to image)技术 build 成容器镜像并推送到镜像仓库,release 阶段只需要从镜像仓库拉去对应版本运行即可。
构建通常较为复杂,目前项目组的构建过程通常在 1-2 分钟之内完成,运行阶段则时间很短,可以实现秒启动。
06 进程无状态无共享
———————————
应用运行后,通常以进程的形式存在,12-factors 要求应用进程是无状态无共享的。也就是说,应用的创建和结束,不能影响业务状态。k8s 中 deployment 托管的无状态应用,就是按照这一原则实现的,所以才支持随意修改副本数量。
与之对应的是有状态应用,它要复杂很多。为降低运维难度,建议将应用进程做成无状态无共享的,做到最大程度的解耦。
07 绑定端口提供服务
———————————
这点没什么说的,web 应用都是绑定端口来提供服务。k8s 中通过 service 貌似在刻意的弱化对外服务中的端口概念,但 service 本身就是强绑定端口的。
08 通过进程管理应用
———————————
12-factor 应用中,进程是一等公民,进程是程序员能够操作的最小单元。开发人员可以将不同的工作分配给不同的进程。
12-factor 应用所具备的进程无状态无共享性,使得多进程扩展非常方便,但单台机器的性能有限(垂直扩展),所以应用程序必须可以在多台物理机间跨进程运行,才能做到水平扩展。
Celery 的进程模型就是这样,任务执行由 worker 多进程完成,心跳服务由 beat 进程完成,监控服务由 flower 进程完成。
12-factor 应用不需要自身成为守护进程或写入 PID 文件。相反,应该使用系统 systemd 或者 supervisord 等工具托管应用,管理输出流,响应崩溃的进程及响应用户触发的重启或关闭操作。
应用容器化运行时,就简单很多。只需要将应用进程作为容器主进程即可,容器的守护进程会自动托管应用的状态。
09 快速启动和优雅终止
————————————
12-factor 应用的进程是易处理的,意思是说它们可以瞬间启动或关闭。这对应用的弹性伸缩非常友好。进程应当追求最小启动时间。
优雅终止是门技术活儿。应用终止前要合理处理完业务请求,同时还要对突然终止保持健壮性。所以 12-factor 应用在设计之初,就应该考虑到意外的,不优雅的退出。
对于 django 应用,通常启动和终止由框架提供并维护,用户无需过多干预。但超出框架外的进程,需要用户自行处理。
10 各环境间等价
————————
应用程序通常由开发环境,测试环境,生成环境等,某些业务还需求增加 UAT 环境等,但无论多少测试环境,应该尽量保持这些环境的一致性。
当然上述只是理想状态,通常开发环境和生产环境之间存在很大差异。这张差异可以归为如下三类:
时间差异:本地开发的代码可能几天甚至几个月才会上线
人员差异:开发编写代码,运维部署代码
工具差异:开发人员或许使用 Nginx, SQLitte,OSX,而线上环境使用 Apache, MySQL, Linux
12-factor 为做到持续发布(CD),必须缩小开发环境和生产环境之间的差异。可以从上述三点切入进行改善。
缩短发布周期,几个小时发布一次
谁开发,谁部署,谁运维
工具尽量保持一致
11 把日志当作事件流
———————————
日志使得应用程序的运行状态变得透明,在基于服务器的部署中,日志通常被写在本地磁盘的日志文件中。但基于容器化部署应用时就显得捉襟见肘,容器的重启会导致所有日志的丢失。
把日志当作事件流,直接打印在标准输出(stdout)中,应用程序本身无需考虑存储输出流信息。输出流日志可以很方便的被采集,集中到日志服务中处理。
目前比较好的处理方式,是 ElasticSearch Stack 系列产品。关于这块的最佳实践,我们后续再讲!
12 优雅处理管理进程
———————————
应用程序运行后的进程被称作常驻进程,与之相对,开发或运维人员执行的用于管理或维护应用的一次性任务,被称为管理进程。如 manage.py migrate 等;
管理进程和常驻进程应该使用相同的环境,相同的配置,所有进程应该使用相同的依赖隔离技术。
对于 django 应用而言,可以通过 command 很容易开发出管理功能,日常对应用的操作应尽量封装在此处。详情请参考 django 官方自定义 command 命令。
13 总结
————
到此 12-factor 就逐一介绍完毕。需要注意的是,这些因素只是开发应用要考虑的方向,而不是必须要遵循的规范。12-factors 是语言无关的,是框架无关的,开发时应根据不同的语言和框架进行微调。
说好的文末有惊喜!
12-factor 在 django 应用开发中的最佳实践,飞哥已经开源在 cc_django 模板中,用此模板生成的项目,完全遵守上述原则。
源码地址:https://github.com/pyfs/cc_django
往期精彩回顾
(一) Django 项目结构优化
(二) 手打代码管理开发环境应用变量
(三) Django Admin 完美替换方案
(四) DRF 多序列化器支持,原创解决方案
cookiecutter,这么好的工具你值得拥有!