Adding external GeoServer service to Traefik with docker swarm

Hi,
I'm trying to reverse proxy an external GeoServer service using Traefik in combination with an R Shiny Docker Swarm set up. I can get the Shiny app to display as expected, but still unable to map GeoServer as a PathPrefix to localhost on my computer (i'm currently testing this on my laptop).

I'd like to be able to see GeoServer at localhost/geoserver2

Thanks for you advice.

The docker-compose-traefik.yml:

       version: "3.7"
       services:
         traefik:
           image: traefik:2.5.3 
           deploy:
             restart_policy:
               condition: any
             placement:
               constraints:
                 - node.role == manager
             labels:
             - "traefik.http.services.traefik.loadbalancer.server.port=80"
             #- "traefik.http.services.traefik.loadbalancer.server.port=443"
           command: 
             - "--log.level=DEBUG"
             - "--api.insecure=true"
             - "--providers.docker=true"
             - "--providers.docker.swarmMode=true"
             - "--providers.docker.exposedbydefault=false"
             - "--entrypoints.web.address=:80"
             #- "--entrypoints.websecure.address=:443"
             #- --providers.docker.watch=true
             #- '--providers.docker.defaultRule=Host("en.localexample.com")'
             - "--providers.file=true"
             - "--providers.file.filename=/home/shiny/webapps/9c9t/config/rules.yml"
             - "--providers.file.watch=true"
           ports:
             - "80:80"
             - "9020:8080"
             #- "443:443"
           volumes:
             - "/var/run/docker.sock:/var/run/docker.sock:ro"
           networks:
             - test_net2          
       networks:
         test_net2:
           driver: overlay
           external: true

My rules.yml:

      http:
        routers:
          router-geoserver:
            rule: "Host(`localhost`) && PathPrefix(`/geoserver2/`)"
            service: service-geoserver
            entryPoints:
              - "web"
        services:
          service-geoserver:
            loadBalancer:
              servers:
                - url: "http://172.17.0.5:8080/geoserver/"
              passHostHeader: false

I have also tried replacing the above URL with "http://localhost:8030/geoserver' which is the published port in the running GeoServer Docker container.

The docker-compose_shinyapp.yml:

     version: "3.7"
     services:
       dataservice:
         image: img_shiny9c9t_v2 
         #ports:
    
         deploy:
           replicas: 5
           labels:
             - "traefik.enable=true"
             - "traefik.http.routers.dataservice.rule=Host(`localhost`)"
             - "traefik.http.routers.dataservice.entrypoints=web"   
             - "traefik.http.middlewares.dataservice.stripprefix.prefixes=/"
             - "traefik.http.routers.dataservice.middlewares=dataservice@docker"
             - "traefik.http.services.dataservice.loadbalancer.server.port=3838"
             - "traefik.http.services.dataservice.loadbalancer.sticky=true"
             - "traefik.http.services.dataservice.loadbalancer.sticky.cookie.name=stickycookie"       
             - "traefik.http.services.dataservice.loadbalancer.sticky.cookie.secure=false"
             - "traefik.http.services.dataservice.loadbalancer.sticky.cookie.httpOnly=true"
           restart_policy:
             condition: on-failure
           update_config:
             delay: 2s
         volumes:
           - /home/shiny/webapps/9c9t:/home/shiny/webapps/9c9t
         networks:
           - test_net2         
     networks:
       test_net2:
         driver: overlay
         external: true

Hello @richardtc,

Thanks for your interest in Traefik !

I've tried a very basic docker setup to have your service working. It seems that you have a trailing / in your rule that makes it impossible to do curl localhost/geoserver2. It is accessible through curl localhost/geoserver2/.

Here is my example (could not find your `img_shiny9c9t_v2` image tough)
version: '3.6'

services:
  traefik:
    image: traefik:v2.6
    command:
      - --providers.docker
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

  dataservice:
    image: traefik/whoami
    labels:
      traefik.http.routers.with.rule: Host(`with.localhost`) && PathPrefix(`/geoserver2/`)
      traefik.http.routers.without.rule: Host(`without.localhost`) && PathPrefix(`/geoserver2`)
