semural
semural

Reputation: 4611

nginx conditional proxy pass based on environment variable

I would like to proxy_pass to the related service conditionally based on environment variable.. What I mean, prox_pass adress should be change based on NODE_ENV variable..

What is the best approach of doing this ? Can I use if statement like as below for proxy_pass? If yes how should I do this ? Apart from this, I tried to create a bash as below as below to pass environment variable to nginx but could not able to set and pass $NGINX_BACKEND_ADDRESS to nginx conf somehow. Any help will be appreciated

   if ($NODE_ENV == "development) {
       proxy_pass http://myservice-dev;
   }

nginx.conf

server {
    listen  3000;
    location / {
        root /usr/src/app;
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
    }
    location /csrf/token {
        proxy_pass ${NGINX_BACKEND_ADDRESS}/csrf/token;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
    location /export/apis {
        proxy_pass ${NGINX_BACKEND_ADDRESS}/export/apis;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

entrypoint.sh

#!/usr/bin/env sh
set -eu
export NODE_ENV=development
if ["$NODE_ENV" == "development"]
then
    export NGINX_BACKEND_ADDRESS=http://backend-dev
elif ["$NODE_ENV" == "stage"]
then
    export NGINX_BACKEND_ADDRESS=http://backend-stage
elif ["$NODE_ENV" == "development"
then
    export NGINX_BACKEND_ADDRESS=http://backend-preprod
elif ["$NODE_ENV" == "development"]
then
    export NGINX_BACKEND_ADDRESS=http://backend
else 
    echo "Error in reading environment variable in nginx-conf.sh."
fi
echo "Will proxy requests for  to ${NGINX_BACKEND_ADDRESS}*"
exec /nginx-conf.sh "$@"

Dockerfile

FROM nginx:alpine AS production-build
WORKDIR /usr/src/app
ARG NODE_ENVIRONMENT=development
ENV NODE_ENV=$NODE_ENVIRONMENT
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx/nginx.conf.template /etc/nginx/conf.d/default.conf.template
COPY nginx-conf.sh /
RUN chgrp -R root /var/cache/nginx /var/run /var/log/nginx /var/run/nginx.pid && \
    chmod -R 775 /var/cache/nginx /var/run /var/log/nginx /var/run/nginx.pid
USER nginx
COPY --from=builder /usr/src/app/dist .
ENTRYPOINT ["/nginx-conf.sh", $NODE_ENVIRONMENT]
EXPOSE 3000
CMD ["nginx", "-g", "daemon off;"]

Upvotes: 6

Views: 5867

Answers (3)

David Maze
David Maze

Reputation: 158908

The Docker Hub nginx image (as of nginx:1.19) has a facility to do environment-variable replacement in configuration files:

[...] this image has a function, which will extract environment variables before nginx starts. [...] this function reads template files in /etc/nginx/templates/*.template and outputs the result of executing envsubst to /etc/nginx/conf.d.

So your first step is to rename your configuration file as is (including proxy_pass ${NGINX_BACKEND_ADDRESS}/...) to something like default.conf.template and put it in the required directory.

I would directly pass that address in your deploy-time configuration. I would not include it in the image in any way. (Imagine setups like "a developer is trying to run this stack on their local desktop system" where none of the URLs in the entrypoint script are right.) That also lets you get rid of pretty much all the code here; you would just have

# Dockerfile
FROM ... AS builder
...
FROM nginx:1.21-alpine
COPY nginx/nginx.conf.template /etc/nginx/conf.d/default.conf.template
COPY --from=builder /usr/src/app/dist /usr/share/nginx/html
# Permissions, filesystem layout, _etc._ are fine in the base image
# Use the base image's ENTRYPOINT/CMD
# docker-compose.yml
version: '3.8'
services:
  proxy:
    build: .
    ports: ['8000:80']
    environment:
      - NGINX_BACKEND_ADDRESS=https://backend-prod.example.com

If you are in fact using Compose, you can use multiple docker-compose.yml files to provide settings for specific environments.

# docker-compose.local.yml
# Run the backend service locally too in development mode
version: '3.8'
services:
  backend: # not in docker-compose.yml
    build: backend
    # and other settings as required
  nginx: # overrides docker-compose.yml settings
    environment:
      - NGINX_BACKEND_ADDRESS=http://backend
    # no other settings
docker-compose -f docker-compose.yml -f docker-compose.local.yml up

Upvotes: 4

Chris Doyle
Chris Doyle

Reputation: 11992

The way i normally do this is I have a generic nginx proxy and i then just pass in the url and protocol as env vars

ubuntu@vps-f116ed9f:/opt/docker_projects/docker_examples/load_balancer$ cat proxy.conf
server {
  listen 80 default_server;

  resolver 127.0.0.11 valid=1s;

  set $protocol $PROXY_PROTOCOL;
  set $upstream $PROXY_UPSTREAM;

  location / {
    proxy_pass $protocol://$upstream$request_uri;

    proxy_pass_header Authorization;

    proxy_http_version 1.1;
    proxy_ssl_server_name on;
    proxy_set_header Host $upstream;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Connection "";

    proxy_buffering off;
    proxy_read_timeout 5s;
    proxy_redirect off;
    proxy_ssl_verify off;
    client_max_body_size 0;
  }
}
ubuntu@vps-f116ed9f:/opt/docker_projects/docker_examples/load_balancer$ cat Dockerfile
FROM nginx:1.13.8

ENV PROXY_PROTOCOL=http PROXY_UPSTREAM=example.com

COPY proxy.conf /etc/nginx/conf.d/default.template
COPY start.sh /

CMD ["/start.sh"]

I then have a start script that will substitue the env vars into my proxy_config.

ubuntu@vps-f116ed9f:/opt/docker_projects/docker_examples/load_balancer$ cat start.sh
#!/usr/bin/env bash
envsubst '$PROXY_PROTOCOL,$PROXY_UPSTREAM' < /etc/nginx/conf.d/default.template > /etc/nginx/conf.d/default.conf

exec nginx -g 'daemon off;'

Upvotes: 1

Shaqil Ismail
Shaqil Ismail

Reputation: 1961

if you want to run an if statement in your dockerfile, then you can use the RUN command in the dockerfile, for example using bash, RUN if [[ -z "$arg" ]] ; then echo Argument not provided ; else echo Argument is $arg ; fi

Upvotes: 1

Related Questions