三十天很快要到了尾声了,今天要来介绍 The Twelve-Factor App(下称 12 Factor),它是开发 SaaS 的方法论,适用於 Web 或网路相关服务等软件开发。怎麽突然讨论起如何开发软件呢?这跟 Docker 好像没有什麽关系?可能有点奇怪,不过这个开发软件的方法论,确实跟 Docker 有很大的关系。
笔者是先接触 Docker 之後,再接触 12 Factor 的。当时笔者还处在把 Docker 当轻量 Vagrant 用的时期,当时只感到阻碍重重。比较经典的例子像是:搞不懂为什麽 Apache container 一定要在 running Apache 的时候,才能透过 docker exec
进入 container。
後来在看 12 Factor 的时候,才发现错把 container 当 VM 使用了。当时的感受是,原来 Docker 是实践 12 Factor 的好例子;而反过来说,我们撰写程序遵守了 12 Factor 方法,会让使用 Docker 更顺利。
今天带读者简单了解 12 Factor 与 Docker 相关联的地方,有兴趣的读者可以阅读原文。
One codebase tracked in revision control, many deploys
一份原始码,可以创造出多份部署。下图是官方提供的示意图:
来源:12 Factor
Docker 也有一样的概念,若把 Dockerfile 作为原始码,则相同的 Dockerfile 可以在不同环境建置并使用 docker run
执行;若把 image 作为原始码又会更精简--不同的环境都能直接使用 docker run
执行。
Explicitly declare and isolate dependencies
明确地声明与隔离依赖。什麽是不明确地声明?比方说,直接在环境安装了全域都可以直接使用的套件,这就很容易踩到不明确声明。
Dockerfile 的流程中,必须明确写出安装依赖的过程,才有办法正常执行应用程序,如 Laravel image 的范例 Dockerfile:
# 安装 bcmath 与 redis
RUN docker-php-ext-install bcmath
RUN pecl install redis
RUN docker-php-ext-enable redis
# 安装程序依赖套件
COPY composer.* ./
RUN composer install --no-dev --no-scripts && composer clear-cache
有了明确声明依赖後,任何人都可以依照这个流程打造出一模一样的环境--执行 docker build。
Store config in the environment
把设定存放在环境里。设定包含下面几种:
不同环境的设定可能差异非常大,但它们都可以在同一份原始码上正常运作。这样的做法有几个好处:
MySQL environment 的范例正是综合了 I. Codebase 方法与 III. Config 来达成同一份原始码,不同设定的部署实例。
Treat backing services as attached resources
後端服务作为附加资源。後端服务包括了 MySQL、Redis 等常见的第三方服务,当然也包括了我们依本篇文章设计的 12 Factor App。
来源:12 Factor
这样设计的好处即容易替换服务,比方说想把 MySQL 换成 MariaDB,或是 Redis 3.0 升级成 Redis 5,只要把连线设定调整即可。
若使用 Docker Compose 会更方便,只要更新完定义档,重新执行启动指令就可以立即替换了:
docker-composer up -d
Strictly separate build and run stages
严格区分 build 和 run。build 阶段才能调整程序与整合依赖成 artifacts;run 阶段则非常单纯,把 artifacts 拿来执行就行了。
来源:12 Factor
在 Docker container 修改程序码,其实是非常麻烦的,要做非常多前置准备。但如果遵守 build / run 分离的方法,就会变得非常简单。
Execute the app as one or more stateless processes
使用一个以上的无状态 process 来执行应用程序。这里的关键在於要设计成无状态,如果有状态或资料要保存,可以透过 IV. Backing services 的资料库保存。
Docker 每次启动 container 都是全新的没有过去状态的,之前提到的一次性。换句话说,设计一个能在 Docker 上运作良好的 container,就代表有符合此方法--Container 应用正是活动此特性的范例参考。
Export services via port binding
透过 port 绑定来提供服务。
Docker 可以用 port forwarding 来对外提供服务,Dockerfile 则可以使用 EXPOSE
指令让 container 之间也可以互相使用 port 存取服务
EXPOSE 80
CMD ["php", "artisan", "serve", "--port", "80"]
Scale out via the process model
透过 process model 做水平扩展。12 Factor 是以工作类型来分类 process,如 HTTP 请求给 web process 处理,背景则是使用 worker process。
来源:12 Factor
配合 VI. Processes 提到的无状态特性,可以让应用程序非常容易做水平扩展,甚至是跨 VM、跨实体机器的扩展。
Container 即 process,因此 Docker 要做水平扩展是非常简单的--多跑几次 docker run
就行了,甚至 Docker Compose 还提供专用的 scale 指令:
docker-compose scale web=2 worker=3
Maximize robustness with fast startup and graceful shutdown
快速启动,优雅终止,最大化系统的强健性。
将应用程序设计成可以快速启动并开放服务,好处就在於扩展和上线变得非常容易。收到终止信号 SIGTERM
并优雅终止,则要求 process 停止接收任务,并把最的任务完成後,才真正结束 process。
Docker 在管理启动或终止都做的很完整,主要还是程序设计要得当。
Keep development, staging, and production as similar as possible
尽可能保持环境一致,环境一致最大的好处还是在於开发除错的效率。「我的电脑上就没问题」这句话正是这个方法的反指标,正因个人环境上有做了特别安装,才让程序有办法正常运作,这个安装过程就得考虑是否要同步到其他人或测试环境上。
Dockerfile 是一个 IoC 很好的实践,因此非常容易做到环境一致。
Treat logs as event streams
使用 event stream 输出 log。正如 IV. Backing services 与 VI. Processes 所提到的,12 Factor App 不应该保存状态,类似的,它也不应该保存 log,而是要把 log 作为 event stream 输出。
Docker 可以截取 process 的标准输出(STDOUT
),并透过内部机制转到 log driver,因此程序只要处理好标准输出即可。
Run admin/management tasks as one-off processes
管理与维护任务作为一次性的 process 执行,像 migration 正是属於这一类的任务。
对 Docker 而言,要在已启动的 container 上执行 process 太简单了,使用 docker exec
即可达成任务。
docker exec -it web php artisan migrate
最後回头来看 12 Factor 当初设计的目标:
这段原文很长,但因为是必要的,所以还是无断复制过来
- Use declarative formats for setup automation, to minimize time and cost for new developers joining the project;
- Have a clean contract with the underlying operating system, offering maximum portability between execution environments;
- Are suitable for deployment on modern cloud platforms, obviating the need for servers and systems administration;
- Minimize divergence between development and production, enabling continuous deployment for maximum agility;
- And can scale up without significant changes to tooling, architecture, or development practices.
简单整理如下:
曾有人问笔者:身为一个开发者,学完 run container,学完 build image,之後要学什麽呢?
学习写出符合 12 Factor 的程序。
能在 Docker 上运作良好的,正是符合 12 Factor 的应用程序。
>>: [Day29]Flutter Netflix UI 底部导航栏上的通知数量
哈罗大家好~ 不知道昨天的进度条做的怎麽样? 想要交作业的人可以贴在昨天的留言区给我呦! 那我们今...
虽然在 WordPress 中扩充功能有很多方式,例如在布景主题上使用子布景主题 (child th...
昨天在处理角色的时候已经把新增的页面处理好了 今天就用之前的方法把角色的其他页面也建立起来吧! 宣告...
每星期及每个月都会有一次固定的会议,加上其他大大小小的会议,一个月至少有6次以上的会议,每次会议看到...
tags: 铁人赛 AWS Outposts network 前情提要 昨天将 AWS 的网路配置好...