使用 NGINX Unit 构建应用堆栈

559 阅读7分钟

原文作者:Artem Konev of F5和Timo Stark of F5
原文链接:使用 NGINX Unit 构建应用堆栈 - NGINX
转载来源:NGINX官方网站

客户经常询问如何在已建好的某种技术堆栈中使用 NGINX Unit。单就其本身而言,NGINX Unit 很容易配置,但要将其整合到各种工具和服务中就没那么容易了;此外,如何让最终客户享受到潜在的好处也面临着同样的问题。本文旨在通过一个相当常见的使用场景来探讨这一问题。


问题:复杂的应用部署工作流

有个问题可能我们的许多读者都遇到过,那就是需要实现生产环境部署流程的自动化,这涉及使用定制的语言运行时版本以及特定的库、模块和扩展,以满足特定的业务需求。一般来说,通过临时采取合理的措施就可以实现这一目标,但是将自定义的半手动工作流转换成适合较大规模的一系列可调整的自动化步骤是一项极其繁琐的工作,不胜其烦的用户最终会选择彻底放弃该项目。下面就让我们看看 NGINX Unit如何帮您解决这一问题。

上述场景通常会因需求重叠而复杂化,例如:

  • 实现 Web 应用本身设置步骤的自动化
  • 构建定制的语言运行库以满足应用的需求
  • 将部署特定的设置注入到生成的通用镜像中
  • 部署和管理实例化镜像

单就每项要求而言,不难满足,但如果需要反复重复所有步骤,则就会带来巨大的压力。在这里,我们推荐了一种可缓解这一压力的方法,支持您将更多的时间用于提升业务价值,而非铺设管道。您可以直接使用该方法,也可以根据具体场景进行 调整 。.


解决方案:Docker 和 NGINX Unit

本质上我们要介绍的是一款面向您所选应用的 NGINX Unit 插件,该插件由我们的同事 Timo Stark 设计并在 GitHub上提供。该构建堆栈以 WordPress 为目标应用,被形象地称为 unitwp。但是,也可以根据其他需求对其进行改编。举例来说,现在它就可以为基于 CakePHP的 kaufdaheim.org 网站提供强大支持,该网站正在帮助德国本地企业在新冠疫情封锁期间生存下去。

在此描述的设置几乎可以毫不费力地创建带有内置 PHP 支持的、基于 NGINX Unit 的自定义容器镜像,准备 WordPress 安装镜像,在 NGINX Unit 中使 Web 应用做好启动准备,最后运行 Web 应用并将其暴露在预先配置的端口上。出于在开发环境和环境变量中所讨论的原因,该设置会刻意避免包含数据库。

该解决方案依赖于官方 NGINX Unit 镜像,该镜像已预先配置了 PHP 7.3 运行时以及适用于 NGINX Unit 的语言模块,与自行准备自定义镜像相比,这可节省大量时间和精力。 如果您希望使用简单的 Docker 镜像,则可以使用 nginx/unit:1.16.0-minimal 镜像,该镜像仅包含核心 NGINX Unit 可执行文件。 有关结合使用 NGINX Unit 与 Docker的建议,请参见我们的官方指南。

注意: 以下步骤假定您拥有面向 PHP 的 Composer 依赖管理器;如有必要,请按照下载说明进行下载。如果没有,则可以继续使用任何其他可用的安装方法。

我们首先使用基础 Composer 配置为我们的 WordPress 应用创建文件的本地副本,该基础 Composer 配置使用了 GitHub 上支持 Composer 的 WordPress 分支:

{
 "require": {
 "johnpbloch/wordpress": "*"
 }
}

如欲下载并安装 Composer,请将此代码段另存为 composer.json,然后在同一目录中运行 composer install 命令。

现在,让我们使用以下 Dockerfile 将文件加载到 Docker 镜像中:

FROM nginx/unit:1.16.0-php7.3
RUN mkdir /var/apphome/ && groupadd -r wpgroup && useradd --no-log-init -r -g wpgroup wpuser && \
 chown -R wpuser:wpgroup /var/apphome/ && \
 apt-get update && apt-get install --no-install-recommends --no-install-suggests -y php7.3-mysql php7.3-gd
 
