Routing between stacks

Routing between stacks #

Sometimes you need to use an internal network to access services which run in other stacks. A reason would include to have individual deployments of services and to reduce the blast radius when things don’t work out as expected.

By default (or implicitly), all services in a stack are in a network called default (stack-default). This is how services can talk to each other when defined in the same stack and without defining explicit networks.

This implies that services in one stack may not necessary see services in other stacks.

Example #

In order for the routing across stacks to work, you should create a new docker network on the Quantum Console (select your cluster/endpoint, Networks » Add Network). Make sure it’s of type overlay and for good measure make it attachable (in order to be able to attach not just Swarm services but also individual containers).

In the following example, we assume you create a network called global-lan.

Then, let’s add a new backend stack and make sure the services are attached to global-lan:

# "backend" stack
version: "3.8"

services:
  api:
    image: foo/bar
    environment:
      DSN: mysql://user:pass@tasks.db:3306/db
    networks:
    - backend
    - lan

  db:
    image: mariadb/server
    networks:
    - backend

networks:
  backend:
  lan:
    name: global-lan
    external: true
The tasks.service host is based on the internal DNS on Docker Swarm. It’s not available in client-side (JavaScript) applications. The beauty of these hosts is that a Swarm service can be one or more containers. This DNS will pick one and you don’t have to remember IP addresses or setup IPAM on the network.

Then, let’s add a frontend stack — this assumes Traefik, but it works with Caddy as well:

# "frontend" stack
version: "3.8"

services:
  frontend-service:
    image: foo/bar
    deploy:
      labels:
        traefik.enable: true
        traefik.frontend.rule: Host:example.org
        traefik.docker.network: public
    environment:
      API_SERVICE: "http://tasks.api:8080"
    networks:
    - lan
    - public

networks:
  lan:
    name: global-lan
    external: true
  public:
    external: true

How does it work? #

These are the rules that we created due our new network:

  • Traefik 👉 frontend-service: ✅
  • Traefik 👉 db: ❌ no access
  • Traefik 👉 api: ❌ no access
  • frontend-service 👉 api: ✅
  • frontend-service 👉 db: ❌ no access
  • api -> db: ✅

In summary #

The backend stack is completely sealed off from the public Internet.

All services connected to global-lan need unique names in order for DNS resolution (tasks.SERVICE-NAME) to work. So make sure to double-check. The same constraint is true for the public network to work as well.

Further reading: