题 如何在Nginx.conf中使用环境变量


[交叉发布和编辑下来 https://stackoverflow.com/questions/21933955 因为它被认为对于StackOverflow来说太像sysadmin了。]

我有一个运行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

root 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

143
2018-02-21 15:02




这不是环境变量的通用解决方案,但是如果您想要将环境变量用于上游服务器的主机名/ IP地址,请注意Docker(至少在最近的版本中)为您修改/ etc / hosts。看到 docs.docker.com/userguide/dockerlinks    这意味着,如果您的链接容器名为“app_web_1”,则docker将在您的Nginx容器中的/ etc / hosts中创建一行。所以你可以替换 server $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"}:5000;    同 server app_web_1:5000; - mozz100
谢谢@ mozz100 - 这非常有用 - 在这种情况下,/ etc / hosts条目比env vars更有效。唯一缺失的是如果重新启动上游容器并获取新IP会发生什么。我假设子容器仍然指向原始IP,而不是新IP? - Hugo Rodger-Brown
是的,如果你重新开始 app_web_1 它会得到一个新的IP地址,所以你也需要重新启动你的nginx容器。 Docker会通过更新重新启动它 /etc/hosts 所以你不需要改变nginx配置文件。 - mozz100


答案:


如果您正在使用链接,则docker会设置环境变量并为链接容器添加别名 /etc/hosts。如果你能够硬编码端口(或者它只是端口80)你可以简单地做:

upstream gunicorn {
    server linked-hostname:5000;
}

该端口仅在环境变量中可用,不能在该变量中使用 upstream 模块,也不在服务器或位置块中。它们只能在主配置中引用,这对您没有帮助。你可以这样做 开放式的捆绑 包括Lua。

如果你不想使用openresty / Lua,另一种选择是在容器启动时进行一些替换。您的 docker run 命令可以创建链接然后运行执行适当替换的包装器脚本:

#!/bin/bash
/usr/bin/sed -i "s/server<gunicorn_server_placeholder>/${APP_WEB_1_PORT_5000_TCP_ADDR}/" default
start nginx

56
2018-03-18 06:14



是的 - 当前(工作)解决方案就是这样。看起来有点hacky。 (也就是说,它只是一个本地开发环境,因此黑客攻击不是一个大问题。) - Hugo Rodger-Brown
我碰到了这个,所以我做了一个更通用的 nginx容器 这会自动扩展配置中的环境变量。 - Shepmaster


 官方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 file作为Nginx服务器的命令选项,我命名了它 run_nginx.sh

#!/usr/bin/env bash
export DOLLAR='$'
envsubst < nginx.conf.template > /etc/nginx/nginx.conf
nginx -g "daemon off;"

而且因为定义了新的 DOLLAR 变量 run_nginx.sh 脚本,现在我的内容 nginx.conf.template Nginx本身变量的文件是这样的:

proxy_set_header        X-Forwarded-Host ${DOLLAR}host;

对于我定义的变量是这样的:

server_name  ${WEB_DOMAIN} www.${WEB_DOMAIN};

这里,有我的真实用例。


61
2018-02-11 12:39



这会杀死像nginx这样的东西 proxy_set_header Host $http_host; - shredding
@shredding你可以传入要替换的变量名称 - 其他的不被触及: command: /bin/bash -c "envsubst '$VAR1 $VAR2' < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'" 为我工作b / c我知道他们叫什么... - pkyeck
好的,忘了逃避 $, 应该 command: /bin/bash -c "envsubst '\$VAR1 \$VAR2' < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'" - pkyeck
考虑到转义这个在你自己的Dockerfile中工作: CMD ["/bin/sh","-c", "if [ -n \"${SOME_ENV}\" ]; then echo envsubst '${SOME_ENV}' with ${SOME_ENV} && envsubst '${SOME_ENV}' < /etc/nginx/conf.d/default.template > /etc/nginx/conf.d/default.conf; fi ; nginx -g 'daemon off;'"] - KCD
这是一个很好的解决方案。谢谢。还有上面提到的链接 github.com/docker-library/docs/issues/496 有一个很好的解决方案。 - kabirbaidhya


使用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/


26
2017-12-01 16:26



你可能需要 env NGINX_SERVERNAME 你的nginx.conf中的某个地方。 - hiroshi
这对我来说不起作用,虽然我的nginx docker图像中有lua模块。这可能与我在我的nginx.conf中包含配置文件的事实有关吗?我想要 set_by_lua 包含的配置文件中的变量,而 env MY_VAR 声明是在主要的nginx.conf中,如建议的那样。真可惜,这本来是最干净的解决方案! - pederpansen
有谁知道使用这种方法的利弊 envsubst?我想亲们是你不需要运行的 envsubstr 在启动服务器之前命令,con是你需要安装lua模块吗?我想知道这两种方法是否都有安全隐患? - Mark Winterbottom
@MarkWinterbottom尚未对此进行测试,但看起来您不必授予对nginx配置文件的写入权限,您必须使用 envsubst 在我的情况下哪个是禁止的 - Griddo


我写了一些可能有用或可能没有帮助的东西: https://github.com/yawn/envplate

它内联编辑配置文件,其中包含对环境变量的$ {key}引用,可选择创建备份/记录它的功能。它是用Go编写的,可以从Linux和MacOS的发布选项卡中简单地下载生成的静态二进制文件。

它还可以exec()进程,替换默认值,日志并具有明智的失败语义。


14
2017-11-15 16:02





我做的是使用 ERB

cat nginx.conf  | grep -i error_log

error_log <%= ENV["APP_ROOT"] %>/nginx/logs/error.log;

- 使用erb后

export APP_ROOT=/tmp

erb nginx.conf  | grep -i error_log

error_log /tmp/nginx/logs/error.log;

这用于 Cloudfoundry staticfile-buildpack

示例nginx配置: https://github.com/cloudfoundry/staticfile-buildpack/blob/master/conf/nginx.conf

在你的情况下

head /etc/nginx/nginx.conf
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;
env APP_WEB_1_PORT_5000_TCP_ADDR;
upstream gunicorn {
    server $APP_HOST_NAME:$APP_HOST_PORT;
}

成为

head /etc/nginx/nginx.conf
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;
env <%= ENV["APP_WEB_1_PORT_5000_TCP_ADDR"] %>
upstream gunicorn {
    server <%= ENV["APP_HOST_NAME"] %>:<%= ENV["APP_HOST_PORT"] %>
}

#After applying erb

export APP_WEB_1_PORT_5000_TCP_ADDR=12.12.12.12
export APP_HOST_NAME=test
export APP_HOST_PORT=7089 

erb /etc/nginx/nginx.conf

head /etc/nginx/nginx.conf
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;
env 12.12.12.12
upstream gunicorn {
    server test: 7089
}

5
2018-02-20 00:11



你能扩展这个答案吗?值得注意的是,上面这个问题已经有了一个已经被接受的答案,但是如果你觉得你的答案增加了价值,那么请扩展它。 - BE77Y


参考关于使用erb的答案,可以如下完成。

将NGINX配置文件写为包含环境变量的erb文件,并使用erb命令将其评估为普通配置文件。

erb nginx.conf.erb > nginx.conf

在nginx.conf.erb文件的服务器块内部可能存在

listen <%= ENV["PORT"] %>;

4
2017-08-01 11:39



我是否需要在容器内安装Ruby? (如果没有,怎么样 erb 能够进行变量替换...在容器启动后,对吗?) - erb 这个Ruby的东西是对的: stuartellis.name/articles/erb - KajMagnus
它是标准Ruby安装附带的命令行工具。如果您没有在容器中使用它,请尝试其他选项。 - Ruifeng Ma
好的,谢谢你的解释 - KajMagnus


我知道这是一个老问题,但万一有人偶然发现(正如我现在所做的那样),有一个更好的方法来做到这一点。因为docker在/ etc / hosts中插入链接容器的别名,所以你可以这样做

upstream upstream_name {
    server docker_link_alias;
}

假设你的docker命令是这样的 docker run --link othercontainer:docker_link_alias nginx_container


3
2018-03-17 05:16



您可以使用此方法获取主机,但不能使用端口。您必须硬编码端口或假设它使用端口80。 - Ben Whaley