traefik: Docker swarm configuration ignored with "port is missing"

Do you want to request a feature or report a bug?

Bug

What did you do?

I tried to run traefik in a docker swarm and wanted to expose the dashboard as shown in the docs here: https://docs.traefik.io/operations/dashboard/#secure-mode

What did you expect to see?

The dashboard on http://127.0.0.11/dashboard/

What did you see instead?

A 404 from traefik

Output of traefik version: (What version of Traefik are you using?)

Version:      2.0.2
Codename:     montdor
Go version:   go1.13.1
Built:        2019-10-09T19:26:05Z
OS/Arch:      linux/amd64

What is your environment & configuration (arguments, toml, provider, platform, …)?

version: '3.7'
services:
  traefik:
    image: traefik:latest
    ports:
      - 80:80
      - 443:443
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == manager
      labels:
        - traefik.enable=true
        - traefik.http.routers.traefik_http.rule=Host(`127.0.0.11`)
        - traefik.http.routers.traefik_http.service=api@internal
        - traefik.http.routers.traefik_http.entrypoints=http
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: >
      --providers.docker
      --providers.docker.exposedbydefault=false
      --providers.docker.swarmmode=true
      --entryPoints.http.address=":80"
      --entryPoints.https.address=":443"
      --accesslog
      --log.level=INFO
      --api=true
      --api.dashboard=true

If applicable, please paste the log output in DEBUG level (--log.level=DEBUG switch)

level=error msg="port is missing" providerName=docker container=traefik-traefik-1jvz83nonxx53q9eel9r4cwlg

Traefik seems to ignore configurations of a docker service if they are missing a service definition setting a loadbalancer port. Since api@internal is an internal service which is already defined and has no port, I didn’t define one. However, if I add a dummy service with a port, the Dashboard works fine:

...
      labels:
        - traefik.enable=true
        - traefik.http.services.dummyService.loadbalancer.server.port=1337
        - traefik.http.routers.traefik_http.rule=Host(`127.0.0.11`)
        - traefik.http.routers.traefik_http.service=api@internal
        - traefik.http.routers.traefik_http.entrypoints=http
...

I did some more experiments:

# Route and Services work fine:
  whoami:
    image: containous/whoami
    deploy:
      replicas: 2
      labels:
        - traefik.enable=true
        - traefik.http.services.whoami.loadbalancer.server.port=80
        - traefik.http.routers.whoami_http.rule=Host(`127.0.0.12`)
        - traefik.http.routers.whoami_http.service=whoami
        - traefik.http.routers.whoami_http.entrypoints=http

# Route for whoami2 is ignored because of missing port:
  whoami2:
    image: containous/whoami
    deploy:
      replicas: 1
      labels:
        - traefik.enable=true
        - traefik.http.routers.whoami2_http.rule=Host(`127.0.0.13`)
        - traefik.http.routers.whoami2_http.service=whoami
        - traefik.http.routers.whoami2_http.entrypoints=http
      
# Route for whoami3 is ignored because of missing port:
  whoami3:
    image: containous/whoami
    deploy:
      replicas: 1
      labels:
        - traefik.enable=true
        - traefik.http.routers.whoami3_http.rule=Host(`127.0.0.14`)

# Route for whoami4 is parsed and served by whoami4:
  whoami4:
    image: containous/whoami
    deploy:
      replicas: 1
      labels:
        - traefik.enable=true
        - traefik.http.services.whoami4.loadbalancer.server.port=80
        - traefik.http.routers.whoami4_http.rule=Host(`127.0.0.15`)

# Route for whoami5 is parsed and served by whoami:
  whoami5:
    image: containous/whoami
    deploy:
      replicas: 1
      labels:
        - traefik.enable=true
        - traefik.http.services.whoami5.loadbalancer.server.port=1337
        - traefik.http.routers.whoami5_http.rule=Host(`127.0.0.16`)
        - traefik.http.routers.whoami5_http.service=whoami

Errors here:

level=error msg="port is missing" providerName=docker container=traefik-whoami2-lvaeohb9ycx9g8vqs5bhhep5b
level=error msg="port is missing" providerName=docker container=traefik-whoami3-mpr8w2rl8lx21b13vt3c3nm0e

It looks like traefik requires a traefik.http.services.*.loadbalancer.server.port label on every service, or it will discard the configuration. Name, existence and port of the service are completely irrelevant.

Traefik should not require a loadbalancer port definition for each docker service, because a router can also reference an internal service or a service defined elsewhere and thus defining a service/loadbalancer is pointless.

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 48
  • Comments: 59 (22 by maintainers)

Most upvoted comments

