[交叉发布和编辑下来从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
脚本上定义了新的DOLLAR
variables, 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中定义env
variables:
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的模板做你想做的事情 。