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

Hug*_*own 287 nginx docker

我有一个运行 Nginx 的 docker 容器,它链接到另一个 docker 容器。第二个容器的主机名和 IP 地址在启动时作为环境变量加载到 Nginx 容器中,但在此之前不知道(它是动态的)。我希望我nginx.conf使用这些值 - 例如

upstream gunicorn {
    server $APP_HOST_NAME:$APP_HOST_PORT;
}
Run Code Online (Sandbox Code Playgroud)

如何在启动时将环境变量添加到 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

重新加载nginx然后错误:

$ nginx -s reload
nginx: [emerg] unknown directive "env" in /etc/nginx/sites-enabled/default:1
Run Code Online (Sandbox Code Playgroud)

编辑 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
Run Code Online (Sandbox Code Playgroud)

根 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;
Run Code Online (Sandbox Code Playgroud)

站点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;
Run Code Online (Sandbox Code Playgroud)

重新加载nginx配置:

root@87ede56e0b11:/# nginx -s reload
nginx: [emerg] directive "server" is not terminated by ";" in /etc/nginx/sites-enabled/default:3
Run Code Online (Sandbox Code Playgroud)

Omi*_*aha 160

来自官方 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;'" 
Run Code Online (Sandbox Code Playgroud)

然后 mysite.template 文件可能包含这样的变量引用:

listen ${NGINX_PORT};
Run Code Online (Sandbox Code Playgroud)

更新:

但是你知道这会导致它的 Nginx 变量是这样的:

proxy_set_header        X-Forwarded-Host $host;
Run Code Online (Sandbox Code Playgroud)

损坏到:

proxy_set_header        X-Forwarded-Host ;
Run Code Online (Sandbox Code Playgroud)

所以,为了防止这种情况,我使用这个技巧:

我有一个运行 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;"
Run Code Online (Sandbox Code Playgroud)

并且由于DOLLARrun_nginx.sh脚本上定义了新变量,现在我nginx.conf.template的 Nginx 自身变量文件的内容是这样的:

proxy_set_header        X-Forwarded-Host ${DOLLAR}host;
Run Code Online (Sandbox Code Playgroud)

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

server_name  ${WEB_DOMAIN} www.${WEB_DOMAIN};
Run Code Online (Sandbox Code Playgroud)

同样在这里,有我真正的用例。

  • @shredding 您可以传入要替换的变量名称 - 其他人不会被触及:`command: /bin/bash -c "envsubst '$VAR1 $VAR2' &lt; /etc/nginx/conf.d/mysite.template &gt; /etc /nginx/conf.d/default.conf &amp;&amp; nginx -g 'daemon off;'"` 对我有用 b/c 我知道它们叫什么...... (33认同)
  • 好的,忘了转义`$`,应该是`command: /bin/bash -c "envsubst '\$VAR1 \$VAR2' &lt; /etc/nginx/conf.d/mysite.template &gt; /etc/nginx/ conf.d/default.conf &amp;&amp; nginx -g '守护进程关闭;'"` (7认同)
  • 在 docker-compose.yml 中转义 $ 应该用另一个 $ 来完成。因此,该命令应如下所示:`command: /bin/bash -c "envsubst '$${VAR1} $${VAR2}' &lt; /etc/nginx/conf.d/mysite.template &gt; /etc/nginx /conf.d/default.conf &amp;&amp; nginx -g '守护进程关闭;'"` (4认同)
  • 这会杀死像 `proxy_set_header Host $http_host;` 这样的 nginx confs (3认同)
  • 考虑到在您自己的 Dockerfile 中转义此工作:`CMD ["/bin/sh","-c", "if [ -n \"${SOME_ENV}\" ]; then echo envsubst '${SOME_ENV}'使用 ${SOME_ENV} &amp;&amp; envsubst '${SOME_ENV}' &lt; /etc/nginx/conf.d/default.template &gt; /etc/nginx/conf.d/default.conf; fi ; nginx -g 'daemon off;' "]` (2认同)
  • 这是一个很好的解决方案。谢谢。上面提到的链接 https://github.com/docker-library/docs/issues/496 也有一个很好的解决方案。 (2认同)

