从 docker-compose 替换 NGINX 配置中的环境变量

Mik*_*ike 11 nginx environment-variables docker docker-compose

我正在尝试在通过 docker-compose 配置的 docker 容器中启动 NGINX 服务器。然而,问题是我想替换 http 部分内的环境变量,特别是在“上游”块内。

让它工作会很棒,因为我还有其他几个容器都是通过环境变量配置的,并且我有大约 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;
}

Run Code Online (Sandbox Code Playgroud)

下面是 NGINX dockerfile

# 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;"]

Run Code Online (Sandbox Code Playgroud)

下面是 NGINX 服务器的 docker-compose.yml 部分(名称和 IP 已更改)。在我的故障排除中, envsubst 命令在这一点上被有意注释掉。

front-end:
        environment:
            - NGINXPROXY=172.31.67.100:9300
        build: http://gitaccount:password@gitserver.com/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;'"
Run Code Online (Sandbox Code Playgroud)

似乎正在发生的事情是,当我在上游块中引用 $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
Run Code Online (Sandbox Code Playgroud)

当我尝试使用 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
Run Code Online (Sandbox Code Playgroud)

我很困惑,所以提前感谢您的帮助。

Jod*_*ody 19

从 nginx 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
        
Run Code Online (Sandbox Code Playgroud)

我将从文档中的示例中稍微偏离脚本。请注意.conf模板文件上的额外扩展名 - 这不是拼写错误。在 nginx 映像的文档中,建议将文件命名为default.conf.template. 启动时,脚本将获取该文件,替换环境变量,然后使用原始文件名将文件输出到 /etc/nginx/conf.d/,去掉.template后缀。

默认情况下,后缀是.template,但这会破坏语法突出显示,除非您配置编辑器。相反,我指定.conf为模板后缀。如果你只命名你的文件default.conf,结果将是一个名为 /etc/nginx/conf.d/default 的文件,你的站点将不会按预期提供服务。

  • 快速澄清一点:该文件需要位于 nginx 容器的 templates 目录中。如果您使用 `./docker/nginx/conf.d/:/etc/nginx/conf.d/` docker compose 卷来管理此文件,则需要将其更改为 `./docker/nginx/ templates/:/etc/nginx/templates/` 改为 (2认同)

bel*_*ckn 9

您可以通过定义自己的入口点来避免 Compose 解释环境变量的一些麻烦。看这个简单的例子:

  • entrypoint.sh(确保这个文件是可执行的)
#!/bin/sh

export NGINXPROXY

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

exec "$@"
Run Code Online (Sandbox Code Playgroud)
  • docker-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;"]
Run Code Online (Sandbox Code Playgroud)

除了我必须使用 Perl 模块对行进行注释这一事实之外,我的config文件与您的nginx.conf.

请注意,我必须先将配置文件挂载到另一个位置,然后才能安装envsubst。我遇到了一些奇怪的行为,即替换后文件最终为空,这种情况可以通过这种方法来避免。在您的特定情况下,这应该不是问题,因为您已经在构建时将其嵌入到您的图像中。


编辑

为了完整起见,要尽可能少地更改您的设置,您只需要确保您export的环境变量。像这样调整你的命令:

command: ["/bin/bash", "-c", "export NGINXPROXY && envsubst '$$NGINXPROXY' < /etc/nginx/nginx.conf > /etc/nginx/nginx.conf && nginx -g 'daemon off;'"]
Run Code Online (Sandbox Code Playgroud)

......你应该很高兴去。不过,我总是建议使用“更干净”的方式来定义您自己的入口点。


Arj*_*jan 9

就像Jody 的回答中已经解释的那样,现在官方的 Nginx Docker 镜像支持解析模板。这种使用envsubst及其处理确保不会弄乱诸如此类的Nginx 变量$host。好的。但是,envsubst它不支持像常规 shell 和 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 "$@"
Run Code Online (Sandbox Code Playgroud)

比如说,一些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;
    }
}
Run Code Online (Sandbox Code Playgroud)

在 Dockerfile 中将模板复制到/etc/nginx/templates/default.conf.template并设置自定义入口点:

# 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在使用,例如,docker run --env PROXY_API_DEST=https://example.com/api/ ...将设置一个值,在本例中,如果未设置,则默认为该值http://host.docker.internal:8000/api/(实际上 http://localhost:8000/api/在本地计算机上)。


Mik*_*ike 6

因此,在解决了这个问题之后,我设法使其与 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;
}

Run Code Online (Sandbox Code Playgroud)

步骤 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;
}
Run Code Online (Sandbox Code Playgroud)

第 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;"]
Run Code Online (Sandbox Code Playgroud)

第 4 步:使用“环境”部分标签在 docker-compos.yml 文件中设置环境变量。确保您的环境变量名称与您在 nginx 配置文件中选择的任何变量名称相匹配。使用 docker 容器中的“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:git-password@git-server.com/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;'"
Run Code Online (Sandbox Code Playgroud)

第 5 步:使用 docker-compose up 运行您的堆栈(使用您需要的任何其他开关),您的 nginx 服务器现在应该以您在 docker-compose.yml 的“环境”部分中提供的任何值开始

正如上面的解决方案中提到的,你也可以定义你自己的入口点,但是这个解决方案也被证明工作得很好,并将所有内容保存在一个配置文件中,让我能够直接从 git 运行一堆服务只有一个 docker-compose.yml 文件。

非常感谢所有花时间准备完成这件事的人,并感谢您花时间帮助我解决问题。