Objective
I have a server running Rancher (Docker). In that server, I have several un-related stacks.
I want to make Traefik the main interaction point of that stack. And make it load balance / proxy the requests to different containers based on the path.
I want to make it send the traffic for /api to the backend server service. And send the traffic for / to the frontendservice.
Current state
Here's a slimmed down version of my docker-compose.yml showing only the relevant services:
version: '2'
services:
server:
image: server
labels:
- "traefik.frontend.rule=PathPrefix:/api"
- "traefik.enable=true"
- "traefik.port=80"
frontend:
image: frontend
labels:
- "traefik.frontend.rule=PathPrefix:/"
- "traefik.enable=true"
- "traefik.port=80"
proxy:
image: traefik:v1.4
command: --web --accessLog --rancher --rancher.exposedbydefault=false --rancher.metadata=true
volumes:
- /dev/null:/traefik.toml
Then using a Rancher load balancer that listens to the public ports, I redirect requests to app1.example.com to my proxy service. Then it takes the requests and re-directs traffic to each of the two containers based on the path.
I also redirect traffic to traefik.app1.example.com to the same proxy service, to the port 8080 to access the web UI.
If I have only one stack, it works.
The problem
If I add another stack (or if I duplicate that stack) and have more services with Traefik labels, the proxy from app1 will read the labels from the services in app2 and any other stack that declares Traefik labels.
Then, in the web UI for the proxy in app1, I can see all the backends from all the different stacks. But the frontend rules get overwritten.
Question
Up to now, I've seen examples mostly of how to use Traefik as a unique global load balancer / proxy. I just realized that I assumed that I could also create isolated Traefik instances per stack, or a hierarchy of Traefik load balancers.
It seems to me like I'm missing some configuration or misunderstanding something.
But still, I have to ask, is it possible to have different isolated Traefik instances per stack and make them only listen and use the services in their own stack?
Is it possible with Rancher? Is it possible with any other Docker stack orchestrator (e.g. Swarm)?
If that's possible, what am I missing?
I think I found how to solve it.
It seems I can't make Traefik only use services in his own stack directly. But it seems I can use Traefik constraints and tags to achieve almost the same.
Here's my updated docker-compose.yml using constraints and tags:
version: '2'
services:
server:
image: server
labels:
- "traefik.frontend.rule=PathPrefix:/api"
- "traefik.enable=true"
- "traefik.port=80"
- "traefik.tags=app1.example.com"
frontend:
image: frontend
labels:
- "traefik.frontend.rule=PathPrefix:/"
- "traefik.enable=true"
- "traefik.port=80"
- "traefik.tags=app1.example.com"
proxy:
image: traefik:v1.4
command: --web --accessLog --constraints=tag==app1.example.com --rancher --rancher.exposedbydefault=false --rancher.metadata=true
volumes:
- /dev/null:/traefik.toml
It will only match services that have the Traefik tag app1.example.com. Those tags can be any string, but as I'm wanting to filter by a specific domain, I just used the domain I wanted to filter as the tag to use.
Also, notice that the tag is set in the services with a label traefik.tag.
If you are interested in Traefik v2 solution for this, you can use --providers.docker.constraints option to achieve the same result.
So, the basic example could be:
services:
traefik:
image: traefik:v2.3
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.constraints=Label(`custom.label`,`custom-value`)"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
custom_service:
image: ...
labels:
- "traefik.enable=true"
- "custom.label=custom-value"
Docs
Related
I have a docker stack configuration with an overlay network called traefik. It contains my traefik reverse-proxy container and then several containers that serve various subdomains. I'm adding a new one that'll need to access a database server that I've created in another container, so I added something like this:
networks:
traefik:
driver: overlay
database:
driver: overlay
services:
traefik:
image: traefik
networks:
- traefik
ports:
- 80:80
- 443:443
- 8080:8080
volumes:
# ...
# ...
database:
image: postgres:9.6-alpine # match what's being used by heroku
networks:
- database
staging:
image: staging
networks:
- traefik
- database
deploy:
labels:
traefik.enable: "true"
traefik.frontend.rule: Host:lotto-ticket.example.com
traefik.docker.network: traefik
traefik.port: 3000
When I did this, the reverse proxy started returning Gateway Timeout codes, indicating that the staging container was no longer available to the traefik network. If I remove the database network from the staging container, the subdomain serves up as expected (although it obviously fails to access the database), and when I add it back in, the reverse-proxy starts getting timeouts from it.
Is there some kind of conflict between the two networks? Do I need to configure IP routing in order to use multiple docker networks on a single container?
EDIT: added more detail to the config excerpt
You need to tell traefik on which network to connect to your service using the label traefik.docker.network. In your case, that label would be set to ${stack_name}_traefik where ${stack_name} is the name of your stack.
I am trying to run Traefik as an API gateway and want to trigger ForwardAuth middleware by using the following docker compose file but the middleware the auth endpoint is not being hit. I am using it with localhost.
version: '3'
services:
reverse-proxy:
image: traefik # The official Traefik docker image
command: --api --docker # Enables the web UI and tells Traefik to listen to docker
ports:
- "80:80" # The HTTP port
- "8080:8080" # The Web UI (enabled by --api)
volumes:
- /var/run/docker.sock:/var/run/docker.sock # So that Traefik can listen to the Docker events
- $PWD/traefik.toml:/traefik.toml
whoami:
image: emilevauge/whoami
labels:
- traefik.enable=true
- "traefik.frontend.rule=Host:whoami.docker.localhost"
- "traefik.http.middlewares.test-redirectscheme.redirectscheme.scheme=https"
- "traefik.http.middlewares.test-replacepath.replacepath.path=/foo"
- "traefik.http.middlewares.testauth.ForwardAuth.Address=http://localhost:55391/api/Auth"
I was struggling with this for a while as well, and couldn't find an answer anywhere other than fairly hidden in the Traefik docs. The ForwardAuth docs don't actually mention this, but looking at the middlewares overview configuration example I suddenly noticed that you not only have to specify the middleware, you also have to apply it to the router.
Adding this label to whoami service should do the trick:
- "traefik.http.routers.whoami.middlewares=testauth"
Note that you can also specify multiple middlewares here, by comma-separating them, so you could add the other middlewares you defined like so:
- "traefik.http.routers.whoami.middlewares=testauth,test-redirectscheme,test-replacepath"
Suppose I have two web service say they are w1 and w2 which are deployed by docker-compose.
Now I want to create multiple container for each of them, and create a load balancer in front of them which can make the services accessed by this:
http://localhost:8880 //for service w1
http://localhost:8888 //for service w2
I have searched, and I got dockercloud-haproxy and nginx-proxy.
However, the former is deprecated, and the later have to be used with hostname to distinguish different service.
It is kind of complicated for set different hosts especially in development environment.
Then I found traefik. Seems like more configurable. This is the config file I used:
version: "3"
services:
w1:
image: jwilder/whoami
labels:
- "traefik.backend=whoami"
- "traefik.protocol=http"
- "traefik.port=8080"
- "traefik.frontend.entryPoints=http_8080"
w2:
build: . # a simple node server which use port 80
labels:
- "traefik.backend=node"
- "traefik.protocol=http"
- "traefik.port=80"
- "traefik.frontend.entryPoints=http_80"
lb:
image: traefik
command: "--docker \
--logLevel=DEBUG \
--entryPoints='Name:http_80 Address::80' \
--entryPoints='Name:http_8080 Address::8080'"
ports:
- 8880:80
- 8888:8080
volumes:
- /var/run/docker.sock:/var/run/docker.sock
While it still can not meet my requirement.
Is this possible or any other alternative solution?
You need to add the matcher -"traefik.frontend.rule=PathPrefixStrip:/" under w1 and w2. Otherwise it defaults to Host:w1.project_name.
Also, I believe jwilder/whoami uses the port 8000 instead of 8080, so change - "traefik.port=8080" to - "traefik.port=8000"
I'm running minio using its official docker-compose file which creates 4 services (containers): minio1,minio2,minio3,minio4. Traefik treats them as 4 unique services, but in reality, they should be treated as a single backend, that is, I want Traefik to generate 1 frontend an 1 backend (with 4 servers) for minio.
I tried putting them into a single group(servicefabric.groupname) but to no avail.
I have the following labels set for each minio service:
labels:
- "traefik.servicefabric.groupname=minio"
- "traefik.basic.frontend.rule=Host:foo.bar.com"
- "traefik.weight=10" # 10,20,30,40 incremented per service
- "traefik.frontend.rule=Host:traefik"
- "traefik.port=9000"
container_name: minio*
Is there any way to achieve this?
#Riverman
I had the same issue as you and I solved it by playing around as it's not fully documented in Traefik documentation. What you need to do is specify the traefik.backend value for all the services to be the same name and set traefik.frontend.backend to that traefik.backend value. You can't use the service piece for this. Below is an example.
services:
minio01:
image: minio/minio
hostname: minio01
restart: always
volumes:
- minio01-data:/export
networks:
- minio
- traefik
command: server http://minio01/export http://minio02/export
labels:
- 'traefik.enable=true'
- 'traefik.docker.network=traefik'
- 'traefik.frontend.rule=Host:minio.local'
- 'traefik.frontend.backend=minio'
- 'traefik.port=9000'
- 'traefik.protocol=http'
- 'traefik.backend=minio'
minio02:
image: minio/minio
hostname: minio02
restart: always
volumes:
- minio02-data:/export
networks:
- minio
- traefik
command: server http://minio01/export http://minio02/export
labels:
- 'traefik.enable=true'
- 'traefik.docker.network=traefik'
- 'traefik.frontend.rule=Host:minio.local'
- 'traefik.frontend.backend=minio'
- 'traefik.port=9000'
- 'traefik.protocol=http'
- 'traefik.backend=minio'
All minio services have to have the same frontend rule and the same backend name.
labels:
- "traefik.frontend.rule=Host:minio.${DOMAIN}"
- "traefik.backend=minio"
- "traefik.port=9000"
Also I think you misunderstood the meaning of "backend". A backend is a server to which Traefik route traffic based on the frontend rules. Same as "upstream"/"location" in nginx.
Edit
As stated in the comment, this configuration creates multiple frontends all pointing to the same backend, although functional this looks ugly. A quick solution is to just have one service with ‘frontend’ but if that service goes down, the frontend will be gone too.
A better way to do this is to set it in the config file, traefik.toml:
[frontends]
[frontends.frontend1]
backend = "minio"
[frontends.frontend1.minio]
rule = "Host: minio.${DOMAIN}”
I have two services, web and helloworld. The following is my docker-compose YAML file:
version: "3"
services:
helloworld:
build: ./hello
volumes:
- ./hello:/usr/src/app
ports:
- 5001:80
web:
build: ./web
volumes:
- ./web:/usr/share/nginx/html
ports:
- 5000:80
depends_on:
- helloworld
Inside the index.html in web, I made a button that opens http://helloworld when clicked on. However, my button ends up going to helloworld.com instead of the correct service. Both services work fine when I do localhost:5001 and localhost:5000. Am I missing something?
Docker's embedded DNS for service discovery is for container-to-container networking. For connections from outside of docker (e.g. from your browser) you need to publish the port (e.g. 5000 and 5001 in your file) and connect to that published port.
To use the container-to-container networking, you would need the DNS lookup to happen inside of the web container and the connection to go from web to helloworld, instead of from your browser to the container.
Edit: from your comment, you may find a reverse proxy helpful. Traefik and nginx-proxy are two examples out there. You can configure these to forward to containers by hostname or by a virtual path, and in your situation, I think path based routing would be easier. The resulting compose file would look something like:
version: "3"
services:
traefik:
image: traefik
command: --docker --docker.watch
volumes:
- /var/lib/docker.sock:/var/lib/docker.sock
ports:
- 8080:80
helloworld:
build: ./hello
volumes:
- ./hello:/usr/src/app
labels:
- traefik.frontend.rule=PathPrefixStrip:/helloworld
- traefik.port=80
web:
build: ./web
volumes:
- ./web:/usr/share/nginx/html
labels:
- traefik.frontend.rule=PathPrefixStrip:/
- traefik.port=80
The above is all untested off the top of my head configuration, but should get you in the right direction. With the PathPrefixStrip rule, you can make a link in web to "/helloworld" which will go to the other container. And since the link doesn't have a hostname or port, it will go to the same traefik hostname/port you are already using.