NGINX Full Version

使用 Docker 部署 NGINX 和 NGINX Plus

编者按 – 面向 DebianAlpine Linux 发行版的 NGINX Plus Dockerfiles 于 2022 年 12 月进行了更新,以适配最新的 NGINX Plus 版本以及两个操作系统的最新发行版。更新后的 NGINX Plus Dockerfiles 在构建 NGINX Plus 镜像时,使用 Docker secrets 来传递许可证信息。

Docker 是一个开放平台,能够以容器的形式(一种独立可执行的轻量级软件包,包含运行应用所需的一切)构建、传输和运行分布式应用。容器则可通过 Kubernetes 等容器管理平台进行部署和编排。(除了本文讨论的 Docker 容器技术之外,NGINX 还提供了面向 Kubernetes 的 NGINX 开源版 Ingress Controller 和 F5 NGINX Ingress Controller;NGINX Plus 用户免费享受支持。)

作为软件应用,NGINX 开源版F5 NGINX Plus 是 Docker 的绝佳用例,我们在 Docker 镜像仓库 Docker Hub 发布了 NGINX 开源软件镜像。这篇文章解释了如何:

 

简介

Docker 开放平台包括 Docker Engine,一种构建、运行和编排容器的开源引擎,还包括 Docker Hub,一种允许整个开发社区或特定组织内部人员分发、共享、协作使用 Docker 化应用的托管服务。

Docker 容器能够将应用与基础架构解耦,从而实开发人员专注于应用“业务逻辑”的实现。Docker 化的应用可以被实时移植到任何基础架构上,比如笔记本电脑、裸机服务器、虚拟机或云等,把它们变成可以轻松组装和重组到多功能分布式应用中的模块化组件,并持续进行实时创新。

有关 Docker 的更多信息,请参阅“为什么使用 Docker?”,或查阅完整的 Docker 文档

 

使用 NGINX 开源版 Docker 镜像

你可以使用来自 Docker Hub 的 NGINX 开源版镜像在 Docker 容器中创建 NGINX 实例。

我们先来看一个非常简单的示例。要启动在容器中运行并使用默认 NGINX 配置的 NGINX 实例,请运行以下命令:

# docker run --name mynginx1 -p 80:80 -d nginx
fcd1fb01b14557c7c9d991238f2558ae2704d129cf9fb97bb4fadf673a58580d

此命令根据 NGINX 镜像创建了一个名为 mynginx1 的容器。该命令返回了长格式的容器 ID,该 ID 可用于日志文件的名称;请查看“管理日志”

选项 -p 将 NGINX 镜像在容器中暴露的端口(端口 80)映射到 Docker 宿主机上的指定端口。第一个参数指定了 Docker 宿主机中的端口,第二个参数映射到容器中暴露的端口。

选项 -d 指定容器以分离模式运行,也就是说它会在结束前一直运行,但不会响应命令行上运行的命令。我们将在下一节解释如何与容器交互。

为了验证容器已经创建并正在运行,以及为了查看端口映射,我们运行 docker ps。(为方便阅读,我们将输出结果分成多行展示。)

# docker ps
CONTAINER ID IMAGE        COMMAND              CREATED        ...  
fcd1fb01b145 nginx:latest "nginx -g 'daemon of 16 seconds ago ... 

    ... STATUS          PORTS                NAMES
    ... Up 15 seconds   0.0.0.0:80->80/tcp   mynginx1

输出结果中的 PORTS 字段报告 Docker 宿主机上的端口 80 已映射到 Docker 容器中的端口 80。验证 NGINX 正在运行的另一种方法是向该端口发送 HTTP 请求。默认 NGINX 欢迎页面的代码显示为:

# curl http://localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
 body {
 width: 35em;
 margin: 0 auto;
 font-family: Tahoma, Verdana, Arial, sans-serif;
 }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully 
installed and working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="https://www.nginx-cn.net/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

 

使用 NGINX Docker 容器

现在我们有了一个运行中的 NGINX Docker 容器,但我们应该如何管理容器的内容和 NGINX 配置,以及如何管理日志呢?

关于 SSH 的说明

使用 SSH 连接到 NGINX 实例十分常见,但我们的 NGINX 镜像并没有安装 OpenSSH,因为 Docker 容器通常仅用于实现一个目的(此处是为了运行 NGINX)。因此,我们将使用 Docker 支持的其他方法。

作为以下管理内容和配置文件中命令的替代方法,您可以运行以下命令,打开一个交互式 shell,并将其放入正在运行的 NGINX 容器(而不是启动 SSH 会话)中。但是,我们只建议高级用户使用此方法。

