从 docker-compose 替换 NGINX 配置中的环境变量
Posted
技术标签:
【中文标题】从 docker-compose 替换 NGINX 配置中的环境变量【英文标题】:Substitute environment variables in NGINX config from docker-compose 【发布时间】:2019-11-01 03:01:16 【问题描述】:我正在尝试在通过 docker-compose 配置的 docker 容器中启动 nginx 服务器。但是,问题是我想在 http 部分中替换一个环境变量,特别是在“上游”块中。
让这个工作真棒,因为我还有几个其他容器都是通过环境变量配置的,而且我有大约 5 个环境需要在任何给定时间运行。我尝试过使用“envsubst”(按照官方 NGINX 文档的建议)、perl_set 和 set_by_lua,但是它们似乎都没有工作。
下面是 NGINX 配置,因为它是我最近一次试用后的样子
user nginx;
worker_processes 1;
env NGINXPROXY;
load_module modules/ngx_http_perl_module.so;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events
worker_connections 1024;
http
perl_set $nginxproxy 'sub return $ENV"NGINXPROXY"; ';
upstream api-upstream
server $nginxproxy;
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile off;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
下面是 NGINX dockerfile
# build stage
FROM node:latest
WORKDIR /app
COPY ./ /app
RUN npm install
RUN npm run build
# production stage
FROM nginx:1.17.0-perl
COPY --from=0 /app/dist /usr/share/nginx/html
RUN apt-get update && apt-get install -y gettext-base
RUN rm /etc/nginx/conf.d/default.conf
RUN rm /etc/nginx/nginx.conf
COPY default.conf /etc/nginx/conf.d
COPY nginx.conf /etc/nginx
RUN mkdir /certs
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]
以下是 NGINX 服务器的 docker-compose.yml 部分(名称和 IP 已更改)。 envsubst 命令在我的故障排除中被有意注释掉了。
front-end:
environment:
- NGINXPROXY=172.31.67.100:9300
build: http://gitaccount:password@gitserver.com/group/front-end.git#develop
container_name: qa_front_end
image: qa-front-end
restart: always
networks:
qa_network:
ipv4_address: 172.28.0.215
ports:
- "9080:80"
# command: /bin/bash -c "envsubst '$$NGINXPROXY' < /etc/nginx/nginx.conf > /etc/nginx/nginx.conf && nginx -g 'daemon off;'"
似乎正在发生的事情是,当我在上游块中引用 $nginxproxy 变量时(就在“服务器”之后),我得到的输出使它看起来像是在引用字符串文字“$nginxproxy”而不是替换值变量。
qa3_front_end | 2019/06/18 12:35:36 [emerg] 1#1: host not found in upstream "$nginx_upstream" in /etc/nginx/nginx.conf:19
qa3_front_end | nginx: [emerg] host not found in upstream "$nginx_upstream" in /etc/nginx/nginx.conf:19
qa3_front_end exited with code 1
当我尝试使用 envsubst 时,我收到一个错误,听起来像是命令与 nginx.conf 文件的格式混淆了
qa3_front_end | 2019/06/18 12:49:02 [emerg] 1#1: no "events" section in configuration
qa3_front_end | nginx: [emerg] no "events" section in configuration
qa3_front_end exited with code 1
我很困惑,所以提前感谢您的帮助。
【问题讨论】:
【参考方案1】:Since nginx 1.19 您现在可以通过 docker-compose 在配置中使用环境变量。我使用了以下设置:
# file: docker/nginx/templates/default.conf.conf
upstream api-upstream
server $API_HOST;
# file: docker-compose.yml
services:
nginx:
image: nginx:1.19-alpine
volumes:
- "./docker/nginx/templates:/etc/nginx/templates/"
environment:
NGINX_ENVSUBST_TEMPLATE_SUFFIX: ".conf"
API_HOST: api.example.com
我将从文档中的示例中稍微偏离脚本。注意模板文件上额外的.conf
扩展名——这不是错字。在 nginx 映像的文档中,建议为文件命名,例如,default.conf.template
。启动时,脚本将获取该文件,替换环境变量,然后使用原始文件名将文件输出到 /etc/nginx/conf.d/,删除 .template
后缀。
默认情况下,后缀是.template
,但这会破坏语法高亮,除非你配置你的编辑器。相反,我将.conf
指定为模板后缀。如果您仅将文件命名为 default.conf
,则结果将是一个名为 /etc/nginx/conf.d/default 的文件,并且您的站点将无法按预期提供。
【讨论】:
只是一个快速的澄清点:这个文件需要在 nginx 容器的模板目录中。如果您使用./docker/nginx/conf.d/:/etc/nginx/conf.d/
docker compose 卷来管理此文件,则需要将其更改为 ./docker/nginx/templates/:/etc/nginx/templates/
@JosephMarikle 好主意,我已经更新了示例以反映您的评论
是否需要手动将模板文件复制到 /etc/nginx/templates 中?我正在遵循这个设置,但它没有复制到模板中。我的模板文件夹是空的。
在本例中,您在一个文件夹中有一个 docker-compose.yml,其中 ./docker/nginx/templates/ 包含与您主机上的模板相关的模板。
如果您将docker-compose.yml
提交给版本控制并希望使用单独的未跟踪.env
文件来维护特定于环境的变量,docker-compose 会将您的.env
变量替换为@ 987654334@ 在它启动你的容器之前。所以docker-compose.yml
可以指定环境变量为API_HOST: $API_HOST
,而.env
可以设置实际值:API_HOST=api.example.com
。对 docker-compose 专家来说可能是显而易见的,但我花了几个小时才弄清楚。【参考方案2】:
您可以通过定义自己的入口点来避免使用 Compose 解释环境变量的一些麻烦。看这个简单的例子:
entrypoint.sh(确保该文件可执行)#!/bin/sh
export NGINXPROXY
envsubst '$NGINXPROXY' < /config.template > /etc/nginx/nginx.conf
exec "$@"
docker-compose.yml
version: "3.7"
services:
front-end:
image: nginx
environment:
- NGINXPROXY=172.31.67.100:9300
ports:
- 80:80
volumes:
- ./config:/config.template
- ./entrypoint.sh:/entrypoint.sh
entrypoint: ["/entrypoint.sh"]
command: ["nginx", "-g", "daemon off;"]
我的config
文件与您的nginx.conf
具有相同的内容,除了我必须使用 Perl 模块注释这些行。
请注意,我必须先将我的配置文件挂载到另一个位置,然后才能envsubst
。我遇到了一些奇怪的行为,即替换后文件最终为空,这种方法可以避免这种情况。在您的特定情况下,这应该不是问题,因为您已经在构建时将其嵌入到您的图像中。
编辑
为了完整起见,要尽可能少地更改您的设置,您只需确保您的环境变量为export
。像这样调整你的命令:
command: ["/bin/bash", "-c", "export NGINXPROXY && envsubst '$$NGINXPROXY' < /etc/nginx/nginx.conf > /etc/nginx/nginx.conf && nginx -g 'daemon off;'"]
...你应该很高兴。不过,我总是会推荐使用“更简洁”的方式来定义自己的入口点。
【讨论】:
我可能会在 Dockerfile 中COPY
这个脚本并将其指定为映像的 ENTRYPOINT
,而不是仅在运行时覆盖它。【参考方案3】:
因此,在解决了这个问题之后,我设法让它的工作方式与 bellackn 提供的答案类似。我将在这里发布我的确切解决方案,以防其他人需要参考完整的解决方案。
步骤 1:按照通常的编写方式编写 nginx.conf 或 default.conf。将文件另存为“nginx.conf.template”或“default.conf.template”,具体取决于您尝试将变量替换为哪个。
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events
worker_connections 1024;
http
upstream api-upstream
server 192.168.25.254;
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile off;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
第 2 步:将格式为 $VARNAME 的变量替换为您要替换为环境变量的任何值:
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events
worker_connections 1024;
http
upstream api-upstream
server $SERVER_NAME;
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile off;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
第 3 步:在您的 docker-file 中,将您的 nginx 配置文件(您的 nginx.conf.template 或 default.conf.template)复制到容器中的适当位置:
# build stage
FROM node:latest
WORKDIR /app
COPY ./ /app
RUN npm install
RUN npm run build
# production stage
FROM nginx:1.17.0-perl
COPY --from=0 /app/dist /usr/share/nginx/html
RUN apt-get update && apt-get install -y gettext-base
RUN rm /etc/nginx/conf.d/default.conf
RUN rm /etc/nginx/nginx.conf
#-----------------------------------#
|COPY default.conf /etc/nginx/conf.d|
|COPY nginx.conf.template /etc/nginx|
#-----------------------------------#
RUN mkdir /certs
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]
第 4 步:使用“环境”部分标签在 docker-compos.yml 文件中设置环境变量。确保您的环境变量名称与您在 nginx 配置文件中选择的任何变量名称匹配。使用 docker 容器中的“envsubt”命令将变量值替换为 nginx.conf.template 中的变量,并将输出写入正确位置的名为 nginx.conf 的文件。这可以通过使用“命令”部分标签在 docker-compose.yml 文件中完成:
version: '2.0'
services:
front-end:
environment:
- SERVER_NAME=172.31.67.100:9100
build: http://git-account:git-password@git-server.com/project-group/repository-name.git#branch-ame
container_name: qa_front_end
image: qa-front-end-vue
restart: always
networks:
qa_network:
ipv4_address: 172.28.0.215
ports:
- "9080:80"
command: >
/bin/sh -c
"envsubst '
$$SERVER_NAME
'< /etc/nginx/nginx.conf.template
> /etc/nginx/nginx.conf
&& nginx -g 'daemon off;'"
第 5 步:使用 docker-compose up 运行您的堆栈(使用您需要的任何其他开关),您的 nginx 服务器现在应该以您在 docker-compose.yml 的“环境”部分中提供的任何值开始
正如上面的解决方案中提到的,您也可以定义自己的入口点,但是这个解决方案也被证明工作得很好,并且将所有内容都包含在一个配置文件中,让我能够运行一堆服务直接来自 git,只有一个 docker-compose.yml 文件。
非常感谢所有花时间准备的人,感谢您花时间帮助我解决问题。
【讨论】:
我得到这个错误:`错误:服务'服务器'构建失败:复制失败:文件在构建上下文中找不到或被.dockerignore排除:stat nginx.conf.template:文件不存在`。我已经尝试了很多方法来保存 nginx.conf.template 并将这个文件检入到容器中,它存在。我需要使用哪些 docker-compose 配置? 我会看看 bellackn 的回答。这是一个比我尝试做的简单得多的设置。【参考方案4】:就像explained in Jody's answer 一样,现在官方的 Nginx Docker 镜像支持解析模板。这使用envsubst
和its handling 确保不会与Nginx variables 混淆,例如$host
等等。好的。但是,envsubst
不支持像常规 shell 和 Docker Compose 在使用 $MY_VAR:-My Default
时所做的默认值。因此,即使使用默认值,这个内置模板总是需要对所有变量进行完整设置。
要在图像本身中定义默认值,可以使用自定义入口点首先设置默认值,然后简单地委托给the original entrypoint。喜欢docker-defaults.sh
:
#!/usr/bin/env sh
set -eu
# As of version 1.19, the official Nginx Docker image supports templates with
# variable substitution. But that uses `envsubst`, which does not allow for
# defaults for missing variables. Here, first use the regular command shell
# to set the defaults:
export PROXY_API_DEST=$PROXY_API_DEST:-http://host.docker.internal:8000/api/
# Due to `set -u` this would fail if not defined and no default was set above
echo "Will proxy requests for /api/* to $PROXY_API_DEST*"
# Finally, let the original Nginx entry point do its work, passing whatever is
# set for CMD. Use `exec` to replace the current process, to trap any signals
# (like Ctrl+C) that Docker may send it:
exec /docker-entrypoint.sh "$@"
还有一些docker-nginx-default.conf
:
# After variable substitution, this will replace /etc/nginx/conf.d/default.conf
server
listen 80;
listen [::]:80;
server_name localhost;
location /
root /usr/share/nginx/html;
index index.html index.htm;
location /api/
# Proxy API calls to another destination; the default for the variable is
# set in docker-defaults.sh
proxy_pass $PROXY_API_DEST;
在 Dockerfile 中将模板复制到/etc/nginx/templates/default.conf.template
并设置自定义入口点:
FROM nginx:stable-alpine
...
# Each time Nginx is started it will perform variable substition in all template
# files found in `/etc/nginx/templates/*.template`, and copy the results (without
# the `.template` suffix) into `/etc/nginx/conf.d/`. Below, this will replace the
# original `/etc/nginx/conf.d/default.conf`; see https://hub.docker.com/_/nginx
COPY docker-nginx-default.conf /etc/nginx/templates/default.conf.template
COPY docker-defaults.sh /
# Just in case the file mode was not properly set in Git
RUN chmod +x /docker-defaults.sh
# This will delegate to the original Nginx `docker-entrypoint.sh`
ENTRYPOINT ["/docker-defaults.sh"]
# The default parameters to ENTRYPOINT (unless overruled on the command line)
CMD ["nginx", "-g", "daemon off;"]
现在使用,例如,docker run --env PROXY_API_DEST=https://example.com/api/ ...
将设置一个值,在此示例中,如果未设置,则默认为 http://host.docker.internal:8000/api/
(在本地计算机上实际上是 http://localhost:8000/api/
)。
【讨论】:
以上是关于从 docker-compose 替换 NGINX 配置中的环境变量的主要内容,如果未能解决你的问题,请参考以下文章
Nginx 从 docker-compose 运行返回“在上游找不到主机”
使用 nginx 将环境变量从 docker-compose 传递到 vue 应用程序
Docker 网络 - nginx:在上游找不到 [emerg] 主机