Accessing container on port 3000 thru traefik - 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!

Related

Traefik different entrypoint and rule combos

I have a docker compose file, I want to host my container on example.com:8080 and api.example.com:443, I can accomplish that goal right now.
However I don't want 2 seperate service for that, I want to eliminate either my_api or abcxyz and have 1 service only and accomplish the same behavior, i.e. my container should be hosted at example.com:8080 and not on example.com:443 AND api.example.com:443 but not on api.example.com:8080
Is there a way to do it under 1 service.
version: "3"
services:
traefik:
image: traefik
command:
- --api.dashboard=false
- --api.insecure=false
- --providers.docker
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entrypoint.to=web-secure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --entrypoints.web.http.redirections.entrypoint.permanent=true
- --entrypoints.web-secure.address=:443
- --entrypoints.spiderman.address=:8080
- --providers.file.directory=/configuration/
- --providers.file.watch=true
ports:
- 80:80
- 443:443
- 8080:8080
volumes:
- ./certificates.yml:/configuration/certificates.yml:ro
- /etc/letsencrypt:/letsencrypt:ro
- /var/run/docker.sock:/var/run/docker.sock
my_api:
image: traefik/whoami
deploy:
replicas: 5
labels:
- "traefik.http.routers.my_api.entrypoints=spiderman"
- "traefik.http.routers.my_api.rule=Host(`example.com`)"
- "traefik.http.routers.my_api.tls=true"
abcxyz:
image: traefik/whoami
deploy:
replicas: 5
labels:
- "traefik.http.routers.abcxyz.entrypoints=web-secure"
- "traefik.http.routers.abcxyz.rule=Host(`api.example.com`)"
- "traefik.http.routers.abcxyz.tls=true"
I could do -
labels:
- "traefik.http.routers.my_api.entrypoints=spiderman,web-secure"
- "traefik.http.routers.my_api.rule=Host(`example.com`,`api.example.com`)"
- "traefik.http.routers.my_api.tls=true"
but it would also serve at example.com:443 which I don't want because i want to host my cool wordpress site there! :)
I think you're looking for something like this:
services:
traefik:
image: traefik
command:
- --api.dashboard=false
- --api.insecure=false
- --providers.docker
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entrypoint.to=web-secure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --entrypoints.web.http.redirections.entrypoint.permanent=true
- --entrypoints.web-secure.address=:443
- --entrypoints.spiderman.address=:8080
ports:
- 127.0.0.3:80:80
- 127.0.0.3:443:443
- 127.0.0.3:8080:8080
volumes:
- /run/docker.sock:/run/docker.sock
my_api:
image: traefik/whoami
hostname: my_api
labels:
- traefik.enable=true
- traefik.http.routers.example_com.entrypoints=spiderman
- traefik.http.routers.example_com.rule=Host(`example.com`)
- traefik.http.routers.example_com.service=my_api
- traefik.http.routers.example_com.tls=true
- traefik.http.routers.my_api.entrypoints=web-secure
- traefik.http.routers.my_api.rule=Host(`api.example.com`)
- traefik.http.routers.my_api.tls=true
- traefik.http.services.my_api.loadBalancer.server.port=80
Note that here I've bound everything to local address 127.0.0.3 for testing, but of course that's not necessary; I did that to avoid conflicts with existing services I have listening on ports 80, 443, and 8080.
Testing
I've defined this shell function that ensures the various hostname:port combinations resolve correctly (you could edit /etc/hosts instead to accomplish the same thing) and shows the HTTP status code for each request:
fetch() {
curl -sf \
--resolve api.example.com:443:127.0.0.3 \
--resolve api.example.com:8080:127.0.0.3 \
--resolve example.com:443:127.0.0.3 \
--resolve example.com:8080:127.0.0.3 \
-k -w '%{stderr}%{http_code}\n' $1
}
Using that, let's test our your various requirements.
my container should be hosted at example.com:8080
$ fetch https://example.com:8080 | grep -i host
200
Hostname: my_api
Host: example.com:8080
X-Forwarded-Host: example.com:8081
and not on example.com:443
$ fetch https://example.com:443 | grep -i host
404
AND api.example.com:443
$ fetch https://api.example.com:443 | grep -i host
200
Hostname: my_api
Host: api.example.com
X-Forwarded-Host: api.example.com
but not on api.example.com:8080
$ fetch https://api.example.com:8080 | grep -i host
404
I think that covers your requirements!

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

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

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

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

502 Proxy Error ( docker + traefik + apache )

