Docker Compose and Nginx

image credits

A reverse proxy accepts a request from a client, forwards it to a server that can fulfill it, and returns the server’s response to the client. – What is a Reverse Proxy vs. Load Balancer?

I played around with the Docker Compose example from my blog post Docker Compose and Raspberry Pi. The application uses two REST calls. One to create a key-value pair and the other to read the value of the key. The values are stored in a Redis database.

I wanted to extend the example with Nginx as reverse proxy. Therefore, I split the two REST calls into two applications each. One writes, the other reads and Nginx merges both.

This would also allow to configure a load balancer. But since everything is running on a single machine the load balancing does not make much sense.

Setup

This setup is based on the article A Docker/docker-compose setup with Redis and Node/Express and the code HugoDF/express-redis-docker which I adapt.

docker-compose.yml

First, we look at the original docker-compose.yml file. The file defines the Redis database and the application:

redis:
  image: redis
  container_name: cache
  expose:
    - 6379
app:
  build: ./
  volumes:
    - ./:/var/www/app
  links:
    - redis
  ports:
    - 3000:3000
  environment:
    - REDIS_URL=redis://cache
    - NODE_ENV=development
    - PORT=3000
  command:
    sh -c 'npm i && node server.js'

I leave the Redis container untouched and split ✂️ the app into app-set and app-get and adjust the ‘command’ entry:

app-set:
  build: ./
  volumes:
    - ./:/var/www/app
  links:
    - redis
  expose:
    - 3000
  environment:
    - REDIS_URL=redis://cache
    - NODE_ENV=development
    - PORT=3000
  command:
    sh -c 'npm i && node server-set.js'

app-get:
  build: ./
  volumes:
    - ./:/var/www/app
  links:
    - redis
  expose:
    - 3000
  environment:
    - REDIS_URL=redis://cache
    - NODE_ENV=development
    - PORT=3000
  command:
    sh -c 'npm i && node server-get.js'

I also changed the ‘ports’ entry to an ‘expose’ entry (see exports vs ports at my blog post Docker Compose and Raspberry Pi) because the app servers are only accessed within the Docker Compose network and not from outside. For an outside access I use Nginx:

nginx:
  build: ./nginx
  links:
    - app-set
    - app-get
  ports:
    - 3000:80

For the Nginx container I use a separate docker file, because I use a custom nginx.conf file to connect the two servers. With the ‘links’ entries I indicate that the container depends on the two applications. Nginx is running on port 80, I map it to port 3000 as the original example did.

Let’s have a look at the docker file:

FROM nginx
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d/default.conf

This uses the current Nginx image, deletes the default.conf file and copies my nginx.conf file:

server {
  location /store/ {
    proxy_pass         http://app-set:3000;
  }  
  
  location / {
    proxy_pass         http://app-get:3000;
  }
}

Both applications run on port 3000, but since they use different hostnames this is not a problem. With a ‘/store/’ request Nginx calls the ‘app-set’ application and returns the response. Otherwise, Nginx calls the ‘app-get’ application.

I copied the original server.js file and removed the routing definitions which were not needed. If you start everything with docker-compose up the requests work as expected. I have the complete example on Github: choas/express-redis-nginx-docker

Summary

With this example I wanted to try out how I can break up and re-assemble a web service that could consist of different tasks.

Of course, the application also runs on a Raspberry Pi. But an error can occur at the first start because both applications call npm install and write to the same directory.

Any comments? Write me on Twitter @choas (DM is open).