Using dynamic traefik labels in docker.compose.yml (e.g., .Task.Slot)?

Is it possible to use dynamic information from docker when creating the labels used by traefik? I've experimented with trying the following inside a service definition:

# sassfs backend service
be:
...
    deploy:
      endpoint_mode: dnsrr
      mode: replicated
      replicas: 3
      update_config:
        failure_action: pause
    environment:
      - TASK_SLOT={{.Task.Slot}}
    labels:
      - 'traefik.enable=true'
      - 'traefik.http.middlewares.sassfs_be.stripprefix.forceslash=false'
      - 'traefik.http.middlewares.sassfs_be.stripprefix.prefixes=/${TASK_SLOT}'
      - 'traefik.http.routers.sassfs_be.entrypoints=http_alt'
      - 'traefik.http.routers.sassfs_be.rule=PathPrefix(`/${TASK_SLOT}`)'
      - 'traefik.http.services.sassfs_be.loadbalancer.server.port=8080'
...

but ${TASK_SLOT} ends up evaluating to the empty string (running docker inspect on the container shows the TASK_SLOT set in the environment, but the labels end up being '/' indicating it wasn't available when it was evaluated.

What I'm trying to do is use traefik to access specific instances of a docker service. For example if I have 3 replicas of a container image running for a service, in some cases I need to be able to get at a specific container to inspect it (this would be for things like gathering performance statistics or debugging).

As a follow-up: what I'm really trying to accomplish is a way to use traefik to allow me to access specific instances of a docker service. To anyone who comes at me and talks about "cattle not pets" you still sometimes need to check the temperature of one of your cattle, you don't just shoot it in the head if you think it might be sick.

I think the only way you could achieve this is by using stickiness, which in turn changes the behavior of the entire router.

I suppose you could make another router on this service just to do this function.

Did either of you have any luck achieving this?

No, I didn't see any way I could accomplish the goal of a single traefik instance that could dynamically route to specific containers by name. The only way I could think to use stickiness would require a client capable of collecting and examining the cookies, which didn't seem useful for my use case.

Someone on the Docker Swarm slack channel did point out that there is a Pull Request to address the problem of not being able to register the container hostname with the dns resolver: https://github.com/moby/moby/pull/39204 which I think would give me a path forward.

For now I'm stuck with my current solution, which is just using haproxy and hashing on the incoming hostname of the request so that each unique hostnames gets routed to a unique backend, w/o control over which one that it is.

HI:
A Im just looking how to implement that with traefik, I have a modification for dockercloud-haproxy project which just implement that and I am using it in production.
O modification of stickiness could work if instead of using IP to locate servers in pkg.server.service.service or pkg.server.service.loadbalancer.wrr, use hostname, for example for:

services:
  whoami:
    image: traefik/whoami:v1.6.0
    hostname: whoami-{{.Task.Slot}}.mydomain.com

instead of storing in SessionCookie:

StickyCookie=http://10.0.4.5:80

a valid option in swarm could be:

StickyCookie=http://whoami-2.mydomain.com:80

Any tip to modify Traefik source code and submit a PR?
Best regards, Marcelo.

I have an initial implementation of sticky functionality with {{.Task.Slot}} support, with only two file changes is possible to route to an specific task setting the cookie value. For example:

version: '3.6'

services:
  ping:
    image: alpine
    command: tail -f /dev/null
    networks:
      - lb_network

  whoami:
    image: traefik/whoami:v1.6.0
    hostname: "{{.Service.Name}}-{{.Task.Slot}}.mydomain.com"
    deploy:
      mode: replicated
      replicas: 3
      labels:
        - traefik.enable=true
        - traefik.docker.network=lb_network
        - traefik.constraint-label=traefik-public
        - traefik.http.routers.whoami.rule=Path(`/whoami`)
        - traefik.http.routers.whoami.priority=10
        - traefik.http.services.whoami.loadbalancer.server.port=80
        - traefik.http.services.whoami.loadbalancer.sticky=true
        - traefik.http.services.whoami.loadbalancer.sticky.cookie.name=StickyCookie

and an URL call like:
$ curl -v --cookie "StickyCookie=http://traefik_whoami-1.mydomain.com:80" --resolve traefik_whoami-1.mydomain.com:80:127.0.0.1 http://traefik_whoami-1.mydomain.com/whoami
will route to the Task Slot 1, if you change the cookie value to traefik_whoami-2.mydomain.com it will be routed to Slot 2.
File changes by now are:

mochoa@pocho:~/docker/traefik$ git status
On branch master
Your branch is behind 'origin/master' by 15 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   pkg/provider/docker/config.go
	modified:   pkg/provider/docker/docker.go

It support wildcard for {{.Task.Slot}}, {{.Service.Name}} and {{.Node.ID}} last is used with service deployed in global mode.
Cookies now look like:
Cookie: StickyCookie=http://traefik_whoami-1.mydomain.com:80
Next, I am trying to implement that if StickyCookie is empty, it will be filled at first call using the URL call, once I have this func working I'll submit a PR on GitHub.
Marcelo.

I ended up adding nginx to the swarm to dynamically route based on dynamic prefix.

nginx.yml

version: "3.9"

services:
  nginx:
    image: "nginx"
    hostname: "nginx"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    deploy:
      placement:
        constraints:
          - node.role == manager
      replicas: 1
      labels:
        - "traefik.enable=true"
        - "traefik.http.services.nginx.loadbalancer.server.port=80"
        - "traefik.http.routers.nginx.entrypoints=websecure"
        - "traefik.http.routers.nginx.tls=true"
        - "traefik.http.routers.nginx.rule=Host(`example.com`) && PathRegexp(`^/[0-9]+`)"
    networks:
      - swarm

networks:
  swarm:
    name: swarm
    external: true

nginx.conf

http {
    server {
        listen 80;
        access_log /dev/stdout;
        error_log /dev/stdout;
        location ~ ^/([0-9]+) {
            resolver 127.0.0.11 ipv6=off;
            proxy_pass http://whoami$1:80;
        }
    }
}

whoami.yml

version: "3.9"

services:
  whoami:
    image: "traefik/whoami"
    hostname: "whoami{{.Task.Slot}}"
    deploy:
      placement:
        constraints:
          - node.role == worker
      replicas: 2
    networks:
      - swarm

networks:
  swarm:
    name: swarm
    external: true