COPY wordpress /var/apphome
RUN chown -R wordpress:wordpress /var/apphome/
COPY .unit.conf.json /docker-entrypoint.d/.unit.conf.json
CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]

该文件首先使用 WordPress 所需的几个程序包对基础镜像的 PHP 环境进行了定制。如果您的项目还需要额外的程序包或定制,那么这将是第一个潜在的扩展点。 如果您希望自己构建整个工具链,则可以使用 -minimal 基础镜像进行构建,而非上文提到的镜像。

随后,该文件会将 WordPress 文件从 Composer 创建的目录复制到镜像中,并设置适当的所有权。最后,它会复制 JSON 代码段,当容器启动时,该代码段就会被上传到 NGINX Unit 配置中。启动脚本会自动运行,但是您也可以使用自定义证书和 shell 脚本,正如在 NGINX Unit 文档中所述。

以下是 NGINX Unit 配置:

{
 "listeners": {
 "*:8080": {
 "pass": "routes/wordpress"
 }
 },
 "routes": {
 "wordpress": [
 {
 "match": {
 "uri": [
 "*.php",
 "*.php/*",
 "/wp-admin/"
 ]
 },
 "action": {
 "pass": "applications/wp_direct"
 }
 },
 {
 "action": {
 "share": "/var/apphome",
 "fallback": {
 "pass": "applications/wp_index"
 }
 }
 }
 ]
 },
 "applications": {
 "wp_direct": {
 "type": "php",
 "options": {
 "file": "/etc/php.ini",
 "admin": {
 "upload_max_filesize": "20M"
 }
 },
 "user": "wordpress",
 "group": "wordpress",
 "root": "/var/apphome"
 },
 "wp_index": {
 "type": "php",
 "options": {
 "file": "/etc/php.ini",
 "admin": {
 "upload_max_filesize": "20M"
 }
 },
 "user": "wpuser",
 "group": "wpgroup",
 "root": "/var/apphome",
 "script": "index.php"
 }
 },
 "settings": {
 "http": {
 "header_read_timeout": 10,
 "body_read_timeout": 10,
 "send_timeout": 10,
 "idle_timeout": 120,
 "max_body_size": 6291456,
 "static": {
 "mime_types": {
 "text/plain": [
 ".log",
 "README",
 "CHANGES"
 ]
 }
 }
 }
 }
}

WordPress 特定的路由方案(选自我们的指南)利用了NGINX Unit 1.16.0 中引入的 fallback 路由选项,该选项类似于 NGINX 的 try_files 指令。 还要注意,我们在“应用”部分的不同 wp_direct 和 wp_index 对象中就不同的网站部分应用了不同的设置;如果您愿意,还可以使用不同的 php.ini 文件。

最后,我们将所有内容都打包到一个小巧的 build.sh 脚本中:

#!/usr/bin/env bash
set -ex
build_container() {
 docker build -t YOURIMAGETAG --no-cache .
}
 
 
containerize() {
 echo "Building Container Image"
 build_container
 docker tag YOURIMAGETAG:latest REMOTEIMAGETAG:latest
 echo "Pushing... "
 docker push REMOTEIMAGETAG:latest
}
 
case $1 in
"push")
 echo "Building and Pushing to Registry ..."
 containerize
 ;;
esac

对于 YOURIMAGETAG,替换本地标签;对于 REMOTEIMAGETAG,最终标签包含目标 Docker 注册表的名称。为简洁起见,该脚本省略了错误处理和报告。

当我们运行 build.sh push 命令时,该脚本会从头构建一个新镜像并对其进行标记,然后将其推送到我们选择的镜像仓库中。您还可以增强 shell 脚本,比如引入可调整的标签和版本控制,或删除同一镜像的旧版本以减少混乱。


设置开发环境

NGINX Unit 的功能还能为其他同样相关的场景带来更多益处,即简单快速地扩展和关闭本地开发或测试环境。得益于动态配置特性,NGINX Unit 支持在语言运行时版本、环境变量或处理应用的几乎所有设置之间无缝切换。下面就让我们基于上述内容进一步展开。

我们 unitwp WordPress 应用的开发版本位于 GitHub 存储库的 dev 子目录。它使用的设置与解决方案:Docker 和 NGINX Unit 中所描述的基本相同,只有几处不同。

