我有一个运行 Nginx 的 docker 容器,它链接到另一个 docker 容器。第二个容器的主机名和 IP 地址在启动时作为环境变量加载到 Nginx 容器中,但在此之前并不知道(它是动态的)。我希望nginx.conf
使用这些值 - 例如
upstream gunicorn {
server $APP_HOST_NAME:$APP_HOST_PORT;
}
如何在启动时将环境变量放入 Nginx 配置中?
编辑1
这是整个文件,在下面建议的答案之后:
env APP_WEB_1_PORT_5000_TCP_ADDR;
# Nginx host configuration for django_app
# Django app is served by Gunicorn, running under port 5000 (via Foreman)
upstream gunicorn {
server $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"}:5000;
}
server {
listen 80;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location /static/ {
alias /app/static/;
}
location /media/ {
alias /app/media/;
}
location / {
proxy_pass http://gunicorn;
}
}
重新加载 nginx 然后出现错误:
$ nginx -s reload
nginx: [emerg] unknown directive "env" in /etc/nginx/sites-enabled/default:1
编辑2:更多细节
当前环境变量
root@87ede56e0b11:/# env | grep APP_WEB_1
APP_WEB_1_NAME=/furious_turing/app_web_1
APP_WEB_1_PORT=tcp://172.17.0.63:5000
APP_WEB_1_PORT_5000_TCP=tcp://172.17.0.63:5000
APP_WEB_1_PORT_5000_TCP_PROTO=tcp
APP_WEB_1_PORT_5000_TCP_PORT=5000
APP_WEB_1_PORT_5000_TCP_ADDR=172.17.0.63
根 nginx.conf:
root@87ede56e0b11:/# head /etc/nginx/nginx.conf
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;
env APP_WEB_1_PORT_5000_TCP_ADDR;
站点nginx配置:
root@87ede56e0b11:/# head /etc/nginx/sites-available/default
# Django app is served by Gunicorn, running under port 5000 (via Foreman)
upstream gunicorn {
server $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"}:5000;
}
server {
listen 80;
重新加载 nginx 配置:
root@87ede56e0b11:/# nginx -s reload
nginx: [emerg] directive "server" is not terminated by ";" in /etc/nginx/sites-enabled/default:3
答案1
这里是另一个带有 envsubst 的例子。
nginx
docker image 已经运行过了envsubst
。/docker-entrypoint.d/20-envsubst-on-templates.sh
你只需要在合适的位置放置一个模板文件:/etc/nginx/templates/my-file.conf.template
。
然后,您可以创建一个专用template
文件,仅用于包含一个或多个map
子句的变量,我们将使用这些子句作为自定义变量。您必须为该conf.d
文件选择一个名称,以便它是 nginx 第一个采用的名称。
这是用 docker-compose 和三个文件解释的基本概念:
docker-compose.yml:
version: "3.9"
services:
nginx:
image: nginx:1.23
volumes:
- ./template-variables:/etc/nginx/templates/10-variables.conf.template:ro
environment:
EXTERNAL_IP: "1.2.3.4"
模板变量:
map $host $external_ip {
default "$EXTERNAL_IP";
}
nginx.conf:
server {
location / {
proxy_set_header X-Real-IP $external_ip;
}
}
答案2
从官方Nginx docker 文件:
在 nginx 配置中使用环境变量:
开箱即用,Nginx 不支持在大多数配置块中使用环境变量。
但是
envsubst
如果您需要在 nginx 启动之前动态生成 nginx 配置,则可以将其用作解决方法。以下是使用 docker-compose.yml 的示例:
image: nginx
volumes:
- ./mysite.template:/etc/nginx/conf.d/mysite.template
ports:
- "8080:80"
environment:
- NGINX_HOST=foobar.com
- NGINX_PORT=80
command: /bin/bash -c "envsubst < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"
mysite.template 文件可能包含如下变量引用:
listen ${NGINX_PORT};
更新:
但是您知道这会导致其 Nginx 变量如下:
proxy_set_header X-Forwarded-Host $host;
损坏至:
proxy_set_header X-Forwarded-Host ;
因此,为了防止这种情况,我使用了这个技巧:
我有一个运行 Nginx 的脚本,它在文件上用作docker-compose
Nginx 服务器的命令选项,我将其命名为run_nginx.sh
:
#!/usr/bin/env bash
export DOLLAR='$'
envsubst < nginx.conf.template > /etc/nginx/nginx.conf
nginx -g "daemon off;"
并且由于在脚本上定义了新的DOLLAR
变量,现在我的Nginx 本身变量的文件内容如下:run_nginx.sh
nginx.conf.template
proxy_set_header X-Forwarded-Host ${DOLLAR}host;
我定义的变量是这样的:
server_name ${WEB_DOMAIN} www.${WEB_DOMAIN};
还这里,这是我的真实用例。
答案3
官方 nginx 镜像建议使用envsubst
, 但正如其他人指出的那样它将替换掉$host
其他变量,这是不可取的。但幸运的envsubst
是,将要替换的变量名称作为参数。
为了避免向容器添加非常复杂的命令参数(如链接的示例所示),您可以编写一个 Docker 入口点脚本,该脚本将在执行命令之前填写环境变量。入口点脚本也是验证参数和设置默认值的好地方。
这是一个 nginx 容器的示例,它将API_HOST
参数API_PORT
作为环境变量。
nginx-default.conf.模板
resolver 127.0.0.11 valid=10s; # recover from the backend's IP changing
server {
listen 80;
location / {
root /usr/share/nginx/html;
}
location /api {
proxy_pass http://${API_HOST}:${API_PORT};
proxy_set_header Host $http_host;
}
}
docker-entrypoint.sh
#!/usr/bin/env sh
set -eu
envsubst '${API_HOST} ${API_PORT}' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf
exec "$@"
Dockerfile
FROM nginx:1.15-alpine
COPY nginx-default.conf.template /etc/nginx/conf.d/default.conf.template
COPY docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]
答案4
使用 Lua 来实现这一点比听起来要简单得多:
server {
set_by_lua $server_name 'return os.getenv("NGINX_SERVERNAME")';
}
我在这里发现了:
https://docs.apitools.com/blog/2014/07/02/using-environment-variables-in-nginx-conf.html
编辑:
显然这需要安装 lua 模块:https://github.com/openresty/lua-nginx-module
编辑2:
请注意,使用此方法您必须env
在 Nginx 中定义变量:
env ENVIRONMENT_VARIABLE_NAME
您必须在 的顶层上下文中执行此操作,nginx.conf
否则它将不起作用! 不能在 服务器块中或 的某些站点的配置中执行此操作/etc/nginx/sites-available
,因为它包含在 上下文nginx.conf
中http
(不是顶层上下文)。
另请注意,如果您尝试使用此方法进行重定向,例如:
server {
listen 80;
server_name $server_name;
return 301 https://$server_name$request_uri;
}
它也不会起作用:
2016/08/30 14:49:35 [emerg] 1#0: the duplicate "server_name" variable in /etc/nginx/sites-enabled/default:8
如果你给它一个单独的变量名:
set_by_lua $server_name_from_env 'return os.getenv("NGINX_SERVERNAME")';
server {
listen 80;
server_name $server_name_from_env;
return 301 https://$server_name$request_uri;
}
nginx 不会解释它并会将您重定向到https://%24server_name_from_env/
。