Я пытаюсь запустить сервер NGINX в контейнере докеров, настроенном с помощью docker-compose. Однако загвоздка в том, что я хотел бы заменить переменную среды внутри раздела http, особенно в блоке «upstream».
Было бы здорово, если бы это работало, потому что у меня есть несколько других контейнеров, которые настраиваются с помощью переменных среды, и у меня есть около 5 сред, которые должны работать в любой момент времени. Я пробовал использовать "envsubst" (как предлагается в официальной документации NGINX), perl_set и set_by_lua, однако ни один из них не работает.
Ниже приведена конфигурация NGINX после последней пробной версии.
user nginx;
worker_processes 1;
env NGINXPROXY;
load_module modules/ngx_http_perl_module.so;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
perl_set $nginxproxy 'sub { return $ENV{"NGINXPROXY"}; }';
upstream api-upstream {
server ${nginxproxy};
}
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile off;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
Ниже приведен файл докера NGINX.
# build stage
FROM node:latest
WORKDIR /app
COPY ./ /app
RUN npm install
RUN npm run build
# production stage
FROM nginx:1.17.0-perl
COPY --from=0 /app/dist /usr/share/nginx/html
RUN apt-get update && apt-get install -y gettext-base
RUN rm /etc/nginx/conf.d/default.conf
RUN rm /etc/nginx/nginx.conf
COPY default.conf /etc/nginx/conf.d
COPY nginx.conf /etc/nginx
RUN mkdir /certs
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]
Ниже приведен раздел файла docker-compose.yml для сервера NGINX (с измененными именами и IP-адресами). Команда envsubst намеренно закомментирована на этом этапе устранения неполадок.
front-end:
environment:
- NGINXPROXY=172.31.67.100:9300
build: http://gitaccount:[email protected]/group/front-end.git#develop
container_name: qa_front_end
image: qa-front-end
restart: always
networks:
qa_network:
ipv4_address: 172.28.0.215
ports:
- "9080:80"
# command: /bin/bash -c "envsubst '$$NGINXPROXY' < /etc/nginx/nginx.conf > /etc/nginx/nginx.conf && nginx -g 'daemon off;'"
Кажется, что происходит, когда я ссылаюсь на переменную $nginxproxy в восходящем блоке (сразу после «сервера»), я получаю вывод, который выглядит так, как будто он ссылается на строковый литерал «$nginxproxy», а не заменяет значение переменной .
qa3_front_end | 2019/06/18 12:35:36 [emerg] 1#1: host not found in upstream "${nginx_upstream}" in /etc/nginx/nginx.conf:19
qa3_front_end | nginx: [emerg] host not found in upstream "${nginx_upstream}" in /etc/nginx/nginx.conf:19
qa3_front_end exited with code 1
Когда я пытаюсь использовать envsubst, я получаю сообщение об ошибке, которое звучит так, будто команда испортила формат файла nginx.conf.
qa3_front_end | 2019/06/18 12:49:02 [emerg] 1#1: no "events" section in configuration
qa3_front_end | nginx: [emerg] no "events" section in configuration
qa3_front_end exited with code 1
Я довольно застрял, поэтому заранее спасибо за вашу помощь.