Sorry, but I don’t consider this done. Just documenting that you have to add a silly dummy value to your configuration isn’t really solving the issue. It’s a workaround at best. It’s also only addressing the issue with the Dashboard/API. It is not addressing the issues with my example config where one docker service is actually referencing an others docker service traefik service.

just spent like 3 hours on this. Not clear from the docs that this flag was needed anywhere in the docs… nearly made me not use traefik!

Just needed to add this line to service: - traefik.http.services.whoami5.loadbalancer.server.port=80

I had been trying like this but wasnt working: - traefik.port=80

Just wanted to note that this issue is not exclusive to Swarm.

I just changed my Traefik container (normal Docker without Swarm) to use host networking, thus removing the “port” definition in docker-compose.yml. Everything worked fine except for the Traefik Dashboard, same issue as above: “port is missing”. Adding this dummy service “solved” the problem:

labels:
  traefik.enable: true
  traefik.http.routers.api.rule: Host(`traefik.example.com`)
  traefik.http.routers.api.service: api@internal
  traefik.http.services.dummy-svc.loadbalancer.server.port: 9999

This isn’t really a solution though, it’s a silly workaround that should not be necessary.

I see why Traefik throws an error when it can’t detect the port to autocreate a service. But in this case, there is no need to create a service at all, because an existing internal service is used.

I think the solution is actually pretty simple: If a container defines a router with an @internal service, don’t try to autocreate an additional service (unless it’s explicitly defined using traefik.http.services.[...] labels). Or maybe do try to autocreate it (if that still makes sense in some way), but don’t ignore the router labels when service autocreation fails, since those router labels are independent from that autocreated service.

I understand that, but I still think this is an error. It explains why, in my example, whoami3 is ignored. whoami3 has no service definition, so the service is automatically generated. As you stated, this does not work in swarm.

However, the fact that whoami2 is ignored, is an error. whoami2 refers to a service with a defined loadbalancer port. The same applies to my initial problem with api@internal. There shouldn’t be a pointless dummy port being required, like what makes whoami5 work.

Hello,

In Swarm mode, the port cannot be automatically detect, so the port must be manually set.

Mandatory for Docker Swarm.

https://docs.traefik.io/v2.0/routing/providers/docker/#services

@AndrewSav I too understood about that, but it should be mentioned in the docs as peoples which are new or upgrading from 1.7 need to aware of this

@MicahZoltu Take a look to

* https://doc.traefik.io/traefik/v2.4/providers/docker/#exposedbydefault

* https://doc.traefik.io/traefik/v2.4/providers/overview/#restrict-the-scope-of-service-discovery
Expose containers by default through Traefik. If set to false, containers that do not have a traefik.enable=true label are ignored from the resulting routing configuration.

Setting next: exposedByDefault = false resolves the error level=error msg="port is missing" for containers which have to be ignored from the routing. ( i.e. simple configuration with containers having database, application which are communicating within backend but only nginx/apache{must have set traefik.enable=true} container to be routed through Traefik)

For the initial issue reported: In the the documentation it’s already showing a dummy service for Swarm port detection. https://doc.traefik.io/traefik/operations/api/#configuration

# Dynamic Configuration
deploy:
  labels:
    - "traefik.http.routers.api.rule=Host(`traefik.example.com`)"
    - "traefik.http.routers.api.service=api@internal"
    - "traefik.http.routers.api.middlewares=auth"
    - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
    # Dummy service for Swarm port detection. The port can be any valid integer value.
    - "traefik.http.services.dummy-svc.loadbalancer.server.port=9999"

@binaryDiv Thank you that works.

Spent a lot of time finding this issue and work around. I hope this gets documented soon enough😢

There shouldn’t be a pointless dummy port being required, like what makes whoami5 work.

@ldez I think @xsrf has a good point here. There are clearly cases when this port is not needed or used, yet without it the provider configuration for the affected container is not being sent.

I think you are aware of that given this post of yours:

https://community.containo.us/t/dashboard-gives-me-an-error-bad-gateway/1883/2

In particular it says:

- "traefik.http.services.traefik.loadbalancer.server.port=888" # required by swarm but not used.

I’m not sure I understand why it is required by swarm. It seems to be required by traefik, but why is it required if it is not used?

@ldez Why is needed to know the port of api@internal? I don’t get it

More fun: this is not limited to docker swarm mode, it also happens with network_mode: host

… as noted by @binaryDiv two years ago: https://github.com/traefik/traefik/issues/5732#issuecomment-894362338

@ldez @AndrewSav why not add an if condition that checks if the service is internal so no port is required? i’ve wasted some hours on this too, makes no sense

