我如何在Nginx.conf中使用环境variables

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

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

 upstream gunicorn { server $APP_HOST_NAME:$APP_HOST_PORT; } 

如何在启动时将环境variables放入Nginxconfiguration中?

编辑1

这是在下面的build议答案后的整个文件:

 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:更多的细节

当前的环境variables

 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; 

网站nginxconfiguration:

 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; 

重新加载nginxconfiguration:

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

    如果您使用的是链接,docker会设置环境variables,并为/etc/hosts的链接容器添加一个别名。 如果你能够对端口进行硬编码(或者只是端口80),你可以简单地做:

     upstream gunicorn { server linked-hostname:5000; } 

    该端口仅在环境variables中可用,不能在upstream模块中使用,也不能在服务器或位置块中使用。 他们只能在主configuration中引用,这对你没有帮助。 你可以用包含Lua 的openresty包来做到这一点。

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

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

    从官方的Nginx docker文件中:

    在nginxconfiguration中使用环境variables:

    开箱即用,Nginx不支持在大多数configuration块中使用环境variables。

    但是,如果需要在nginx启动之前dynamic生成nginxconfiguration,则可以使用envsubst作为解决方法。

    这里是一个使用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文件可能包含这样的variables引用:

     listen ${NGINX_PORT}; 

    更新:

    但是你知道这是由于它的Nginxvariables如下所示:

     proxy_set_header X-Forwarded-Host $host; 

    损坏到:

     proxy_set_header X-Forwarded-Host ; 

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

    我有一个脚本来运行Nginx,用于run_nginx.sh 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_nginx.sh脚本上定义了新的DOLLARvariables, run_nginx.sh现在我的nginx.conf.template文件的Nginx自身variables的内容是这样的:

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

    对于我定义的variables是这样的:

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

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

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

    请注意,使用这种方法,您必须在Nginx中定义envvariables:

     env ENVIRONMENT_VARIABLE_NAME 

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

    另外请注意,如果您尝试进行redirect,例如:

     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 

    如果你给它一个单独的variables名称:

     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将不会解释它,并将您redirect到https://%24server_name_from_env/

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

    它内嵌编辑configuration文件,并带有$ {key}引用的环境variables,可选地创build备份/logging它的function。 它是用Go编写的,所产生的静态二进制文件可以简单地从Linux和MacOS版本选项卡下载。

    它也可以执行()进程,替代默认值,日志和明智的失败语义。

    我所做的就是使用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中使用

    示例nginxconfiguration: 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 } 

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

    将NGINXconfiguration文件写成一个包含环境variables的erb文件,并使用erb命令对正常configuration文件进行评估。

     erb nginx.conf.erb > nginx.conf 

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

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

    我知道这是一个古老的问题,但为了防止有人绊倒这个(就像我现在所做的那样),还有更好的方法来做到这一点。 因为docker在/ etc / hosts中插入链接的容器的别名,所以你可以这样做

     upstream upstream_name { server docker_link_alias; } 

    假设你的docker命令是像docker run --link othercontainer:docker_link_alias nginx_container东西 – docker run --link othercontainer:docker_link_alias nginx_container

    另一种select…我今天刚刚find这个工具: https : //github.com/kreuzwerker/envplate …写在Go中,它可以很容易地安装。 使用它非常简单。 尽pipe你需要在你的nginx.conf中放置模板variables。

    例如,在文件上调用envplate的ep命令时, ${SOME_ENV_VAR}将被replace。 所以,如果你的Dockerfile无法获得这个二进制文件,或者它不应该运行的原因,它会使你的configuration无效。 与其他解决scheme(比如使用perl或lua扩展)相比,这只是一个小小的logging。

    我真的很喜欢你可以设置默认值,当环境variables没有设置。 防爆。 ${SOME_ENV_VAR:default-value} (你可以转义值)。 同样,envplate仍然必须成功运行。

    使用这种方法的一个好处是,你不会得到比必要的更大的Docker镜像,因为你没有安装所有你不需要的额外模块。 如果事情开始变得复杂,它也可能比使用sed更容易,并且它包含默认值function。

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

    而replace环境variables的脚本在这里:

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

    你应该可以用Dockerize的模板做你想做的事情 。