微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

转载:https://serverfault.com/questions/577370/how-can-i-use-environment-variables-in-nginx-conf#   205

[从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

根目录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
  分享改善这个问题 17年5月23日于12:41编辑 社区♦ 1个 问2014年2月21日在15:02 雨果·罗杰·布朗 2,19722金徽章1414银徽章1212枚青铜徽章
  • 8 这不是环境变量的通用解决方案,但是如果您想将环境变量用于上游服务器的主机名/ 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 2014年 7月10日在17:18 
  • 1个 感谢@ mozz100-这非常有用-在这种情况下/ etc / hosts条目比env vars有效得多。唯一缺少的是上游容器重新启动并获取新IP时会发生什么。我假设子容器仍将指向原始IP,而不是新IP? – 雨果·罗杰·布朗 (  Hugo Rodger-Brown)2014年7月11日在13:04
  • 1个 是的,如果重新启动app_web_1,它将获得一个新的IP地址,因此您也需要重新启动Nginx容器。Docker将通过更新将其重新启动,/etc/hosts因此您无需更改Nginx配置文件。 –  mozz100 2014年 7月14日14:36 
添加评论

14个回答

活跃的最古老的选票 115

 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;"

并且由于DOLLARrun_Nginx.sh脚本上定义了新变量,现在我Nginx.conf.templateNginx自身变量文件内容如下:

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

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

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

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

分享改善这个答案 修改为2017年10月3日19:15     16年2月11日在12:39 回答 奥米德·拉哈(Omid Raha) 1,25911金徽章88银徽章44枚青铜徽章
  • 2 那样会杀死Nginx confsproxy_set_header Host $http_host; – 在 2016年  2月17日17:00粉碎
  • 27 @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 16年  2月19日在10:13
  • 6 好的,忘了逃脱$,应该是command: /bin/bash -c "envsubst '\$VAR1 \$VAR2' < /etc/Nginx/conf.d/mysite.template > /etc/Nginx/conf.d/default.conf && Nginx -g 'daemon off;'" –  pkyeck '16 Feb 19'在10:33
  • 2 考虑到转义,这可以在您自己的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 2016年  11月28日,0:12 
  • 2 这是一个很好的解决方案。谢谢。同样,上面提到的链接github.com/docker-library/docs/issues/496也提供了一个很好的解决方案。 – kabirbaidhya 17年  2月9日在17:48
再显示6条评论 36

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

请注意,使用这种方法必须envNginx中定义变量:

env ENVIRONMENT_VARIABLE_NAME

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

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

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/

分享改善这个答案 16年8月30日在18:21编辑 特罗·基尔卡宁(Tero Kilkanen) 22.4千22金徽章3333银徽章4646枚青铜徽章 15年12月1日在16:26 回答 科尔顿·李克利·温斯洛 46144银徽章33枚青铜徽章
  • 3 您可能需要env Nginx_SERVERNAMENginx.conf中的某个位置。 – hiroshi 16年  2月15日,14:28
  •   尽管我在Nginx docker映像中有lua模块,但这对我不起作用。这可能与我在Nginx.conf中包含配置文件的事实有关吗?如所建议的那样set_by_lua,我在env MY_VAR声明位于主Nginx.conf中的同时尝试包含的配置文件中的变量。真可惜,这本来是最干净的解决方案! – pederpansen 17年  5月16日在10:16
  •   有谁知道使用这种方法的利弊envsubst?我想专业人士是您不需要envsubstr在启动服务器之前运行命令,缺点是您需要安装lua模块?我想知道这两种方法是否对安全性有影响? –  Mark Winterbottom 18年3月4日在16:19
  •   @MarkWinterbottom尚未对此进行测试,但看来您不必授予对Nginx配置文件的写访问权限,而envsubst在我的情况下这是不行的 – Griddo 18年  月5日,14: 04
  •   当您说env必须在“ Nginx.conf的顶级上下文中”完成指令时,“ Nginx.conf”到底是什么?该文件应该在哪里? –  杰克M 19年10月12日在20:23
添加评论 25

官方的Nginx图像建议使用envsubst,但是正如其他人指出的那样,它也会替代$host和其他变量,这是不希望的。但是幸运的是envsubst可以将要替换的变量名称作为参数

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

这是一个Nginx容器的示例,该容器将in 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;
  }
}

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 "$@"

Docker文件

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;"]
分享改善这个答案 18年7月2日于17:38编辑     18年7月2日在17:28 回答 埃斯科·朗托拉(Esko Luontola) 47377银徽章99枚青铜徽章
  •   我稍微更改了脚本,以从conf.d中清除模板。最终版本:```#!/ usr / bin / env sh set -eu envsubst'$ {API_HOST} $ {API_PORT}'</etc/Nginx/conf.d/default.conf.template> / etc / Nginx / conf.d / default.conf rm /etc/Nginx/conf.d/default.conf/template exec“ $ @”```很棒的脚本,谢谢! – phillipuniverse 19年  11月20日在18:29 
添加评论 14

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

它使用对环境变量的$ {key}引用内联编辑配置文件,可以选择创建备份/记录其操作。它是用Go语言编写的,生成的静态二进制文件可以简单地从Linux和MacOS的发行版标签中下载。

它也可以执行()处理,替换认值,记录日志并具有明智的故障语义。

分享改善这个答案 15年6月15日于21:34编辑 社区♦ 1个 14年11月15日在16:02 回答 打哈欠 24122银徽章33枚青铜徽章 添加评论 5

