Merge pull request #1 from jtyberg/seed

Initial version.
This commit is contained in:
Min RK 2016-04-18 10:51:21 +02:00
commit 07be477148
17 changed files with 758 additions and 90 deletions

5
.dockerignore Normal file
View File

@ -0,0 +1,5 @@
.git
.gitignore
examples/
internal/
README.md

27
.env Normal file
View File

@ -0,0 +1,27 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
# Use this file to set default values for environment variables specified in
# docker-compose configuration file. docker-compose will substitute these
# values for environment variables in the configuration file IF the variables
# are not set in the shell environment.
# To override these values, set the shell environment variables.
# Name of Docker machine
DOCKER_MACHINE_NAME=jupyterhub
# Name of Docker network
DOCKER_NETWORK_NAME=jupyterhub-network
# Single-user Jupyter Notebook server container image
DOCKER_CONTAINER_IMAGE=jupyter/scipy-notebook:2d878db5cbff
# Docker run command to use when spawning single-user containers
DOCKER_SPAWN_CMD=start-singleuser.sh
# Name of JupyterHub container data volume
DATA_VOLUME_HOST=jupyterhub-data
# Data volume container mount point
DATA_VOLUME_CONTAINER=/data

65
.gitignore vendored
View File

@ -1,62 +1,3 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
# Sphinx documentation
docs/_build/
# PyBuilder
target/
#Ipython Notebook
.ipynb_checkpoints
secrets/
userlist
.DS_Store

19
Dockerfile.jupyterhub Normal file
View File

