Nginx in front of docker swarm services

dockerdocker-swarmnginx

I am thinking about how this is meant to be done in a serious way for weeks now and didn't come to a conclusion yet.
Maybe I think into the wrong direction.

Lets say you have a hundred web apps which come and go.
You want an nginx configuration like that with the example of gitlab:

location / {
  proxy_pass http://gitlab;
}

As gitlab has been created with docker service create, nginx will be able to resolve gitlab by it's swarm-vip fake dns name within your ingress network.

BUT: only if the service container is running. Else, nginx will not start due to [emerg] 1#1: host not found in upstream "gitlab"

Now that's a neckbreaker when you have to run a high availability nginx and it's not your business to ensure that the proxy_pass'ed apps are running.

Everytime you update the nginx service, it will not come up if only one of the hundred other swarm services are not running even for the same second .. wtf?

If that ain't working, why in the world do we need name resolution in swarm?
How do you solve this problem?

I thought about consul and dynamic generation of nginx virtual host templates (don't even think about docker-nginx-proxy!), but the apps are very different that you can say each app has its own individual configuration. And all this work not for a special reason, only to solve the resolving problem of nginx?

Best Answer

I'd recommend swapping out the statically defined nginx reverse proxy for traefik which is swarm aware and can dynamically reconfigure itself as services are deployed and destroyed.

Here's a sample implementation:

  1. create a network for the traefik to talk to containers: docker network create proxy

  2. Make a traefik.toml, here's an example:

traefik.toml

accessLogsFile = "/proc/1/fd/1"
defaultEntryPoints = ["http"]
[entryPoints]
  [entryPoints.http]
  address = ":80"
[web]
address = ":8080"
[docker]
endpoint = "unix:///var/run/docker.sock"
domain = "localhost"
watch = true
swarmmode = true
constraints = ["tag==frontend"]
  1. Sample compose file for traefik:

docker-compose.traefik.yml

version: '3'

networks:
  proxy:
    external:
      name: proxy

services:
  traefik:
    image: traefik:latest
    volumes:
    - ./traefik.toml:/etc/traefik/traefik.toml:ro
    - /var/run/docker.sock:/var/run/docker.sock
    ports:
    - 80:80
    - 8080:8080
    networks:
    - proxy
    restart: unless-stopped
  1. Configure your app with labels and on the same network.

docker-compose.app.yml

version: '3'

networks:
  proxy:
    external: true

services:
  webtest:
    image: nginx:latest
    networks:
    - default
    - proxy
    labels:
    - traefik.frontend.rule=PathPrefixStrip:/webtest
    - traefik.port=80
    - traefik.docker.network=proxy
    - traefik.tags=frontend
    restart: unless-stopped

The rule above uses path prefixes for the container for simplicity, but you can use any rule you prefer to proxy your application.

Related Topic