Search code examples
dockerdocker-composejupytertraefik

Issues rewriting url route/add a PathPrefix to jupyter lab with traefik


I am having trouble rewriting the route or adding a path prefix to a route for a jupyterlab services in docker so that http://jupyter-test.localhost/user starts jupyterlab. I also tried removing the stripprefix with no luck. Any help would be appreciated, thank you

docker-compose.yml

version: "3.8"

services:
  reverse-proxy:
    image: traefik:v2.4
    command: --api.insecure=true --providers.docker # --log.level=DEBUG
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    labels:
      - traefik.enable=false

  jupyter_rewrite_path:
    restart: always
    image: jupyter/scipy-notebook
    command: jupyter-lab --ip='*' --NotebookApp.token='' --NotebookApp.password='' --NotebookApp.base_url=/user
    labels:
      - traefik.http.routers.jupyter_rewrite_path.rule=Host(`jupyter-test.localhost`) && PathPrefix(`/user`)
      - traefik.http.services.jupyter_rewrite_path.loadbalancer.server.port=8888
      - "traefik.http.routers.jupyter_rewrite_path.middlewares=jupyter_rewrite_path_stripprefix"
      - "traefik.http.middlewares.jupyter_rewrite_path_stripprefix.stripprefix.prefixes=/user"

use docker-compose up


Solution

  • When I start containers using your docker-compose.yaml file, I see that the jupyter_rewrite_path container is marked as "unhealthy". Look at the STATUS column in this output:

    $ docker compose ps
    NAME                             ...  STATUS                      ...
    jupyter_jupyter_rewrite_path_1   ...  Up 58 seconds (unhealthy)   ...
    jupyter_reverse-proxy_1          ...  Up 58 seconds               ...
    

    Traefik will not direct traffic to an unhealthy service; if you look at your Traefik dashboard (http://localhost:8080/dashboard/#/http/routers), you'll see that the Jupyter container doesn't show up in the list.

    The container is marked unhealthy because of a healthcheck defined in the image; we can see that with docker image inspect which shows us:

    "Healthcheck": {
        "Test": [
            "CMD-SHELL",
            "wget -O- --no-verbose --tries=1 --no-check-certificate     http${GEN_CERT:+s}://localhost:${JUPYTER_PORT}${JUPYTERHUB_SERVICE_PREFIX:-/}api || exit 1"
        ],
        "Interval": 5000000000,
        "Timeout": 3000000000,
        "StartPeriod": 5000000000,
        "Retries": 3
    },
    

    So it's connecting to /api on the container and expecting a successful response. As we can see from the container logs, it is in fact getting a 404 error:

    jupyter_rewrite_path_1  | [W 2023-02-02 20:50:38.456 ServerApp] 404 GET /api (6d36d539cca44c57bb06702c21c5cc9b@127.0.0.1) 0.84ms referer=None
    

    And that's because you've set --NotebookApp.base_url=/user, but the healthcheck is request /api rather than /user/api.

    If you look at the healthcheck, you can see that it builds the URL from a number of variables:

    http${GEN_CERT:+s}://localhost:${JUPYTER_PORT}${JUPYTERHUB_SERVICE_PREFIX:-/}api
    

    By setting the JUPYTERHUB_SERVICE_PREFIX variable, we can get the healthcheck to connect to Jupyter at the expected path. That looks like:

    jupyter_rewrite_path:
      restart: always
      image: docker.io/jupyter/scipy-notebook
      environment:
        JUPYTERHUB_SERVICE_PREFIX: /user/
      command:
        - jupyter-lab
        - --ip=*
        - --NotebookApp.token=
        - --NotebookApp.password=
        - --NotebookApp.base_url=/user
      labels:
        - traefik.enable=true
        - traefik.http.routers.jupyter_rewrite_path.rule=Host(`jupyter-test.localhost`) && PathPrefix(`/user`)
        - traefik.http.services.jupyter_rewrite_path.loadbalancer.server.port=8888
    

    You'll note I've dropped the stripprefix bits here, because they're no longer necessary -- by setting the --NotebookApp.base_url option, you're telling Jupyter that it's hosted at /user, so we don't need (or want) to strip the prefix.

    With the above configuration, I can successfully access the notebook server at http://localhost/user/.