How to create a Traefik v2.x ReplacePath label in a simple docker-compose file? - traefik

Given the following url's, I'm trying to create some routers with Traefik v2.x
Route forwards to
-----------------------------
/users -> /users
/users/* -> /users/*
/users/swagger -> /swagger
So in these examples, my webserver is has some endpoints for users GET /users, GET /users/1, POST /users, DELETE /users/1, etc.. but also has a Swagger/OpenApi definition/docs located /swagger.
So I'm trying to access these endpoints through Traefik.
I'm under the impression that I need to create labels that use the routers + PathPrefex and routers + Path for the endpoint matching ... but use middleware for the replace.
I'm too sure how to do this properly.
Here's what I'm trying to do...
version: '3.5'
services:
users-api:
image: spike.openapi/users.api
build:
context: ./
dockerfile: src/Users/Dockerfile
ports:
- "80"
networks:
- backend
container_name: users.api
labels:
- "traefik.enable=true"
- "traefik.http.routers.users-api.rule=PathPrefix(`/users`)"
- "traefik.http.routers.users-api.rule=Path(`/users/swagger`)"
- "traefik.http.routers.users-api.entrypoints=web"
reverse-proxy:
image: traefik
<snipped>
...

Without the Host rule, traefik will not know on which backend to redirect the request so the first thing you are missing might be that. I think following should work.
services:
users-api:
...
labels:
# /users/swagger -> /swagger
traefik.http.middlewares.replacepath-middleware.replacepath.path: /swagger
traefik.http.routers.swagger-router.rule: Host(`your-domain.net`) && PathPrefix(`/users/swagger`)
traefik.http.routers.swagger-router.entrypoints: http
traefik.http.routers.swagger-router.middlewares: replacepath-middleware
# everything else (/users -> /users)
traefik.http.routers.base-router.entrypoints: http
traefik.http.routers.base-router.rule: Host(`your-domain.net`)
You could also use the stripprefix middleware to achieve the exact same thing
services:
users-api:
...
labels:
# /users/swagger -> /swagger
traefik.http.middlewares.stripprefix-middleware.stripprefix.prefixes: /users
traefik.http.routers.swagger-router.rule: Host(`your-domain.net`) && PathPrefix(`/users/swagger`)
traefik.http.routers.swagger-router.entrypoints: http
traefik.http.routers.swagger-router.middlewares: stripprefix-middleware
# everything else (/users -> /users)
traefik.http.routers.base-router.entrypoints: http
traefik.http.routers.base-router.rule: Host(`your-domain.net`)
I noticed traefik redirect the request (http 304). If what you want is some kind of url rewriting, I don't think traefik can handle it - this should be your backend's job, users-api in your case.
IMO understanding traefik middleware behaviours is not easy. I tried to reproduce your setup with a simple nginx backend. Have a try at it:
version: '3.7'
services:
traefik:
image: traefik:v2.1
ports:
- 80:80
command:
- --entrypoints.http.address=:80
- --providers.docker.exposedByDefault=false
- --log.level=DEBUG
volumes:
- /var/run/docker.sock:/var/run/docker.sock
nginx:
image: nginx:1.16.1
labels:
traefik.enable: 'true'
# /users/swagger -> /swagger
traefik.http.middlewares.replacepath-middleware.replacepath.path: /swagger
traefik.http.routers.swagger-router.rule: Host(`127.0.0.1`) && PathPrefix(`/users/swagger`)
traefik.http.routers.swagger-router.entrypoints: http
traefik.http.routers.swagger-router.middlewares: replacepath-middleware
# everything else (/users -> /users)
traefik.http.routers.base-router.entrypoints: http
traefik.http.routers.base-router.rule: Host(`127.0.0.1`)
Run the following commands first to create the folder and dummy pages:
docker-compose up -d
docker-compose exec nginx mkdir /usr/share/nginx/html/swagger
docker-compose exec nginx mkdir /usr/share/nginx/html/users
docker-compose exec nginx sh -c "echo 'users page here' > /usr/share/nginx/html/users/index.html" bbouchereau#bbouchereau
docker-compose exec nginx sh -c "echo 'swagger page here' > /usr/share/nginx/html/swagger/index.html"
The results:
http://127.0.0.1/users/ -> users page here
http://127.0.0.1/users/swagger/ -> redirect to http://127.0.0.1/swagger/ -> swagger page here

Related

Traefik: Cannot login to service with digest authentication

I am trying to use Traefik to reverse proxy to a service that uses digest authentication.
When I access the service directly by its port after exposing it in the docker-compose it works fine but when I access it through Traefik the login pop up keeps appearing because a 401 is returned.
I also had a look at the Traefik middleware but I think it is only to add digest authentication and not to be used with services that already have it.
How do I have to configure Traefik to resolve this?
Working docker-compose:
version: "3"
services:
service:
image: service:tag
cap_add:
- NET_ADMIN
ports:
- "8082:8082/tcp"
docker-compose to be used with traefik:
version: "3"
networks:
web:
external: true
internal:
external: false
services:
service:
image: service:tag
cap_add:
- NET_ADMIN
labels:
- traefik.api.frontend.rule=Host:domain.com
- traefik.docker.network=web
- traefik.port=8082
networks:
- internal
- web
ports:
- "1194:1194/udp"
and the traefik.toml:
logLevel = "DEBUG"
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.dashboard]
address = ":8080"
[entryPoints.dashboard.auth]
[entryPoints.dashboard.auth.basic]
users = ["user:hash"]
[entryPoints.http]
address = ":80"
[api]
entrypoint="dashboard"
[docker]
domain = "domain.com"
watch = true
network = "web"
I start traefik like this:
docker run -d -v /var/run/docker.sock:/var/run/docker.sock -v $PWD/traefik.toml:/traefik.toml -p 80:80 -l traefik.frontend.rule=Host:monitor.domain.com -l traefik.port=8080 --network web --name traefik traefik:1.7.2-alpine
and then the service with:
docker-compose up
Everything works fine except the authentication.
This seems to be a bug in Traefik: https://github.com/containous/traefik/issues/4281

Docker haproxy issue with web path

I am trying to get haproxy to connect to the webserver using localhost/path
My current setup is as folows:
HAproxy_test
│ docker-compose.yml
│
├───haproxy
│ Dockerfile
│ haproxy.cfg
│
└───website
index.php
docker-compose.yml:
version: '3'
services:
website1:
image: php:apache
hostname: website_1
volumes:
- ./website:/var/www/html
ports:
- 8080:80
haproxy:
build: ./haproxy
ports:
- 80:80
depends_on:
- website1
haproxy/Dockerfile:
FROM haproxy
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg
haproxy/haproxy.cfg:
global
maxconn 5000
defaults
mode http
timeout connect 5s
timeout client 5s
timeout server 5s
frontend http_in
bind *:80
acl has_web1 path_beg -i /web1
use_backend http_out if has_web1
default_backend http_out
backend http_out
server web1 website1:80 check
Using docker-compose up --build everything comes up fine.
If type in localhost or localhost:8080 it takes me to the website and everything is great BUT if i try localhost/web1 I get a 404 Not Found
HOWEVER, if I change my dockder-compose.yml for example to use image: dockercloud/hello-world instead of image: php:apache the localhost/web1 works just fine
Am I missing some configuration in apache that is pressent in the dockercloud/hello-world image?
It seems to me like your redirect rule in NGINX is working, but the apache server is receiving a request for the path WWWROOT/web1, so Apache is looking for the file or directory at path WWWROOT/web1 which is non-existent, thus giving a 404. You could test by adding the sub directory web1 with basic index.html, and check if that gets served.

Running multiple docker-compose files with nginx reverse proxy

I asked a question here and got part of my problem solved, but I was advised to create another question because it started to get a bit lengthy in the comments.
I'm trying to use docker to run multiple PHP,MySQL & Apache based apps on my Mac, all of which would use different docker-compose.yml files (more details in the post I linked). I have quite a few repositories, some of which communicate with one another, and not all of them are the same PHP version. Because of this, I don't think it's wise for me to cram 20+ separate repositories into one single docker-compose.yml file. I'd like to have separate docker-compose.yml files for each repository and I want to be able to use an /etc/hosts entry for each app so that I don't have to specify the port. Ex: I would access 2 different repositories such as http://dockertest.com and http://dockertest2.com (using /etc/hosts entries), rather than having to specify the port like http://dockertest.com:8080 and http://dockertest.com:8081.
Using the accepted answer from my other post I was able to get one app running at a time (one docker-compose.yml file), but if I try to launch another with docker-compose up -d it results in an error because port 80 is already taken. How can I runn multiple docker apps at the same time, each with their own docker-compose.yml files and without having to specify the port in the url?
Here's a docker-compose.yml file for the app I made. In my /etc/hosts I have 127.0.0.1 dockertest.com
version: "3.3"
services:
php:
build: './php/'
networks:
- backend
volumes:
- ./public_html/:/var/www/html/
apache:
build: './apache/'
depends_on:
- php
- mysql
networks:
- frontend
- backend
volumes:
- ./public_html/:/var/www/html/
environment:
- VIRTUAL_HOST=dockertest.com
mysql:
image: mysql:5.6.40
networks:
- backend
environment:
- MYSQL_ROOT_PASSWORD=rootpassword
nginx-proxy:
image: jwilder/nginx-proxy
networks:
- backend
ports:
- 80:80
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
networks:
frontend:
backend:
I would suggest to extract the nginx-proxy to a separate docker-compose.yml and create a repository for the "reverse proxy" configuration with the following:
A file with extra contents to add to /etc/hosts
127.0.0.1 dockertest.com
127.0.0.1 anothertest.com
127.0.0.1 third-domain.net
And a docker-compose.yml which will have only the reverse proxy
version: "3.3"
services:
nginx-proxy:
image: jwilder/nginx-proxy
ports:
- 80:80
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
Next, as you already mentioned, create a docker-compose.yml for each of your repositories that act as web endpoints. You will need to add VIRTUAL_HOST env var to the services that serve your applications (eg. Apache).
The nginx-proxy container can run in "permanent mode", as it has a small footprint. This way whenever you start a new container with VIRTUAL_HOST env var, the configuration of nginx-proxy will be automatically updated to include the new local domain. (You will still have to update /etc/hosts with the new entry).
If you decide to use networks, your web endpoint containers will have to be in the same network as nginx-proxy, so your docker-compose files will have to be modified similar to this:
# nginx-proxy/docker-compose.yml
version: "3.3"
services:
nginx-proxy:
image: jwilder/nginx-proxy
ports:
- 80:80
networks:
- reverse-proxy
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
networks:
reverse-proxy:
# service1/docker-compose.yml
version: "3.3"
services:
php1:
...
networks:
- backend1
apache1:
...
networks:
- nginx-proxy_reverse-proxy
- backend1
environment:
- VIRTUAL_HOST=dockertest.com
mysql1:
...
networks:
- backend1
networks:
backend1:
nginx-proxy_reverse-proxy:
external: true
# service2/docker-compose.yml
version: "3.3"
services:
php2:
...
networks:
- backend2
apache2:
...
networks:
- nginx-proxy_reverse-proxy
- backend2
environment:
- VIRTUAL_HOST=anothertest.com
mysql2:
...
networks:
- backend2
networks:
backend2:
nginx-proxy_reverse-proxy:
external: true
The reverse-proxy network that is created in nginx-proxy/docker-compose.yml is referred as nginx-proxy_reverse-proxy in the other docker-compose files because whenever you define a network - its final name will be {{folder name}}_{{network name}}
If you want to have a look at a solution that relies on browser proxy extension instead of /etc/hosts, check out mitm-proxy-nginx-companion

Accessing container on port 3000 thru traefik

Okay, so I've got a node-js app I'd like to access thru traefik.
The node-js app runs on port 3000
I've got traefik running after following the test-it instructions from the getting started page.
docker-compose.yml
version: '2'
services:
app:
build:
context: .
dockerfile: docker/app/Dockerfile
environment:
- NODE_ENV=development
- NODE_PORT=3000
volumes:
- ./app:/app
expose:
- "3000"
networks:
- web
labels:
- "traefik.backend=microservice"
- "traefik.backend.port=3000"
- "traefik.port=3000"
- "traefik.frontend.rule=Host:microservice.docker.localhost"
networks:
web:
external:
name: traefik_webgateway
Trying to connect:
curl -H Host:microservice.docker.localhost http://localhost/
Bad Gateway
curl -H Host:microservice.docker.localhost http://localhost:3000/
curl: (52) Empty reply from server
But curl -H Host:whoami.docker.localhost http://localhost/ works like intended.
The problem was that my microservice was bound to listen to localhost:3000 instead I changed it to 0.0.0.0:3000 and it worked like a charm.
removed - "traefik.backend.port=3000" from the docker-compose.yml
added 127.0.0.1 microservice.docker.localhost to /etc/hosts
which rendered me able to:
curl http://microservice.docker.localhost/ and get the response I was expecting
I'm a microservice!

Issues with Path and PathPrefix matching in Traefik

Using Traefix version 1.2.3 from the docker container I've set up the following file.
traefik:
image: traefik
command: --web --docker --docker.domain=docker.localhost --logLevel=DEBUG
ports:
- "80:80"
- "8080:8080"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /dev/null:/traefik.toml
services:
image: opencoredata/ocdservices:0.2
labels:
- "traefik.backend=services"
- "traefik.frontend.rule=Host:opencore.dev"
- "traefik.frontend.rule=PathPrefix:/api"
# web:
# image: opencoredata/ocdweb:0.3
# labels:
# - "traefik.backend=web"
# - "traefik.frontend.rule=Host:opencore.dev"
If I remove the comments around the "web" section all traffic will go to that container ignoring the Path or PathPrefix or any other attempt to get URLs with the beginning /api/ to go to the services container.
Commenting out the "web" container like above and the traffic goes to the services container. Which is expected since there is no other container of course.
I simply can not find how to get Traefik to work with Path, PathPrefix, PathPrefixStrip or any other combination. Examples here and in the docs seem to indicate I should get the behavior I want, but I can not realize it.
I think that this part
- "traefik.frontend.rule=Host:opencore.dev"
- "traefik.frontend.rule=PathPrefix:/api"
is wrong because second line overwrite the first one, try this :
- "traefik.frontend.rule=Host:opencore.dev;PathPrefix:/api"
I think it will combine stuff the way you want.