Esk*_*ola 71

官方 nginx 镜像推荐使用envsubst,但正如其他人指出的那样,它会替换 also$host和其他变量,这是不可取的。不过还好envsubst可以把变量名作为参数来替换

为了避免对容器使用非常复杂的命令参数(如链接示例中所示),您可以编写一个 Docker 入口点脚本,该脚本将在执行命令之前填充环境变量。入口点脚本也是验证参数和设置默认值的好地方。

这是一个 nginx 容器的示例,它接受API_HOSTAPI_PORT参数作为环境变量。

nginx-default.conf.template

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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 "$@"
Run Code Online (Sandbox Code Playgroud)

文件

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;"]
Run Code Online (Sandbox Code Playgroud)

  • 我_稍微_更改了脚本以清除 conf.d 中的模板。最终版本:``` #!/usr/bin/env sh set -eu envsubst '${API_HOST} ${API_PORT}' &lt; /etc/nginx/conf.d/default.conf.template &gt; /etc/nginx/ conf.d/default.conf rm /etc/nginx/conf.d/default.conf/template exec "$@" ``` 很棒的脚本虽然谢谢! (3认同)

小智 47

用 Lua 做这件事比听起来容易得多:

server {
    set_by_lua $server_name 'return os.getenv("NGINX_SERVERNAME")';
}
Run Code Online (Sandbox Code Playgroud)

我在这里发现:

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
Run Code Online (Sandbox Code Playgroud)

您必须在顶级上下文中执行此操作,nginx.conf否则将无法正常工作!不在服务器块或某些站点的配置中/etc/nginx/sites-available,因为它包含nginx.confhttp上下文中(不是顶级上下文)。

另请注意,如果您尝试使用这种方法进行重定向,例如:

server {
    listen 80;
    server_name $server_name;
    return 301 https://$server_name$request_uri;
}
Run Code Online (Sandbox Code Playgroud)

它也不会起作用:

2016/08/30 14:49:35 [emerg] 1#0: the duplicate "server_name" variable in /etc/nginx/sites-enabled/default:8
Run Code Online (Sandbox Code Playgroud)

如果你给它一个单独的变量名:

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;
}
Run Code Online (Sandbox Code Playgroud)

nginx 不会解释它并将您重定向到https://%24server_name_from_env/.

  • 您可能需要在 nginx.conf 中的某处使用 `env NGINX_SERVERNAME`。 (3认同)

Mar*_*tin 31

envsubstrnginx现在由图像自动处理。

从文档

开箱即用的 nginx 不支持大多数配置块内的环境变量。但是这个镜像有一个功能,会在nginx启动前提取环境变量。

