Search code examples
dockerdocker-composedocker-swarmtraefik

Exposing a Docker database service only on the internal network with Traefik


Let's say I defined two services "frontend" and "db" in my docker-compose.yml which are deployed to a Docker swarm, i.e. they may also run in different stacks. With this setup Traefik automatically generates the frontend and backend for each stack which is fine.

Now I have another Docker container running temporarily in a Jenkins pipeline which shall be able to access the db service in a specific stack. My first idea was to expose the db service by adding it to the cluster-global-net network so that Traefik can generate a frontend route to the bakend. This basically works.

But I'd like to hide the database service from "the public" while still being able to connect another Docker container to it via its stack or service name using the internal "default" network.

Can this be done somehow?

version: '3.6'

networks:
  default: {}
  cluster-global-net:
    external: true

services:
  frontend:
    image: frontend_image
    ports:
    - 8080
    networks:
    - cluster-global-net
    - default
    deploy:
      labels:
        traefik.port: 8080
        traefik.docker.network: cluster-global-net
        traefik.backend.loadbalancer.swarm: 'true'
        traefik.backend.loadbalancer.stickiness: 'true'
      replicas: 1
      restart_policy:
        condition: any

  db:
    image: db_image
    environment:
    - MYSQL_ALLOW_EMPTY_PASSWORD=false
    - MYSQL_DATABASE=db_schema
    - MYSQL_USER=db_user
    - MYSQL_PASSWORD=db_pass
    ports:
    - 3306
    volumes:
    - db_volume:/var/lib/mysql
    networks: 
    - default
    restart: on-failure
    deploy:
      labels:
        traefik.port: 3306
        traefik.docker.network: default

Solution

  • What you need is a network on which both of them are deployed, but that it's not visible from anyone else.

    To do such, create a network , add it to your db service and frontend, and also to your temporary service. And indeed, remove traefik label on db because they are not needed anymore here.

    EG :

    ...
    networks:
      default: {}
      cluster-global-net:
        external: true
      db-net:
        external: true
    
    services:
      frontend:
        image: frontend_image
        networks:
        - cluster-global-net
        - default
        - db-net
        deploy:
            ...
    
      db:
        image: db_image
        ...
        networks: 
        - default
        - db-net
        restart: on-failure
        #no labels
    


    docker network create db-net 
    docker stack deploy -c <mycompose.yml> <myfront>
    docker service create --network db-net <myTemporaryImage> <temporaryService>
    

    Then, the temporaryService as well as the frontend can reach the db through db:3306

    BTW : you don't need to open the port for the frontend, since traefik will access it internally (trafik.port).

    EDIT : new exemple with network created from compose file.

    ...
    networks:
      default: {}
      cluster-global-net:
        external: true
      db-net: {}
    
    services:
      frontend:
        image: frontend_image
        networks:
        - cluster-global-net
        - default
        - db-net
        deploy:
            ...
    
      db:
        image: db_image
        ...
        networks: 
        - default
        - db-net
        restart: on-failure
        #no labels
    


    docker stack deploy -c <mycompose.yml> someStackName
    docker service create --network someStackName_db-net <myTemporaryImage> <temporaryService>