Routing requests to containers with nginx
Tags: docker, node, tutorial, nginx
Published: 2020-09-09 14:30:55

Often in large application ecosystems a common nginx is used as a loadbalancer and reverse-proxy for many applications. In a [previous post](https://dev.to/tirthaguha/load-balancing-containers-l5e) of this series, we have seen how *nginx can be used to load balance traffic multiple containers*. 
In this post, we're going to see how we can **use nginx as a reverse-proxy which would route requests to different containers based on path**. 

## What we are going to do
1. create an express application, with path `/app-first` path and launch it in a container
2. create an express application, with path `app-second` path and launch it in a container
3. create an nginx container, and have 2 paths configured in it. path-1 forwards the request to first container, and path-2 forwards the request to second container

## Create the directories
```bash
mkdir first-app
mkdir second-app
mkdir nginx-docker
```

## Create the first application
I just created a very basic nodeJs application. The app.js looks like the following
```javascript
var express = require("express");
var app = express();

app.use(express.json());
app.use(express.urlencoded({ extended: false }));

app.get("/app-first", function (req, res, next) {
  res.send({ title: "Express", application: "Application 1" });
});

app.listen(3000, () => console.log("Running on http://localhost:3000"));
```
The package.json looks like the following 
```javascript
{
  "name": "express-with-env",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "~4.16.1"
  }
}
```
and in parallel to these, the Dockerfile looks like the following
```docker
FROM node:12-slim
WORKDIR /app
COPY ./package*.json ./
RUN npm install
COPY ./ ./
EXPOSE 3000

# Run the code
CMD [ "npm", "start" ]
```
## Create the second application
The second application is exactly like the first application, except, it runs on PORT 4000, and exposes path `/app-second`. the app.js looks like the following
```javascript
var express = require("express");
var app = express();

app.use(express.json());
app.use(express.urlencoded({ extended: false }));

app.get("/app-second", function (req, res, next) {
  res.send({ title: "Express", application: "Application 2" });
});
app.listen(4000, () => console.log("Running on http://localhost:4000"));
```
and the Dockerfile looks like the following
```
FROM node:12-slim
WORKDIR /app
COPY ./package*.json ./
RUN npm install
COPY ./ ./
EXPOSE 4000

# Run the code
CMD [ "npm", "start" ]
```
The packageJSON looks exactly like the first application

## Create the nginx config
Go to the nginx-docker directory and create nginx.conf file
```nginx
upstream first-app {
    server 172.17.0.1:3000 weight=1;
}

upstream second-app {
    server 172.17.0.1:4000 weight=1;
}

server {
  
  location /app-first {
    proxy_set_header X-Forwarded-Host $host:$server_port;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://first-app;
  }

  location /app-second {
    proxy_set_header X-Forwarded-Host $host:$server_port;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://second-app;
  }

}
```
Create a dockerfile for the nginx and here's what it looks like
```docker
FROM nginx
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d/default.conf
```

## Create the images and run the containers

Go to `first-app` directory and build the image
```bash
sudo docker build -t first-app .
```
Go to the second-app directory and build the image
```bash
sudo docker build -t second-app .
```

Run the images now
```bash
sudo docker run -p 3000:3000 -d first-app
sudo docker run -p 4000:4000 -d second-app
```
 Now, if you open http://localhost:3000/app-first in browser, you should get the following JSON
```
{
"title": "Express",
"application": "Application 1"
}
```
and by opening http://localhost:4000/app-second, you should get
```
{
"title": "Express",
"application": "Application 2"
}
```

You see, the applications are running in different ports. Your target is to expose them both in the same port. 

Go to the nginx-docker directory and build the nginx image and run it.
```
sudo docker build -t nginx-proxy .
sudo docker run -p 8000:80 -d nginx-proxy
```

Now you'll be able to see both the applications running in port 8000, as 
* http://localhost:8000/app-second
* http://localhost:8000/app-first

PS:
We wrote code for 2 applications(first-app and second-app) here, and wrote configuration for the third(nginx). We created their docker images, and we ran those images. Of course, these are too many commands to handle. In our [next post](https://dev.to/tirthaguha/build-images-and-run-them-with-docker-compose-4fg0), we'll look into `docker-compose` to simplify these image building and running. 
The code for this tutorial is checked in at https://github.com/tirthaguha/container-reverse-proxy