dockerdocker-composeroutesjwilder-nginx-proxy

Docker mis-forwarding ports


I have several domains sharing one public IP (EC2 instance). My setup is like this:

/home/ubuntu contains docker-compose.yml:

version: '3'
services:
  nginx-proxy:
    image: "jwilder/nginx-proxy"
    container_name: nginx-proxy
    volumes:
    - /var/run/docker.sock:/tmp/docker.sock:ro
    ports:
    - "80:80"
    restart: "always"

This creates a network named ubuntu_default which will allow other compose instances to join. The nginx-proxy image creates reverse proxies for these other compose instances so that you can visit example.com and be routed to the appropriate UI within the appropriate compose instance.

/home/ubuntu/example.com/project-1 contains a docker-compose.yml like:

version: '3'
services:
  db:
    build: "./db"   # mongo
    volumes:
    - "./data:/data/db"
    restart: "always"
  api:
    build: "./api"   # a node backend
    ports:
    - "9005:9005"
    restart: "always"
    depends_on:
      - db
  ui:
    build: "./ui"   # a react front end
    ports:
    - "8005:8005"
    restart: "always"
    environment:
    - VIRTUAL_HOST=project-1.example.com   # this tells nginx-proxy which domain to proxy
    - VIRTUAL_PORT=8005  # this tells nginx-proxy which port to proxy
networks:
  default:
    external:
      name: ubuntu_default

/home/ubuntu/testing.com/project-2 contains a docker-compose.yml like:

version: '3'
services:
  db:
    build: "./db"  # postgres
    volumes:
    - "./data:/var/lib/postgresql/data"
    restart: "always"
  api:
    build: "./api"  # a python backend
    ports:
    - "9000:9000"
    restart: "always"
    depends_on:
      - db
  ui:
    build: "./ui"  # a react front end
    ports:
    - "8000:8000"
    restart: "always"
    environment:
    - VIRTUAL_HOST=testing.com,www.testing.com # tells nginx-proxy which domains to proxy
    - VIRTUAL_PORT=8000 # tells nginx-proxy which port to proxy
networks:
  default:
    external:
      name: ubuntu_default

So basically:

...and that all works perfectly as long as I only run one at a time. The moment I start both Compose instances, the /api urls start clashing. I can sit on one of them and refresh repeatedly and sometimes I'll see the one for example.com/api and sometimes I'll see the one for testing.com/api.

I have no idea whats going on at this point. Maybe the premise I'm working against is fundamentally flawed but it seems like an intended use of Docker/Compose. I'm open to suggestions to accomplish the same otherwise.


Solution

  • Docker containers communicate using DNS lookups on their network. If multiple containers have the same alias on the same network, it will round robin load balance between the containers with each network connection. If you don't want containers to talk to each other, then you don't want them on the same docker network. The good news is you solve this by using more than one network, and not putting the api and db server on the frontend proxy network:

    version: '3'
    services:
      db:
        build: "./db"  # postgres
        volumes:
        - "./data:/var/lib/postgresql/data"
        restart: "always"
      api:
        build: "./api"  # a python backend
        ports:
        - "9000:9000"
        restart: "always"
        depends_on:
          - db
      ui:
        build: "./ui"  # a react front end
        ports:
        - "8000:8000"
        restart: "always"
        networks:
        - default
        - proxy
        environment:
        - VIRTUAL_HOST=testing.com,www.testing.com # tells nginx-proxy which domains to proxy
        - VIRTUAL_PORT=8000 # tells nginx-proxy which port to proxy
    networks:
      proxy:
        external:
          name: ubuntu_default
    

    If you do not override the default network, docker will create one for your compose project and use it for any containers not assigned to another network.