【问题标题】:Substitute environment variables in NGINX config from docker-compose从 docker-compose 替换 NGINX 配置中的环境变量
【发布时间】:2019-11-01 03:01:16
【问题描述】:

我正在尝试在通过 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;
}

下面是 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;"]

以下是 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;'"

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

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

【问题讨论】:

    标签: docker nginx docker-compose environment-variables


    【解决方案1】:

    Since 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
            
    

    我将从文档中的示例中稍微偏离脚本。注意模板文件上额外的.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 变量替换为@ 987654334@ 在它启动你的容器之前。所以docker-compose.yml可以指定环境变量为API_HOST: ${API_HOST},而.env可以设置实际值:API_HOST=api.example.com。对 docker-compose 专家来说可能是显而易见的,但我花了几个小时才弄清楚。
    【解决方案2】:

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

    • entrypoint.sh(确保该文件可执行)
    #!/bin/sh
    
    export NGINXPROXY
    
    envsubst '${NGINXPROXY}' < /config.template > /etc/nginx/nginx.conf
    
    exec "$@"
    
    • 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;"]
    

    我的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;'"]
    

    ...你应该很高兴。不过,我总是会推荐使用“更简洁”的方式来定义自己的入口点。

    【讨论】:

    • 我可能会在 Dockerfile 中 COPY 这个脚本并将其指定为映像的 ENTRYPOINT,而不是仅在运行时覆盖它。
    【解决方案3】:

    因此,在解决了这个问题之后,我设法让它的工作方式与 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-file 中,将您的 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 配置文件中选择的任何变量名称匹配。使用 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;'"
    

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

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

    非常感谢所有花时间准备的人,感谢您花时间帮助我解决问题。

    【讨论】:

    • 我得到这个错误:`错误:服务'服务器'构建失败:复制失败:文件在构建上下文中找不到或被.dockerignore排除:stat nginx.conf.template:文件不存在`。我已经尝试了很多方法来保存 nginx.conf.template 并将这个文件检入到容器中,它存在。我需要使用哪些 docker-compose 配置?
    • 我会看看 bellackn 的回答。这是一个比我尝试做的简单得多的设置。
    【解决方案4】:

    就像explained in Jody's answer 一样,现在官方的 Nginx Docker 镜像支持解析模板。这使用envsubstits handling 确保不会与Nginx variables 混淆,例如$host 等等。好的。但是,envsubst 不支持像常规 shell 和 Docker Compose 在使用 ${MY_VAR:-My Default} 时所做的默认值。因此,即使使用默认值,这个内置模板总是需要对所有变量进行完整设置。

    要在图像本身中定义默认值,可以使用自定义入口点首先设置默认值,然后简单地委托给the original entrypoint。喜欢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/)。

    【讨论】:

      猜你喜欢
      • 2022-07-01
      • 1970-01-01
      • 2015-06-17
      • 2022-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多