4

I try to make an nginx reverse proxy load balance between two containers with the same nodejs app.

The directory structure:

.
+-- docker-compose.yml
+-- nginx
+-- nodejs
|   +-- index.js
|   +-- …
+-- php

docker-compose.yml:

version: "3.1"

services:

  nginx-proxy:
    image: nginx:alpine
    ports:
      - "8000:80"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    links:
      - php:php-app
      - nodejs:nodejs-app

  nodejs:
    image: node:alpine
    environment: 
      NODE_ENV: production
    working_dir: /home/app
    restart: always
    volumes:
      - ./nodejs:/home/app
    command: ["node", "index.js"]

  php:
    image: php:apache
    volumes:
      - ./php:/var/www/html

index.js listens at port 8080

nginx conf default.conf:

upstream nodejs-upstream {
  server nodejs-app:8080;
}

server {
  listen 80;
  root /srv/www;

  location / {
    try_files $uri @nodejs;  
  }

  location @nodejs {
    proxy_pass http://nodejs-upstream:8080;
    proxy_set_header Host $host;
  }

  location /api {
    proxy_pass http://php-app:80/api;
    proxy_set_header Host $host;
  }
}

Now I start the app with

docker-compose up  --scale nodejs=2

Does it load-balance?

  • I don't think so because the two instances of the nodejs app listen on the same port 8080.

How is it possible to make the nginx server load-balance between the two instances of the nodejs app?

Is there a better way to do this?


EDIT 1

I am still curious to know how to do that without jwilder/nginx-proxy. Thanks


EDIT 2

I have something which kind of works with:

default.conf:

upstream nodejs-upstream {
  server nodejs_1:8080;
  server nodejs_2:8080;
}

This works while the two nodejs containers are up. When I docker stop nodejs_2, the app is still available (load-balancing seems to work), but the request can be really slow to end up (up to 1min on localhost). If I restart this container, it works fine again…

François Romain
  • 13,617
  • 17
  • 89
  • 123

1 Answers1

5

Q. Is there a better way to do this?

Yes, IMO. Use jwilder/nginx approach. It is automatically updated and discover any new container, adding it to its balancing pool.

https://github.com/jwilder/nginx-proxy

version: "3.1"

services:

  nginx-proxy:
    image: jwilder/nginx
    ports:
      - "8000:80"
     volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro

  nodejs:
    image: node:alpine
    environment: 
      NODE_ENV: production
      VIRTUAL_ENV: localhost
      VIRTUAL_PORT: 8080
    working_dir: /home/app
    restart: always
    volumes:
      - ../nodejs:/home/app
    command: ["node", "index.js"]

Nginx wil be automatically updated when scaling. Note the VIRTUAL_ENV env var in the app service. That var will be read from nginx. And you don't need any further config. (None .conf file)

Robert
  • 33,429
  • 8
  • 90
  • 94
  • Nice nginx plugin, did not know it, but already like it's ease of use ;) – François Maturel Jul 07 '17 at 07:34
  • @Robert with this plugin, 1. how to serve static files, like in my nginx config with: `location / { try_files $uri @nodejs; }`? 2. how to serve a container on a specific url like: `location /api { proxy_pass http://php-app:80/api; proxy_set_header Host $host; }`? – François Romain Jul 07 '17 at 08:26
  • It is fully customizable. See the title "Per-VIRTUAL_HOST", here https://github.com/jwilder/nginx-proxy/blob/master/README.md – Robert Jul 07 '17 at 11:32
  • Combine that with shared *named volumes* between app container and nginx container – Robert Jul 07 '17 at 11:33
  • @Robert Thank you very much. I'm trying to get this to work right now. – François Romain Jul 07 '17 at 12:38
  • Ok, you're welcome. Please tell me whether it gets working – Robert Jul 07 '17 at 13:55
  • @Robert This makes a 503 from nginx. (I also replaced `VIRTUAL_ENV` with `VIRTUAL_HOST` and added the VIRTUAL_PORT: 8080 env variable) – François Romain Jul 07 '17 at 14:19
  • Oh nice! Was it too difficult to achieve? – Robert Jul 07 '17 at 15:17
  • @Robert, with your instructions it was quite easy actually. (The VIRTUAL_HOST=localhost is missing from the doc). Thanks. Now I have a follow up question: https://stackoverflow.com/questions/44974513/with-jwilder-nginx-proxy-how-to-proxypass-an-url-to-a-specific-container – François Romain Jul 07 '17 at 15:27