Замените переменные окружения в конфиге NGINX из docker-compose

Я пытаюсь запустить сервер 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

Я довольно застрял, поэтому заранее спасибо за вашу помощь.

Развертывание модели машинного обучения с помощью Flask - Angular в Kubernetes
Развертывание модели машинного обучения с помощью Flask - Angular в Kubernetes
Kubernetes - это портативная, расширяемая платформа с открытым исходным кодом для управления контейнерными рабочими нагрузками и сервисами, которая...
Как создать PHP Image с нуля
Как создать PHP Image с нуля
Сегодня мы создадим PHP Image from Scratch для того, чтобы легко развернуть базовые PHP-приложения. Пожалуйста, имейте в виду, что это разработка для...
16
0
33 277
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы можете избежать некоторых проблем с интерпретацией переменных среды Compose, определив собственную точку входа. Посмотрите этот простой пример:

  • entrypoint.sh (убедитесь, что этот файл является исполняемым)
#!/bin/sh

export NGINXPROXY

envsubst '${NGINXPROXY}' < /config.template > /etc/nginx/nginx.conf

exec "$@"
  • докер-compose.yml
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;'"]

... и вы должны быть готовы идти. Тем не менее, я всегда рекомендую «более чистый» способ с определением собственной точки входа.

Я мог бы COPY этот скрипт в Dockerfile и указать его как ENTRYPOINT изображения, а не переопределять его только во время выполнения.

David Maze 21.05.2020 12:55
Ответ принят как подходящий

Итак, после некоторой борьбы с этой проблемой мне удалось заставить ее работать аналогично ответу, предоставленному 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 мне нужно использовать?

Alexey Poloz 01.05.2021 21:28

Я бы посмотрел на ответ Беллакна. Это гораздо более простая установка, чем то, что я пытался сделать.

Mike 06.08.2021 18:38

Начиная с нгинкс 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/ вместо этого.

Joseph Marikle 24.08.2020 16:00

@JosephMarikle хорошая идея, я обновил пример, чтобы отразить ваш комментарий

Jody 24.08.2020 18:46

Нужно ли вручную копировать файлы шаблонов в /etc/nginx/templates? Я следую этой настройке, но она не копируется в шаблоны. Папка с моими шаблонами пуста.

Kent Wong 16.03.2021 06:42

в этом примере у вас есть docker-compose.yml в папке с ./docker/nginx/templates/, содержащим ваши шаблоны относительно шаблонов на вашем хосте.

Chris Becke 26.06.2021 19:37

Честно говоря, это здорово. Я хотел бы, чтобы это работало, когда я настраивал это.

Mike 06.08.2021 18:36

Если вы передаете свой docker-compose.yml системе контроля версий и хотите использовать отдельный неотслеживаемый .env файл для поддержки переменных, специфичных для среды, docker-compose заменит ваши .env переменные на docker-compose.yml до того, как запустит ваши контейнеры. Таким образом, docker-compose.yml может указать переменную среды как API_HOST: ${API_HOST}, а .env может установить фактическое значение: API_HOST=api.example.com. Это может быть очевидно для экспертов по созданию докеров, но мне потребовалось несколько часов, чтобы понять.

Brian Dunne 05.11.2021 18:54

Как и объяснено в ответе Джоди, в настоящее время официальный образ 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/ на локальном компьютере).

Другие вопросы по теме