dockernginxdocker-composeenvironment-variables

Substitute environment variables in NGINX config from docker-compose


I am trying to start an NGINX server within a docker container configured through docker-compose. The catch is, however, that I would like to substitute an environment variable inside of the http section, specifically within the "upstream" block.

It would be awesome to have this working, because I have several other containers that are all configured through environment variables, and I have about 5 environments that need to be running at any given time. I have tried using "envsubst" (as suggested by the official NGINX docs), perl_set, and set_by_lua, however none of them appear to be working.

Below is the NGINX config, as it is after my most recent trial

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;
}

Below is the 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;"]

Below is the section of the docker-compose.yml for the NGINX server (with names and IPs changed). The envsubst command is intentionally commented out at this point in my troubleshooting.

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;'"

What appears to be happening is when I reference the $nginxproxy variable in the upstream block (right after "server"), I get output that makes it look like it's referencing the string literal "$nginxproxy" rather than substituting the value of the variable.

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

When I attempt to use envsubst, I get an error that makes it sound like the command messed with the format of the nginx.conf file

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

I'm pretty stuck, so thanks in advance for your help.


Solution

  • So after some wrestling with this issue, I managed to get it working similarly to the answer provided by bellackn. I am going to post my exact solution here, in case anybody else needs to reference a complete solution.

    Step1: Write your nginx.conf or default.conf how you would normally write it. Save the file as "nginx.conf.template", or "default.conf.template" depending on which you are trying to substitute variables into.

    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;
    }
    
    

    Step2: Substitute a variable in the format ${VARNAME} for whatever value(s) you want to replace with an environment variable:

    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;
    }
    

    Step 3: In your docker-file, copy your nginx configuration files (your nginx.conf.template or default.conf.template) into your container at the appropriate location:

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

    Step 4: Set your environment variable in your docker-compos.yml file using the "environment" section label. Make sure your environment variable name matches whatever variable name you chose within your nginx config file. Use the "envsubt" command within your docker container to substitute your variable values in for your variables within your nginx.conf.template, and write the output to a file named nginx.conf in the correct location. This can be done within the docker-compose.yml file by using the "command" section label:

    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;'"
    

    Step 5: Run your stack with docker-compose up (with whatever additional switches you need) and your nginx server should now start with whatever value you supplied in the "environment" section of your docker-compose.yml

    As mentioned in the solution above, you can also define your own entry point, however this solution has also proven to work pretty well, and keeps everything contained into a single configuration file, giving me the ability to run a stack of services directly from git with nothing but a docker-compose.yml file.

    A big thank you to everybody who took the time to ready through this, and bellackn for taking the time to help me solve the issue.