从docker-compose

时间:2019-06-18 12:50:05

标签: docker nginx docker-compose environment-variables

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

我很困,所以在此先感谢您的帮助。

4 个答案:

答案 0 :(得分:7)

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的文件,并且不会按预期提供您的网站。

答案 1 :(得分: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;'"]

...而且您应该很好。不过,我总是建议您使用“清洁”的方式定义自己的入口点。

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

因此,在对此问题进行了一些努力之后,我设法使其工作与贝拉肯提供的答案类似。如果有人需要参考完整的解决方案,我将在此处发布确切的解决方案。

第一步:按照通常的方式编写您的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配置文件中选择的任何变量名称匹配。在泊坞窗容器中使用“ 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的“ environment”部分中提供的任何值开头。 >

如上面的解决方案中所述,您也可以定义自己的入口点,但是事实证明该解决方案可以很好地工作,并将所有内容都包含在单个配置文件中,使我能够运行服务栈直接从git中获取,只有一个docker-compose.yml文件。

非常感谢所有花时间为这一问题做准备的人,并且贝拉肯花时间帮助我解决问题。