У меня есть докер-контейнер под управлением 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
РЕДАКТИРОВАТЬ 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
Из официальный файл докеров Nginx:
Использование переменных окружения в конфигурации 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};
Также Вот, вот мой реальный вариант использования для этого.
Официальный образ nginx рекомендует использовать envsubst
, но как указано другими он заменит также $host
и другие переменные, что нежелательно. Но к счастью envsubst
жестяная банка взять в качестве параметра имена переменных для замены.
Чтобы избежать очень сложного параметра команды для контейнера (как в связанном примере), вы можете написать сценарий точки входа Docker, который будет заполнять переменные среды перед выполнением команды. Сценарий точки входа также является хорошим местом для проверки параметров и установки значений по умолчанию.
Вот пример контейнера nginx, который принимает API_HOST
и API_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 "$@"
Dockerfile
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;"]
Сделать это с помощью 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/
.
Я написал кое-что, что может оказаться полезным, а может и не оказаться: https://github.com/yawn/envplate
Он оперативно редактирует файлы конфигурации с помощью ссылок $ {key} на переменные среды, при необходимости создавая резервные копии / регистрируя то, что он делает. Он написан на Go, и полученный статический двоичный файл можно просто загрузить с вкладки выпуска для Linux и MacOS.
Он также может обрабатывать exec (), заменять значения по умолчанию, журналы и имеет разумную семантику ошибок.
Что касается ответа об использовании erb, это можно сделать, как показано ниже.
Запишите файл конфигурации NGINX как файл erb, содержащий переменную среды, и оцените его с помощью команды erb в обычном файле конфигурации.
erb nginx.conf.erb > nginx.conf
Внутри серверного блока файла nginx.conf.erb может быть
listen <%= ENV["PORT"] %>;
Я использовал 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
}
Я делаю это с помощью сценария оболочки.
Вот шаблон 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;'
envsubstr
обрабатывается автоматически nginx
изображение сейчас.По умолчанию nginx не поддерживает переменные среды внутри большинства блоков конфигурации. Но у этого изображения есть функция, которая будет извлекать переменные окружения перед запуском nginx.
По умолчанию эта функция читает файлы шаблонов в
/etc/nginx/templates/*.template
и выводит результат выполненияenvsubst
к/etc/nginx/conf.d
.
Я знаю, что это старый вопрос, но на тот случай, если кто-то наткнется на это (как я сейчас), есть гораздо лучший способ сделать это. Поскольку докер вставляет псевдоним связанного контейнера в / etc / hosts, вы можете просто сделать
upstream upstream_name {
server docker_link_alias;
}
предполагая, что ваша команда docker похожа на docker run --link othercontainer:docker_link_alias nginx_container
.
Другой вариант ... Я только что нашел этот инструмент сегодня: https://github.com/kreuzwerker/envplate ... Написано на Go, очень легко устанавливается. Пользоваться им довольно просто. Хотя вам нужно будет разместить переменные шаблона в вашем nginx.conf.
Например ${SOME_ENV_VAR}
будет заменен, когда envplate ep
команда вызывается для файла. Поэтому, если ваш Dockerfile не сможет получить этот двоичный файл или он не запустится по какой-либо причине, он оставит вашу конфигурацию недействительной. Небольшое примечание по сравнению с другими решениями, такими как использование расширений perl или lua.
Мне очень нравится, как вы также можете установить значения по умолчанию, когда переменная среды не установлена. Ex. ${SOME_ENV_VAR:default-value}
(и вы можете избежать значений). Опять же, envplate все еще должен успешно работать.
Одно из преимуществ использования такого подхода заключается в том, что вы не получите образ Docker, который больше, чем необходимо, потому что вы ушли с установки всевозможных дополнительных модулей, которые в противном случае вам не нужны. Это также может быть проще, чем использование sed, если все начинает усложняться и он содержит функции значения по умолчанию.
Вот пример использования sed
подход, не обязательно лучше, но может быть полезным для некоторых. Сначала добавьте настраиваемое ключевое слово, которое нужно заменить в файле conf. Во-вторых, создайте файл докеров, который объявляет 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
Есть много способов. Некоторые были изложены в некоторых ответах.
Если вы используете 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 или новее.
Другая возможность - использовать команду sed с регулярными выражениями, тогда вам вообще не придется возиться с вашими файлами конфигурации! Таким образом, вы можете использовать свои файлы конфигурации в обычном режиме, но когда вы запустите докер, он заменит значения переменными env. Ничего из этого «не добавляйте строку текста в ваши файлы конфигурации, которую вы ищете и заменяете».
Вы можете создать файл run.sh со значениями замены, используя переменные среды.
Чтобы изменить «семерки» в этой строке:
client_body_timeout 7s; #Default 60s
Sed Command с использованием client_body_timeout как строка поиска и $ client_body_timeout в качестве замены переменной env:
sed -i "s/\(client_body_timeout\).*\?\;/\1 $client_body_timeout;/" /usr/local/nginx/conf/nginx.conf
Скопируйте / вставьте эту строку для каждого параметра, который вы хотите установить, и измените client_body_timeout с помощью параметра конфигурации и $ client_body_timeout с переменной env, с которой он связан. Используйте с существующим файлом конфигурации, и он будет работать.
Правильный способ сделать это с помощью lua, поскольку ответ выше устарел:
server {
set_by_lua $curr_server_name 'return os.getenv("NGINX_SERVERNAME")';
server_name = $curr_server_name;
}
Если вам нужно сделать это просто: bash
eval
sed
И соблюдайте два простых правила: ${ENV_IN_PARENTHESIS}
и всегда использовать '
цитаты в шаблоне
Вы можете поменять местами все переменные среды за один общий вызов.
eval "echo \"$(sed 's/\$/##/g; s/\##{/${/g;' some.template)\"" | sed 's/\##/$/g' > some.conf
Что творится:
А. $(sed 's/\$/##/g; s/\##{/${/g;' some.template)
Пропустите шаблон через sed
и:
1. заменить все $
персонажи с ##
2. заменить все ##{
вернуться к ${
Б. eval "echo \"$(sed 's/\$/##/g; s/\##{/${/g;' some.template)\""
Запускает измененный шаблон через eval
который заменяет видимый ${ENV_IN_PARENTHESIS}
С. ... | sed 's/\##/$/g' > some.conf
Трубы результат eval
другому sed
вызов, который возвращает все исходные $
назад от ##
и выведите окончательный результат в ваш новый блестящий файл conf.
Вы должны уметь делать то, что хотите, с Dockerizeшаблоны.