docker-compose --scale X nginx.conf configuration
Nginx
Dynamic upstreams are possible in Nginx (normal, sans Plus) but with tricks and limitations.
You give up on
upstream
directive and use plainproxy_pass
.It gives round robin load balancing and failover, but no extra feature of the directive like weights, failure modes, timeout, etc.
Your upstream hostname must be passed to
proxy_pass
by a variable and you must provide aresolver
.It forces Nginx to re-resolve the hostname (against Docker networks' DNS).
You lose
location
/proxy_pass
behaviour related to trailing slash.In the case of reverse-proxying to bare
/
like in the question, it does not matter. Otherwise you have to manuallyrewrite
the path (see the references below).
Let's see how it works.
docker-compose.yml
version: '2.2'services: reverse-proxy: image: nginx:1.15-alpine volumes: - ./nginx.conf:/etc/nginx/nginx.conf ports: - 8080:8080 app: # A container that exposes an API to show its IP address image: containous/whoami scale: 4
nginx.conf
worker_processes 1;events { worker_connections 1024;}http { access_log /dev/stdout; error_log /dev/stderr; server { listen 8080; server_name localhost; resolver 127.0.0.11 valid=5s; set $upstream app; location / { proxy_pass http://$upstream:80; } }}
Then...
docker-compose up -dseq 10 | xargs -I -- curl -s localhost:8080 | grep "IP: 172"
...produces something like the following which indicates the requests are distributed across 4 app
containers:
IP: 172.30.0.2IP: 172.30.0.2IP: 172.30.0.3IP: 172.30.0.3IP: 172.30.0.6IP: 172.30.0.5IP: 172.30.0.3IP: 172.30.0.6IP: 172.30.0.5IP: 172.30.0.5
References:
- Nginx with dynamic upstreams
- Using Containers to Learn Nginx Reverse Proxy
- Dynamic Nginx configuration for Docker with Python
Traefik
Traefik relies on Docker API directly and may be a simpler and more configurable option. Let's see it in action.
docker-compose.yml
version: '2.2'services: reverse-proxy: image: traefik # Enables the web UI and tells Traefik to listen to docker command: --api --docker ports: - 8080:80 - 8081:8080 # Traefik's web UI, enabled by --api volumes: # So that Traefik can listen to the Docker events - /var/run/docker.sock:/var/run/docker.sock app: image: containous/whoami scale: 4 labels: - "traefik.frontend.rule=Host:localhost"
Then...
docker-compose up -dseq 10 | xargs -I -- curl -s localhost:8080 | grep "IP: 172"
...also produces something the output that indicates the requests are distributed across 4 app
containers:
IP: 172.31.0.2IP: 172.31.0.5IP: 172.31.0.6IP: 172.31.0.4IP: 172.31.0.2IP: 172.31.0.5IP: 172.31.0.6IP: 172.31.0.4IP: 172.31.0.2IP: 172.31.0.5
In the Traefik UI (http://localhost:8081/dashboard/
in the example) you can see it recognised the 4 app
containers:
References:
It's not possible with your current config since it's static. You have two options -
1. Use docker engine swarm mode - You can define replicas & swarm internal DNS will automatically balance the load across those replicas.
Ref - https://docs.docker.com/engine/swarm/
2. Use famous Jwilder nginx proxy - This image listens to the docker sockets, uses templates in GO to dynamically change your nginx configs when you scale your containers up or down.
Ref - https://github.com/jwilder/nginx-proxy