应用程序开发和蓝绿部署,基于十二因素应用程序方法论,并以 php 和 docker 为例

应用程序开发和蓝绿部署,基于十二因素应用程序方法论,并以 php 和 docker 为例

首先,一点理论。 发生了什么事 十二要素应用程序?

简而言之,本文档旨在简化 SaaS 应用程序的开发,通过向开发人员和 DevOps 工程师通报现代应用程序开发中最常遇到的问题和实践来提供帮助。

该文档由 Heroku 平台的开发人员创建。

十二要素应用程序可应用于以任何编程语言编写并使用支持服务(数据库、消息队列、缓存等)的任意组合的应用程序。

简要介绍一下该方法所依据的因素:

  1. 代码库 – 在版本控制中跟踪一个代码库 – 多个部署
  2. 依存关系 – 显式声明并隔离依赖关系
  3. 布局 – 在运行时保存配置
  4. 支持服务 – 考虑将支持服务作为插件资源
  5. 构建、发布、运行 – 严格分离组装和执行阶段
  6. 流程 – 将应用程序作为一个或多个无状态进程运行
  7. 端口绑定 – 通过端口绑定导出服务
  8. 排比 – 使用流程扩展您的应用程序
  9. 一次性使用 – 通过快速启动和干净关闭来最大限度地提高可靠性
  10. 应用程序开发/运营平价 – 使您的开发、登台和生产环境尽可能相似
  11. 记录 – 将日志视为事件流
  12. 行政任务 – 使用临时流程执行行政/管理任务

您可以从以下资源获取有关 12 个因素的更多信息:

什么是蓝绿部署?

蓝绿部署是一种将应用程序交付给 生产 以这样的方式,最终客户看不到他的任何变化。 换句话说,部署一个应用程序为零 停机.

经典的 BG Deploy 方案如下图所示。

应用程序开发和蓝绿部署,基于十二因素应用程序方法论,并以 php 和 docker 为例

  • 一开始有2台物理服务器,具有完全相同的代码、应用程序、项目,并且有一个路由器(平衡器)。
  • 路由器最初将所有请求定向到其中一台服务器(绿色).
  • 当你需要再次发布的时候,整个项目就更新到了另一台服务器上(),当前未处理任何请求。
  • 代码开启后 蓝色 服务器已完全更新,路由器收到切换命令 绿 服务器。
  • 现在所有客户端都可以看到代码运行的结果 服务器。
  • 一段时间以来, 绿色 如果部署失败,服务器将作为备份副本 服务器,如果发生故障和错误,路由器会将用户流切换回 绿色 服务器使用旧的稳定版本,并发送新代码进行修订和测试。
  • 并且在该过程结束时,以同样的方式更新 绿色 服务器。 更新后,路由器将请求流切换回 绿色 服务器。

一切看起来都很好,乍一看应该没有任何问题。
但由于我们生活在现代世界,经典方案中所示的物理切换选项并不适合我们。 暂时记录一下信息,稍后我们再回顾。

好的和坏的建议

免责声明:下面的示例显示了我使用的实用程序/方法,您绝对可以使用具有类似功能的任何替代方案。

大多数示例都会以某种方式与 PHP 和 Docker 的 Web 开发相交叉(这是一个惊喜)。

下面的段落使用具体示例对因子的使用进行了简单实用的描述;如果您想获得有关此主题的更多理论,请点击上面的链接访问原始来源。

1. 代码库

使用 FTP 和 FileZilla 一次上传一个文件到服务器,不要将代码存储在生产服务器以外的任何地方。

项目应该始终有一个单一的代码库,即所有代码都来自一个 混帐 存储库。 服务器(生产、登台、test1、test2...)使用来自一个公共存储库的分支的代码。 这样我们就实现了代码的一致性。

2. 依赖关系

将文件夹中的所有库直接下载到项目的根目录。 只需将新代码传输到包含当前版本库的文件夹即可进行更新。 直接在运行 20 多个服务的主机服务器上安装所有必要的实用程序。

项目应该始终有一个清晰易懂的依赖项列表(我所说的依赖项也指环境)。 所有依赖项都必须明确定义和隔离。
我们举个例子 作曲家 и 码头工人.

作曲家 — 一个包管理器,允许您在 PHP 中安装库。 Composer 允许您严格或宽松地指定版本,并显式定义它们。 服务器上可以有 20 个不同的项目,每个项目都有一个相互独立的个人包和库列表。

码头工人 — 一个实用程序,允许您定义和隔离应用程序运行的环境。 因此,就像作曲家一样,但更彻底的是,我们可以确定应用程序的工作原理。 选择特定版本的 PHP,仅安装项目运行所需的包,而不添加任何额外的内容。 最重要的是,不会干扰主机和其他项目的软件包和环境。 也就是说,服务器上通过 Docker 运行的所有项目都可以使用绝对任何一组包和完全不同的环境。