I'm trying to setup traefik for SSL termination on my local development instance. Following up this guide I have the following configuration.
docker-compose.yml
version: '2.1'
services:
mariadb:
image: wodby/mariadb:10.2-3.0.2
healthcheck:
test: "/usr/bin/mysql --user=dummyuser --password=dummypasswd --execute \"SHOW DATABASES;\" | grep database"
interval: 3s
timeout: 1s
retries: 5
restart: always
environment:
MYSQL_ROOT_PASSWORD: dummy
MYSQL_DATABASE: database
volumes:
- ./mariadb-init:/docker-entrypoint-initdb.d # Place init .sql file(s) here.
- mysql:/var/lib/mysql # I want to manage volumes manually.
php:
depends_on:
mariadb:
condition: service_healthy
ports:
- "25:25"
- "587:587"
environment:
PHP_FPM_CLEAR_ENV: "no"
DB_HOST: mariadb
#DB_USER: dummy
DB_PASSWORD: dummypasswd
DB_NAME: database
DB_DRIVER: mysql
PHP_POST_MAX_SIZE: "256M"
PHP_UPLOAD_MAX_FILESIZE: "256M"
PHP_MAX_EXECUTION_TIME: 300
volumes:
- codebase:/var/www/html/
- private:/var/www/html/private
solr:
image: mxr576/apachesolr-4.x-drupal-docker
ports:
- "8983:8983"
labels:
- 'traefik.backend=solr'
- 'traefik.port=8983'
# - 'traefik.frontend.rule=Host:192.168.33.10'
volumes:
- solr:/opt/solr/example/solr/collection1/data
restart: always
portainer:
image: portainer/portainer
command: --no-auth -H unix:///var/run/docker.sock
volumes:
- /var/run/docker.sock:/var/run/docker.sock
labels:
- 'traefik.backend=portainer'
- 'traefik.port=9000'
restart: always
apache:
image: wodby/php-apache:2.4-2.0.2
# ports:
# - "80:80"
depends_on:
- php
environment:
APACHE_LOG_LEVEL: warn
APACHE_BACKEND_HOST: php
APACHE_SERVER_ROOT: /var/www/html/drupal
volumes:
- codebase:/var/www/html/
- private:/var/www/html/private
labels:
- 'traefik.backend=apache'
- 'traefik.docker.network=proxy'
- "traefik.frontend.rule=Host:127.0.0.1"
- "traefik.enable=true"
- "traefik.port=80"
- "traefik.default.protocol=http"
restart: always
networks:
- proxy
traefik:
image: traefik
command: -c /traefik.toml --web --docker --logLevel=INFO
ports:
- '80:80'
- '443:443'
- '8888:8080' # Dashboard
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /codebase/traefik.toml:/traefik.toml
- /codebase/certs/cert.crt:/cert.crt
- /codebase/certs/cert.key:/cert.key
volumes:
solr:
external: true
mysql:
external: true
codebase:
external: true
private:
external: true
networks:
proxy:
external: true
traefik.toml
logLevel = "DEBUG" # <---
defaultEntryPoints = ["https", "http"] # <---
[accessLog]
[traefikLog]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
[[entryPoints.https.tls.certificates]]
certFile = "/cert.crt"
keyFile = "/cert.key"
[retry]
[docker]
endpoint = "unix:///var/run/docker.sock"
watch = true
exposedbydefault = false
When trying to verify the instance, I get a 502 Bad Gateway
curl -i -k https://127.0.0.1
HTTP/1.1 502 Bad Gateway
Content-Length: 392
Content-Type: text/html; charset=iso-8859-1
Date: Fri, 14 Sep 2018 16:34:36 GMT
Server: Apache/2.4.29 (Unix) LibreSSL/2.5.5
X-Content-Type-Options: nosniff
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>502 Proxy Error</title>
</head><body>
<h1>Proxy Error</h1>
<p>The proxy server received an invalid
response from an upstream server.<br />
The proxy server could not handle the request <em>GET /index.php</em>.<p>
Reason: <strong>DNS lookup failure for: php</strong></p></p>
</body></html>
A reset for docker-compose and the docker network didn't help.
I've checked the issue on their repo and it seems like nobody got a definitive solution. Anybody has an idea on how to solve this?
Edit:Update for full docker-compose file.
You are trying to connect to php container from apache service using service discovery. But php container is not attached to the network proxy, Because you haven't declared network for it. The same case is with mariabd as well. So, When you connect to apache/traefik they look for host php which is not attached to the network proxy and throw error 502.
Unless and until you specify external network, Docker containers will not be connected to them.
Hence, You have to specify the network as follows for all the services in order to make docker service discovery work properly.
networks:
- proxy
Bonus:
Since you have done port mapping. You can also use public Ip of your host machine followed by the port to connect to services from docker container and from outside containers as well.
Example:
Let us assume your ip is 192.168.0.123 then you can connect to php from
any services in docker container and even from outside docker as 192.168.0.123:25 and 192.168.0.123:587. This is because you have exposed ports
25,587 by mapping them to host ports 25,587.
Some references:
Docker networking
Networking using the host network
Connect a container to a user-defined bridge
Networking with standalone containers
Service discovery
Networking in Compose (check "Specify custom networks" section)