Installation

Traefik-proxy installation

  1. Install JupyterHub:

    $ python3 -m pip install jupyterhub
    
  2. Install jupyterhub-traefik-proxy, which is available now as pre-release:

    python3 -m pip install jupyterhub-traefik-proxy
    
  3. In order to be able to launch JupyterHub with traefik-proxy or run the tests, traefik, etcd and consul must first be installed and added to your PATH.

    There are two ways you can install traefik, etcd and consul:

    1. Through traefik-proxy’s install utility.

      From traefik-proxy directory:

      $ python3 -m jupyterhub_traefik_proxy.install --output=/usr/local/bin
      

      This will install the default versions of traefik, etcd and consul, namely traefik-1.7.5, etcd-3.3.10 and consul_1.5.0 to /usr/local/bin specified through the --output option.

      If no directory is passed to the installer, a dependencies directory will be created in the traefik-proxy directory. In this case, you must add this directory to PATH, e.g.

      $ export PATH=$PATH:{$PWD}/dependencies
      

      If you want to install other versions of traefik, etcd and consul in a directory of your choice, just specify it to the installer through the following arguments:

      • --traefik-version
      • --etcd-version
      • --consul-version
      • --output

      Example

      $ python3 -m jupyterhub_traefik_proxy.install --output=dep \
               --traefik-version=1.6.6 --etcd-version=3.2.24 --consul-version=1.5.0
      

      If the desired install directory doesn’t exist, it will be created by the installer.

    2. From traefik, etcd and consul release pages:

Enabling traefik-proxy in JupyterHub

TraefikTomlProxy, TraefikEtcdProxy and TraefikConsulProxy are custom proxy implementations that subclass Proxy and can register in JupyterHub config using c.JupyterHub.proxy_class entrypoint.

On startup, JupyterHub will look by default for a configuration file, jupyterhub_config.py, in the current working directory. If the configuration file is not in the current working directory, you can load a specific config file and start JupyterHub using:

$ jupyterhub -f /path/to/jupyterhub_config.py

There is an example configuration file here that configures JupyterHub to run with TraefikEtcdProxy as the proxy and uses dummyauthenticator and simplespawner to enable testing without administrative privileges.

In jupyterhub_config.py:

c.JupyterHub.proxy_class = "traefik_toml"
# will configure JupyterHub to run with TraefikTomlProxy
c.JupyterHub.proxy_class = "traefik_etcd"
# will configure JupyterHub to run with TraefikEtcdProxy
c.JupyterHub.proxy_class = "traefik_consul"
# will configure JupyterHub to run with TraefikConsulProxy

Implementation details

  1. Traefik Dashboard

    Traefik provides a Web UI dashboard where you can see the frontends and backends registered, the routing rules, some metrics, but also other configuration elements. Find out more about traefik api’s, here.

    Because of security concerns, in traefik-proxy implementation, traefik api endpoint isn’t exposed on the public http endpoint. Instead, it runs on a dedicated authenticated endpoint that’s on localhost by default.

    The port on which traefik-proxy’s api will run, as well as the username and password used for authenticating, can be passed to the proxy through jupyterhub_config.py, e.g.:

    c.TraefikTomlProxy.traefik_api_url = "http://127.0.0.1:8099"
    c.TraefikTomlProxy.traefik_api_password = "admin"
    c.TraefikTomlProxy.traefik_api_username = "admin"
    

    Check out TraefikProxy’s API Reference for more configuration options.

  2. TKvProxy class

    TKvProxy is a JupyterHub Proxy implementation that uses traefik and a key-value store. TraefikEtcdProxy and TraefikConsulProxy are proxy implementations that sublass TKvProxy. Other custom proxies that wish to implementat a JupyterHub Trafik KV store Proxy can sublass TKvProxy. TKvProxy implements JupyterHub’s Proxy public API and there is no need to override these public methods. The methods that must be implemented by the proxies that sublass TKvProxy are:

    • _define_kv_specific_static_config()

      • Define the traefik static configuration that configures traefik’s communication with the key-value store.
      • Will be called during startup if should_start is True.
      • Subclasses must define this method if the proxy is to be started by the Hub.
      • In order to be picked up by the proxy, the static configuration must be stored into proxy.static_config dict under the kv_name key.
    • _kv_atomic_add_route_parts(jupyterhub_routespec, target, data, route_keys, rule)

      • Add the key-value pairs associated with a route within a key-value store transaction.

      • Will be called during add_route.

      • When retrieving or deleting a route, the parts of a route are expected to have the following structure:

        [ key: jupyterhub_routespec            , value: target ]
        [ key: target                          , value: data   ]
        [ key: route_keys.backend_url_path     , value: target ]
        [ key: route_keys.frontend_rule_path   , value: rule   ]
        [ key: route_keys.frontend_backend_path, value: route_keys.backend_alias]
        [ key: route_keys.backend_weight_path  , value: w(int) ]
        # where w is the weight of the backend to be used during load balancing)
        
      • Returns:

        • result (tuple):
          • The transaction status (int, 0: failure, positive: success)
          • The transaction response(str)
    • _kv_atomic_delete_route_parts(jupyterhub_routespec, route_keys)

      • Delete the key-value pairs associated with a route, within a key-value store transaction (if the route exists).
      • Will be called during delete_route.
      • The keys associated with a route are:
        • jupyterhub_routespec
        • target
        • route_keys.backend_url_path
        • route_keys.frontend_rule_path
        • route_keys.frontend_backend_path
        • route_keys.backend_weight_path
      • Returns:
        • result (tuple):
          • The transaction status (int, 0: failure, positive: success)
          • The transaction response (str)
    • _kv_get_target(jupyterhub_routespec)

      • Retrive the target from the key-value store.
      • The target is the value associated with jupyterhub_routespec key.
      • Returns:
        • The full URL associated with this route (str)
    • _kv_get_data(target)

      • Retrive the data associated with the target from the key-value store.
      • Returns:
        • A JSONable dict that holds extra info about the route (dict)
    • _kv_get_route_parts(kv_entry)

      • Retrive all the parts that make up a route (i.e. routespec, target, data) from the key-value store given a kv_entry.
      • A kv_entry is a key-value store entry where the key starts with proxy.jupyterhub_prefix. It is expected that only the routespecs will be prefixed with proxy.jupyterhub_prefix when added to the kv store.
      • Returns:
        • routespec: The normalized route specification passed in to add_route ([host]/path/)
        • target: The target host for this route (proto://host)
        • data: The arbitrary data dict that was passed in by JupyterHub when adding this route.
    • _kv_get_jupyterhub_prefixed_entries()

      • Retrive from the kv store all the key-value pairs where the key starts with proxy.jupyterhub_prefix.
      • It is expected that only the routespecs will be prefixed with proxy.jupyterhub_prefix when added to the kv store.
      • Returns:
        • routes: A list of key-value store entries where the keys start with proxy.jupyterhub_prefix.

Testing jupyterhub-traefik-proxy

There are some tests that use etcdctl command line client for etcd. Make sure to set environment variable ETCDCTL_API=3 before running the tests, so that the v3 API to be used, e.g.:

$ export ETCDCTL_API=3

You can then run the all the test suite from the traefik-proxy directory with:

$ pytest -v ./tests

Or you can run a specific test with:

$ pytest -v ./tests/<test-file-name>