Вы можете избежать некоторых проблем с интерпретацией переменных среды Compose, определив собственную точку входа. Посмотрите этот простой пример:
#!/bin/sh
export NGINXPROXY
envsubst '${NGINXPROXY}' < /config.template > /etc/nginx/nginx.conf
exec "$@"
version: "3.7"
services:
front-end:
image: nginx
environment:
- NGINXPROXY=172.31.67.100:9300
ports:
- 80:80
volumes:
- ./config:/config.template
- ./entrypoint.sh:/entrypoint.sh
entrypoint: ["/entrypoint.sh"]
command: ["nginx", "-g", "daemon off;"]
Мой файл config имеет то же содержание, что и ваш nginx.conf, за исключением того, что мне пришлось комментировать строки с помощью модуля Perl.
Обратите внимание, что мне пришлось смонтировать файл конфигурации в другое место, прежде чем я смог его envsubst. Я столкнулся с некоторым странным поведением в виде того, что файл оказывается пустым после подстановки, чего можно избежать с помощью этого подхода. Это не должно быть проблемой в вашем конкретном случае, потому что вы уже встроили его в свой образ во время сборки.
РЕДАКТИРОВАТЬ
Для полноты, чтобы изменить настройки как можно меньше, вам просто нужно убедиться, что вы export свою переменную среды. Адаптируйте свою команду следующим образом:
command: ["/bin/bash", "-c", "export NGINXPROXY && envsubst '$$NGINXPROXY' < /etc/nginx/nginx.conf > /etc/nginx/nginx.conf && nginx -g 'daemon off;'"]
... и вы должны быть готовы идти. Тем не менее, я всегда рекомендую «более чистый» способ с определением собственной точки входа.
Итак, после некоторой борьбы с этой проблемой мне удалось заставить ее работать аналогично ответу, предоставленному bellackn. Я собираюсь опубликовать свое точное решение здесь, если кому-то еще понадобится полное решение.
Шаг 1: Напишите свой nginx.conf или default.conf так, как вы обычно это пишете. Сохраните файл как «nginx.conf.template» или «default.conf.template» в зависимости от того, в какой вы пытаетесь подставить переменные.
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
upstream api-upstream {
server 192.168.25.254;
}
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile off;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
Шаг 2: Замените переменную в формате ${VARNAME} на любые значения, которые вы хотите заменить переменной среды:
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
upstream api-upstream {
server ${SERVER_NAME};
}
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile off;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
Шаг 3: В файле docker скопируйте файлы конфигурации nginx (ваш nginx.conf.template или default.conf.template) в свой контейнер в соответствующее место:
# build stage
FROM node:latest
WORKDIR /app
COPY ./ /app
RUN npm install
RUN npm run build
# production stage
FROM nginx:1.17.0-perl
COPY --from=0 /app/dist /usr/share/nginx/html
RUN apt-get update && apt-get install -y gettext-base
RUN rm /etc/nginx/conf.d/default.conf
RUN rm /etc/nginx/nginx.conf
#-----------------------------------#
|COPY default.conf /etc/nginx/conf.d|
|COPY nginx.conf.template /etc/nginx|
#-----------------------------------#
RUN mkdir /certs
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]
Шаг 4: Установите переменную среды в файле docker-compos.yml, используя метку раздела «среда». Убедитесь, что имя вашей переменной среды совпадает с любым именем переменной, которое вы выбрали в файле конфигурации nginx. Используйте команду «envsubt» в вашем контейнере докеров, чтобы заменить значения ваших переменных на ваши переменные в вашем nginx.conf.template, и запишите вывод в файл с именем nginx.conf в правильном месте. Это можно сделать в файле docker-compose.yml с помощью метки раздела «команда»:
version: '2.0'
services:
front-end:
environment:
- SERVER_NAME=172.31.67.100:9100
build: http://git-account:[email protected]/project-group/repository-name.git#branch-ame
container_name: qa_front_end
image: qa-front-end-vue
restart: always
networks:
qa_network:
ipv4_address: 172.28.0.215
ports:
- "9080:80"
command: >
/bin/sh -c
"envsubst '
$${SERVER_NAME}
'< /etc/nginx/nginx.conf.template
> /etc/nginx/nginx.conf
&& nginx -g 'daemon off;'"
Шаг 5: Запустите стек с помощью docker-compose up (с любыми дополнительными переключателями, которые вам нужны), и теперь ваш сервер nginx должен запускаться с тем значением, которое вы указали в разделе «environment» вашего docker-compose.yml.
Как упоминалось в приведенном выше решении, вы также можете определить свою собственную точку входа, однако это решение также доказало свою эффективность и сохраняет все содержимое в одном файле конфигурации, что дает мне возможность запускать стек сервисов непосредственно из git. только с файлом docker-compose.yml.
Большое спасибо всем, кто потратил время, чтобы подготовиться к этому, и bellackn за то, что нашли время, чтобы помочь мне решить проблему.
Я получаю эту ошибку: `ОШИБКА: не удалось построить сервер службы: ошибка COPY: файл не найден в контексте сборки или исключен .dockerignore: stat nginx.conf.template: файл не существует`. И я пробовал много способов сохранить nginx.conf.template и проверил этот файл в контейнере, он существовал. Какие конфигурации docker-compose мне нужно использовать?
Я бы посмотрел на ответ Беллакна. Это гораздо более простая установка, чем то, что я пытался сделать.
Начиная с нгинкс 1.19 теперь вы можете использовать переменные среды в своей конфигурации с помощью docker-compose. Я использовал следующую настройку:
# file: docker/nginx/templates/default.conf.conf
upstream api-upstream {
server ${API_HOST};
}
# file: docker-compose.yml
services:
nginx:
image: nginx:1.19-alpine
volumes:
- "./docker/nginx/templates:/etc/nginx/templates/"
environment:
NGINX_ENVSUBST_TEMPLATE_SUFFIX: ".conf"
API_HOST: api.example.com
Я немного отхожу от сценария из примера в документации. Обратите внимание на дополнительное расширение .conf в файле шаблона — это не опечатка. В документации к образу nginx предлагается назвать файл, например, default.conf.template. При запуске скрипт возьмет этот файл, заменит переменные среды, а затем выведет файл в /etc/nginx/conf.d/ с исходным именем файла, отбрасывая суффикс .template.
По умолчанию этот суффикс — .template, но это нарушает подсветку синтаксиса, если вы не настроите свой редактор. Вместо этого я указал .conf в качестве суффикса шаблона. Если вы назовете свой файл только default.conf, результатом будет файл с именем /etc/nginx/conf.d/default, и ваш сайт не будет обслуживаться должным образом.
Просто быстрое уточнение: этот файл должен находиться в каталоге шаблонов контейнера nginx. Если вы использовали том ./docker/nginx/conf.d/:/etc/nginx/conf.d/ docker compose для управления этим файлом, вам нужно изменить его на ./docker/nginx/templates/:/etc/nginx/templates/ вместо этого.
@JosephMarikle хорошая идея, я обновил пример, чтобы отразить ваш комментарий
Нужно ли вручную копировать файлы шаблонов в /etc/nginx/templates? Я следую этой настройке, но она не копируется в шаблоны. Папка с моими шаблонами пуста.
в этом примере у вас есть docker-compose.yml в папке с ./docker/nginx/templates/, содержащим ваши шаблоны относительно шаблонов на вашем хосте.
Честно говоря, это здорово. Я хотел бы, чтобы это работало, когда я настраивал это.
Если вы передаете свой docker-compose.yml системе контроля версий и хотите использовать отдельный неотслеживаемый .env файл для поддержки переменных, специфичных для среды, docker-compose заменит ваши .env переменные на docker-compose.yml до того, как запустит ваши контейнеры. Таким образом, docker-compose.yml может указать переменную среды как API_HOST: ${API_HOST}, а .env может установить фактическое значение: API_HOST=api.example.com. Это может быть очевидно для экспертов по созданию докеров, но мне потребовалось несколько часов, чтобы понять.
Как и объяснено в ответе Джоди, в настоящее время официальный образ Nginx Docker поддерживает парсинг шаблонов. Здесь используется envsubst, а его обработка гарантирует, что Переменные Nginx не будет путаться, например, $host и все такое. Хороший. Однако envsubst не поддерживает значения по умолчанию, как обычная оболочка и Docker Compose при использовании ${MY_VAR:-My Default}. Таким образом, для этого встроенного шаблона всегда требуется полная настройка всех переменных, даже при использовании значений по умолчанию.
Чтобы определить значения по умолчанию в самом изображении, можно использовать пользовательскую точку входа, чтобы сначала установить значения по умолчанию, а затем просто делегировать их исходная точка входа. Как docker-defaults.sh:
#!/usr/bin/env sh
set -eu
# As of version 1.19, the official Nginx Docker image supports templates with
# variable substitution. But that uses `envsubst`, which does not allow for
# defaults for missing variables. Here, first use the regular command shell
# to set the defaults:
export PROXY_API_DEST=${PROXY_API_DEST:-http://host.docker.internal:8000/api/}
# Due to `set -u` this would fail if not defined and no default was set above
echo "Will proxy requests for /api/* to ${PROXY_API_DEST}*"
# Finally, let the original Nginx entry point do its work, passing whatever is
# set for CMD. Use `exec` to replace the current process, to trap any signals
# (like Ctrl+C) that Docker may send it:
exec /docker-entrypoint.sh "$@"
Наряду, скажем, с некоторыми docker-nginx-default.conf:
# After variable substitution, this will replace /etc/nginx/conf.d/default.conf
server {
listen 80;
listen [::]:80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /api/ {
# Proxy API calls to another destination; the default for the variable is
# set in docker-defaults.sh
proxy_pass $PROXY_API_DEST;
}
}
В Dockerfile скопируйте шаблон в /etc/nginx/templates/default.conf.template и установите пользовательскую точку входа:
FROM nginx:stable-alpine
...
# Each time Nginx is started it will perform variable substition in all template
# files found in `/etc/nginx/templates/*.template`, and copy the results (without
# the `.template` suffix) into `/etc/nginx/conf.d/`. Below, this will replace the
# original `/etc/nginx/conf.d/default.conf`; see https://hub.docker.com/_/nginx
COPY docker-nginx-default.conf /etc/nginx/templates/default.conf.template
COPY docker-defaults.sh /
# Just in case the file mode was not properly set in Git
RUN chmod +x /docker-defaults.sh
# This will delegate to the original Nginx `docker-entrypoint.sh`
ENTRYPOINT ["/docker-defaults.sh"]
# The default parameters to ENTRYPOINT (unless overruled on the command line)
CMD ["nginx", "-g", "daemon off;"]
Теперь использование, например, docker run --env PROXY_API_DEST=https://example.com/api/ ... установит значение, которое в этом примере по умолчанию будет http://host.docker.internal:8000/api/, если не установлено (что на самом деле http://localhost:8000/api/ на локальном компьютере).
Я мог бы
COPYэтот скрипт в Dockerfile и указать его какENTRYPOINTизображения, а не переопределять его только во время выполнения.