diff --git a/.env b/.env index f35d49c..8ae4660 100644 --- a/.env +++ b/.env @@ -30,3 +30,12 @@ DATA_VOLUME_HOST=jupyterhub-data # Data volume container mount point DATA_VOLUME_CONTAINER=/data + +# Name of JupyterHub postgres database data volume +DB_VOLUME_HOST=jupyterhub-db-data + +# Postgres volume container mount point +DB_VOLUME_CONTAINER=/var/lib/postgresql/data/jupyterhub + +# The name of the postgres database containing JupyterHub state +POSTGRES_DB=jupyterhub diff --git a/Dockerfile.jupyterhub b/Dockerfile.jupyterhub index f3056a0..3f7a3ec 100644 --- a/Dockerfile.jupyterhub +++ b/Dockerfile.jupyterhub @@ -2,8 +2,10 @@ # Distributed under the terms of the Modified BSD License. FROM jupyterhub/jupyterhub-onbuild:0.7.2 -# Install dockerspawner and its dependencies -RUN /opt/conda/bin/pip install \ +# Install dockerspawner, oauth, postgres +RUN /opt/conda/bin/conda install psycopg2=2.7 && \ + /opt/conda/bin/conda clean -tipsy && \ + /opt/conda/bin/pip install \ oauthenticator==0.5.* \ dockerspawner==0.7.* diff --git a/Makefile b/Makefile index 72d12b4..b2bd66e 100644 --- a/Makefile +++ b/Makefile @@ -10,10 +10,15 @@ network: volumes: @docker volume inspect $(DATA_VOLUME_HOST) >/dev/null 2>&1 || docker volume create --name $(DATA_VOLUME_HOST) + @docker volume inspect $(DB_VOLUME_HOST) >/dev/null 2>&1 || docker volume create --name $(DB_VOLUME_HOST) self-signed-cert: # make a self-signed cert +secrets/postgres.env: + @echo "Generating postgres password in $@" + @echo "POSTGRES_PASSWORD=$(shell openssl rand -hex 32)" > $@ + secrets/jupyterhub.crt: @echo "Need an SSL certificate in secrets/jupyterhub.crt" @exit 1 @@ -36,7 +41,7 @@ else cert_files= endif -check-files: userlist $(cert_files) +check-files: userlist $(cert_files) secrets/oauth.env secrets/postgres.env pull: docker pull $(DOCKER_NOTEBOOK_IMAGE) diff --git a/docker-compose.yml b/docker-compose.yml index 8943af3..7c3e7d1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,10 +5,25 @@ version: "2" services: + hub-db: + image: postgres:9.5 + container_name: jupyterhub-db + restart: always + environment: + POSTGRES_DB: ${POSTGRES_DB} + PGDATA: ${DB_VOLUME_CONTAINER} + env_file: + - secrets/postgres.env + volumes: + - "db:${DB_VOLUME_CONTAINER}" + hub: + depends_on: + - hub-db build: context: . dockerfile: Dockerfile.jupyterhub + restart: always image: jupyterhub container_name: jupyterhub volumes: @@ -19,6 +34,8 @@ services: - "data:${DATA_VOLUME_CONTAINER}" ports: - "443:443" + links: + - hub-db environment: # All containers will join this network DOCKER_NETWORK_NAME: ${DOCKER_NETWORK_NAME} @@ -28,10 +45,12 @@ services: DOCKER_NOTEBOOK_DIR: ${DOCKER_NOTEBOOK_DIR} # 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} + # Postgres db info + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_HOST: hub-db + env_file: + - secrets/postgres.env + - secrets/oauth.env command: > jupyterhub -f /srv/jupyterhub/jupyterhub_config.py @@ -39,6 +58,9 @@ volumes: data: external: name: ${DATA_VOLUME_HOST} + db: + external: + name: ${DB_VOLUME_HOST} networks: default: diff --git a/jupyterhub_config.py b/jupyterhub_config.py index 84dbce5..dc925dd 100644 --- a/jupyterhub_config.py +++ b/jupyterhub_config.py @@ -57,10 +57,16 @@ 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') +c.JupyterHub.db_url = 'postgresql://postgres:{password}@{host}/{db}'.format( + host=os.environ['POSTGRES_HOST'], + password=os.environ['POSTGRES_PASSWORD'], + db=os.environ['POSTGRES_DB'], +) + # Whitlelist users and admins c.Authenticator.whitelist = whitelist = set() c.Authenticator.admin_users = admin = set()