管理内容和配置文件

你可以通过多种方式管理 NGINX 提供的内容和 NGINX 配置文件。我们来介绍几个选项。

选项 1 – 在 Docker 宿主机上维护 NGINX 的内容和配置

在创建容器时,我们可以指示 Docker 将 Docker 宿主机上的本地目录挂载到容器的目录中。NGINX 镜像使用默认的 NGINX 配置,该配置使用 /usr/share/nginx/html 作为容器的根目录,并将配置文件放在 /etc/nginx。对于内容位于本地目录 /var/www、配置文件位于 /var/nginx/conf 的 Docker 宿主机,运行以下命令(为方便阅读,输出结果在此处以多行展示):

# docker run --name mynginx2 --mount type=bind source=/var/www,target=/usr/share/nginx/html,readonly --mount type=bind,source=/var/nginx/conf,target=/etc/nginx/conf,readonly -p 80:80 -d nginx

现在,Docker 宿主机本地目录 /var/www/var/nginx/conf 中的文件所做的任何更改都将在容器目录 /usr/share/nginx/html/etc/nginx 中体现。readonly 选项意味着这些目录仅可以在 Docker 宿主机上更改,而不能在容器内更改。

选项 2 –从 Docker 宿主机复制文件

另一种选择是让 Docker 在容器创建期间从 Docker 宿主机的本地目录复制内容和配置文件。容器创建完成后,要维护文件,你可以在文件更改时创建一个新容器,也可以修改容器中的文件。有一种简单的方法来复制这些文件,即创建一个包含了命令的 Dockerfile 这些命令会在使用 Docker Hub 的 NGINX 镜像生成新 Docker 镜像期间运行。对于 Dockerfile 中的文件复制(COPY)命令,本地目录路径相对于 Dockerfile 所在的构建上下文。

在我们的示例中,内容位于 content 目录,配置文件位于conf 目录,这两个目录都是 Dockerfile 所在目录的子目录。NGINX 镜像包含了默认的 NGINX 配置文件 /etc/nginx/nginx.conf/etc/nginx/conf.d/default.conf。我们想要使用来自宿主机的配置文件,因此我们添加了一个 RUN 命令来删除默认文件:

FROM nginx
RUN rm /etc/nginx/nginx.conf /etc/nginx/conf.d/default.conf
COPY content /usr/share/nginx/html
COPY conf /etc/nginx

以下这些命令来自 Dockerfile 所在目录,我们运行这些命令从而创建自己的 NGINX 镜像。请注意命令末尾的句点(“.”)。它将当前目录定义为构建上下文,其中包含了 Dockerfile 和需要被复制的目录。

# docker build -t mynginx_image1 .

现在,我们运行以下命令,根据 mynginx_image1 镜像创建一个名为 mynginx3 的容器:

# docker run --name mynginx3 -p 80:80 -d mynginx_image1

如果我们想要更改容器中的文件,我们可以使用选项 3 中描述的辅助容器(helper container)。

选项 3 – 在容器中维护文件

我们在 “关于 SSH 的说明”部分提到,我们无法使用 SSH 连接 NGINX 容器,所以如果我们想要直接编辑内容或配置文件,就必须创建一个具有 shell 访问权限的辅助容器。为了让辅助容器能够访问这些文件,我们必须创建一个新的镜像,该镜像需要具有针对该镜像正确定义的 Docker 数据卷。假设我们想要像选项 2 一样复制文件,同时还要定义数据卷,我们可以使用以下 Dockerfile

FROM nginx
RUN rm /etc/nginx/nginx.conf /etc/nginx/conf.d/default.conf
COPY content /usr/share/nginx/html
COPY conf /etc/nginx
VOLUME /usr/share/nginx/html
VOLUME /etc/nginx

然后,我们运行以下命令,创建新的 NGINX 镜像(还是要注意末尾的句点):

# docker build -t mynginx_image2 .

现在我们运行以下命令,根据 mynginx_image2 镜像创建一个 NGINX 容器(mynginx4):

# docker run --name mynginx4 -p 80:80 -d mynginx_image2

然后,我们运行以下命令来启动一个具有 shell 的辅助容器 mynginx4_files,以访问刚刚创建的 mynginx4 容器的内容和配置目录:

# docker run -i -t --volumes-from mynginx4 --name mynginx4_files debian /bin/bash
root@b1cbbad63dd1:/#

新的 mynginx4_files 辅助容器在前台运行,并提供持久化的标准输入(-i 选项)和 tty(-t 选项)。mynginx4 中定义的所有数据卷都作为本地目录挂载到辅助容器中。

