JupyterHub Traefik Proxy#
An implementation of the JupyterHub proxy api with traefik : an extremely lightweight, portable reverse proxy implementation, that supports load balancing and can configure itself automatically and dynamically.
Why traefik?#
Currently, the default proxy implementation for JupyterHub is configurable-http-proxy (CHP), which stores the routing table in-memory. This might be the best approach in most of the cases, but because you can only run a single copy of the proxy at a time, it has its limitations when used in dynamic large scale systems.
When using a proxy implementation based on traefik, you can run multiple instances of traefik by using a distributed key-value store like etcd or consul to store the routing table. This makes the proxy highly available and improves the scalability and stability of the system. Moreover it offers HTTPS support through a straight-forward ACME (Let’s Encrypt) configuration.
There are three versions for the proxy, depending on how traefik stores the routes:
for smaller, single-node deployments:
TraefikFileProviderProxy
for distributed setups:
TraefikEtcdProxy
TraefikConsulProxy
Contents#
Installation Guide#
Installation#
Traefik-proxy installation#
Install JupyterHub:
$ python3 -m pip install jupyterhub
Install jupyterhub-traefik-proxy, which is available now as pre-release:
python3 -m pip install jupyterhub-traefik-proxy
In order to be able to launch JupyterHub with traefik-proxy or run the tests, traefik, must first be installed and added to your
PATH
.There are two ways you can install traefik:
Through traefik-proxy’s install utility.
$ python3 -m jupyterhub_traefik_proxy.install --output=/usr/local/bin
This will install
traefik
.This will install the default versions of traefik, to 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 toPATH
, e.g.$ export PATH=$PATH:{$PWD}/dependencies
If you want to install other versions of traefik in a directory of your choice, just specify it to the installer through the following arguments:
--traefik-version
--output
Example:
$ python3 -m jupyterhub_traefik_proxy.install --output=dep \ --traefik-version=2.4.8
If the desired install directory doesn’t exist, it will be created by the installer.
To get a list of all the available options:
$ python3 -m jupyterhub_traefik_proxy.install --help
From traefik release page:
Install
traefik
Installing a key-value store#
If you want to use a key-value store to mediate configuration (mainly for use in distributed deployments, such as containers), you can get etcd or consul via their respective release pages:
Or, more likely, select the appropriate container image. You will also need to install a Python client for the Key-Value store of your choice:
etcdpy
python-consul2
Enabling traefik-proxy in JupyterHub#
TraefikFileProviderProxy, 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_file"
# will configure JupyterHub to run with TraefikFileProviderProxy
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
Getting Started#
Enabling HTTPS with TraefikProxy#
When running JupyterHub, you almost always want to use TLS (HTTPS). Traefik has a few ways to do that. The first tricky bit is that traefik separates dynamic configuration from static configuration, and some configuration needs to go in static, while some goes in dynamic.
Static configuration#
If you are using externally-managed traefik (c.TraefikProxy.should_start = False
),
you must write the static configuration file yourself.
The only static configuration required by juptyerhub-traefik-proxy
is the creation of the entrypoints for the api and jupyterhub itself:
# static configuration
# enable API
[api]
[entryPoints.auth_api]
address = "localhost:8099" # should match c.TraefikProxy.traefik_api_url
[entrypoints.https]
address = ":443"
[entrypoints.https.http.tls]
options = "default"
jupyterhub-traefik-proxy can take care of the rest because it will apply its dynamic configuration when JupyterHub starts.
Manual SSL#
Configuring SSL with your own certificates works the same with traefik proxy as any other JupyterHub proxy implementation:
c.JupyterHub.ssl_cert = "/path/to/ssl.cert"
c.JupyterHub.ssl_key = "/path/to/ssl.key"
This will set the traefik dynamic configuration:
# dynamic configuration
[tls.stores.default.defaultCertificate]
certFile = "path/to/cert.crt"
keyFile = "path/to/cert.key"
If you don’t tell jupyterhub about these files,
you will need to set this configuration yourself in dynamic configuration
(Traefik ignores TLS configuration in the “static” configuration file).
Passing the certificates via JupyterHub configuration assumes the options = "default"
static configuration:
# static configuration
[entrypoints.https.http.tls]
options = "default"
If you use your own static and dynamic configuration files, you don’t have to use the ‘default’ TLS options or tell jupyterhub anything about your TLS configuration.
Let’s Encrypt#
Traefik supports using Let’s Encrypt for automatically issuing and renewing certificates. It’s great!
To configure traefik to use let’s encrypt, first we need to register a certificate resolver in static configuration:
# static configuration
# redirect all http requests to https
[entrypoints.httponly]
address = ":80"
[entryPoints.httponly.http.redirections.entryPoint]
to = "https"
scheme = "https"
# configure
[certificatesResolvers.letsencrypt.acme]
email = "you@example.com"
storage = "acme.json" # file where certificates are stored
# use the staging server to test your deployment
# uncomment this when you are ready for production
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
# tlsChallenge means you don't need an http endpoint
[certificatesResolvers.letsencrypt.acme.tlsChallenge]
And in your extra dynamic configuration, specify the domain(s) you want certificates for:
# dynamic configuration
[tls.stores.default.defaultGeneratedCert]
resolver = "letsencrypt"
[tls.stores.default.defaultGeneratedCert.domain]
main = "hub.example.com"
sans = [
# if you are serving more than one domain
"other.service.example.com",
]
If you are using JupyterHub-managed traefik (c.TraefikProxy.should_start = True
),
you can specify this same configuration via TraefikProxy’s extra_static_config
and extra_dynamic_config
:
c.TraefikProxy.traefik_entrypoint = "https"
c.TraefikProxy.extra_static_config = {
"entryPoints": {
"http": {
"address": ":80"
},
"https": {
"http": {
"tls": {
"options": "default"
},
},
},
},
"certificatesResolvers": {
"letsencrypt": {
"acme": {
"email": "you@example.com",
"storage": "acme.json",
},
"tlsChallenge": {},
},
},
}
c.TraefikProxy.extra_dynamic_config = {
"tls": {
"stores": {
"default": {
"defaultGeneratedCert": {
"resolver": "letsencrypt",
"domain": {
"main": "hub.example.com",
},
},
},
},
},
}
Using TraefikFileProviderProxy#
jupyterhub-traefik-proxy can be used with simple toml or yaml configuration files, for smaller, single-node deployments such as The Littlest JupyterHub.
How-To install TraefikFileProviderProxy#
Install jupyterhub
Install jupyterhub-traefik-proxy
Install traefik
You can find the full installation guide and examples in the installation section
How-To enable TraefikFileProviderProxy#
You can enable JupyterHub to work with TraefikFileProviderProxy
in jupyterhub_config.py, using the proxy_class
configuration option.
You can choose to:
use the
traefik_file
entrypoint, new in JupyterHub 1.0, e.g.:c.JupyterHub.proxy_class = "traefik_file"
use the TraefikFileProviderProxy object, in which case, you have to import the module, e.g.:
from jupyterhub_traefik_proxy.fileprovider import TraefikFileProviderProxy c.JupyterHub.proxy_class = TraefikFileProviderProxy
Traefik configuration#
Traefik’s configuration is divided into two parts:
The static configuration (loaded only at the beginning)
The dynamic configuration (can be hot-reloaded, without restarting the proxy), where the routing table will be updated continuously.
Traefik allows us to have one file for the static configuration file (traefik.toml
or traefik.yaml
) and one or several files for the routes, that traefik would watch.
Note
TraefikFileProviderProxy, uses two configuration files: one file for the routes (rules.toml or rules.yaml), and one for the static configuration (traefik.toml or traefik.yaml).
By default, Traefik will search for traefik.toml
and rules.toml
in the following places:
/etc/traefik/
$HOME/.traefik/
. the working directory
You can override this in TraefikFileProviderProxy, by modifying the static_config_file argument:
c.TraefikFileProviderProxy.static_config_file = "/path/to/static_config_filename.toml"
Similarly, you can override the dynamic configuration file by modifying the dynamic_config_file argument:
c.TraefikFileProviderProxy.dynamic_config_file = "/path/to/dynamic_config_filename.toml"
Note
When JupyterHub starts the proxy, it writes the static config once, then only edits the dynamic config file.
When JupyterHub does not start the proxy, the user is totally responsible for the static config and JupyterHub is responsible exclusively for the routes.
When JupyterHub does not start the proxy, the user should tell
traefik
to get its dynamic configuration from a directory. Then, one (or more) dynamic configuration file(s) can be managed externally, anddynamic_config_file
will be managed by JupyterHub. This allows e.g., the administrator to configure traefik’s API outside of JupyterHub.
Externally managed TraefikFileProviderProxy#
When TraefikFileProviderProxy is externally managed, service managers like systemd or docker will be responsible for starting and stopping the proxy.
If TraefikFileProviderProxy is used as an externally managed service, then make sure you follow the steps enumerated below:
Let JupyterHub know that the proxy being used is TraefikFileProviderProxy, using the proxy_class configuration option:
c.JupyterHub.proxy_class = "traefik_file"
Configure
TraefikFileProviderProxy
in jupyterhub_config.pyJupyterHub configuration file, jupyterhub_config.py must specify at least:
That the proxy is externally managed
The traefik api credentials
The dynamic configuration file, if different from rules.toml or if this file is located in a place other than traefik’s default search directories (etc/traefik/, $HOME/.traefik/, the working directory). traefik must also be able to access the dynamic configuration file.
Example configuration:
# JupyterHub shouldn't start the proxy, it's already running c.TraefikFileProviderProxy.should_start = False # if not the default: c.TraefikFileProviderProxy.dynamic_config_file = "/path/to/somefile.toml" # traefik api credentials c.TraefikFileProviderProxy.traefik_api_username = "abc" c.TraefikFileProviderProxy.traefik_api_password = "xxx" # Validate the certificate on traefik's API? Default = True # c.TraefikFileProviderProxy.traefik_api_validate_cert = True # jupyterhub will configure traefik for itself, using this Host name # (and optional path) on the router rule: c.JupyterHub.bind_url = 'http://hub.contoso.com' # jupyterhub will also configure traefik's 'service' url, so this needs # to be accessible from traefik. By default, jupyterhub will bind to # 'localhost', but this will bind jupyterhub to its hostname # (only necessary when traefik is in a different machine or container) c.JupyterHub.hub_bind_url = 'http://:8000'
Ensure traefik.toml / traefik.yaml
The static configuration file, traefik.toml (or traefik.yaml) must configure at least:
The default entrypoint
The api entrypoint (and authenticate it in a user-managed dynamic configuration file)
The websockets protocol
The dynamic configuration directory to watch (make sure this configuration directory exists, even if empty before the proxy is launched)
Check
tests/config_files/traefik.toml
for an example.
Example setup#
This is an example setup for using JupyterHub and TraefikFileProviderProxy managed by another service than JupyterHub.
Configure the proxy through the JupyterHub configuration file, jupyterhub_config.py, e.g.:
# mark the proxy as externally managed c.TraefikFileProviderProxy.should_start = False # traefik api endpoint login password c.TraefikFileProviderProxy.traefik_api_password = "admin" # traefik api endpoint login username c.TraefikFileProviderProxy.traefik_api_username = "api_admin" # traefik's dynamic configuration file, which will be managed by JupyterHub c.TraefikFileProviderProxy.dynamic_config_file = "/var/run/traefik/rules.toml" # configure JupyterHub to use TraefikFileProviderProxy c.JupyterHub.proxy_class = "traefik_file"
Create a traefik static configuration file, traefik.toml, e.g.:
# enable the api [api] # the public port where traefik accepts http requests [entryPoints.http] address = ":8000" # the port on localhost where the traefik api should be found [entryPoints.auth_api] address = "localhost:8099" # the dynamic configuration directory # This must match the directory provided in Step 1. above. [providers.file] directory = "/var/run/traefik" watch = true
Start traefik with the configuration specified above, e.g.:
$ traefik --configfile traefik.toml
Using TraefikEtcdProxy#
Etcd is a distributed key-value store. This and TraefikConsulProxy is the choice to use when using jupyterhub-traefik-proxy in a distributed setup, such as a Kubernetes cluster, e.g. with multiple traefik-proxy instances.
How-To install TraefikEtcdProxy#
Install jupyterhub
Install jupyterhub-traefik-proxy
Install traefik
Install etcd
You can find the full installation guide and examples in the installation section
How-To enable TraefikEtcdProxy#
You can enable JupyterHub to work with TraefikEtcdProxy
in jupyterhub_config.py,
using the proxy_class
configuration option.
You can choose to:
use the
traefik_etcd
entrypoint, new in JupyterHub 1.0, e.g.:c.JupyterHub.proxy_class = "traefik_etcd"
use the TraefikEtcdProxy object, in which case, you have to import the module, e.g.:
from jupyterhub_traefik_proxy import TraefikEtcdProxy c.JupyterHub.proxy_class = TraefikEtcdProxy
Etcd configuration#
Depending on the value of the
should_start
proxy flag, you can choose whether or not TraefikEtcdProxy willl be externally managed.When should_start is set to True, TraefikEtcdProxy will auto-generate its static configuration (using the override values or the defaults) and store it in
traefik.toml
file. The traefik process will then be launched using this file.When should_start is set to False, prior to starting the traefik process, you must create a toml file with the desired traefik static configuration and pass it to traefik. Keep in mind that in order for the routes to be stored in etcd, this toml file must specify etcd as the provider.
TraefikEtcdProxy searches in the etcd key-value store the keys starting with the kv_traefik_prefix prefix in order to build its static configuration.
Similarly, the dynamic configuration is built by searching the kv_jupyterhub_prefix.
Note
If you want to change or add traefik’s static configuration options, you can add them to etcd under this prefix and traefik will pick them up.
The default values of this configurations options are:
kv_traefik_prefix = "/traefik/" kv_jupyterhub_prefix = "/jupyterhub/"
You can override the default values of the prefixes by passing their desired values through
jupyterhub_config.py
e.g.:c.TraefikEtcdProxy.kv_traefik_prefix = "/some_static_config_prefix/" c.TraefikEtcdProxy.kv_jupyterhub_prefix = "/some_dynamic_config_prefix/"
By default, TraefikEtcdProxy assumes etcd accepts client requests on the official default etcd port
2379
for client requests.c.TraefikEtcdProxy.etcd_url = "http://127.0.0.1:2379"
If the etcd cluster is deployed differently than using the etcd defaults, then you must pass the etcd url to the proxy using the
etcd_url
option in jupyterhub_config.py:c.TraefikEtcdProxy.etcd_url = "scheme://hostname:port"
Note
TraefikEtcdProxy does not manage the etcd cluster and assumes it is up and running before the proxy itself starts.
However, based on how etcd is configured and started, TraefikEtcdProxy needs to be told about some etcd configuration details, such as:
etcd address where it accepts client requests
c.TraefikEtcdProxy.etcd_url="scheme://hostname:port"
etcd credentials (if etcd has authentication enabled)
c.TraefikEtcdProxy.etcd_username="abc" c.TraefikEtcdProxy.etcd_password="123"
Etcd has two API versions: the API V3 and the API V2. Traefik suggests using Etcd API V3, because the API V2 won’t be supported in the future.
Checkout the etcd documentation to find out more about possible etcd configuration options.
Externally managed TraefikEtcdProxy#
If TraefikEtcdProxy is used as an externally managed service, then make sure you follow the steps enumerated below:
Let JupyterHub know that the proxy being used is TraefikEtcdProxy, using the proxy_class configuration option:
c.JupyterHub.proxy_class = "traefik_etcd"
Configure
TraefikEtcdProxy
in jupyterhub_config.pyJupyterHub configuration file, jupyterhub_config.py must specify at least:
That the proxy is externally managed
The traefik api credentials
The etcd credentials (if etcd authentication is enabled)
Example configuration:
# JupyterHub shouldn't start the proxy, it's already running c.TraefikEtcdProxy.should_start = False # if not the default: c.TraefikEtcdProxy.etcd_url = "http://etcd-host:2379" # traefik api credentials c.TraefikEtcdProxy.traefik_api_username = "abc" c.TraefikEtcdProxy.traefik_api_password = "123" # etcd credentials c.TraefikEtcdProxy.etcd_username = "def" c.TraefikEtcdProxy.etcd_password = "456"
Create a toml file with traefik’s desired static configuration
Before starting the traefik process, you must create a toml file with the desired traefik static configuration and pass it to traefik when you launch the process. Keep in mind that in order for the routes to be stored in etcd, this toml file must specify etcd as the provider/
Keep in mind that the static configuration must configure at least:
The default entrypoint
The api entrypoint
The etcd provider
Example:
[api] [entryPoints.http] address = "127.0.0.1:8000" [entryPoints.auth_api] address = "127.0.0.1:8099" [providers.etcd] endpoints = [ "127.0.0.1:2379",] rootKey = "traefik"
Note
If you choose to enable the authentication on etcd, you can use this toml file to pass the credentials to traefik, e.g.:
```toml [providers.etcd] username = "root" password = "admin" ... ```
Example setup#
This is an example setup for using JupyterHub and TraefikEtcdProxy managed by another service than JupyterHub.
Configure the proxy through the JupyterHub configuration file, jupyterhub_config.py, e.g.:
# mark the proxy as externally managed c.TraefikEtcdProxy.should_start = False # traefik api endpoint login password c.TraefikEtcdProxy.traefik_api_password = "abc" # traefik api endpoint login username c.TraefikEtcdProxy.traefik_api_username = "123" # etcd url where it accepts client requests c.TraefikEtcdProxy.etcd_url = "http://127.0.0.1:2379" # configure JupyterHub to use TraefikEtcdProxy c.JupyterHub.proxy_class = "traefik_etcd"
Note
If you intend to enable authentication on etcd, add the etcd credentials to jupyterhub_config.py:
# etcd username c.TraefikEtcdProxy.etcd_username = "def" # etcd password c.TraefikEtcdProxy.etcd_password = "456"
Start a single-note etcd cluster on the default port on localhost. e.g.:
$ etcd
Note
If you intend to enable authentication on etcd checkout this guide.
Create a traefik static configuration file, traefik.toml, e.g:.
# enable the api [api] # the public port where traefik accepts http requests [entryPoints.http] address = ":8000" # the port on localhost where the traefik api should be found [entryPoints.auth_api] address = "localhost:8099" [providers.etcd] # the etcd username (if auth is enabled) username = "def" # the etcd password (if auth is enabled) password = "456" # the etcd address endpoints = ["127.0.0.1:2379"] # the prefix to use for the static configuration rootKey = "traefik"
Start traefik with the configuration specified above, e.g.:
$ traefik -c traefik.toml
Using TraefikConsulProxy#
Warning
While it works today (2023), there does not appear to be a maintained Python client for the consul API. As such, using jupyterhub-traefik-proxy with consul is deprecated in jupyterhub-traefik-proxy 1.0. You can use etcd instead to achieve the same functionality (and slightly better performance).
Consul is a distributed key-value store. This and TraefikEtcdProxy is the choice to use when using jupyterhub-traefik-proxy in a distributed setup, such as a Kubernetes cluster, e.g. with multiple traefik-proxy instances.
How-To install TraefikConsulProxy#
Install jupyterhub
Install jupyterhub-traefik-proxy
Install traefik
Install consul
You can find the full installation guide and examples in the installation section
How-To enable TraefikConsulProxy#
You can enable JupyterHub to work with TraefikConsulProxy
in jupyterhub_config.py,
using the proxy_class
configuration option.
You can choose to:
use the
traefik_consul
entrypoint, new in JupyterHub 1.0, e.g.:c.JupyterHub.proxy_class = "traefik_consul"
use the TraefikConsulProxy object, in which case, you have to import the module, e.g.:
from jupyterhub_traefik_proxy import TraefikConsulProxy c.JupyterHub.proxy_class = TraefikConsulProxy
Consul configuration#
Depending on the value of the
should_start
proxy flag, you can choose whether or not TraefikConsulProxy willl be externally managed.When should_start is set to True, TraefikConsulProxy will auto-generate its static configuration (using the override values or the defaults) and store it in
traefik.toml
file. The traefik process will then be launched using this file.When should_start is set to False, prior to starting the traefik process, you must create a toml file with the desired traefik static configuration and pass it to traefik. Keep in mind that in order for the routes to be stored in consul, this toml file must specify consul as the provider.
TraefikConsulProxy searches in the consul key-value store the keys starting with the kv_traefik_prefix prefix in order to build its static configuration.
Similarly, the dynamic configuration is built by searching the kv_jupyterhub_prefix.
Note
If you want to change or add traefik’s static configuration options, you can add them to consul under this prefix and traefik will pick them up.
The default values of this configurations options are:
kv_traefik_prefix = "traefik/" kv_jupyterhub_prefix = "jupyterhub/"
You can override the default values of the prefixes by passing their desired values through
jupyterhub_config.py
e.g.:c.TraefikConsulProxy.kv_traefik_prefix="some_static_config_prefix/" c.TraefikConsulProxy.kv_jupyterhub_prefix="some_dynamic_config_prefix/"
By default, TraefikConsulProxy assumes consul accepts client requests on the official default consul port
8500
for client requests.c.TraefikConsulProxy.consul_url = "http://127.0.0.1:8500"
If the consul cluster is deployed differently than using the consul defaults, then you must pass the consul url to the proxy using the
consul_url
option in jupyterhub_config.py:c.TraefikConsulProxy.consul_url = "scheme://hostname:port"
Note
TraefikConsulProxy does not manage the consul cluster and assumes it is up and running before the proxy itself starts. However, based on how consul is configured and started, TraefikConsulProxy needs to be told about some consul configuration details, such as:
consul address where it accepts client requests
c.TraefikConsulProxy.consul_url = "scheme://hostname:port"
consul credentials (if consul has acl enabled)
c.TraefikConsulProxy.consul_password = "123"
Checkout the consul documentation to find out more about possible consul configuration options.
Externally managed TraefikConsulProxy#
If TraefikConsulProxy is used as an externally managed service, then make sure you follow the steps enumerated below:
Let JupyterHub know that the proxy being used is TraefikConsulProxy, using the proxy_class configuration option:
c.JupyterHub.proxy_class = "traefik_consul"
Configure
TraefikConsulProxy
in jupyterhub_config.pyJupyterHub configuration file, jupyterhub_config.py must specify at least:
That the proxy is externally managed
The traefik api credentials
The consul credentials (if consul acl is enabled)
Example configuration:
# JupyterHub shouldn't start the proxy, it's already running c.TraefikConsulProxy.should_start = False # if not the default: c.TraefikConsulProxy.consul_url = "http://consul-host:2379" # traefik api credentials c.TraefikConsulProxy.traefik_api_username = "abc" c.TraefikConsulProxy.traefik_api_password = "123" # consul acl token c.TraefikConsulProxy.consul_password = "456"
Create a toml file with traefik’s desired static configuration
Before starting the traefik process, you must create a toml file with the desired traefik static configuration and pass it to traefik when you launch the process. Keep in mind that in order for the routes to be stored in consul, this toml file must specify consul as the provider/
Keep in mind that the static configuration must configure at least:
The default entrypoint
The api entrypoint
The consul provider
Example:
# enable the api [api] # the public port where traefik accepts http requests [entryPoints.http] address = ":8000" # the port on localhost where the traefik api should be found [entryPoints.auth_api] address = "localhost:8099" [providers.consul] # the consul username (if auth is enabled) username = "def" # the consul password (if auth is enabled) password = "456" # the consul address endpoints = ["127.0.0.1:8500"] # the prefix to use for the static configuration rootKey = "traefik"
Note
If you choose to enable consul Access Control Lists (ACLs) to secure the UI, API, CLI, service communications, and agent communications, you can use this toml file to pass the credentials to traefik, e.g.:
[providers.consul] password = "admin" ...
Example setup#
This is an example setup for using JupyterHub and TraefikConsulProxy managed by another service than JupyterHub.
Configure the proxy through the JupyterHub configuration file, jupyterhub_config.py, e.g.:
# mark the proxy as externally managed c.TraefikConsulProxy.should_start = False # traefik api endpoint login password c.TraefikConsulProxy.traefik_api_password = "abc" # traefik api endpoint login username c.TraefikConsulProxy.traefik_api_username = "123" # consul url where it accepts client requests c.TraefikConsulProxy.consul_url = "http://127.0.0.1:8500" # configure JupyterHub to use TraefikConsulProxy c.JupyterHub.proxy_class = "traefik_consul"
Note
If you intend to enable consul acl, add the acl token to jupyterhub_config.py under consul_password:
# consul token c.TraefikConsulProxy.consul_password = "456"
Starts the agent in development mode on the default port on localhost. e.g.:
$ consul agent -dev
Note
If you intend to enable consul acl, checkout this guide.
Create a traefik static configuration file, traefik.toml, e.g:.
# enable the api [api] # the public port where traefik accepts http requests [entryPoints.http] address = ":8000" # the port on localhost where the traefik api should be found [entryPoints.auth_api] address = "localhost:8099" [providers.consul] # the username (if auth is enabled) username = "def" # the password (if auth is enabled) password = "456" # the consul address endpoints = ["127.0.0.1:8500"] # the prefix to use for the static configuration rootKey = "traefik"
Start traefik with the configuration specified above, e.g.:
$ traefik -c traefik.toml
API Reference#
Proxy API#
Module: jupyterhub_traefik_proxy
#
Traefik implementation of the JupyterHub proxy API
TraefikProxy
#
- class jupyterhub_traefik_proxy.proxy.TraefikProxy(**kwargs: Any)[source]#
JupyterHub Proxy implementation using traefik
- async add_route(routespec, target, data)[source]#
Add a route to the proxy.
Subclasses must define this method
- Parameters:
routespec (str) – A URL prefix ([host]/path/) for which this route will be matched, e.g. host.name/path/
target (str) – A full URL that will be the target of this route.
data (dict) – A JSONable dict that will be associated with this route, and will be returned when retrieving information about this route.
Will raise an appropriate Exception (FIXME: find what?) if the route could not be added.
The proxy implementation should also have a way to associate the fact that a route came from JupyterHub.
- check_route_timeout c.TraefikProxy.check_route_timeout = Int(30)#
Timeout (in seconds) when waiting for traefik to register an updated route.
- concurrency c.TraefikProxy.concurrency = Int(10)#
The number of requests allowed to be concurrently outstanding to the proxy
Limiting this number avoids potential timeout errors by sending too many requests to update the proxy at once
- enable_setup_dynamic_config c.TraefikProxy.enable_setup_dynamic_config = Bool(True)#
Whether to initialize the traefik dynamic configuration from JupyterHub configuration, when should_start is False (dynamic configuration is always applied when should_start is True).
Creates the traefik API router and TLS setup, if any. When True, only traefik static configuration must be managed by the external service (configuration of the endpoints and provider). The traefik api router should not already be configured via other dynamic configuration providers.
When False, initial dynamic configuration must be handled externally and match TraefikProxy configuration, such as traefik_api_url, traefik_api_username` and traefik_api_password. Choose this if the traefik api router is already configured via dynamic configuration elsewhere.
New in version 1.1.
- extra_dynamic_config c.TraefikProxy.extra_dynamic_config = Dict()#
Extra dynamic configuration for treafik.
Merged with the default dynamic config during startup.
Always takes effect unless should_start and enable_setup_dynamic_config are both False.
- extra_routes c.TraefikProxy.extra_routes = Dict()#
Additional routes to be maintained in the proxy.
A dictionary with a route specification as key, and a URL as target. The hub will ensure this route is present in the proxy.
If the hub is running in host based mode (with JupyterHub.subdomain_host set), the routespec must have a domain component (example.com/my-url/). If the hub is not running in host based mode, the routespec must not have a domain component (/my-url/).
Helpful when the hub is running in API-only mode.
- extra_static_config c.TraefikProxy.extra_static_config = Dict()#
Extra static configuration for treafik.
Merged with the default static config before writing to .static_config_file.
Has no effect if Proxy.should_start is False.
- async get_all_routes()[source]#
Fetch and return all the routes associated by JupyterHub from the proxy.
Subclasses must define this method
Should return a dictionary of routes, where the keys are routespecs and each value is a dict of the form:
{ 'routespec': the route specification ([host]/path/) 'target': the target host URL (proto://host) for this route 'data': the attached data dict for this route (as specified in add_route) }
- is_https Bool(False)#
Whether
public_url
specifies an https entrypoint
- provider_name Unicode('')#
The provider name that Traefik expects, e.g. file, consul, etcd
- should_start c.TraefikProxy.should_start = Bool(True)#
Should the Hub start the proxy
If True, the Hub will start the proxy and stop it. Set to False if the proxy is managed externally, such as by systemd, docker, or another service manager.
- async start()[source]#
Start the proxy.
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
- static_config_file c.TraefikProxy.static_config_file = Unicode('traefik.toml')#
traefik’s static configuration file
- async stop()[source]#
Stop the proxy.
Will be called during teardown if should_start is True.
Subclasses must define this method if the proxy is to be started by the Hub
- toml_static_config_file c.TraefikProxy.toml_static_config_file = Unicode('')#
Deprecated. Use static_config_file
- traefik_api_entrypoint c.TraefikProxy.traefik_api_entrypoint = Unicode('auth_api')#
The traefik entrypoint name to use for API access.
Separate from traefik_entrypoint, because this is usually only on localhost.
- traefik_api_password c.TraefikProxy.traefik_api_password = Unicode('')#
The password for traefik api login
- traefik_api_url c.TraefikProxy.traefik_api_url = Unicode('http://localhost:8099')#
traefik authenticated api endpoint url
- traefik_api_username c.TraefikProxy.traefik_api_username = Unicode('')#
The username for traefik api login
- traefik_api_validate_cert c.TraefikProxy.traefik_api_validate_cert = Bool(True)#
validate SSL certificate of traefik api endpoint
- traefik_entrypoint c.TraefikProxy.traefik_entrypoint = Unicode('')#
The traefik entrypoint name to use.
By default, will be http if http or https if https.
If running traefik externally with your own specified entrypoint name, set this value.
- traefik_env c.TraefikProxy.traefik_env = Dict()#
Environment variables to set for the traefik process.
Only has an effect when traefik is a subprocess (should_start=True).
- traefik_log_level c.TraefikProxy.traefik_log_level = Unicode('')#
traefik’s log level
- traefik_providers_throttle_duration c.TraefikProxy.traefik_providers_throttle_duration = Unicode('0s')#
throttle traefik reloads of configuration.
When traefik sees a change in configuration, it will wait this long before applying the next one. This affects how long adding a user to the proxy will take.
See https://doc.traefik.io/traefik/providers/overview/#providersprovidersthrottleduration
TraefikFileProviderProxy
#
- class jupyterhub_traefik_proxy.fileprovider.TraefikFileProviderProxy(**kwargs: Any)[source]#
JupyterHub Proxy implementation using traefik and toml or yaml config file
- check_route_timeout c.TraefikFileProviderProxy.check_route_timeout = Int(30)#
Timeout (in seconds) when waiting for traefik to register an updated route.
- concurrency c.TraefikFileProviderProxy.concurrency = Int(10)#
The number of requests allowed to be concurrently outstanding to the proxy
Limiting this number avoids potential timeout errors by sending too many requests to update the proxy at once
- dynamic_config_file c.TraefikFileProviderProxy.dynamic_config_file = Unicode('rules.toml')#
traefik’s dynamic configuration file
- enable_setup_dynamic_config c.TraefikFileProviderProxy.enable_setup_dynamic_config = Bool(True)#
Whether to initialize the traefik dynamic configuration from JupyterHub configuration, when should_start is False (dynamic configuration is always applied when should_start is True).
Creates the traefik API router and TLS setup, if any. When True, only traefik static configuration must be managed by the external service (configuration of the endpoints and provider). The traefik api router should not already be configured via other dynamic configuration providers.
When False, initial dynamic configuration must be handled externally and match TraefikProxy configuration, such as traefik_api_url, traefik_api_username` and traefik_api_password. Choose this if the traefik api router is already configured via dynamic configuration elsewhere.
New in version 1.1.
- extra_dynamic_config c.TraefikFileProviderProxy.extra_dynamic_config = Dict()#
Extra dynamic configuration for treafik.
Merged with the default dynamic config during startup.
Always takes effect unless should_start and enable_setup_dynamic_config are both False.
- extra_routes c.TraefikFileProviderProxy.extra_routes = Dict()#
Additional routes to be maintained in the proxy.
A dictionary with a route specification as key, and a URL as target. The hub will ensure this route is present in the proxy.
If the hub is running in host based mode (with JupyterHub.subdomain_host set), the routespec must have a domain component (example.com/my-url/). If the hub is not running in host based mode, the routespec must not have a domain component (/my-url/).
Helpful when the hub is running in API-only mode.
- extra_static_config c.TraefikFileProviderProxy.extra_static_config = Dict()#
Extra static configuration for treafik.
Merged with the default static config before writing to .static_config_file.
Has no effect if Proxy.should_start is False.
- async get_route(routespec)[source]#
Return the route info for a given routespec.
- Parameters:
routespec (str) – A URI that was used to add this route, e.g. host.tld/path/
- Returns:
dict with the following keys:
'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.
None: if there are no routes matching the given routespec
- Return type:
result (dict)
- should_start c.TraefikFileProviderProxy.should_start = Bool(True)#
Should the Hub start the proxy
If True, the Hub will start the proxy and stop it. Set to False if the proxy is managed externally, such as by systemd, docker, or another service manager.
- static_config_file c.TraefikFileProviderProxy.static_config_file = Unicode('traefik.toml')#
traefik’s static configuration file
- toml_static_config_file c.TraefikFileProviderProxy.toml_static_config_file = Unicode('')#
Deprecated. Use static_config_file
- traefik_api_entrypoint c.TraefikFileProviderProxy.traefik_api_entrypoint = Unicode('auth_api')#
The traefik entrypoint name to use for API access.
Separate from traefik_entrypoint, because this is usually only on localhost.
- traefik_api_password c.TraefikFileProviderProxy.traefik_api_password = Unicode('')#
The password for traefik api login
- traefik_api_url c.TraefikFileProviderProxy.traefik_api_url = Unicode('http://localhost:8099')#
traefik authenticated api endpoint url
- traefik_api_username c.TraefikFileProviderProxy.traefik_api_username = Unicode('')#
The username for traefik api login
- traefik_api_validate_cert c.TraefikFileProviderProxy.traefik_api_validate_cert = Bool(True)#
validate SSL certificate of traefik api endpoint
- traefik_entrypoint c.TraefikFileProviderProxy.traefik_entrypoint = Unicode('')#
The traefik entrypoint name to use.
By default, will be http if http or https if https.
If running traefik externally with your own specified entrypoint name, set this value.
- traefik_env c.TraefikFileProviderProxy.traefik_env = Dict()#
Environment variables to set for the traefik process.
Only has an effect when traefik is a subprocess (should_start=True).
- traefik_log_level c.TraefikFileProviderProxy.traefik_log_level = Unicode('')#
traefik’s log level
- traefik_providers_throttle_duration c.TraefikFileProviderProxy.traefik_providers_throttle_duration = Unicode('0s')#
throttle traefik reloads of configuration.
When traefik sees a change in configuration, it will wait this long before applying the next one. This affects how long adding a user to the proxy will take.
See https://doc.traefik.io/traefik/providers/overview/#providersprovidersthrottleduration
TKvProxy
#
- class jupyterhub_traefik_proxy.kv_proxy.TKvProxy(**kwargs: Any)[source]#
JupyterHub Proxy implementation using traefik and a key-value store.
Custom proxy implementations based on traefik and a key-value store can sublass
TKvProxy
.- check_route_timeout c.TKvProxy.check_route_timeout = Int(30)#
Timeout (in seconds) when waiting for traefik to register an updated route.
- concurrency c.TKvProxy.concurrency = Int(10)#
The number of requests allowed to be concurrently outstanding to the proxy
Limiting this number avoids potential timeout errors by sending too many requests to update the proxy at once
- enable_setup_dynamic_config c.TKvProxy.enable_setup_dynamic_config = Bool(True)#
Whether to initialize the traefik dynamic configuration from JupyterHub configuration, when should_start is False (dynamic configuration is always applied when should_start is True).
Creates the traefik API router and TLS setup, if any. When True, only traefik static configuration must be managed by the external service (configuration of the endpoints and provider). The traefik api router should not already be configured via other dynamic configuration providers.
When False, initial dynamic configuration must be handled externally and match TraefikProxy configuration, such as traefik_api_url, traefik_api_username` and traefik_api_password. Choose this if the traefik api router is already configured via dynamic configuration elsewhere.
New in version 1.1.
- extra_dynamic_config c.TKvProxy.extra_dynamic_config = Dict()#
Extra dynamic configuration for treafik.
Merged with the default dynamic config during startup.
Always takes effect unless should_start and enable_setup_dynamic_config are both False.
- extra_routes c.TKvProxy.extra_routes = Dict()#
Additional routes to be maintained in the proxy.
A dictionary with a route specification as key, and a URL as target. The hub will ensure this route is present in the proxy.
If the hub is running in host based mode (with JupyterHub.subdomain_host set), the routespec must have a domain component (example.com/my-url/). If the hub is not running in host based mode, the routespec must not have a domain component (/my-url/).
Helpful when the hub is running in API-only mode.
- extra_static_config c.TKvProxy.extra_static_config = Dict()#
Extra static configuration for treafik.
Merged with the default static config before writing to .static_config_file.
Has no effect if Proxy.should_start is False.
- flatten_dict_for_kv(data, prefix='')[source]#
Flatten a dictionary of data for storage in the KV store, prefixing each key with prefix and joining each key with
TKvProxy.kv_separator
.If the final value is a
list
, then the provided bottom-level key shall be appended with an incrementing numeric number, in the style that is used by traefik’s KV store, e.g.flatten_dict_for_kv({ 'x' : { 'y' : { 'z': 'a' } }, { 'foo': 'bar' }, 'baz': [ 'a', 'b', 'c' ] })
- Returns:
The flattened dictionary
- Return type:
e.g.
{ 'traefik/x/y/z' : 'a', 'traefik/x/foo': 'bar', 'traefik/baz/0': 'a', 'traefik/baz/1': 'b', 'traefik/baz/2': 'c', }
Inspired by this answer on StackOverflow
- async get_route(routespec)[source]#
Return the route info for a given routespec.
- Parameters:
routespec (str) – A URI that was used to add this route, e.g. host.tld/path/
- Returns:
dict with the following keys:
'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.
None: if there are no routes matching the given routespec
- Return type:
result (dict)
- kv_jupyterhub_prefix c.TKvProxy.kv_jupyterhub_prefix = KVStorePrefix('jupyterhub')#
The key value store key prefix for traefik dynamic configuration
- kv_separator c.TKvProxy.kv_separator = Unicode('/')#
The separator used for the path in the KV store
- kv_traefik_prefix c.TKvProxy.kv_traefik_prefix = KVStorePrefix('traefik')#
The key value store key prefix for traefik static configuration
- should_start c.TKvProxy.should_start = Bool(True)#
Should the Hub start the proxy
If True, the Hub will start the proxy and stop it. Set to False if the proxy is managed externally, such as by systemd, docker, or another service manager.
- static_config_file c.TKvProxy.static_config_file = Unicode('traefik.toml')#
traefik’s static configuration file
- toml_static_config_file c.TKvProxy.toml_static_config_file = Unicode('')#
Deprecated. Use static_config_file
- traefik_api_entrypoint c.TKvProxy.traefik_api_entrypoint = Unicode('auth_api')#
The traefik entrypoint name to use for API access.
Separate from traefik_entrypoint, because this is usually only on localhost.
- traefik_api_password c.TKvProxy.traefik_api_password = Unicode('')#
The password for traefik api login
- traefik_api_url c.TKvProxy.traefik_api_url = Unicode('http://localhost:8099')#
traefik authenticated api endpoint url
- traefik_api_username c.TKvProxy.traefik_api_username = Unicode('')#
The username for traefik api login
- traefik_api_validate_cert c.TKvProxy.traefik_api_validate_cert = Bool(True)#
validate SSL certificate of traefik api endpoint
- traefik_entrypoint c.TKvProxy.traefik_entrypoint = Unicode('')#
The traefik entrypoint name to use.
By default, will be http if http or https if https.
If running traefik externally with your own specified entrypoint name, set this value.
- traefik_env c.TKvProxy.traefik_env = Dict()#
Environment variables to set for the traefik process.
Only has an effect when traefik is a subprocess (should_start=True).
- traefik_log_level c.TKvProxy.traefik_log_level = Unicode('')#
traefik’s log level
- traefik_providers_throttle_duration c.TKvProxy.traefik_providers_throttle_duration = Unicode('0s')#
throttle traefik reloads of configuration.
When traefik sees a change in configuration, it will wait this long before applying the next one. This affects how long adding a user to the proxy will take.
See https://doc.traefik.io/traefik/providers/overview/#providersprovidersthrottleduration
- unflatten_dict_from_kv(kv_list, root_key='')[source]#
Reconstruct tree dict from list of key/value pairs
This is the inverse of flatten_dict_for_kv, not including str coercion.
Args:
- kv_list (list):
list of (key, value) pairs. keys and values should all be strings.
- root_key (str, optional):
The key representing the root of the tree, if not the root of the key-value store.
Returns:
- tree (dict):
The reconstructed dictionary. All values will still be strings, even those that originated as numbers or booleans.
TraefikEtcdProxy
#
- class jupyterhub_traefik_proxy.etcd.TraefikEtcdProxy(**kwargs: Any)[source]#
JupyterHub Proxy implementation using traefik and etcd
- check_route_timeout c.TraefikEtcdProxy.check_route_timeout = Int(30)#
Timeout (in seconds) when waiting for traefik to register an updated route.
- concurrency c.TraefikEtcdProxy.concurrency = Int(10)#
The number of requests allowed to be concurrently outstanding to the proxy
Limiting this number avoids potential timeout errors by sending too many requests to update the proxy at once
- enable_setup_dynamic_config c.TraefikEtcdProxy.enable_setup_dynamic_config = Bool(True)#
Whether to initialize the traefik dynamic configuration from JupyterHub configuration, when should_start is False (dynamic configuration is always applied when should_start is True).
Creates the traefik API router and TLS setup, if any. When True, only traefik static configuration must be managed by the external service (configuration of the endpoints and provider). The traefik api router should not already be configured via other dynamic configuration providers.
When False, initial dynamic configuration must be handled externally and match TraefikProxy configuration, such as traefik_api_url, traefik_api_username` and traefik_api_password. Choose this if the traefik api router is already configured via dynamic configuration elsewhere.
New in version 1.1.
- etcd_client_kwargs c.TraefikEtcdProxy.etcd_client_kwargs = Dict()#
Extra keyword arguments to pass to the etcd Python client constructor
- etcd_password c.TraefikEtcdProxy.etcd_password = Unicode('')#
Password for accessing etcd.
- etcd_url c.TraefikEtcdProxy.etcd_url = Unicode('http://127.0.0.1:2379')#
URL for the etcd endpoint.
- etcd_username c.TraefikEtcdProxy.etcd_username = Unicode('')#
Username for accessing etcd.
- extra_dynamic_config c.TraefikEtcdProxy.extra_dynamic_config = Dict()#
Extra dynamic configuration for treafik.
Merged with the default dynamic config during startup.
Always takes effect unless should_start and enable_setup_dynamic_config are both False.
- extra_routes c.TraefikEtcdProxy.extra_routes = Dict()#
Additional routes to be maintained in the proxy.
A dictionary with a route specification as key, and a URL as target. The hub will ensure this route is present in the proxy.
If the hub is running in host based mode (with JupyterHub.subdomain_host set), the routespec must have a domain component (example.com/my-url/). If the hub is not running in host based mode, the routespec must not have a domain component (/my-url/).
Helpful when the hub is running in API-only mode.
- extra_static_config c.TraefikEtcdProxy.extra_static_config = Dict()#
Extra static configuration for treafik.
Merged with the default static config before writing to .static_config_file.
Has no effect if Proxy.should_start is False.
- kv_jupyterhub_prefix c.TraefikEtcdProxy.kv_jupyterhub_prefix = KVStorePrefix('jupyterhub')#
The key value store key prefix for traefik dynamic configuration
- kv_password c.TraefikEtcdProxy.kv_password = Unicode('DEPRECATED')#
No help string is provided.
- kv_separator c.TraefikEtcdProxy.kv_separator = Unicode('/')#
The separator used for the path in the KV store
- kv_traefik_prefix c.TraefikEtcdProxy.kv_traefik_prefix = KVStorePrefix('traefik')#
The key value store key prefix for traefik static configuration
- kv_url c.TraefikEtcdProxy.kv_url = Unicode('DEPRECATED')#
No help string is provided.
- kv_username c.TraefikEtcdProxy.kv_username = Unicode('DEPRECATED')#
No help string is provided.
- should_start c.TraefikEtcdProxy.should_start = Bool(True)#
Should the Hub start the proxy
If True, the Hub will start the proxy and stop it. Set to False if the proxy is managed externally, such as by systemd, docker, or another service manager.
- static_config_file c.TraefikEtcdProxy.static_config_file = Unicode('traefik.toml')#
traefik’s static configuration file
- toml_static_config_file c.TraefikEtcdProxy.toml_static_config_file = Unicode('')#
Deprecated. Use static_config_file
- traefik_api_entrypoint c.TraefikEtcdProxy.traefik_api_entrypoint = Unicode('auth_api')#
The traefik entrypoint name to use for API access.
Separate from traefik_entrypoint, because this is usually only on localhost.
- traefik_api_password c.TraefikEtcdProxy.traefik_api_password = Unicode('')#
The password for traefik api login
- traefik_api_url c.TraefikEtcdProxy.traefik_api_url = Unicode('http://localhost:8099')#
traefik authenticated api endpoint url
- traefik_api_username c.TraefikEtcdProxy.traefik_api_username = Unicode('')#
The username for traefik api login
- traefik_api_validate_cert c.TraefikEtcdProxy.traefik_api_validate_cert = Bool(True)#
validate SSL certificate of traefik api endpoint
- traefik_entrypoint c.TraefikEtcdProxy.traefik_entrypoint = Unicode('')#
The traefik entrypoint name to use.
By default, will be http if http or https if https.
If running traefik externally with your own specified entrypoint name, set this value.
- traefik_env c.TraefikEtcdProxy.traefik_env = Dict()#
Environment variables to set for the traefik process.
Only has an effect when traefik is a subprocess (should_start=True).
- traefik_log_level c.TraefikEtcdProxy.traefik_log_level = Unicode('')#
traefik’s log level
- traefik_providers_throttle_duration c.TraefikEtcdProxy.traefik_providers_throttle_duration = Unicode('0s')#
throttle traefik reloads of configuration.
When traefik sees a change in configuration, it will wait this long before applying the next one. This affects how long adding a user to the proxy will take.
See https://doc.traefik.io/traefik/providers/overview/#providersprovidersthrottleduration
TraefikConsulProxy
#
- class jupyterhub_traefik_proxy.consul.TraefikConsulProxy(**kwargs: Any)[source]#
JupyterHub Proxy implementation using traefik and Consul
- check_route_timeout c.TraefikConsulProxy.check_route_timeout = Int(30)#
Timeout (in seconds) when waiting for traefik to register an updated route.
- concurrency c.TraefikConsulProxy.concurrency = Int(10)#
The number of requests allowed to be concurrently outstanding to the proxy
Limiting this number avoids potential timeout errors by sending too many requests to update the proxy at once
- consul_client_kwargs c.TraefikConsulProxy.consul_client_kwargs = Dict()#
Extra consul client constructor arguments
- consul_password c.TraefikConsulProxy.consul_password = Unicode('')#
Password or token for accessing consul.
- consul_url c.TraefikConsulProxy.consul_url = Unicode('http://127.0.0.1:8500')#
URL for the consul endpoint.
- consul_username c.TraefikConsulProxy.consul_username = Unicode('')#
Usrname for accessing consul.
- enable_setup_dynamic_config c.TraefikConsulProxy.enable_setup_dynamic_config = Bool(True)#
Whether to initialize the traefik dynamic configuration from JupyterHub configuration, when should_start is False (dynamic configuration is always applied when should_start is True).
Creates the traefik API router and TLS setup, if any. When True, only traefik static configuration must be managed by the external service (configuration of the endpoints and provider). The traefik api router should not already be configured via other dynamic configuration providers.
When False, initial dynamic configuration must be handled externally and match TraefikProxy configuration, such as traefik_api_url, traefik_api_username` and traefik_api_password. Choose this if the traefik api router is already configured via dynamic configuration elsewhere.
New in version 1.1.
- extra_dynamic_config c.TraefikConsulProxy.extra_dynamic_config = Dict()#
Extra dynamic configuration for treafik.
Merged with the default dynamic config during startup.
Always takes effect unless should_start and enable_setup_dynamic_config are both False.
- extra_routes c.TraefikConsulProxy.extra_routes = Dict()#
Additional routes to be maintained in the proxy.
A dictionary with a route specification as key, and a URL as target. The hub will ensure this route is present in the proxy.
If the hub is running in host based mode (with JupyterHub.subdomain_host set), the routespec must have a domain component (example.com/my-url/). If the hub is not running in host based mode, the routespec must not have a domain component (/my-url/).
Helpful when the hub is running in API-only mode.
- extra_static_config c.TraefikConsulProxy.extra_static_config = Dict()#
Extra static configuration for treafik.
Merged with the default static config before writing to .static_config_file.
Has no effect if Proxy.should_start is False.
- kv_jupyterhub_prefix c.TraefikConsulProxy.kv_jupyterhub_prefix = KVStorePrefix('jupyterhub')#
The key value store key prefix for traefik dynamic configuration
- kv_password c.TraefikConsulProxy.kv_password = Unicode('DEPRECATED')#
No help string is provided.
- kv_separator c.TraefikConsulProxy.kv_separator = Unicode('/')#
The separator used for the path in the KV store
- kv_traefik_prefix c.TraefikConsulProxy.kv_traefik_prefix = KVStorePrefix('traefik')#
The key value store key prefix for traefik static configuration
- kv_url c.TraefikConsulProxy.kv_url = Unicode('DEPRECATED')#
No help string is provided.
- kv_username c.TraefikConsulProxy.kv_username = Unicode('DEPRECATED')#
No help string is provided.
- should_start c.TraefikConsulProxy.should_start = Bool(True)#
Should the Hub start the proxy
If True, the Hub will start the proxy and stop it. Set to False if the proxy is managed externally, such as by systemd, docker, or another service manager.
- static_config_file c.TraefikConsulProxy.static_config_file = Unicode('traefik.toml')#
traefik’s static configuration file
- toml_static_config_file c.TraefikConsulProxy.toml_static_config_file = Unicode('')#
Deprecated. Use static_config_file
- traefik_api_entrypoint c.TraefikConsulProxy.traefik_api_entrypoint = Unicode('auth_api')#
The traefik entrypoint name to use for API access.
Separate from traefik_entrypoint, because this is usually only on localhost.
- traefik_api_password c.TraefikConsulProxy.traefik_api_password = Unicode('')#
The password for traefik api login
- traefik_api_url c.TraefikConsulProxy.traefik_api_url = Unicode('http://localhost:8099')#
traefik authenticated api endpoint url
- traefik_api_username c.TraefikConsulProxy.traefik_api_username = Unicode('')#
The username for traefik api login
- traefik_api_validate_cert c.TraefikConsulProxy.traefik_api_validate_cert = Bool(True)#
validate SSL certificate of traefik api endpoint
- traefik_entrypoint c.TraefikConsulProxy.traefik_entrypoint = Unicode('')#
The traefik entrypoint name to use.
By default, will be http if http or https if https.
If running traefik externally with your own specified entrypoint name, set this value.
- traefik_env c.TraefikConsulProxy.traefik_env = Dict()#
Environment variables to set for the traefik process.
Only has an effect when traefik is a subprocess (should_start=True).
- traefik_log_level c.TraefikConsulProxy.traefik_log_level = Unicode('')#
traefik’s log level
- traefik_providers_throttle_duration c.TraefikConsulProxy.traefik_providers_throttle_duration = Unicode('0s')#
throttle traefik reloads of configuration.
When traefik sees a change in configuration, it will wait this long before applying the next one. This affects how long adding a user to the proxy will take.
See https://doc.traefik.io/traefik/providers/overview/#providersprovidersthrottleduration
Changelog#
For detailed changes from the prior release, click on the version number
and its link will bring up a GitHub listing of changes. Use git log
on
the command line for details.
1.1.0 - 2023-06-06#
New features added#
add TraefikProxy.enable_setup_dynamic_config for opt-out of dynamic config setup #210 (@minrk, @alexleach)
Contributors to this release#
The following people contributed discussions, new ideas, code and documentation contributions, and review. See our definition of contributors.
(GitHub contributors page for this release)
@alexleach (activity) | @consideRatio (activity) | @minrk (activity)
1.0.1 - 2023-05-25#
1.0.1 fixes some packing issues in the 1.0 release. No changes to actual behavior.
Maintenance and upkeep improvements#
Contributors to this release#
The following people contributed discussions, new ideas, code and documentation contributions, and review. See our definition of contributors.
1.0.0 - 2023-05-16#
1.0.0 is a big release for jupyterhub-traefik-proxy! It updates support for traefik to version 2.x (current default: 2.10.1). Traefik versions < 2.0 are no longer supported. If you have custom traefik configuration, make sure to checkout traefik’s v1 to v2 migration guide, since your configuration may need updating.
A major consequence of the v2 updates is that the performance of adding and removing routes when there are a large number already defined is now greatly improved, and no longer grows significantly with the number of routes.
Major changes:
Traefik v2 is required. Traefik v1 is not supported.
TraefikTomlProxy
is deprecated in favor ofTraefikFileProviderProxy
, which supports both toml and yaml. Replacetraefik_toml
withtraefik_file
in your configuration.python3 -m jupyterhub_traefik_proxy.install
will now only install traefik, not any key-value-store providers. You can follow your KV store’s own installation instructions.python3 -m jupyterhub_traefik_proxy.install --traefik-version x.y.z
now supports fetching any published traefik version on any architecture, instead of a few preset versions.
Performance and responsiveness is also greatly improved.
API and Breaking Changes#
remove kv stores from install.py #156 (@minrk, @GeorgianaElena, @consideRatio)
Traefik v2 support #145 (@GeorgianaElena, @minrk, @alexleach)
New features added#
Traefik v2 support #145 (@GeorgianaElena, @minrk, @alexleach)
Enhancements made#
update default bootstrapped traefik to 2.10.1 #206 (@minrk, @consideRatio)
simplify ssl, passthrough configuration #199 (@minrk, @GeorgianaElena)
reduce requirements of KV store implementations and custom methods #185 (@minrk, @GeorgianaElena)
Improve performance, scaling #165 (@minrk, @GeorgianaElena)
Improve error message on traefik api access error #140 (@twalcari, @minrk)
Bugs fixed#
make sure that
/prefix
and/prefix/
are handled the same #197 (@minrk, @GeorgianaElena, @manics)Fix handling of empty dicts in traefik config #173 (@minrk, @GeorgianaElena)
Maintenance and upkeep improvements#
dependabot: monthly updates of github actions #201 (@consideRatio)
trade versioneer for tbump #193 (@minrk, @consideRatio)
make api endpoint configurable, restore previous entrypoint names #192 (@minrk, @GeorgianaElena)
set providersThrottleDuration=0s in tests #190 (@minrk, @consideRatio)
Make traefik_entrypoint name explicit #184 (@minrk, @GeorgianaElena)
test: fix consul auth port #183 (@minrk, @consideRatio, @GeorgianaElena)
deprecate consul due to unhealthy API clients #182 (@minrk, @consideRatio)
add dependabot config for github actions #178 (@minrk, @GeorgianaElena)
reuse backends across tests #174 (@minrk, @GeorgianaElena)
simplify some test fixtures #169 (@minrk, @GeorgianaElena)
avoid deprecation warning in
--slow-last
sorting #168 (@minrk)Minor cleanup of start/stop methods and logging #166 (@minrk, @GeorgianaElena)
Respect ip address config in urls, don’t serve dashboard by default #162 (@minrk, @GeorgianaElena, @consideRatio)
minor logging tweaks #161 (@minrk, @consideRatio)
Use checksums from traefik releases #160 (@minrk, @GeorgianaElena)
Add pre-commit config and configure autoformating tools and pytest from pyproject.toml #157 (@GeorgianaElena, @minrk, @consideRatio)
Deprecations for v2 upgrades #154 (@minrk, @GeorgianaElena, @alexleach, @consideRatio)
switch etcd3 client for Python 3.11 support #153 (@minrk, @consideRatio)
Support External Traefik Certificate Resolver and update grpc #152 (@alexleach, @minrk)
improve test time #150 (@minrk, @GeorgianaElena)
Get the repo back running #144 (@minrk, @GeorgianaElena, @consideRatio)
prepare to rename default branch to main #143 (@minrk, @GeorgianaElena)
Documentation improvements#
add document on enabling https #204 (@minrk, @consideRatio)
doc: update sample configuration for v2 #189 (@minrk, @GeorgianaElena)
Fix CI README badges #177 (@manics, @consideRatio)
changelog for 1.0 #176 (@minrk, @GeorgianaElena, @manics)
Update performance benchmarks for v2 #163 (@minrk, @GeorgianaElena, @manics)
Revert some entrypoint names in docs, renumber how to install steps #196 (@rcthomas, @GeorgianaElena)
Contributors to this release#
The following people contributed discussions, new ideas, code and documentation contributions, and review. See our definition of contributors.
(GitHub contributors page for this release)
@alexleach (activity) | @consideRatio (activity) | @dependabot (activity) | @devnull-mr (activity) | @dolfinus (activity) | @GeorgianaElena (activity) | @manics (activity) | @maulikjs (activity) | @minrk (activity) | @pre-commit-ci (activity) | @rcthomas (activity) | @twalcari (activity)
0.3.0 2021-10-18#
Enhancements made#
Support ARM in binary package installs #129 (@yuvipanda)
Bugs fixed#
Fix handling default server routes in TraefikTomlProxy #131 (@dolfinus)
Make etcd3 & python-consul2 soft dependencies #127 (@yuvipanda)
Continuous integration#
ci: don’t run tests if docs change #139 (@consideRatio)
ci/docs: install autodocs-traits as a PyPI package & pin sphinx #138 (@consideRatio)
Contributors to this release#
(GitHub contributors page for this release)
@alexleach | @consideRatio | @dolfinus | @GeorgianaElena | @yuvipanda
0.2.0 2021-02-24#
Bugs fixed#
Maintenance and upkeep improvements#
Switch to pydata-sphinx-theme and myst-parser #122 (@GeorgianaElena)
Try unpinning deps and use a more up to date python consul client #115 (@GeorgianaElena)
Other merged PRs#
Remove CircleCI docs build since now we’re using the RTD CI #121 (@GeorgianaElena)
Update readthedocs config options and version #119 (@GeorgianaElena)
pip-compile is actually just pip in dependabots config file #117 (@GeorgianaElena)
Freeze requirements and setup dependabot #116 (@GeorgianaElena)
ci: make pushing tags trigger release workflow properly #114 (@consideRatio)
Travis -> GitHub workflows #113 (@GeorgianaElena)
Contributors to this release#
(GitHub contributors page for this release)
@consideRatio | @GeorgianaElena | @manics | @minrk | @mofanke
0.1.6 2020-05-16#
Merged PRs#
Fix circular reference error #107 (@GeorgianaElena)
fix TypeError: ‘NoneType’ object is not iterable, in delete_route when route doesn’t exist #104 (@mofanke)
New Proxy config option traefik_api_validate_cert #98 (@devnull-mr)
Contributors to this release#
0.1.5 2020-03-31#
Merged PRs#
Fix named servers routing #96 (@GeorgianaElena)
Show a message when no binary is provided to the installer #95 (@GeorgianaElena)
Travis deploy tags to PyPI #89 (@GeorgianaElena)
Update README #87 (@consideRatio)
Handle ssl #84 (@GeorgianaElena)
CONTRIBUTING: use long option in “pip install -e” #82 (@muxator)
Change traefik default version #81 (@GeorgianaElena)
Add info about TraefikConsulProxy in readme #80 (@GeorgianaElena)
Contributors to this release#
(GitHub contributors page for this release)
@consideRatio | @devnull-mr | @GeorgianaElena | @jtpio | @manics | @muxator | @yuvipanda
0.1.4 2019-09-20#
Merged PRs#
Add info about TraefikConsulProxy in readme #80 (@GeorgianaElena)
Stop assuming kv_traefik_prefix ends with a slash #79 (@GeorgianaElena)
Log info about what dynamic config file it’s used by the Hub #77 (@GeorgianaElena)
Install script #76 (@GeorgianaElena)
Set defaults for traefik api username and password #75 (@GeorgianaElena)
Allow etcd and consul client ssl settings #70 (@GeorgianaElena)
Fix format in install script warnings #69 (@GeorgianaElena)
Create test coverage report #65 (@GeorgianaElena)
Explicitly close consul client session #64 (@GeorgianaElena)
Throughput results updated #62 (@GeorgianaElena)
Make trefik’s log level configurable #61 (@GeorgianaElena)
TraefikConsulProxy #57 (@GeorgianaElena)
WIP Common proxy profiling suite #54 (@GeorgianaElena)
Contributors to this release#
0.1.3 2019-02-26#
Load initial routing table from disk in TraefikTomlProxy when resuming from a previous session.
Merged PRs#
Try to load routes from file if cache is empty #52 (@GeorgianaElena)
Contributors to this release#
0.1.2 2019-02-22#
Fix possible race in atomic_writing with TraefikTomlProxy
0.1.1 2019-02-22#
make proxytest reusable with any Proxy implementation
improve documentation
improve logging and error handling
make check_route_timeout configurable
Merged PRs#
Update documentation and readme #47 (@GeorgianaElena)
Define only the proxy fixture in test_proxy #46 (@GeorgianaElena)
add mocks so that test_check_routes needs only proxy fixture #44 (@minrk)
Etcd with credentials #43 (@GeorgianaElena)
Contributors to this release#
0.1.0#
First release!
Implementation details#
Traefik API#
traefik-proxy uses the Traefik API to monitor routes and configurations.
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.TraefikFileProviderProxy.traefik_api_url = "http://127.0.0.1:8099"
c.TraefikFileProviderProxy.traefik_api_password = "admin"
c.TraefikFileProviderProxy.traefik_api_username = "admin"
Check out TraefikProxy’s API Reference for more configuration options.
Class structure#
A JupyterHub Proxy implementation must implement these methods:
start
/stop
(starting and stopping the proxy)add_route
delete_route
get_all_routes
get_route
(a default implementation is provided by the base class, based on get_all_routes)
Additionally, for traefik we need to set up the separate “static config” (API access and where routes will be stored) and “dynamic config” (the routing table itself, which changes as servers start and stop). Where and how dynamic_config is stored is the ~only difference between TraefikProxy subclasses.
Setting up traefik configuration is in these methods:
_setup_traefik_static_config
- must be extended by subclasses to add any provider-specific configuration toself.static_config
_setup_traefik_dynamic_config
- usually not modified
TraefikProxy is organized into three levels:
First, is TraefikProxy
. This class is responsible for everything traefik-specific.
It implements talking to the traefik API, and computing what dynamic configuration is needed for each step.
This base class provides implementations of start
, stop
, add_route
, delete_route
, and get_all_routes
.
TraefikProxy subclasses must implement how dynamic config is stored, and set the appropriate static config to tell traefik how to load the dynamic config. Specifically, the generic methods:
_setup_traefik_static_config
should extendself.static_config
to configure the traefik configuration discovery provider_apply_dynamic_config
stores a given dynamic config dictionary in the appropriate config store_delete_dynamic_config
removes keys from the dynamic config store_get_jupyterhub_dynamic_config
reads the whole jupyterhub part (not read by traefik itself)
We have two classes at this level:
TraefikFileProviderProxy, which stores dynamic config in a toml or yaml file, and
TKvProxy - another base class, which implements the above, based on a generic notion of a key-value store
TKvProxy is an adapter layer, implementing all of the above methods, based on a few basic actions on key-value stores:
_kv_atomic_set
should take a flat dictionary of key paths and string values, and store them._kv_atomic_delete
should delete a number of keys in a single transaction_kv_get_tree
should recursively read everything in the key-value store under a prefix (returning the flattened dictionary)
TKvProxy is responsible for translating between key-value-friendly “flat” dictionaries and the ‘true’ nested dictionary format of the configuration (i.e. the nested dictionary {"a": {"b": 5}}
will be flattened to {"a/b": "5"}
).
Finally, we have our specific key-value store implementations: TraefikEtcdProxy
and TraefikConsulProxy
.
These classes only need to implement:
configuration necessary to connect to the key-value provider
_setup_traefik_static_config
to tell traefik how to talk to the same key-value providerthe above three
_kv_
methods for reading, writing, and deleting keys
Testing jupyterhub-traefik-proxy#
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>