> curl with.localhost/geoserver2/
Hostname: ba6d95896d1e
IP: 127.0.0.1
IP: 172.18.0.3
RemoteAddr: 172.18.0.2:46678
GET /geoserver2/ HTTP/1.1
Host: with.localhost
User-Agent: curl/7.79.0-DEV
Accept: application/json, application/xml, text/plain, */*
Accept-Encoding: gzip
X-Forwarded-For: 172.18.0.1
X-Forwarded-Host: with.localhost
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Server: f8c0222f6450
X-Real-Ip: 172.18.0.1

> curl with.localhost/geoserver2
404 page not found

> curl without.localhost/geoserver2/
Hostname: ba6d95896d1e
IP: 127.0.0.1
IP: 172.18.0.3
RemoteAddr: 172.18.0.2:46678
GET /geoserver2/ HTTP/1.1
Host: without.localhost
User-Agent: curl/7.79.0-DEV
Accept: application/json, application/xml, text/plain, */*
Accept-Encoding: gzip
X-Forwarded-For: 172.18.0.1
X-Forwarded-Host: without.localhost
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Server: f8c0222f6450
X-Real-Ip: 172.18.0.1

> curl without.localhost/geoserver2
Hostname: ba6d95896d1e
IP: 127.0.0.1
IP: 172.18.0.3
RemoteAddr: 172.18.0.2:46678
GET /geoserver2 HTTP/1.1
Host: without.localhost
User-Agent: curl/7.79.0-DEV
Accept: application/json, application/xml, text/plain, */*
Accept-Encoding: gzip
X-Forwarded-For: 172.18.0.1
X-Forwarded-Host: without.localhost
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Server: f8c0222f6450
X-Real-Ip: 172.18.0.1

So, to have a working solution, you should try this :

version: '3.6'

services:
  traefik:
    image: traefik:v2.6
    command:
      - --providers.docker
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

  dataservice:
    image: traefik/whoami
    labels:
      traefik.http.routers.whoami.rule: Host(`localhost`) && PathPrefix(`/geoserver2`)

Many thanks for your quick response.

I'm still having some difficulty with this.

I added the following line to docker-compose-shinyapp.yml under labels:

     - "traefik.http.routers.service-geoserver.rule:Host(`localhost`) && PathPrefix(`/geoserver2`)"

However, I'm then unable to access localhost in the browser.

I'm not too sure what I should replace 'whoami' in the above line with, but have used 'service-geoserver' (as in the rules.yml file), and maybe that is the problem.

I also removed the trailing / in the rules/yml file:

      http:
        routers:
          router-geoserver:
            rule: "Host(`localhost`) && PathPrefix(`/geoserver2`)"
            service: service-geoserver
            entryPoints:
              - "web"
        services:
          service-geoserver:
            loadBalancer:
              servers:
                - url: "http://172.17.0.5:8080/geoserver/"

I also have tried modifying the rules.yml file as follows, but still I'm not able to access GeoServer at localhost/geoserver2:

      http:
        routers:
          router-geoserver:
            rule: "Host(`localhost`) && PathPrefix(`/geoserver2`)"
            service: service-geoserver
            entryPoints:
              - "web"
        services:
          service-geoserver:
            loadBalancer:
              servers:
                - url: "http://172.17.0.5:8080/geoserver/"
            deploy:
              labels:
                - "traefik.http.routers.service-geoserver.rule:Host(`localhost`) && PathPrefix(`/geoserver2`)"

Perhaps the issue is related to the providers file. Here is the traefik log output:

9c9t_traefik.1.i6ddka5vkjqd@desktop_rc | time="2021-12-29T18:35:08Z" level=error msg="Cannot start the provider *file.Provider: error reading configuration file: /home/shiny/webapps/9c9t/config/rules.yml - open /home/shiny/webapps/9c9t/config/rules.yml:: no such file or directory"

I'm making some progress having resolved the provider file issue above - the issue I have now is a 'Gateway Timeout' for localhost/geoserver2:

 traefik.1.5c8lm74nmeal@desktop_rc    | time="2021-12-30T07:44:15Z" level=debug msg="'504 Gateway Timeout' caused by: dial tcp 172.17.0.5:8081: i/o timeout"

Would be grateful for any idea to resolve the Gateway Timeout.
Many thanks.

docker-compose-traefik.yml:

       version: "3.7"
       services:
         traefik:
           image: traefik:2.5.3 
           deploy:
             restart_policy:
               condition: any
             placement:
               constraints:
                 - node.role == manager
             labels:
             - "traefik.port=80"
             - "traefik.docker.network=test_net2"
             - "traefik.http.services.traefik.loadbalancer.server.port=80"
             #- "traefik.http.services.traefik.loadbalancer.server.port=443"
           command: 
             - "--log.level=DEBUG"
             - "--api.insecure=true"
             - "--providers.docker=true"
             - "--providers.docker.swarmMode=true"
             - "--providers.docker.exposedbydefault=false"
             - "--entrypoints.web.address=:80"
             #- "--entrypoints.websecure.address=:443"
             #- --providers.docker.watch=true
             #- '--providers.docker.defaultRule=Host("en.localexample.com")'
             - "--providers.file=true"
             - "--providers.file.filename=/usr/local/sbin/rules.yml"
             - "--providers.file.watch=true"
           ports:
             - "80:80"
             - "9020:8080"
             #- "443:443"
           volumes:
             - "/var/run/docker.sock:/var/run/docker.sock:ro"
             - "/home/shiny/webapps/9c9t/config/:/usr/local/sbin/"
           networks:
             - test_net2    
       networks:
         test_net2:
           driver: overlay
           external: true

docker-compose-shinyapp.yml:

       version: "3.7"
       services:
         dataservice:
           image: img_shiny9c9t_v2 #sustainableos/shiny:9c9t_r4051_ub2004
           deploy:
             replicas: 5
             labels:
               - "traefik.enable=true"
               - "traefik.http.routers.dataservice.rule=Host(`localhost`)"
               - "traefik.http.routers.dataservice.entrypoints=web"   
               - "traefik.http.middlewares.dataservice.stripprefix.prefixes=/"
               - "traefik.http.routers.dataservice.middlewares=dataservice@docker"
               - "traefik.http.services.dataservice.loadbalancer.server.port=3838"
               - "traefik.http.services.dataservice.loadbalancer.sticky=true"
               - "traefik.http.services.dataservice.loadbalancer.sticky.cookie.name=stickycookie"       
               - "traefik.http.services.dataservice.loadbalancer.sticky.cookie.secure=false"
               - "traefik.http.services.dataservice.loadbalancer.sticky.cookie.httpOnly=true"      
             restart_policy:
               condition: on-failure
             update_config:
               delay: 2s
           volumes:
             - /home/shiny/webapps/9c9t:/home/shiny/webapps/9c9t
           networks:
             - test_net2 
            
       networks:
         test_net2:
           driver: overlay
           external: true

rules.yml:

      http:
        routers:
          router-geoserver:
            rule: "Host(`localhost`) && PathPrefix(`/geoserver2`)"
            service: service-geoserver
            entryPoints:
              - "web"
        services:
          service-geoserver:
            loadBalancer:
              servers:
                - url: "http://172.17.0.5:8081/geoserver/"

NOTE: I changed the port in GeoServer from 8080 to 8081

I see a couple of errors so far:

  • You have a typo here - "traefik.http.routers.service-geoserver.rule:Host(`localhost`) && PathPrefix(`/geoserver2`)". It should be - "traefik.http.routers.service-geoserver.rule=Host(`localhost`) && PathPrefix(`/geoserver2`)"
  • Adding the labels using the deploy argument is redundant.

In the router section, it states:

add labels starting with traefik.http.routers.<name-of-your-choice>. and followed by the option you want to change

So you can replace whoami with "anything".

It says that Traefik cannot reach the dynamic configuration file

That because Traefik cannot reach 172.17.0.5:8081.

Many thanks for the guidance.

When I change the following, I get a 'Not found' browser response for localhost/geoserver2 rather than a gateway timeout.

traefik.http.routers.service-geoserver.rule:Host(localhost)
to
traefik.http.routers.service-geoserver.rule=Host(localhost)

I fixed the provider file (now renamed to traefik-file.yml) as per Traefik documentation.

I also checked the standalone GeoServer Docker container is on the test_net2 network the same as the Docker swarm:

      $ docker network inspect test_net2
      Remove existing network
      $docker network remove
      Create Docker overlay (to connect all containers): 
      $ docker network create -d overlay --attachable test_net2
      To connect standalone container to test_net2 network
      $docker network connect test_net2 geoserver_v1.10_mekong9c9t_v3

I also modified the following in the docker-compose-traefik.yml file to make containers discoverable:

 providers.docker.exposedbydefault=true"

However, I still cannot access GeoServer homepage at http://localhost/geoserver2

I'd be grateful for any further suggestions. It seems like it is a network issue but the standalone GeoServer container is on the overlay network?

These are my files again and output from '$ docker inspect test_net2':

      docker-compose-traefik.yml:
       version: "3.7"
       services:
         traefik:
           image: traefik:2.5.3 
           deploy:
             restart_policy:
               condition: any
             placement:
               constraints:
                 - node.role == manager
             labels:
             - "traefik.port=80"
             - "traefik.docker.network=test_net2"
             - "traefik.http.services.traefik.loadbalancer.server.port=80"
             #- "traefik.http.services.traefik.loadbalancer.server.port=443"
           command: 
             - "--log.level=DEBUG"
             - "--api.insecure=true"
             - "--providers.docker=true"
             - "--providers.docker.swarmMode=true"
             - "--providers.docker.exposedbydefault=true"
             - "--entrypoints.web.address=:80"
             #- "--entrypoints.websecure.address=:443"
             #- "--providers.docker.watch=true"
             #- "--providers.docker.defaultRule=Host("en.localexample.com")"
             - "--providers.file=true"
             - "--providers.file.filename=/etc/traefik/traefik-file.yml"
             - "--providers.file.watch=true"
             - "--providers.docker.network=test_net2"
           ports:
             #- "8098:8098"
             - "80:80"
             - "9020:8080"
             #- "443:443"
           volumes:
             - "/var/run/docker.sock:/var/run/docker.sock:ro"
             - "/home/shiny/webapps/9c9t:/etc/traefik"
          # extra_hosts:
           #  - "host.docker.internal:{docker0_IP}"
           networks:
             - test_net2    
       networks:
         test_net2:
           driver: overlay
           external: true
      

      docker-compose-shinyapp.yml:
       version: "3.7"
       services:
         dataservice:
           image: img_shiny9c9t_v2 #sustainableos/shiny:9c9t_r4051_ub2004
           deploy:
             replicas: 5
             labels:
               - "traefik.enable=true"
               - "traefik.http.routers.dataservice.rule=Host(`localhost`)"
               - "traefik.http.routers.dataservice.entrypoints=web"   
               - "traefik.http.middlewares.dataservice.stripprefix.prefixes=/"
               - "traefik.http.routers.dataservice.middlewares=dataservice@docker"
               - "traefik.http.services.dataservice.loadbalancer.server.port=3838"
               - "traefik.http.services.dataservice.loadbalancer.sticky=true"
               - "traefik.http.services.dataservice.loadbalancer.sticky.cookie.name=stickycookie"       
               - "traefik.http.services.dataservice.loadbalancer.sticky.cookie.secure=false"
               - "traefik.http.services.dataservice.loadbalancer.sticky.cookie.httpOnly=true"
               #- "traefik.http.routers.router-geoserver.rule:Host(`localhost`) && PathPrefix(`/geoserver2`)"     
             restart_policy:
               condition: on-failure
             update_config:
               delay: 2s
           volumes:
             - /home/shiny/webapps/9c9t:/home/shiny/webapps/9c9t
           networks:
             - test_net2    
       networks:
         test_net2:
           driver: overlay
           external: true
      

      traefik-file.yml (the provider file):
      http:
        routers:
          router-geoserver:
            rule: "Host(`localhost`) && PathPrefix(`/geoserver2`)"
            service: service-geoserver
            entryPoints:
              - web
        services:
          service-geoserver:
            loadBalancer:
              servers:
                - url: "http://10.0.15.2:8081/geoserver/"

$ docker inspect test_net2

      [
          {
              "Name": "test_net2",
              "Id": "nk073rr6tisegwhr8typ6f6uu",
              "Created": "2021-12-31T18:18:39.911700944+07:00",
              "Scope": "swarm",
              "Driver": "overlay",
              "EnableIPv6": false,
              "IPAM": {
                  "Driver": "default",
                  "Options": null,
                  "Config": [
                      {
                          "Subnet": "10.0.15.0/24",
                          "Gateway": "10.0.15.1"
                      }
                  ]
              },
              "Internal": false,
              "Attachable": true,
              "Ingress": false,
              "ConfigFrom": {
                  "Network": ""
              },
              "ConfigOnly": false,
              "Containers": {
                  "0e2857fb1188a39285fc40c771b29bf5df3038a2d4d30da044f8046190e1d6c9": {
                      "Name": "9c9t_dataservice.1.i3dxywcvdfzdbs8qw3j40e8n5",
                      "EndpointID": "d1a42e4c03100f99bd066c409dfc4ab9e8e55cf27b927c85e4e5bfef815a8e55",
                      "MacAddress": "02:42:0a:00:0f:47",
                      "IPv4Address": "10.0.15.71/24",
                      "IPv6Address": ""
                  },
                  "0e987db9668462873a574dda1a8ddbb95c571249fe22ec360b4bfc25f94593e4": {
                      "Name": "9c9t_dataservice.4.q9n87ugm8ar80n3kk89cc1rcj",
                      "EndpointID": "565954bdca770a57a23c29c3d4ad30722eb3ab1fdb60dcddaca55f9f1578df20",
                      "MacAddress": "02:42:0a:00:0f:4a",
                      "IPv4Address": "10.0.15.74/24",
                      "IPv6Address": ""
                  },
                  "203c6097b82611184c7d827e9473dc8da9e04473e6bc1d78906389f094e6e13c": {
                      "Name": "9c9t_dataservice.5.exau419fycoab7oe7wpgzet0i",
                      "EndpointID": "96d0434a5be37574f46abd3400aded2b5fd5186238c86e443e5427713c30c384",
                      "MacAddress": "02:42:0a:00:0f:4b",
                      "IPv4Address": "10.0.15.75/24",
                      "IPv6Address": ""
                  },
                  "298ef5ccb828013a0283939496953570219e1f5e3a3d889117e8bac49337290c": {
                      "Name": "9c9t_traefik.1.mkmy5tizr5271nwtwa30zful3",
                      "EndpointID": "a2451ae9894af8834cdc825ff3d34363e0dd1c62871d6a08fb15b79a16c37673",
                      "MacAddress": "02:42:0a:00:0f:45",
                      "IPv4Address": "10.0.15.69/24",
                      "IPv6Address": ""
                  },
                  "48d7e033242789e115cf86e5fd10555be13d5e4271179b3d1e5518aa47a71caa": {
                      "Name": "9c9t_dataservice.3.wrun17iiizvpomrfr1xk1ye8x",
                      "EndpointID": "cf009eb48393adce427ea850efb20afbb26e0cabe01d339c68182678bd26d278",
                      "MacAddress": "02:42:0a:00:0f:49",
                      "IPv4Address": "10.0.15.73/24",
                      "IPv6Address": ""
                  },
                  "856f7f93241ee21971a59c2fbb7814397c718134d35d3797ea9d8b4f74d53775": {
                      "Name": "9c9t_dataservice.2.ifoxtmdnhsvgjdkx3gienwrja",
                      "EndpointID": "1715a66e2948431fe441e530c7cebb39e130c2ccbf7c49ca5aa039d0575628c5",
                      "MacAddress": "02:42:0a:00:0f:48",
                      "IPv4Address": "10.0.15.72/24",
                      "IPv6Address": ""
                  },
                  "98bd2c70045ace1e329566a65b7dea9aff53bdd819d744b0f6ba4ded7a4dacdf": {
                      "Name": "geoserver_v1.10_mekong9c9t_v3",
                      "EndpointID": "5e5350eae734ba69b339103908022bded8d52ef6ca369b0e7141677ef271b625",
                      "MacAddress": "02:42:0a:00:0f:02",
                      "IPv4Address": "10.0.15.2/24",
                      "IPv6Address": ""
                  },
                  "lb-test_net2": {
                      "Name": "test_net2-endpoint",
                      "EndpointID": "9885c01cb6b253a839fc7f88522e00184dc9261e50764b21e33959bce8f2d40d",
                      "MacAddress": "02:42:0a:00:0f:03",
                      "IPv4Address": "10.0.15.3/24",
                      "IPv6Address": ""
                  }
              },
              "Options": {
                  "com.docker.network.driver.overlay.vxlanid_list": "4111"
              },
              "Labels": {},
              "Peers": [
                  {
                      "Name": "b1a9ed4bda6b",
                      "IP": "192.168.0.14"
                  }
              ]
          }
      ]

$ docker inspect geoserver_v1.10_mekong9c9t_v3 | grep "IPAddress"

        "SecondaryIPAddresses": null,
        "IPAddress": "172.17.0.5",
                "IPAddress": "172.17.0.5",
                "IPAddress": "10.0.15.2",

I resolved this, the IP address of the GeoServer container is 10.0.15.2 as per the 'docker inspect test_net2' output.

I've updated the traefik-file.yml above.

Thanks for your help with this!

1 Like

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.