3. 配置

将配置直接存储在代码中作为常量。 测试服务器的单独常量,生产服务器的单独常量。 使用 if else 构造将取决于环境的应用程序操作直接绑定到项目的业务逻辑中。

配置 - 这是项目部署应该有所不同的唯一方式。 理想情况下,配置应该通过环境变量(env vars)传递。

也就是说,即使您存储多个配置文件 .config.prod .config.local 并在部署时将它们重命名为 .config (应用程序从中读取数据的主配置) - 这也不是正确的方法,因为在这种情况下,配置中的信息将向所有应用程序开发人员公开,并且生产服务器中的数据将受到损害。 所有配置必须直接存储在部署系统(CI/CD)中,并针对不同环境生成部署时特定环境所需的不同值。

4. 第三方服务

与环境严格绑定,在特定环境下对相同服务使用不同的连接。

事实上,这一点与配置的点有很大的重叠,因为没有这一点,就无法进行正常的配置数据,一般来说,配置的能力就会下降。

对于本地环境和第三方/生产环境,与外部服务(例如队列服务器、数据库、缓存服务)的所有连接必须相同。 换句话说,在任何时候,通过更改连接字符串,我都可以用基#1 替换对基#2 的调用,而无需更改应用程序代码。 或者,展望未来,例如,在扩展服务时,您不必以任何特殊方式为附加缓存服务器指定连接。

5. 构建、发布、执行

服务器上只有最终版本的代码,没有机会回滚版本。 无需填满磁盘空间。 任何认为自己可以将带有错误的代码发布到生产中的人都是糟糕的程序员!

部署的所有阶段必须彼此分开。

有机会翻盘。 使用保存在快速访问中的旧应用程序副本(已组装并准备好战斗)进行发布,以便在出现错误时可以恢复旧版本。 即有条件有一个文件夹 发布 和文件夹 当前,并在成功部署和组装文件夹后 当前 通过符号链接链接到内部的新版本 发布 与版本号的常规名称。

这就是我们记得蓝绿部署的地方,它不仅允许您在代码之间切换,还可以在所有资源甚至环境之间切换,并具有回滚所有内容的能力。

6. 流程

直接将应用程序状态数据存储在应用程序本身内。 在应用程序本身的 RAM 中使用会话。 尽可能多地使用第三方服务之间的共享。 依赖于这样一个事实:应用程序只能有一个进程并且不允许扩展。

关于会话,仅将数据存储在由第三方服务(memcached、redis)控制的缓存中,因此即使您有 20 个应用程序进程正在运行,其中任何一个访问了缓存,都将能够继续与客户端一起工作用户在另一个进程中使用应用程序时的相同状态。 通过这种方法,事实证明,无论您使用多少个第三方服务副本,一切都会正常工作,并且不会出现数据访问问题。

7. 端口绑定

只有网络服务器应该知道如何与第三方服务一起工作。 或者更好的是,直接在 Web 服务器内安装第三方服务。 例如,作为 Apache 中的 PHP 模块。
您的所有服务必须可以通过访问某个地址和端口(localgost:5432,localhost:3000,nginx:80,php-fpm:9000)来相互访问,也就是说,从nginx我可以访问php-fpm和postgres,从 php-fpm 到 postgres 和 nginx,实际上我可以从每个服务访问另一个服务。 这样,一个服务的生存能力就不会与另一个服务的生存能力联系在一起。

8. 并行性

与一个进程一起工作,否则多个进程将无法相处!

留出扩展空间。 Docker swarm 非常适合这个。
Docker Swarm 是一个用于创建和管理不同机器之间的容器集群以及同一机器上的一堆容器的工具。

使用 swarm,我可以确定为每个进程分配多少资源以及将启动同一服务的多少个进程,并且内部平衡器在给定端口上接收数据时会自动将其代理到进程。 因此,看到服务器上的负载增加了,我可以添加更多进程,从而减少某些进程的负载。

9. 一次性使用

不要使用队列来处理进程和数据。 终止一个进程应该会影响整个应用程序。 如果一项服务出现故障,那么所有服务都会出现故障。

每个进程和服务都可以随时关闭,并且这不应该影响其他服务(当然,这并不是说该服务对另一个服务不可用,而是另一个服务在这个服务之后不会关闭)。 所有进程都必须优雅地终止,这样当它们终止时,不会损坏任何数据,并且系统下次开机时也能正常工作。 也就是说,即使在紧急终止的情况下,数据也不应该被损坏(事务机制在这里是合适的,数据库中的查询只能成组工作,并且如果该组中至少有一个查询失败或使用错误,那么实际上该组中没有其他查询最终失败)。

10.应用程序开发/运营平价