debian 参数表示辅助容器使用来自 Docker Hub 的 Debian 镜像。由于 NGINX 镜像也使用 Debian(到目前为止列举的所有示例都使用 NGINX 镜像),所以使用 Debian 运行辅助容器是最有效的,而不是让 Docker 加载另一个操作系统。

/bin/bash 参数表示 bash shell 在辅助容器中运行,并显示一个允许您按需修改文件的提示符。

如要启动和终止容器,请运行以下命令:

# docker start mynginx4_files
# docker stop mynginx4_files

如要退出 shell 但不终止容器的运行,请按 Ctrl+p,然后按 Ctrl+q。如要重新获得对正在运行的容器的 shell 访问权限,请运行以下命令:

# docker attach mynginx4_files

如要退出 shell 并终止容器,请运行 exit 命令。

管理日志

你可以配置默认或自定义日志。

使用默认日志

默认情况下,NGINX 镜像被配置为将主要的 NGINX 访问日志和错误日志发送到 Docker 日志收集器。方法是将它们分别链接到 stdoutstderr;然后,两个日志的所有信息都会被写入 Docker 宿主机上的文件 /var/lib/docker/containers/<container_ID>/<container_ID>-json.log,其中 <container_ID> 是创建容器时返回的长格式 ID。例如,在 “使用 NGINX 开源软件 Docker 镜像”部分创建的初始容器中,其 ID 为 fcd1fb01b14557c7c9d991238f2558ae2704d129cf9fb97bb4fadf673a58580d

要获取现有容器的容器 ID,请运行以下命令,其中 <container_name> 是创建容器时由 --name 参数设置的值(例如,对于上面的容器 Id,容器名称是 mynginx1):

# docker inspect --format '{{ .Id }}' <container_name>

尽管您可以直接打开 <container_ID>-json.log 文件查看日志,但通常运行以下命令更方便查看:

# docker logs <container_name>

您还可以通过向 Docker Unix socket 发出 GET 请求,使用 Docker Engine API 提取日志消息。以下命令将同时返回访问日志(表示为 stdout=1)和错误日志(stderr=1),但您也可以单独请求:

# curl --unix-socket /var/run/docker-sock http://localhost/containers/<container_name>/logs?stdout=1&stderr=1

如欲了解其他查询参数,请参阅 Docker Engine API 文档(在该页上搜索“获取容器日志”)。

使用自定义日志

如果您想要使用另一种日志收集方法,或者如果你想要在某个配置块(例如 server{}location{})中实施不同的日志配置,您可以为容器中存储日志文件的一个或多个目录定义 Docker 数据卷,创建一个用来访问日志文件的辅助容器,并使用你喜欢的任何日志工具。要实现这一点,你需要创建一个包含日志文件一个或多个数据卷的新镜像。

举例来说,如要将 NGINX 配置为在 /var/log/nginx/log 中存储日志文件,我们可以首先使用选项 3 中的 Dockerfile,并为该目录添加一个 VOLUME 定义:

FROM nginx
RUN rm /etc/nginx/nginx.conf /etc/nginx/conf.d/default.conf
COPY content /usr/share/nginx/html
COPY conf /etc/nginx
VOLUME /var/log/nginx/log

然后,我们可以创建一个如上所述的镜像,并用它来创建一个 NGINX 容器和一个用来访问日志目录的辅助容器。这个辅助容器可以安装任何所需的日志记录工具。

控制 NGINX

由于我们不能直接访问 NGINX 容器的命令行,我们无法使用 nginx 命令控制 NGINX。幸运的是,我们可以使用 signals 控制 NGINX,并且 Docker 提供了 kill 命令来向容器发送信号。

如要重载 NGINX 配置,请运行以下命令:

# docker kill -s HUP <container_name>

如要重启 NGINX,请运行以下命令重启容器:

# docker restart <container_name>

 

使用 Docker 部署 NGINX Plus

到目前为止,我们讨论了面向 NGINX 开源软件的 Docker,但它还可以与商用产品 NGINX Plus 一起使用,区别是我们首先要创建一个 NGINX Plus 镜像,因为 NGINX Plus 是商用产品,不在 Docker Hub 中提供。好在 NGINX Plus 的镜像很好创建。

注意:切勿将 NGINX Plus 镜像上传到 Docker Hub 等公共仓库,否则您就违反了许可协议要求

创建 NGINX Plus 的 Docker 镜像