Hi @msonowal @cookiejest @xsrf , we hear you, and a first step has been done when merging #5795 by adding a Docker Swarm example, separately than “Docker”, for both Dashboard (https://docs.traefik.io/v2.0/operations/dashboard/#secure-mode) and API (https://docs.traefik.io/v2.0/operations/api/#configuration).

@dduportal I am referring to the thing when deploying traefik as a service and how to use the traefik dashboard as it is a internal special kind of service so people will not simply set the load balancer port for the traefik service itself thats why it is confusing peple

@xsrf Any solutions yet, @traefiker Please someone should look into this as critical issue

@ldez Usually why/what questions reqruire a bit more than “yes” or “no” 😉))))

This line is not the root of the behavior

Oh, you know the code base and I don’t - you wrote it, I don’t doubt you. I’m just seeing an issue that is being brought up again and again on the forums, and I’m trying being helpful to find a solution.

it’s a bit more complex: it’s the expected behavior for the common cases but in some cases it can be a problem.

It seems to me that it is possible to fix the problem and not break the common cases. I tried to explain that above, but perhaps not adequatly.

It feels like you have it under control and don’t have time to discuss this in more details, so I’ll leave this up to you. Let me know if you want discuss / my help.

Yep, this is understood. All the above I said still applies -may be it’s time to change status quo.

The main issue here, is that when you have a router that points to an existing service (traefik.http.routers.<router_name>.service) and there is no service explicitly defined, chances are you do not need to automatically create a service.

Trying to create a service that was not requested, failing and refusing to apply the rest of labels in swarm mode is not nescessary, detremental and surprising. I’m proposing to improve this.

Do you @ldez agree that there is a problem here, that would benefit from a fix?

If it is desirable to attempt to create a service automatically even if there is no router without an explicit service (so the automatically created service is unlikely to be used), then the improvement should be, when failing to create a service not to fail the rest of configuration for the docker container/service, I currently cannot see downsides of this.

Yeah. This one caused me some headache as well.

I have a slightly different use case, I don’t want to expose an @internal service, but I want to define some common middlewares that I want to re-use on several of my 50+ services.

I work on docker swarm and those 50+ services currently are loadbalanced by an nginx container.

Most services fall into 2 categories and could use some of the same middlewares. I want to define those middlewares via labels on the traefik container itself, so that I can re-use them on the services and I can manage them in one place, instead of duplicating it on every single service.

Currently this would look like this Example:

            labels:
                traefik.enable: "true"
                # HERE IS THE DUMMY STUFF
                traefik.http.services.dummy-service.loadbalancer.server.port: 9999
                traefik.http.routers.dummy-route.entrypoints: web
                traefik.http.routers.dummy-route.service: noop@internal

                # HERE is the stuff that I REALLY want to define.
                traefik.http.middlewares.slowrate.ratelimit.average: 5
                traefik.http.middlewares.slowrate.ratelimit.burst: 10

                traefik.http.middlewares.fastrate.ratelimit.average: 100
                traefik.http.middlewares.fastrate.ratelimit.burst: 500

                traefik.http.middlewares.source-ip-is-private.ipwhitelist.sourcerange: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1/8
... and so on ... 

I really want to migrate to traefik, so that I don’t need to rebuild the nginx container every time we add a new service, some rate limit changes or some some api endpoint is added … but currently I have to employ two workarounds to get this up and running…

  • I have to define a service
  • I have to define a route

Both, the route and the service, simply speaking should not be needed to declare middlewares that are not even used by them.

There might be workarounds, by declaring middlewares in files, but seriously? I want to switch to traefik, to get rid of config files … be that as part of a custom built image, or as a separate artifact that needs to be deployed.

Have just lost 2 hours on this… How can this be possible… The documentation does NOT contain anymore the dummy service line, as far as i can tell (nothing here for example : https://doc.traefik.io/traefik/operations/api/#configuration)

how is this still an open issue? running traefik on host networking, trying to access the dashboard, won’t work without the dummy port

It’s a issue with swarm and the internal service, I already explain that: https://github.com/containous/traefik/issues/5732#issuecomment-549096489

With Docker, the port is autodetected but With Swarm is impossible to know the port (related to swarm networking).

@AndrewSav just a little note in the same place to say it needs to be on the traefik service itself as well would be helpful if nothing else 😃

@AndrewSav I saw that but in case for deploying traefik as in the cluster its not appropriate that we define the port for that label, so this is a confusing for people

@msonowal well, the issue is understood and workaround is known, as per discussion above. I would not call it critical, in fact it’s pretty minor. Traefik team is well aware of this issue as evidenced by post from ldez, traefik team member.

Same here, there seems to be no other way to enable traefik dashboard secure mode in Docker Swarm without creating a dummy service.