应用程序的生产版本、暂存版本和本地版本必须不同。 在生产中我们使用 Yii Lite 框架和本地 Yii,这样它在生产中运行得更快!

实际上,所有部署和代码工作都应该在几乎相同的环境中(我们不是在谈论物理硬件)。 此外,任何开发员工都应该能够在必要时将代码部署到生产中,而不是某些经过专门培训的 DevOps 部门,只有特殊的力量才能将应用程序提升到生产中。

Docker 也在这方面帮助我们。 如果遵守前面的所有要点,使用 docker 将使在生产和本地机器上部署环境的过程只需输入一两个命令。

11. 日志

我们将日志写入文件和数据库! 我们不会清除日志中的文件和数据库。 我们买一块 9000 Peta 字节的硬盘就可以了。

所有日志都应被视为事件流。 应用程序本身不应参与日志处理。 日志应该输出到 stdout 或通过 udp 等协议发送,以便使用日志不会给应用程序带来任何问题。 Graylog 对此很有用。 Graylog通过udp接收所有日志(该协议不需要等待有关成功接收数据包的响应)不会以任何方式干扰应用程序,仅处理结构化和处理日志。 应用程序逻辑不会改变以使用此类方法。

12. 行政任务

要更新数据、数据库等,请在 API 中使用单独创建的端点,连续执行两次将导致所有内容重复。 但你并不傻,你不会点击两次,而且我们也不需要迁移。

所有管理任务都应在与所有代码相同的环境中在发布级别执行。 也就是说,如果我们需要更改数据库的结构,那么我们不会通过一些可视化数据库管理工具手动更改列名和添加新列。 对于此类事情,我们创建单独的脚本 - 迁移,这些脚本在任何地方和所有环境中以相同的方式执行,并产生常见且可理解的结果。 对于所有其他任务,例如用数据填充项目,应使用类似的方法。

PHP、Laravel、Laradock、Docker-Compose 中的示例实现

PS 所有示例均在 MacOS 上进行。 其中大多数也适用于 Linux。 Windows 用户请原谅我,我已经很长时间没有使用 Windows 了。

让我们想象一下这样一种情况:我们的 PC 上没有安装任何版本的 PHP,而且什么也没有安装。
安装最新版本的 docker 和 docker-compose。 (这个可以在网上查到)

docker -v && 
docker-compose -v

应用程序开发和蓝绿部署,基于十二因素应用程序方法论,并以 php 和 docker 为例

1. 放置 拉拉多克

git clone https://github.com/Laradock/laradock.git && 
ls

应用程序开发和蓝绿部署,基于十二因素应用程序方法论,并以 php 和 docker 为例

关于Laradock,我会说它是一个非常酷的东西,里面包含了很多容器和辅助的东西。 但我不建议在生产中不进行修改就使用 Laradock,因为它具有冗余性。 最好基于 Laradock 中的示例创建自己的容器,这会更加优化,因为没有人需要同时存在的所有内容。

2. 配置 Laradock 来运行我们的应用程序。

cd laradock && 
cp env-example .env

应用程序开发和蓝绿部署,基于十二因素应用程序方法论,并以 php 和 docker 为例

2.1. 在某个编辑器中打开 habr 目录(laradock 克隆到的父文件夹)。 (以我的 PHPStorm 为例)

在这个阶段我们只给项目一个名称。

应用程序开发和蓝绿部署,基于十二因素应用程序方法论,并以 php 和 docker 为例

2.2. 启动工作区图像。 (就您而言,图像需要一些时间来构建)
工作区是专门为开发人员使用框架而准备的映像。

我们进入容器内部使用

docker-compose up -d workspace && 
docker-compose exec workspace bash

应用程序开发和蓝绿部署,基于十二因素应用程序方法论,并以 php 和 docker 为例

2.3. 安装 Laravel

composer create-project --prefer-dist laravel/laravel application

应用程序开发和蓝绿部署,基于十二因素应用程序方法论,并以 php 和 docker 为例

2.4. 安装完成后,我们检查项目所在的目录是否已创建并杀死 compose。

ls
exit
docker-compose down

应用程序开发和蓝绿部署,基于十二因素应用程序方法论,并以 php 和 docker 为例

2.5. 让我们回到 PHPStorm 并在 .env 文件中设置 Laravel 应用程序的正确路径。

应用程序开发和蓝绿部署,基于十二因素应用程序方法论,并以 php 和 docker 为例

3. 将所有代码添加到Git。

为此,我们将在 Github(或其他任何地方)上创建一个存储库。 让我们进入终端中的 habr 目录并执行以下代码。

echo "# habr-12factor" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin [email protected]:nzulfigarov/habr-12factor.git # здесь будет ссылка на ваш репо
git push -u origin master
git status

让我们检查一下是否一切正常。