@ -0,0 +1,19 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
FROM jupyter/jupyterhub:master
# Install dockerspawner and its dependencies
RUN /opt/conda/bin/pip install \
-e git+https://github.com/jupyter/jupyterhub@62a5e9dbce86cbb8992def81600ff9881d515935#egg=jupyterhub \
-e git+https://github.com/jupyter/oauthenticator@011f6ea25c6bafca087d94a6c73d24dcbb0bf80e#egg=oauthenticator \
-e git+https://github.com/jupyter/dockerspawner@3c5e36bc96a252a04bb7700fdb009bd572996f3a#egg=dockerspawner
# Copy TLS certificate and key
ENV SSL_CERT /srv/jupyterhub/secrets/jupyterhub.cer
ENV SSL_KEY /srv/jupyterhub/secrets/jupyterhub.key
COPY ./secrets/*.cer $SSL_CERT
COPY ./secrets/*.key $SSL_KEY
RUN chmod 700 /srv/jupyterhub/secrets && \
chmod 600 /srv/jupyterhub/secrets/*
COPY ./userlist /srv/jupyterhub/userlist

27
LICENSE
View File

@ -1,27 +0,0 @@
Copyright (c) 2016, JupyterHub
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of jupyterhub-deploy-docker nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

60
LICENSE.md Normal file
View File

@ -0,0 +1,60 @@
# Licensing terms
This project is licensed under the terms of the Modified BSD License
(also known as New or Revised or 3-Clause BSD), as follows:
- Copyright (c) 2001-2015, IPython Development Team
- Copyright (c) 2015-, Jupyter Development Team
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
Neither the name of the Jupyter Development Team nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
## About the Jupyter Development Team
The Jupyter Development Team is the set of all contributors to the Jupyter project.
This includes all of the Jupyter subprojects.
The core team that coordinates development on GitHub can be found here:
https://github.com/jupyter/.
## Our Copyright Policy
Jupyter uses a shared copyright model. Each contributor maintains copyright
over their contributions to Jupyter. But, it is important to note that these
contributions are typically only changes to the repositories. Thus, the Jupyter
source code, in its entirety is not the copyright of any single person or
institution. Instead, it is the collective copyright of the entire Jupyter
Development Team. If individual contributors want to maintain a record of what
changes/contributions they have specific copyright on, they should indicate
their copyright in the commit message of the change, when they commit the
change to one of the Jupyter repositories.
With this in mind, the following banner should be used in any source code file
to indicate the copyright and license terms:
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

247
README.md
View File

@ -1,2 +1,247 @@
# jupyterhub-deploy-docker
Reference deployment of JupyterHub with docker
This repository provides a reference deployment of [JupyterHub](https://github.com/jupyter/jupyterhub), a multi-user [Jupyter Notebook](http://jupyter.org/) environment, on a **single host** using [Docker](https://docs.docker.com).
This deployment:
* Runs the [JupyterHub components](https://jupyterhub.readthedocs.org/en/latest/getting-started.html#overview) in a Docker container on the host
* Uses [DockerSpawner](https://github.com/jupyter/dockerspawner) to spawn single-user Jupyter Notebook servers in separate Docker containers on the same host
* Persists JupyterHub data in a Docker volume on the host
* Persists user notebook directories in Docker volumes on the host
* Uses [OAuthenticator](https://github.com/jupyter/oauthenticator) and [GitHub OAuth](https://developer.github.com/v3/oauth/) to authenticate users
![JupyterHub single host Docker deployment](internal/jupyterhub-docker.png)
## Use Cases
Possible use cases for this deployment may include, but are not limited to:
* A JupyterHub demo environment that you can spin up relatively quickly.
* A multi-user Jupyter Notebook environment for small classes, teams, or departments.
## Disclaimer
This deployment is **NOT** intended for a production environment.
## Prerequisites
* This deployment uses Docker for all the things. It assumes that you wiil be using [Docker Machine](https://docs.docker.com/machine/overview/) and [Docker Compose](https://docs.docker.com/compose/overview/) on a local workstation or laptop to create, build, and run Docker images on a remote host. It requires [Docker Toolbox](https://www.docker.com/products/docker-toolbox) 1.11.0 or higher. See the [installation instructions](https://docs.docker.com/engine/installation/) for your environment.
* This example configures JupyterHub for HTTPS connections (the default). As such, you must provide TLS certificate chain and key files to the JupyterHub server. If you do not have your own certificate chain and key, you can either [create self-signed versions](https://jupyter-notebook.readthedocs.org/en/latest/public_server.html#using-ssl-for-encrypted-communication), or obtain real ones from [Let's Encrypt](https://letsencrypt.org) (see the [letsencrypt example](examples/letsencrypt/README.md) for instructions).
## Create a Docker Machine
Use [Docker Machine](https://docs.docker.com/machine/) to provision a new host, or to connect to an existing host. In either case, the result will be a "machine" configured on your local workstation or laptop that represents the remote host. After you activate the machine on your local workstation, when you run `docker` commands locally, Docker Machine will execute them on the remote host for you.
### Provision a new host
Docker Machine can provision new hosts on various platforms using one of its [supported drivers](https://docs.docker.com/machine/drivers/). When provisioning a host, Docker Machine will automatically install the latest version of [Docker Engine](https://www.docker.com/products/docker-engine) on the host. It will also generate local TLS certificate and key files to connect to the host and authenticate with the `docker` daemon on the host.
In the following example, we provision a new virtual server on [IBM SoftLayer](https://www.softlayer.com/promo/freeCloud/freecloud). (You can provision similarly on RackSpace, AWS, and other hosting providers). We set `DRIVER_OPTS` to the SoftLayer-specific options.
```
# Set Docker Machine SoftLayer driver options
export DRIVER_OPTS="--driver softlayer \
--softlayer-api-key <my_softlayer_api_key> \
--softlayer-user <my_softlayer_username> \
--softlayer-domain mydomain \
--softlayer-cpu 16 \
--softlayer-memory 65536 \
--softlayer-disk-size 100 \
--softlayer-region wdc01"
# Create a machine named jupyterhub
docker-machine create $DRIVER_OPTS jupyterhub
```
### Connect to an existing host
This example configures a "machine" on your local workstation that connects to an existing remote host at IP address `10.0.0.10`. To do this, you must use Docker Machine's `generic` driver, and your local workstation must have a private SSH key that allows you to perform password-less login to the host.
Substitute your own IP address and path to SSH key in the `DRIVER_OPTS` below. Note that when you run the `docker-machine create` command, Docker Machine will install and configure the latest Docker Engine on the remote host (or upgrade the Docker Engine on the host if it is already installed).
```
# Use the generic driver to create a Docker machine that
# controls the Docker daemon on an existing host
export DRIVER_OPTS="--driver generic \
--generic-ip-address 10.0.0.10 \
--generic-ssh-key /Users/jtyberg/.ssh/myhost_rsa.pem"
# Create a machine named jupyterhub
docker-machine create $DRIVER_OPTS jupyterhub
```
## Activate Docker Machine
You must set specific `DOCKER_*` environment variables to tell `docker` that it should run commands against a particular machine. The remainder of this document assumes that the machine called `jupyterhub` is active, and therefore, all `docker` commands will run on the `jupyterhub` remote host.
To set the `jupyterhub` machine as active:
```
eval "$(docker-machine env jupyterhub)"
```
All this does is set the right environment variables:
```
env|grep DOCKER
DOCKER_HOST=tcp://10.0.0.10:2376
DOCKER_MACHINE_NAME=jupyterhub
DOCKER_TLS_VERIFY=1
DOCKER_CERT_PATH=/Users/jtyberg/.docker/machine/machines/jupyterhub
```
To see which machine is active:
```
docker-machine active
```
## Create a Docker Network
Create a Docker network for inter-container communication. The benefits of using a Docker network are:
* container isolation - only the containers on the network can access one another
* name resolution - Docker daemon runs an embedded DNS server to provide automatic service discovery for containers connected to user-defined networks. This allows us to access containers on the same network by name.
Here we create a Docker network named `jupyterhub-network`. Later, we will configure the JupyterHub and single-user Jupyter Notebook containers to run attached to this network.
```
docker network create jupyterhub-network
```
## Setup GitHub Authentication
This deployment uses GitHub OAuth to authenticate users. It requires that you create a [GitHub application](https://github.com/settings/applications/new). You will need to specify an OAuth callback URL in the following form:
```
https://<myhost.mydomain>/hub/oauth_callback
```
You must pass the secrets that GitHub provides for your application to JupyterHub at runtime. You can do this by setting the `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`, and `OAUTH_CALLBACK_URL` environment variables when you run the JupyterHub container, or you can add them to the `.env` file in the root directory of this repository. For example,
```
GITHUB_CLIENT_ID=<github_client_id>
GITHUB_CLIENT_SECRET=<github_client_secret>
OAUTH_CALLBACK_URL=https://<myhost.mydomain>/hub/oauth_callback
```
**Note:** The `.env` file is a special file that Docker Compose uses to lookup environment variables. If you choose to place the GitHub secrets in this file, you should ensure that this file remains private (e.g., do not commit the secrets to source control).
## Build the JupyterHub Docker image
Configure JupyterHub and build it into a Docker image.
1. Copy your TLS certificate and key to a directory named `secrets` within this repository directory. These will be added to the Docker image at build time.
```
mkdir -p secrets
cp jupyterhub.cer jupyterhub.key secrets/
```
1. Create a `userlist` file with a list of authorized users. At a minimum, this file should contain a single admin user. The username should be a GitHub username. For example:
```
jtyberg admin
```
The admin user will have the ability to add more users in the JupyterHub admin console.
1. Build the JupyterHub Docker image. For convenience, this repo provides a `hub.sh` script that wraps [docker-compose](https://docs.docker.com/compose/reference/), so you can run it with the docker-compose [command line arguments](https://docs.docker.com/compose/reference/overview/). To build the JupyterHub image on the active Docker machine host, run:
```
./hub.sh build
```
## Create a JupyterHub Data Volume
Create a Docker volume to persist JupyterHub data. This volume will reside on the host machine. Using a volume allows user lists, cookies, etc., to persist across JupyterHub container restarts.
```
docker volume create --name jupyterhub-data
```
## Pull the Jupyter Notebook Image
Pull the Jupyter Notebook Docker image that you would like JupyterHub to spawn for each user.
Note: Even though Docker will pull the image to the host the first time a user container is spawned, JupyterHub may timeout if the image is large, so it's better to do it beforehand.
This deployment uses the [jupyter/scipy-notebook](https://hub.docker.com/r/jupyter/scipy-notebook/) Docker image, which is built from the `scipy-notebook` [Docker stacks](https://github.com/jupyter/docker-stacks).
Note that the Docker stacks `*-notebook` images tagged `2d878db5cbff` include the `start-singleuser.sh` script required to start a single-user instance of the Notebook server that is compatible with JupyterHub.
```
docker pull jupyter/scipy-notebook:2d878db5cbff
```
Note: If you choose to use a different container image, be sure to set the `DOCKER_CONTAINER_IMAGE` environment variable either in the shell you use to launch JupyterHub or in the `.env` file.
## Run the JupyterHub container
Run the JupyterHub container on the host.
To run the JupyterHub container in detached mode:
```
./hub.sh up -d
```
Once the container is running, you should be able to access the JupyterHub console at
```
https://myhost.mydomain
```
To bring down the JupyterHub container:
```
./hub.sh down
```
## FAQ
### How can I view the logs for JupyterHub or users' Notebook servers?
Use `docker logs <container>`. For example, to view the logs of the `jupyterhub` container
```
docker logs jupyterhub
```
### How can I backup a user's notebook directory?
There are multiple ways to [backup and restore](https://docs.docker.com/engine/userguide/containers/dockervolumes/#backup-restore-or-migrate-data-volumes) data in Docker containers.
Suppose you have the following running containers:
```
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Names}}"
CONTAINER ID IMAGE NAMES
bc02dd6bb91b jupyter/minimal-notebook jupyter-jtyberg
7b48a0b33389 jupyterhub jupyterhub
```
In this deployment, the user's notebook directories (`/home/jovyan/work`) are backed by Docker volumes.
```
docker inspect -f '{{ .Mounts }}' jupyter-jtyberg
[{jtyberg /var/lib/docker/volumes/jtyberg/_data /home/jovyan/work local rw true rprivate}]
```
We can backup the user's notebook directory by running a separate container that mounts the user's volume and creates a tarball of the directory.
```
docker run --rm \
-u root \
-v /tmp:/backups \
-v jtyberg:/notebooks \
jupyter/minimal-notebook \
tar cvf /backups/jtyberg-backup.tar /notebooks
```
The above command creates a tarball in the `/tmp` directory on the host.

52
docker-compose.yml Normal file
View File

@ -0,0 +1,52 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
# JupyterHub docker-compose configuration file
version: "2"
services:
hub:
build:
context: .
dockerfile: Dockerfile.jupyterhub
image: jupyterhub
container_name: jupyterhub
volumes:
# Bind Docker binary from host machine so we can invoke Docker commands
# from inside container
- "/usr/local/bin/docker:/usr/local/bin/docker:ro"
# Bind Docker TLS certs from host machine so we can authenticate with the
# daemon on the host (DOCKER_HOST should be set to host's IP)
- "/etc/docker:/etc/docker:ro"
# Bind Docker volume on host for JupyterHub database and cookie secrets
- "data:${DATA_VOLUME_CONTAINER}"
ports:
- "443:443"
environment:
# Pass DOCKER_HOST to container to allow it to connect to daemon on host
DOCKER_HOST: ${DOCKER_HOST}
# Locations of TLS certificate and key needed to auth with daemon on host
DOCKER_TLS_CERT: "/etc/docker/server.pem"
DOCKER_TLS_KEY: "/etc/docker/server-key.pem"
# All containers will join this network
DOCKER_NETWORK_NAME: ${DOCKER_NETWORK_NAME}
# JupyterHub will spawn this image for users
DOCKER_CONTAINER_IMAGE: ${DOCKER_CONTAINER_IMAGE}
# Using this run command (optional)
DOCKER_SPAWN_CMD: ${DOCKER_SPAWN_CMD}
# Required to authenticate users using GitHub OAuth
GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID}
GITHUB_CLIENT_SECRET: ${GITHUB_CLIENT_SECRET}
OAUTH_CALLBACK_URL: ${OAUTH_CALLBACK_URL}
command: >
jupyterhub -f /srv/jupyterhub/jupyterhub_config.py
volumes:
data:
external:
name: ${DATA_VOLUME_HOST}
networks:
default:
external:
name: ${DOCKER_NETWORK_NAME}

View File

@ -0,0 +1,21 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
# Pin to version of notebook image that includes start-singleuser.sh script
FROM jupyter/scipy-notebook:2d878db5cbff
# Install packages in default Python 3 environment
RUN pip install \
beautifulsoup4==4.4.*
# Install packages in Python 2 environment
RUN $CONDA_DIR/envs/python2/bin/pip install \
beautifulsoup4==4.4.*
# Use custom startup script
USER root
COPY docker-entrypoint.sh /srv/docker-entrypoint.sh
ENTRYPOINT ["tini", "--", "/srv/docker-entrypoint.sh"]
CMD ["start-singleuser.sh"]
USER jovyan

View File

@ -0,0 +1,28 @@
# Custom Jupyter Notebook Server Image
This is an example of using a custom Jupyter Notebook server Docker image with JupyterHub. The `Dockerfile` builds from the `jupyter/scipy-notebook` image, but customizes the image in the following ways:
* installs an additional Python package to make it available to notebooks
* uses a custom entrypoint script that copies sample notebooks to the user's notebook directory before executing the run command that was provided to the container
## Build the Image
Build and tag the image using the `Dockerfile` in this directory.
```
docker build -t my-custom-notebook .
```
## Run JupyterHub Container
To have JupyterHub spawn the `my-custom-notebook` image for single-user Notebook servers, set the `DOCKER_CONTAINER_IMAGE` environment variable to the image name when you run the JupyterHub container. For example, run the following **from the root directory** of this repository:
```
export DOCKER_CONTAINER_IMAGE=my-custom-notebook
# bring down the JupyterHub container, if running
./hub.sh down
# bring it back up
./hub.sh up -d
```

View File

@ -0,0 +1,20 @@
#!/bin/bash
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
set -e
# If the run command is the default, do some initialization first
if [ "$(which "$1")" = "/usr/local/bin/start-singleuser.sh" ]; then
# Clone sample notebooks to user's notebook directory. Assume $NB_USER's work
# directory if notebook directory not explicitly set. `git clone` will fail
# if target directory already exists and is not empty, which likely means
# that we've already done it, so just ignore.
: ${NOTEBOOK_DIR:=/home/$NB_USER/work}
git clone https://gist.github.com/parente/facb555dfbae28e817e0 \
--depth 1 \
"$NOTEBOOK_DIR/notebook_count" || true
fi
# Run the command provided
exec "$@"

View File

@ -0,0 +1,47 @@
# Let's Encrypt
This example includes a Docker Compose configuration file that you can use to deploy [JupyterHub](https://github.com/jupyter/jupyterhub) with TLS certificate and key files generated by [Let's Encrypt](https://letsencrypt.org).
The `docker-compose.yml` configuration file in this example extends the JupyterHub service defined in the `docker-compose.yml` file in the root directory of this repository.
When you run the JupyterHub Docker container using the configuration file in this directory, Docker mounts an additional volume containing the Let's Encrypt TLS certificate and key files, and overrides the `SSL_CERT` and `SSL_KEY` environment variables to point to these files.
## Create a secrets volume
This example stores the Let's Encrypt TLS certificate and key files in a Docker volume, and mounts the volume to the JupyterHub container at runtime.
Create a volume to store the certificate and key files.
```
# Activate Docker machine where JupyterHub will run
eval "$(docker-machine env jupyterhub)"
docker volume create --name jupyterhub-secrets
```
## Generate Let's Encrypt certificate and key
Run the `letsencrypt.sh` script to create a TLS full-chain certificate and key.
The script downloads and runs the `letsencrypt` Docker image to create a full-chain certificate and private key, and stores the files in a Docker volume. You must provide a valid, routable, fully-qualified domain name (you must own it), and you must activate the Docker machine host that the domain points to before you run this script. You must also provide a valid email address and the name of the volume you created above.
_Notes:_ The script hard codes several `letsencrypt` options, one of which automatically agrees to the Let's Encrypt Terms of Service.
```
# Activate Docker machine where JupyterHub will run
eval "$(docker-machine env jupyterhub)"
./letsencrypt.sh \
--domain myhost.mydomain \
--email me@mydomain \
--volume jupyterhub-secrets
```
## Run JupyterHub container
To run the JupyterHub container using the configuration in this directory, run the `hub.sh` script **from the root directory** of this repository and specify the `docker-compose.yml` file in this directory. Set the `SECRETS_VOLUME` environment variable to the name of the Docker volume containing the TLS certificate and key files.
```
SECRETS_VOLUME=jupyterhub-secrets \
./hub.sh -f examples/letsencrypt/docker-compose.yml up -d
```

View File

@ -0,0 +1,38 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
# JupyterHub docker-compose configuration that uses Let's Encrypt TLS
# certificate and key.
# Extends the JupyterHub configuration in the root directory of this repository.
# Mounts an additional secrets volume that stores the Let's Encrypt TLS
# certificate and key files, and overrides the `SSL_CERT` and `SSL_KEY`
# environment variables to point to these files.
version: "2"
services:
hub:
extends: # hub service in repository root directory
file: ../../docker-compose.yml
service: hub
volumes:
- "secrets:/etc/letsencrypt"
environment:
SSL_KEY: "/etc/letsencrypt/privkey.pem"
SSL_CERT: "/etc/letsencrypt/cert.pem"
# Explicitly declare volume and network dependencies
# (they cannot be extended)
volumes:
data:
external:
name: ${DATA_VOLUME_HOST}
secrets:
external:
name: ${SECRETS_VOLUME}
networks:
default:
external:
name: ${DOCKER_NETWORK_NAME}

View File

@ -0,0 +1,84 @@
#!/bin/bash
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
# Wrapper script that runs https://letsencrypt.org Docker container to generate
# a certificate for a single domain and store it in a Docker volume.
set -e
USAGE="
Usage: `basename $0` --domain FQDN --email EMAIL --volume SECRETS_VOLUME
[--staging]
"
while [[ $# > 0 ]]
do
key="$1"
case $key in
--domain)
FQDN="$2"
shift # past argument
;;
--email)
EMAIL="$2"
shift # past argument
;;
--volume)
SECRETS_VOLUME="$2"
shift # past argument
;;
--staging)
CERT_SERVER=--staging
;;
*) # unknown option
;;
esac
shift # past argument or value
done
if [ -z "${FQDN:+x}" ]; then
echo "ERROR: Must provide --domain option or set FQDN environment varable"
echo "$USAGE" && exit 1
fi
if [ -z "${EMAIL:+x}" ]; then
echo "ERROR: Must provide --email option set EMAIL environment varable"
echo "$USAGE" && exit 1
fi
if [ -z "${SECRETS_VOLUME:+x}" ]; then
echo "ERROR: Must provide --volume option or set SECRETS_VOLUME environment varable"
echo "$USAGE" && exit 1
fi
# letsencrypt certificate server type (default is production).
# Set `CERT_SERVER=--staging` for staging.
: ${CERT_SERVER=''}
# Generate the cert and save it to the Docker volume
docker run --rm -it \
-p 80:80 \
-v $SECRETS_VOLUME:/etc/letsencrypt \
quay.io/letsencrypt/letsencrypt:latest \
certonly \
--non-interactive \
--keep-until-expiring \
--standalone \
--standalone-supported-challenges http-01 \
--agree-tos \
--force-renewal \
--domain "$FQDN" \
--email "$EMAIL" \
$CERT_SERVER
# Set permissions so nobody can read the cert and key.
# Also symlink the certs into the root of the /etc/letsencrypt
# directory so that the FQDN doesn't have to be known later.
docker run --rm -it \
-v $SECRETS_VOLUME:/etc/letsencrypt \
--entrypoint=/bin/bash \
quay.io/letsencrypt/letsencrypt:latest \
-c "find /etc/letsencrypt/* -maxdepth 1 -type l -delete && \
ln -s /etc/letsencrypt/live/$FQDN/* /etc/letsencrypt/ && \
find /etc/letsencrypt -type d -exec chmod 755 {} +"

27
hub.sh Executable file
View File

@ -0,0 +1,27 @@
#!/bin/bash
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
# Wrapper script around docker-compose
set -e
for i in "$@" ; do
if [[ "$i" == "up" ]]; then
# Check for required environment variables on startup
if [ -z ${GITHUB_CLIENT_ID:+x} ]; then
echo "ERROR: Must set GITHUB_CLIENT_ID environment variable"; exit 1;
fi
if [ -z ${GITHUB_CLIENT_SECRET:+x} ]; then
echo "ERROR: Must set GITHUB_CLIENT_SECRET environment variable"; exit 1;
fi
if [ -z ${OAUTH_CALLBACK_URL:+x} ]; then
echo "ERROR: Must set OAUTH_CALLBACK_URL environment variable"; exit 1;
fi
# Set DOCKER_HOST to daemon of target machine
DOCKER_HOST=$(docker-machine url $(docker-machine active))
fi
done
exec docker-compose "$@"

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

81
jupyterhub_config.py Normal file
View File

@ -0,0 +1,81 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
# Configuration file for JupyterHub
import os
c = get_config()
# We rely on environment variables to configure JupyterHub so that we
# avoid having to rebuild the JupyterHub container every time we change a
# configuration parameter.
# Spawn single-user servers as Docker containers
c.JupyterHub.spawner_class = 'dockerspawner.DockerSpawner'
# Spawn containers from this image
c.DockerSpawner.container_image = os.environ['DOCKER_CONTAINER_IMAGE']
# JupyterHub requires a single-user instance of the Notebook server, so we
# default to using the `start-singleuser.sh` script included in the
# jupyter/docker-stacks *-notebook images as the Docker run command when
# spawning containers. Optionally, you can override the Docker run command
# using the DOCKER_SPAWN_CMD environment variable.
spawn_cmd = os.environ.get('DOCKER_SPAWN_CMD', "start-singleuser.sh")
c.DockerSpawner.extra_create_kwargs.update({ 'command': spawn_cmd })
# Connect containers to this Docker network
network_name = os.environ['DOCKER_NETWORK_NAME']
c.DockerSpawner.use_internal_ip = True
c.DockerSpawner.network_name = network_name
# Pass the network name as argument to spawned containers
c.DockerSpawner.extra_host_config = { 'network_mode': network_name }
c.DockerSpawner.extra_start_kwargs = { 'network_mode': network_name }
# Explicitly set notebook directory because we'll be mounting a host volume to
# it. Most jupyter/docker-stacks *-notebook images run the Notebook server as
# user `jovyan`, and set the notebook directory to `/home/jovyan/work`.
# We follow the same convention.
c.DockerSpawner.notebook_dir = '/home/jovyan/work'
# Mount the real user's Docker volume on the host to the notebook user's
# notebook directory in the container
c.DockerSpawner.volumes = { '{username}': '/home/jovyan/work' }
c.DockerSpawner.extra_create_kwargs.update({ 'volume_driver': 'local' })
# Remove containers once they are stopped
c.DockerSpawner.remove_containers = True
# Specify paths to TLS certificate and key used to authenticate to Docker
# daemon at DOCKER_HOST
c.DockerSpawner.tls_cert = os.environ['DOCKER_TLS_CERT']
c.DockerSpawner.tls_key = os.environ['DOCKER_TLS_KEY']
# For debugging arguments passed to spawned containers
c.DockerSpawner.debug = True
# User containers will access hub by container name on the Docker network
c.JupyterHub.hub_ip = 'jupyterhub'
c.JupyterHub.hub_port = 8080
# TLS config
c.JupyterHub.port = 443
c.JupyterHub.ssl_key = os.environ['SSL_KEY']
c.JupyterHub.ssl_cert = os.environ['SSL_CERT']
# Authenticate users with GitHub OAuth
c.JupyterHub.authenticator_class = 'oauthenticator.GitHubOAuthenticator'
c.GitHubOAuthenticator.oauth_callback_url = os.environ['OAUTH_CALLBACK_URL']
# Persist hub data on volume mounted inside container
data_dir = os.environ.get('DATA_VOLUME_CONTAINER', '/data')
c.JupyterHub.db_url = os.path.join('sqlite:///', data_dir, 'jupyterhub.sqlite')
c.JupyterHub.cookie_secret_file = os.path.join(data_dir,
'jupyterhub_cookie_secret')
# Whitlelist users and admins
c.Authenticator.whitelist = whitelist = set()
c.Authenticator.admin_users = admin = set()
c.JupyterHub.admin_access = True
pwd = os.path.dirname(__file__)
with open(os.path.join(pwd, 'userlist')) as f:
for line in f:
if not line:
continue
parts = line.split()
name = parts[0]
whitelist.add(name)
if len(parts) > 1 and parts[1] == 'admin':
admin.add(name)