第一个区别在于 Dockerfile,它具有以下命令,而没有将 WordPress 文件复制到镜像中的命令。此更改支持您在每次构建镜像时添加特定的 php.ini 设置。

COPY php.ini /etc/php/7.3/cli/conf.d/php-unit.ini 

另一个显著差异是使用 docker-compose 添加 MariaDB 数据库容器。以下是 docker-compose.yaml 文件:

version: "3.7"
services:
 mariadb:
 image: mariadb:latest
 ports:
 - 3306:3306
 restart: always
 volumes:
 - ./db-volume:/var/lib/mysql
 environment:
 MYSQL_USER: wordpressdev
 MYSQL_PASSWORD: wordpressdev
 MYSQL_DATABASE: wordpress
 MYSQL_ROOT_PASSWORD: devpassword
 wordpress:
 image: YOURIMAGETAG:latest
 environment:
 DB_USER: wordpressdev
 volumes:
 - ../wordpress:/var/apphome/wordpress
 - ../vendor:/var/apphome/vendor
 ports:
 - 8044:8080 

一个容器维护一个服务是 Docker 的最佳工作方式,这也是我们没有在 解决方案:Docker 和 NGINX Unit 中添加数据库的原因。在生产环境中,数据库连接可以有多种形式,因此我们使容器通用化,可在任何情况下使用,而无需选择特定的数据库。相反,开发环境通常需要保持独立,因此这部分的动态配置会在运行时创建完全可操作的堆栈。这涉及对环境变量的大量使用,下一部分将对此进行详细讨论。

如要运行此设置,我们需要一个升级的 shell 脚本,在这里我们称作 run-local.sh,其中一个选项用于构建镜像并运行容器,另一个选项用于停止服务并清理:

#!/usr/bin/env bash
set -ex
case $1 in
"dev")
 echo "Building local image and mounting code base..."
 docker build -t YOURIMAGETAG:latest .
 docker-compose up
 ;;
"stop")
 echo "Stopping services..."
 docker-compose down
 docker rm --force dev_wordpress_1
 ;;
esac
 
 

如要启动或停止本地开发环境,请分别运行 ./run-local.sh dev 和 ./run-local.sh stop 命令。


使用环境变量

尽管在开发环境中存储一次性凭证是合理的,但将硬编码的数据库密码或 API 访问令牌推送到生产代码仓库却不是个好主意,虽然总是有人这么做。避免此类事件的方法有很多,但环境变量是硬编码证书最基本的替代方案。

您可以通过多种方式设置环境变量,包括:

  • 启动时将它们作为 docker run --env 或 docker run --env-file 命令参数提供
  • 在 docker-compose.yaml 文件中将它们列出,正如在“设置开发环境”中
  • 最后一点也是很重要的一点是,使用 NGINX Unit 配置 API,特别是其环境选项

在 NGINX Unit 配置的开发版本中,我们针对两款应用使用了“环境”选项,如本示例所示:

"applications": {
 "wp_direct": {
 "type": "php",
 "environment": {
 "DB_HOST": "mariadb"
 }
 }
}

如您所见,“环境”部分中的 JSON 非常简单:您只需为应用提供变量名称的键值清单及其设置即可。这样做的好处是这些值仅对单个应用可见,而不会与同一环境中的其他应用共享。

当然,具体方法取决于您个人的选择。但是,我们建议选择一个位置来配置环境变量并且不再更改。


结语

正如文中所述,NGINX Unit 包含许多特性和选项,可将涉及诸多工具和技术的多层方案简化为几个 shell 命令:

  • 动态配置支持您随时更新应用或其特定部分。
  • 配置精细,支持您在不影响整体设置的情况下微调个别设置。
  • Docker 镜像提供了几乎无需调整的现成解决方案,支持您快速封装任何应用以供独立使用或与其他服务一起部署。

综上所述,NGINX Unit 是各量级应用交付堆栈的理想之选。我们也在推进几项重要更新,以增强请求路由和应用隔离,从而提供更多选择,敬请期待!


资源

想要更及时全面地获取NGINX相关的技术干货、互动问答、系列课程、活动资源?请前往NGINX开源社区官方网站 。