应用程序开发和蓝绿部署,基于十二因素应用程序方法论,并以 php 和 docker 为例

为了方便起见,我建议使用 Git 的一些可视化界面,在我的例子中是 GitKraken。 (这是一个推荐链接)

4. 开始吧!

开始之前,请确保端口 80 和 443 上没有挂起任何内容。

docker-compose up -d nginx php-fpm

应用程序开发和蓝绿部署,基于十二因素应用程序方法论,并以 php 和 docker 为例

因此,我们的项目由 3 项独立的服务组成:

  • nginx - 网络服务器
  • php-fpm - 用于接收来自 Web 服务器的请求的 php
  • 工作区 - 面向开发人员的 php

目前,我们已经创建了一个满足 4 点中的 12 点的应用程序,即:

1. 代码库 — 所有代码都在一个存储库中(小提示:在 laravel 项目中添加 docker 可能是正确的,但这并不重要)。

2. 依存关系 - 我们所有的依赖项都明确写入 application/composer.json 和每个容器的每个 Dockerfile 中。

3. 支持服务 — 每项服务(php-fom、nignx、workspace)都有自己的生命周期,并从外部连接,当使用一项服务时,另一项服务不会受到影响。

4. 流程 — 每项服务都是一个进程。 每个服务都不维护内部状态。

5. 端口绑定

docker ps

应用程序开发和蓝绿部署,基于十二因素应用程序方法论,并以 php 和 docker 为例

正如我们所看到的,每个服务都在自己的端口上运行,并且可供所有其他服务访问。

6. 排比

Docker 允许我们生成相同服务的多个进程,并在它们之间自动进行负载平衡。

让我们停止容器并通过标志运行它们 - 规模

docker-compose down && 
docker-compose up -d --scale php-fpm=3 nginx php-fpm

应用程序开发和蓝绿部署,基于十二因素应用程序方法论,并以 php 和 docker 为例

正如我们所看到的,已经创建了 php-fpm 容器的副本。 我们不需要改变任何使用这个容器的东西。 我们还继续在端口 9000 上访问它,Docker 为我们调节容器之间的负载。

7. 一次性使用 - 每个容器都可以被杀死而不伤害另一个容器。 停止或重新启动容器不会影响应用程序在后续启动期间的运行。 每个集装箱也可以随时提升。

8. 应用程序开发/运营平价 - 我们所有的环境都是一样的。 通过在生产服务器上运行系统,您无需更改命令中的任何内容。 一切都将以同样的方式基于 Docker。

9. 记录 — 这些容器中的所有日志都会进入流式传输并在 Docker 控制台中可见。 (这样的话,其实用其他自制的容器,如果不爱护的话也可能不会出现这种情况)

 docker-compose logs -f

应用程序开发和蓝绿部署,基于十二因素应用程序方法论,并以 php 和 docker 为例

但有一个问题是 PHP 和 Nginx 中的默认值也会将日志写入文件。 要满足这12个因素,必须 关闭 分别将日志写入每个容器配置中的文件。

Docker 还提供了不仅将日志发送到 stdout 的功能,还提供了将日志发送到我上面提到的graylog 之类的功能。 在graylog内部,我们可以随意操作日志,我们的应用程序不会以任何方式注意到这一点。

10. 行政任务 — 所有管理任务都由 Laravel 解决,这要归功于 Artisan 工具,正如 12 Factor 应用程序的创建者所希望的那样。

作为示例,我将展示一些命令是如何执行的。
我们进入容器。

 
docker-compose exec workspace bash
php artisan list

应用程序开发和蓝绿部署,基于十二因素应用程序方法论,并以 php 和 docker 为例

现在我们可以使用任何命令。 (请注意,我们没有配置数据库和缓存,因此一半命令将无法正确执行,因为它们被设计为与缓存和数据库一起使用)。

应用程序开发和蓝绿部署,基于十二因素应用程序方法论,并以 php 和 docker 为例

11. 配置 和12。 构建、发布、运行

我想将这一部分专门讨论蓝绿部署,但事实证明它对于本文而言过于广泛。 我将就此单独写一篇文章。

简而言之,这个概念基于 CI/CD 系统,例如 詹金斯 и 亚特实验室持续集成。 在两者中,您都可以设置与特定环境关联的环境变量。 因此,在这种情况下,c点将得到满足 配置.

还有一点是关于 构建、发布、运行 通过名为的内置函数解决 管道.

管道 允许您将部署过程分为多个阶段,突出显示组装、发布和执行阶段。 同样在 Pipeline 中,您可以创建备份,甚至任何东西。 这是一个具有无限潜力的工具。

申请代码位于 Github上.
克隆此存储库时不要忘记初始化子模块。

PS:所有这些方法都可以与任何其他实用程序和编程语言一起使用。 最主要的是本质没有区别。

来源: habr.com

添加评论