要生成一个 NGINX Plus 镜像,首先需要创建一个 Dockerfile。我们在此提供的示例使用 Debian 11 (Bullseye)Alpine Linux 3.17 作为基础 Docker 镜像。在创建 NGINX Plus Docker 镜像之前,必须要先下载你的 nginx-repo.crtnginx-repo.key文件。NGINX Plus 客户请前往客户门户下载;NGINX Plus 免费试用版用户请前往试用包查看。将文件复制到 Dockerfile 所在的目录(Docker 构建上下文)。

与 NGINX 开源软件一样,NGINX Plus 访问日志和错误日志会被默认链接到 Docker 日志收集器。它没有指定任何数据卷,但你可以根据需要添加,或者使用每个 Dockerfile 创建基础镜像,你可以在这些镜像的基础上创建带有数据卷的新镜像(如上所述)。

我们特意不在示例 Dockerfiles 中指定 NGINX Plus 版本,这样你在更新到新版 NGINX Plus 后就不用编辑该文件了。但如果你想指定文件版本,我们提供了相关说明的注释版本,以方便你取消注释。

同样,我们也提供了安装 NGINX Plus 官方动态模块的说明(见已注释掉的部分)。

默认情况下,创建容器时不会从 Docker 宿主机复制任何文件。你可以向每个 Dockerfile 添加 COPY 定义,也可以将你创建的镜像用作另一个镜像的基础(如上所述)。

NGINX Plus Dockerfile (Debian 11)

Failed loading gist https://gist.github.com/36e97fc87efb5cf0039978c8e41a34b5.json: timeout

NGINX Plus Dockerfile (Alpine Linux 3.17)

Failed loading gist https://gist.github.com/36e97fc87efb5cf0039978c8e41a34b5.json: timeout

创建 NGINX Plus 镜像

Dockerfilenginx-repo.crtnginx-repo.key 文件位于同一目录之后,运行以下命令,创建一个名为 nginxplus 的镜像(同样需要注意末尾的句点):

# DOCKER_BUILDKIT=1 docker build --no-cache -t nginxplus --secret id=nginx-crt,src=</path/to/your/nginx-repo.crt> --secret id=nginx-key,src=</path/to/your/nginx-repo.key> .

DOCKER_BUILDKIT=1 这个参数表明我们正在使用 Docker BuildKit 来构建镜像,当包含下面提到的 --secret 选项时,这个参数设置是必需的。

--no-cache 选项指示 Docker 从头开始构建镜像,并确保安装最新版本的 NGINX Plus。如果 Dockerfile 之前用于构建过镜像,并且没有 --no-cache 选项, 那么新镜像将使用来自 Docker 缓存的 NGINX Plus 版本。(我们特意不在 Dockerfile 中指定版本,这样你就不需要在每次推出新版 NGINX Plus 后更新该文件。)如果可以使用来自此前构建的镜像的 NGINX Plus 版本,则删除 --no-cache 选项

--secret 选项将 NGINX Plus 许可证的证书和密钥传递给 Docker 来构建上下文环境,并且不会泄露数据或让数据永久存储在 Docker 构建层之间。id 的参数值只能再改变根 Dockerfile 的情况下更改,但是您需要将 src 参数设置为您 NGINX Plus 证书和密钥文件的路径(如果您按照前面的说明操作,这个路径就是您构建 Docker 镜像的文件夹的路径)。

docker images nginxplus 命令的如下输出结果表明镜像创建成功:

# docker images nginxplus
REPOSITORY  TAG     IMAGE ID      CREATED        VIRTUAL SIZE
nginxplus   latest  ef2bf65931cf  6 seconds ago  91.2 MB

要根据此镜像创建一个名为 mynginxplus 的容器,请运行以下命令:

# docker run --name mynginxplus -p 80:80 -d nginxplus

NGINX Plus 容器也可以像 NGINX 开源容器一样进行控制和管理。

 

总结

NGINX、NGINX Plus 和 Docker 能够高效地协同工作。无论您是使用来自 Docker Hub 的 NGINX 开源软件镜像,还是创建您自己的 NGINX Plus 镜像,您都可以轻松地在 Docker 容器中启动 NGINX 和 NGINX Plus 的新实例并将它们部署到 Kubernetes 环境中。您还可以使用基础镜像轻松创建新的 Docker 镜像,以便更轻松地控制和管理容器。请确保您的订阅包括在 Docker 容器中运行的所有 NGINX Plus 实例。如欲了解详细信息,请联系 NGINX 销售团队

Docker 的功能还有很多,本文将不再一一介绍。有关更多信息,请下载我们免费的 O’Reilly 电子书:《容器网络:从 Docker 到 Kubernetes》,或访问 www.docker.com