默认情况下,该函数读取模板文件/etc/nginx/templates/*.template并将执行结果输出envsubst/etc/nginx/conf.d.

  • 据我所知,只有当你的 docker-compose 中没有“命令”时,这才有效。看来这些会阻止“docker-entrypoint.sh”脚本运行。如果您执行到正在运行的容器中,您可以看到该脚本。在此发布有关此内容的文章(/sf/ask/4671256831/)。 (3认同)
  • @JohnL我看起来并不全面,但似乎这个动作是通过[包含的脚本](https://github.com/nginxinc/docker-nginx/blob/master/stable/alpine/Dockerfile#L112 ) 通过 Dockerfile。您需要运行该脚本才能使用该功能 (2认同)
  • 这应该是新接受的答案。奇迹般有效。 (2认同)
  • 文档有点稀疏。为我工作的缺失步骤是:1)在`templates/`目录中创建一个模板,`default.conf.template` 2)在我的`docker-compose.yml`中添加`volumes:- ./templates :/etc/nginx/templates` 复制模板。然后只需执行`docker-compose up`等等。 (2认同)

小智 21

这是另一个带有 envsubst 的。

nginxdocker 镜像已经运行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"
Run Code Online (Sandbox Code Playgroud)

模板变量

map $host $external_ip {
  default "$EXTERNAL_IP";
}
Run Code Online (Sandbox Code Playgroud)

nginx.conf

server {
     location / {

          proxy_set_header X-Real-IP $external_ip;
      }
}
Run Code Online (Sandbox Code Playgroud)

  • 将其标记为解决方案,虽然它不是第一个突出显示该解决方案的解决方案,但它是最有用的,因为它包含所有各种元素,而不是指向另一篇文章。 (3认同)

小智 15

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

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

它还可以 exec() 处理、替换默认值、记录并具有合理的故障语义。


小智 6

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

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

erb nginx.conf.erb > nginx.conf
Run Code Online (Sandbox Code Playgroud)

在 nginx.conf.erb 文件的 server 块内可能有

listen <%= ENV["PORT"] %>;
Run Code Online (Sandbox Code Playgroud)


小智 6

我使用 shell 脚本完成此任务。

这是 nginx 模板:

server {

    listen 80;
    server_name ___MY_DOMAIN_NAME___;
    charset utf-8;

    location /proxy {
        proxy_pass http://___PROXY_IP___:___PROXY_PORT___;
        proxy_set_header Host            $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
    }

    location / {
        return         200;
    }

}
Run Code Online (Sandbox Code Playgroud)

替换环境变量的脚本在这里:

echo sleep 3
sleep 3

echo build starting nginx config


echo replacing ___MY_DOMAIN_NAME___/$MY_DOMAIN_NAME
echo replacing ___PROXY_IP___/$LETSENCRYPT_IP
echo replacing ___PROXY_PORT___/$PROXY_PORT

sed -i "s/___MY_DOMAIN_NAME___/$MY_DOMAIN_NAME/g" /etc/nginx/nginx.conf
sed -i "s/___PROXY_IP___/$PROXY_IP/g" /etc/nginx/nginx.conf
sed -i "s/___PROXY_PORT___/$PROXY_PORT/g" /etc/nginx/nginx.conf

cat /etc/nginx/nginx.conf

if [ -z "$MY_DOMAIN_NAME" ]; then
    echo "Need to set MY_DOMAIN_NAME"
    exit 1
fi  
if [ -z "$LETSENCRYPT_IP" ]; then
    echo "Need to set LETSENCRYPT_IP"
    exit 1
fi  
if [ -z "$LETSENCRYPT_PORT" ]; then
    echo "Need to set LETSENCRYPT_PORT"
    exit 1
fi
if [ -z "$LETSENCRYPT_HTTPS_IP" ]; then
    echo "Need to set LETSENCRYPT_HTTPS_IP"
    exit 1
fi 
if [ -z "$LETSENCRYPT_HTTPS_PORT" ]; then
    echo "Need to set LETSENCRYPT_HTTPS_PORT"
    exit 1
fi

nginx -g 'daemon off;'
Run Code Online (Sandbox Code Playgroud)


Sab*_*K S 5

我所做的是使用erb

cat nginx.conf  | grep -i error_log

error_log <%= ENV["APP_ROOT"] %>/nginx/logs/error.log;
Run Code Online (Sandbox Code Playgroud)

--使用erb后

export APP_ROOT=/tmp

erb nginx.conf  | grep -i error_log

error_log /tmp/nginx/logs/error.log;
Run Code Online (Sandbox Code Playgroud)

这在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;
}
Run Code Online (Sandbox Code Playgroud)

变得

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
}
Run Code Online (Sandbox Code Playgroud)


小智 5

有很多方法。有些已经在一些答案中概述了。

如果您使用ngx_http_js_module,还有一种方法可以使用 JS 来实现:

## /etc/nginx/fetch_env.js
function fetch_upstream_host(r) {
  return process.env.UPSTREAM_HOST;
}

function fetch_upstream_port(r) {
  return process.env.UPSTREAM_PORT;
}
Run Code Online (Sandbox Code Playgroud)

## /etc/nginx/nginx.conf
load_module modules/ngx_http_js_module.so;

env UPSTREAM_HOST;
env UPSTREAM_PORT;

http {
  js_include fetch_env.js;
  js_set $upstream_host fetch_upstream_host;
  js_set $upstream_port fetch_upstream_port;

  server {
    ...

    location / {
      ...

      proxy_pass http://$upstream_host:$upstream_port;
    }
  }
Run Code Online (Sandbox Code Playgroud)

请确保您使用 njs 模块 0.33 或更高版本