Вопрос: Как использовать переменные среды в Nginx.conf


[Отправлено и отредактировано с https://stackoverflow.com/questions/21933955 поскольку он считался слишком системным для StackOverflow.]

У меня есть докер-контейнер с Nginx, который связывается с другим контейнером докера. Имя хоста и 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

EDIT 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

130
2018-02-21 15:02


Источник


Это не общее решение для переменных среды, но если вы хотите использовать переменные среды для имен хостов / IP-адресов серверов, работающих на восходящем пути, обратите внимание, что Docker (по крайней мере, в последних версиях) изменяет / etc / hosts для вас. Видеть docs.docker.com/userguide/dockerlinks    Это означает, что если ваш связанный контейнер называется «app_web_1», докер будет создавать строку в / etc / hosts в вашем контейнере Nginx. Поэтому вы можете просто заменить server $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"}:5000;    с server app_web_1:5000; - mozz100
Спасибо @ mozz100 - это невероятно полезно - записи / etc / hosts намного эффективнее, чем env vars в этом случае. Единственный бит отсутствует, что происходит, если восходящий контейнер перезапускается и получает новый IP-адрес. Я предполагаю, что дочерние контейнеры будут по-прежнему указывать на оригинальный IP-адрес, а не на новый? - Hugo Rodger-Brown
Да, если вы перезагрузились app_web_1 он получит новый IP-адрес, поэтому вам также потребуется перезагрузить контейнер nginx. Docker перезапустит его с обновленным /etc/hosts поэтому вам не нужно будет изменять конфигурационные файлы nginx. - mozz100


Ответы:


Если вы используете ссылку, докер устанавливает переменные среды и добавляет псевдоним для связанного контейнера в /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

49
2018-03-18 06:14



Yup - текущее (рабочее) решение именно это. Просто кажется немного взломанным. (Тем не менее, это только локальная среда для разработчиков, поэтому взлом не является большой проблемой.) - Hugo Rodger-Brown
Я столкнулся с этим, поэтому сделал несколько более общий контейнер nginx который автоматически расширяет переменные среды в конфигурации. - Shepmaster


Из официальный файл докеров Dginx:

Использование переменных среды в конфигурации 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;"

И из-за определенных новых DOLLAR переменная на run_nginx.sh сценарий, теперь содержимое моего nginx.conf.template файл для самой переменной Nginx выглядит следующим образом:

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

И для моей определенной переменной:

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

Также Вот, для этого есть мой реальный прецедент.


51
2018-02-11 12:39



Это убивает nginx confs like 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
Это не сработало для меня, хотя у меня есть модуль lua в моем изображении nginx docker. Может ли это быть связано с тем, что я включаю файл конфигурации в свой 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

Он inline редактирует файлы конфигурации с помощью ссылок $ {key} на переменные среды, при необходимости создавая резервные копии / протоколирование того, что он делает. Он написан в Go, и полученный статический бинарный файл можно просто загрузить с вкладки release для Linux и MacOS.

Он также может выполнять операции exec (), заменять значения по умолчанию, журналы и иметь разумную семантику отказа.


14
2017-11-15 16:02





То, что я сделал, это использовать эрб!

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

Пример конфигурации 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


Я знаю, что это старый вопрос, но на всякий случай кто-то натыкается на это (как я теперь есть), есть намного лучший способ сделать это. Поскольку докер вводит псевдоним связанного контейнера в / etc / hosts, вы можете просто сделать

upstream upstream_name {
    server docker_link_alias;
}

Предположим, что ваша команда докеров - это что-то вроде docker run --link othercontainer:docker_link_alias nginx_container,


3
2018-03-17 05:16



Вы можете получить хост, используя этот метод, но не порт. Вы должны либо жестко закодировать порт, либо предположить, что он использует порт 80. - Ben Whaley