Search code examples
dockerdocker-swarmdocker-network

ways to contact container in docker swarm without using different ports


I have a cluster setup in docker swarm where I have one nginx container and an arbiter number of instances of an app. This app is always a new enclosed app, just running the same container.

cluster schematic

The apps will be contacted by nginx with a reverse proxy, and I'm now wondering if I need to always expose an individual port per app.

As an example, instance 1 could be placed on port 10001 then the configuration of the reverse proxy would look like this:

  location / {
     proxy_pass                          http://localhost:10001;
     proxy_set_header  Host              $http_host;
     proxy_set_header  X-Real-IP         $remote_addr;
     proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
     proxy_set_header  X-Forwarded-Proto $scheme;
     proxy_read_timeout                  900;
  }

It would be great to be able to access the app instance by its container name (and a fixed port like 8080) rather than by its port. Like this:

  location / {
     proxy_pass                          http://app-name:8080;
     proxy_set_header  Host              $http_host;
     proxy_set_header  X-Real-IP         $remote_addr;
     proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
     proxy_set_header  X-Forwarded-Proto $scheme;
     proxy_read_timeout                  900;
  }

If anyone knows something regarding this topic it would be greatly appreciated.


Solution

  • I think what your are looking for is the docker swarm builtin overlay networking. When you're configuring your swarm to run in an overlay network, the services are accessible via their service name and you don't have to deal with separate port mappings.

    Lets say your stackfile looks something like this for your service app-name which exposes something on port 8080 and your stack is called "mystack":

    ...
    services:
      nginx:
        <nginx config here>
    ...
      app-name:
        hostname: "{{.Service.Name}}{{.Task.Slot}}"
        image: myimage
      deploy:
        replicas: 3
    ...
    
    networks:
     default:
       attachable: true
    

    Then nginx and your service will run on the same overlay network (by using the default network configuration). Now if you want to reach a certain instance of the service "app-name" from within the overlay network you can use mystack_app-name-1. This hostname is gernerated using the Docker Swarm templating Syntax.

    Or if you want to forward the traffic using the nginx proxy you could just use tasks.app-name. This will be resolved by the docker swarm internal DNS service to an instance of the service "app-name". In the docs are good visualizations about overlay and ingress network configurations.

    Note that I did not provide any port mappings for the service "app-name" since inside the overlay network all services can communicate without explicit port mappings, so nginx is able to proxy the traffic from port 8080 of each instance.

    If you provide the attachable: true from my example you are able to test and debug the network configuration with standalone containers. For example:

    docker run --rm -it --name debug_container --network mystack_default alpine
    

    Would spin up an interactive alpine Container inside the overlay network. You should be able to ping the other instances and perform DNS lookups like:

    nslookup tasks.app-name