关于使用erb的答案,可以按照以下步骤进行。

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

erb Nginx.conf.erb > Nginx.conf

Nginx.conf.erb文件的服务器块内可能有

listen <%= ENV["PORT"] %>;
分享改善这个答案 16年8月1日在11:39 回答 马瑞峰 15111银徽章44枚青铜徽章
  • 1个 然后,我需要在容器内安装Ruby吗?(如果没有,那么如何erb在容器启动之后执行变量替换,对吧?)— erb这是Ruby的正确内容stuartellis.name/articles/erb – KajMagnus 17年  4月8日,7:57 
  • 1个 这是标准Ruby安装随附的命令行工具。如果您没有在容器中尝试其他选项。 –  马瑞峰 17年4月12日在11:45
添加评论 5

我所做的就是使用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
}
分享改善这个答案 17年2月21日于17:40编辑     15年2月20日在0:11 回答 萨比特KS 5111银徽章33枚青铜徽章 添加评论 3

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

upstream upstream_name {
    server docker_link_alias;
}

假设您的docker命令类似于docker run --link othercontainer:docker_link_alias Nginx_container

分享改善这个答案 16年3月17日在5:16 回答 Cderwin 13122枚青铜徽章
  • 1个 您可以使用此方法获取主机,但不能获取端口。你必须要么硬编码端口或承担它的使用端口80 -  本·惠利 03月18日在'16 13:58
添加评论 3

我使用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;
    }

}

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

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;'
分享改善这个答案 2017年1月20日在6:35回答 盖革五世 13133枚青铜徽章 添加评论 2

一个选择...我今天才发现此工具:https : //github.com/kreuzwerker/envplate ...用Go语言编写,可以很容易地安装。使用它非常简单。尽管您将需要在Nginx.conf中放置模板变量。

例如,在文件调用${SOME_ENV_VAR}envplate的ep命令时将被替换。因此,如果您的Dockerfile无法获取该二进制文件或由于某种原因而无法运行,它将使您的配置无效。与使用perl或lua扩展之类的其他解决方案相比,仅需一点注意。

我真的很喜欢如何在未设置环境变量的情况下设置认值。例如 ${SOME_ENV_VAR:default-value}(并且您可以转义值)。同样,envplate必须仍然成功运行。

使用这种方法一个好处是,您最终不会获得比所需镜像更大的Docker镜像,因为您无需安装其他不需要的各种额外模块。如果事情开始变得复杂并且包含认值功能,它可能比使用sed更容易。

分享改善这个答案 2016年7月20日在0:14 回答 汤姆 12133枚青铜徽章
  •   当我遇到envplate的许多错误和问题时,我建议github.com/gliderlabs/sigil。 –  尼克 '18 May 23'17:49
添加评论 2

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

如果您使用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;
}

## /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;
    }
  }

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

分享改善这个答案 19年10月21日在3:31 回答 特朗·莱 2111枚青铜徽章 添加评论 1个

另一种可能性是将'sed'命令与正则表达式一起使用,那么您根本不必弄乱配置文件!这样,您可以正常使用配置文件,但是在运行docker时,它将与env变量交换出值。这些都不是“向搜索并替换为的配置文件添加文本字符串”。

您可以使用环境变量创建带有替换值的run.sh文件

要更改此行上的“ 7s”:

client_body_timeout 7s;         #Default 60s

使用client_body_timeout作为搜索行和$ client_body_timeout作为替换环境变量的Sed命令:

sed -i "s/\(client_body_timeout\).*\?\;/\1 $client_body_timeout;/" /usr/local/Nginx/conf/Nginx.conf

复制/粘贴您要设置的每个参数的这一行,并使用config选项更改client_body_timeout,并使用与其关联的env变量更改$ client_body_timeout。与现有的配置文件一起使用,它将可以正常工作。

分享改善这个答案 2017年12月12日在20:53回答 杰夫 1111枚青铜徽章 添加评论 1个

这是使用此sed方法的示例,不一定更好,但对某些人可能有用。首先,在conf文件添加要替换的自定义关键字。二,创建声明的一个dockerfile ENV变量,然后CMD使用sed到编辑Nginx的明确运行前的配置。

因此,假设您的default.conf包含关键字docker_host

location /api { proxy_pass http://docker_host:9000/api; }

并编写类似于以下内容的Dockerfile:

ENV docker_host localhost
ADD default.conf /etc/Nginx/conf.d/default.conf
CMD sed -i.bak s/docker_host/$docker_host/g /etc/Nginx/conf.d/default.conf &&   Nginx -g "daemon off;"

然后构建映像并使用以下命令运行容器

docker run -d -p 80:80 -e "docker_host=${env:COmpuTERNAME}" imagename
分享改善这个答案 18年9月23日于8:15编辑 汤玛士 3,69355金徽章1616银徽章2525铜牌 18年9月22日在22:53 回答 史蒂文 1111枚青铜徽章 添加评论 0

用lua做到这一点的正确方法,因为上面的答案是陈旧的:

server {
    set_by_lua $curr_server_name 'return os.getenv("Nginx_SERVERNAME")';
    server_name = $curr_server_name;
}

分享改善这个答案 19年8月13日在19:48 回答 埃里克·梅多斯(Eric Meadows) 101 添加评论 -2

您应该能够使用Dockerize的模板执行所需的操作。

分享改善这个答案 16年5月20日在11:11 回答 克劳德 97

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