Using Nginx Reverse Proxy for Local Microservice Development

In this post, I share how I added a NGINX proxy to route traffic to microservices for local development

This is a continuation from my previous posts; Dockerizing Blazor Wasm Application

The previous setup was a follows: 3-Service Architecture

One drawback of this setup was that both the SPA service and the API service had to expose ports in order to be accessible and the ports had to be different. I didn’t like that very much and for my learning purposes, I set out to figure out how to accomplish the following setup:

4-Service Architecture

The first task was to figure out how to configure NGINX to forward requests to multiple back-end services on the same port. After much research, this is the nginx.conf I eventually came up with:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
worker_processes 1;
  
events { worker_connections 1024; }

http {

    sendfile on;

    upstream spa-service {
        server SPA_SERVICE;
    }

    upstream api-service {
        server API_SERVICE;
    }
    
    # https://www.bogotobogo.com/DevOps/Docker/Docker-Compose-Nginx-Reverse-Proxy-Multiple-Containers.php
    proxy_set_header   Host $host;
    proxy_set_header   X-Real-IP $remote_addr;
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Host $server_name;
    
    server {
        listen PORT;
 
        location / {
            proxy_pass         http://spa-service/;
            proxy_redirect     off;
        }
 
        # https://stackoverflow.com/a/53354944
        location ~* /Api/v1\.0/(.*) {
            # https://stackoverflow.com/a/8130872
            proxy_pass         http://api-service/Api/v1.0/$1$is_args$args;
            proxy_redirect     off;
            # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-5.0
            proxy_http_version 1.1;
            proxy_set_header   Upgrade $http_upgrade;
            proxy_set_header   Connection keep-alive;
            proxy_cache_bypass $http_upgrade;
            proxy_set_header   X-Forwarded-Proto $scheme;
        }
    }
}

The tricky part was to ensure that only traffic intended for the SPA service was routed there. Somehow, I could not get it to work using just the path directive. I had to resort to regex to get it to work.

Next, I created a configure-environment.sh script very similar to the previous article:

1
2
3
4
5
6
7
#!/bin/sh  

# replace the placeholders in the nginx congfiguration file  
# https://stackoverflow.com/a/23134318
sed -i -e "s|API_SERVICE|${API_SERVICE}|g" /etc/nginx/nginx.conf
sed -i -e "s|SPA_SERVICE|${SPA_SERVICE}|g" /etc/nginx/nginx.conf
sed -i -e 's/PORT/'"${PORT}"'/g' /etc/nginx/nginx.conf

Details of the configuration settings are clearly outlined in the referenced articles.

Next, the Dockerfile:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
FROM nginx:alpine AS runtime

# copy startup commands
COPY configure-environment.sh /docker-entrypoint.d/
RUN chmod +x /docker-entrypoint.d/configure-environment.sh

# https://www.bogotobogo.com/DevOps/Docker/Docker-Compose-Nginx-Reverse-Proxy-Multiple-Containers.php
COPY nginx.conf /etc/nginx/nginx.conf

ENV SPA_SERVICE=spa:8080 
ENV API_SERVICE=api:8080
ENV PORT=8080

EXPOSE 8080

WORKDIR /home/site/wwwroot

Finally, the docker-compose.yml to tie it all together:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# https://docs.docker.com/compose/compose-file/compose-file-v3/
version: "3"

services:
  proxy:
    build:
      context: .
      dockerfile: Dockerfile
    image: business/proxy:latest
    container_name: business-proxy
    environment:
      - SPA_SERVICE=spa:8080
      - API_SERVICE=api:8080
    # https://docs.docker.com/compose/startup-order/
    depends_on:
      - spa
      - api
    restart: always
    ports:
      - 80:8080

References:
DOCKER COMPOSE : NGINX REVERSE PROXY WITH MULTIPLE CONTAINERS
Host ASP.NET Core on Linux with Nginx
“proxy_pass” cannot have URI part in location given by regular expression
How can query string parameters be forwarded through a proxy_pass with nginx?
Environment variable substitution in sed

Built with Hugo
Theme Stack designed by Jimmy