Commit 9fa641b4 authored by Evgeny Lavrik's avatar Evgeny Lavrik
Browse files

Redo the container stack

Add cbmroot-notebook container which is ready to be used in jupyterhub docker spawner
Rework cbmroot-jupyterhub to be docker spawner notebook server and make use of cbmroot-notebook
parent c2ace523
......@@ -10,7 +10,8 @@ There are following containers in this package build upon each other in the foll
* fairsoft
* fairroot
* cbmroot
* jupyterlab
* jupyter notebook with cbmroot (cbmroot-notebook)
* jupyterhub with cbmroot-notebook enabled (cbmroot-jupyterhub)
To run any of these containers, you need to initialize them first, e.g. run the following commands:
```bash
......@@ -19,13 +20,9 @@ docker-compose up -d cbmroot
The pre-build container images will then be downloaded. The containers will run and keep any files you create inside it until you won't remove them from your system with:
```bash
docker-compose down --rmi=all -v
docker-compose down -v
```
If you want more persistency, keep your files in the `code` directory and they will never be removed by docker.
The `code` directory is mounted in all containers in the `/opt/code` directory and also in `/home/cbmdock/code` in cbmroot-dev container.
The software is installed in `/opt` within containers, e.g.:
* `/opt/fairsoft`
......@@ -42,12 +39,12 @@ docker-compose exec cbmroot bash
Run the following command in your terminal
```bash
docker-compose up -d jupyterhub
docker-compose up cbmroot-jupyterhub
```
You can navigate to http://localhost:8000 in your browser.
Authorize yourself with user `user` and password `test`.
Authorize yourself with user `dummy` and password `dummy`.
### CbmRoot development environment
......
FROM jupyterhub/jupyterhub:1.2.1
COPY requirements.txt /tmp/requirements.txt
RUN python3 -m pip install --no-cache -r /tmp/requirements.txt
c = get_config()
import os
# dummy for testing. Don't use this in production!
c.JupyterHub.authenticator_class = 'dummyauthenticator.DummyAuthenticator'
c.DummyAuthenticator.password = "dummy"
#************** Port and IP settings
# we need the hub to listen on all ips when it is in a container
c.JupyterHub.hub_ip = '0.0.0.0'
#*************Spawner settings
# pick a docker image. This should have the same version of jupyterhub
# in it as our Hub.
# launch with docker
c.JupyterHub.spawner_class = 'dockerspawner.DockerSpawner'
# The admin must pull these before they can be used.
c.DockerSpawner.image_whitelist = {
'CbmRoot':'elavrik/cbmroot-notebook:latest',
'Scipy':'jupyter/scipy-notebook:latest',
'Data science':'jupyter/datascience-notebook:latest'
}
# tell the user containers to connect to our docker network
c.DockerSpawner.network_name = 'jupyterhub'
# delete containers when it stops
c.DockerSpawner.remove = True
\ No newline at end of file
dockerspawner
jupyterhub-dummyauthenticator
oauthenticator
ARG BASE_IMAGE=elavrik/cbmroot:latest
FROM $BASE_IMAGE
LABEL maintainer="Evgeny Lavrik <e.lavrik@gsi.de>"
ARG NB_USER="cbmuser"
ARG NB_UID="1000"
ARG NB_GID="100"
# Fix DL4006
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
USER root
# ---- Miniforge installer ----
# Default values can be overridden at build time
# (ARGS are in lower case to distinguish them from ENV)
# Check https://github.com/conda-forge/miniforge/releases
# Conda version
ARG conda_version="4.9.0"
# Miniforge installer patch version
ARG miniforge_patch_number="4"
# Miniforge installer architecture
ARG miniforge_arch="x86_64"
# Python implementation to use
# can be either Miniforge3 to use Python or Miniforge-pypy3 to use PyPy
ARG miniforge_python="Miniforge3"
# Miniforge archive to install
ARG miniforge_version="${conda_version}-${miniforge_patch_number}"
# Miniforge installer
ARG miniforge_installer="${miniforge_python}-${miniforge_version}-Linux-${miniforge_arch}.sh"
# Miniforge checksum
ARG miniforge_checksum="dae28a05f0fcfed0b47c66468e8434ab42cb1ff90de96540a506949cdecd2b5a"
# Install all OS dependencies for notebook server that starts but lacks all
# features (e.g., download as all possible file formats)
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update \
&& apt-get install -yq --no-install-recommends \
wget \
bzip2 \
ca-certificates \
sudo \
locales \
fonts-liberation \
run-one \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && \
locale-gen
# Configure environment
ENV CONDA_DIR=/opt/conda \
SHELL=/bin/bash \
NB_USER=$NB_USER \
NB_UID=$NB_UID \
NB_GID=$NB_GID \
LC_ALL=en_US.UTF-8 \
LANG=en_US.UTF-8 \
LANGUAGE=en_US.UTF-8
ENV PATH=$CONDA_DIR/bin:$PATH \
HOME=/home/$NB_USER \
CONDA_VERSION="${conda_version}"
# Copy a script that we will use to correct permissions after running certain commands
COPY fix-permissions /usr/local/bin/fix-permissions
RUN chmod a+rx /usr/local/bin/fix-permissions
# Enable prompt color in the skeleton .bashrc before creating the default NB_USER
# hadolint ignore=SC2016
RUN sed -i 's/^#force_color_prompt=yes/force_color_prompt=yes/' /etc/skel/.bashrc && \
# Add call to conda init script see https://stackoverflow.com/a/58081608/4413446
echo 'eval "$(command conda shell.bash hook 2> /dev/null)"' >> /etc/skel/.bashrc
# Create NB_USER with name cbmuser user with UID=1000 and in the 'users' group
# and make sure these dirs are writable by the `users` group.
RUN echo "auth requisite pam_deny.so" >> /etc/pam.d/su && \
sed -i.bak -e 's/^%admin/#%admin/' /etc/sudoers && \
sed -i.bak -e 's/^%sudo/#%sudo/' /etc/sudoers && \
useradd -m -s /bin/bash -N -u $NB_UID $NB_USER && \
mkdir -p $CONDA_DIR && \
chown $NB_USER:$NB_GID $CONDA_DIR && \
chmod g+w /etc/passwd && \
fix-permissions $HOME && \
fix-permissions $CONDA_DIR
USER $NB_UID
ARG PYTHON_VERSION=3.6
# Setup work directory for backward-compatibility
USER root
RUN mkdir "/home/${NB_USER}/cbmroot" && \
fix-permissions "/home/$NB_USER"
# Install conda as cbmuser and check the sha256 sum provided on the download site
WORKDIR /tmp
RUN wget --quiet "https://github.com/conda-forge/miniforge/releases/download/${miniforge_version}/${miniforge_installer}" && \
echo "${miniforge_checksum} *${miniforge_installer}" | sha256sum --check && \
/bin/bash "${miniforge_installer}" -f -b -p $CONDA_DIR && \
rm "${miniforge_installer}" && \
# Conda configuration see https://conda.io/projects/conda/en/latest/configuration.html
echo "conda ${CONDA_VERSION}" >> $CONDA_DIR/conda-meta/pinned && \
conda config --system --set auto_update_conda false && \
conda config --system --set show_channel_urls true && \
if [ ! $PYTHON_VERSION = 'default' ]; then conda install --yes python=$PYTHON_VERSION; fi && \
conda list python | grep '^python ' | tr -s ' ' | cut -d '.' -f 1,2 | sed 's/$/.*/' >> $CONDA_DIR/conda-meta/pinned && \
conda install --quiet --yes "conda=${CONDA_VERSION}" && \
conda install --quiet --yes pip && \
conda update --all --quiet --yes && \
conda clean --all -f -y && \
rm -rf /home/$NB_USER/.cache/yarn && \
fix-permissions $CONDA_DIR && \
fix-permissions /home/$NB_USER
# Install Tini
RUN conda install --quiet --yes 'tini=0.18.0' && \
conda list tini | grep tini | tr -s ' ' | cut -d ' ' -f 1,2 >> $CONDA_DIR/conda-meta/pinned && \
conda clean --all -f -y && \
fix-permissions $CONDA_DIR && \
fix-permissions /home/$NB_USER
# Install Jupyter Notebook, Lab, and Hub
# Generate a notebook server config
# Cleanup temporary files
# Correct permissions
# Do all this in a single RUN command to avoid duplicating all of the
# files across image layers when the permissions change
RUN conda install --quiet --yes \
'notebook=6.1.4' \
'jupyterhub=1.2.1' \
'jupyterlab=2.2.9' \
'ipywidgets=7.6.*' \
'ipympl=0.6.*' \
'widgetsnbextension=3.5.*' && \
conda clean --all -f -y && \
npm cache clean --force && \
jupyter notebook --generate-config && \
rm -rf $CONDA_DIR/share/jupyter/lab/staging && \
rm -rf /home/$NB_USER/.cache/yarn && \
fix-permissions $CONDA_DIR && \
fix-permissions /home/$NB_USER
# Install python2 and pip2 since PyROOT relies on it
RUN apt-get update && apt-get -y install python python-dev python-pip python3 python3-dev python3-pip nodejs curl python3-pycurl && \
python2 -m pip install -U pip && python3 -m pip install -U pip && \
python2 -m pip install metakernel && python2 -m pip install ipykernel && python2 -m ipykernel install && \
python3 -m pip install metakernel && python3 -m pip install ipykernel && python3 -m ipykernel install && \
python3 -m pip install --upgrade pip setuptools notebook jupyterhub
#Add Root-c++ kernel to Jypter notebook
RUN jupyter kernelspec install /opt/fairsoft/share/root/etc/notebook/kernels/root &&\
apt-get clean && rm -rf /var/lib/apt/lists/* &&\
fix-permissions $CONDA_DIR && \
fix-permissions /home/$NB_USER && \
fix-permissions /opt/cbmroot
# Configure container startup
ENTRYPOINT ["tini", "-g", "--"]
CMD ["start-notebook.sh"]
# Copy local files as late as possible to avoid cache busting
COPY start.sh start-notebook.sh start-singleuser.sh /usr/local/bin/
COPY jupyter_notebook_config.py /etc/jupyter/
#COPY ./notebook /home/${NB_USER}/cbmroot
# Fix permissions on /etc/jupyter as root
USER root
RUN fix-permissions /etc/jupyter/
WORKDIR /home/${NB_USER}/cbmroot
# Switch back to cbmuser to avoid accidental container runs as root
USER $NB_UID
EXPOSE 8888
#!/bin/bash
# set permissions on a directory
# after any installation, if a directory needs to be (human) user-writable,
# run this script on it.
# It will make everything in the directory owned by the group $NB_GID
# and writable by that group.
# Deployments that want to set a specific user id can preserve permissions
# by adding the `--group-add users` line to `docker run`.
# uses find to avoid touching files that already have the right permissions,
# which would cause massive image explosion
# right permissions are:
# group=$NB_GID
# AND permissions include group rwX (directory-execute)
# AND directories have setuid,setgid bits set
set -e
for d in "$@"; do
find "$d" \
! \( \
-group $NB_GID \
-a -perm -g+rwX \
\) \
-exec chgrp $NB_GID {} \; \
-exec chmod g+rwX {} \;
# setuid, setgid *on directories only*
find "$d" \
\( \
-type d \
-a ! -perm -6000 \
\) \
-exec chmod +6000 {} \;
done
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from jupyter_core.paths import jupyter_data_dir
import subprocess
import os
import errno
import stat
c = get_config() # noqa: F821
c.NotebookApp.ip = '0.0.0.0'
c.NotebookApp.port = 8888
c.NotebookApp.open_browser = False
# https://github.com/jupyter/notebook/issues/3130
c.FileContentsManager.delete_to_trash = False
# Generate a self-signed certificate
if 'GEN_CERT' in os.environ:
dir_name = jupyter_data_dir()
pem_file = os.path.join(dir_name, 'notebook.pem')
try:
os.makedirs(dir_name)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(dir_name):
pass
else:
raise
# Generate an openssl.cnf file to set the distinguished name
cnf_file = os.path.join(os.getenv('CONDA_DIR', '/usr/lib'), 'ssl', 'openssl.cnf')
if not os.path.isfile(cnf_file):
with open(cnf_file, 'w') as fh:
fh.write('''\
[req]
distinguished_name = req_distinguished_name
[req_distinguished_name]
''')
# Generate a certificate if one doesn't exist on disk
subprocess.check_call(['openssl', 'req', '-new',
'-newkey', 'rsa:2048',
'-days', '365',
'-nodes', '-x509',
'-subj', '/C=XX/ST=XX/L=XX/O=generated/CN=generated',
'-keyout', pem_file,
'-out', pem_file])
# Restrict access to the file
os.chmod(pem_file, stat.S_IRUSR | stat.S_IWUSR)
c.NotebookApp.certfile = pem_file
# Change default umask for all subprocesses of the notebook server if set in
# the environment
if 'NB_UMASK' in os.environ:
os.umask(int(os.environ['NB_UMASK'], 8))
#!/bin/bash
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
set -e
wrapper=""
if [[ "${RESTARTABLE}" == "yes" ]]; then
wrapper="run-one-constantly"
fi
if [[ ! -z "${JUPYTERHUB_API_TOKEN}" ]]; then
# launched by JupyterHub, use single-user entrypoint
exec /usr/local/bin/start-singleuser.sh "$@"
elif [[ ! -z "${JUPYTER_ENABLE_LAB}" ]]; then
. /usr/local/bin/start.sh $wrapper jupyter lab "$@"
else
. /usr/local/bin/start.sh $wrapper jupyter notebook "$@"
fi
#!/bin/bash
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
set -e
# set default ip to 0.0.0.0
if [[ "$NOTEBOOK_ARGS $@" != *"--ip="* ]]; then
NOTEBOOK_ARGS="--ip=0.0.0.0 $NOTEBOOK_ARGS"
fi
# handle some deprecated environment variables
# from DockerSpawner < 0.8.
# These won't be passed from DockerSpawner 0.9,
# so avoid specifying --arg=empty-string
if [ ! -z "$NOTEBOOK_DIR" ]; then
NOTEBOOK_ARGS="--notebook-dir='$NOTEBOOK_DIR' $NOTEBOOK_ARGS"
fi
if [ ! -z "$JPY_PORT" ]; then
NOTEBOOK_ARGS="--port=$JPY_PORT $NOTEBOOK_ARGS"
fi
if [ ! -z "$JPY_USER" ]; then
NOTEBOOK_ARGS="--user=$JPY_USER $NOTEBOOK_ARGS"
fi
if [ ! -z "$JPY_COOKIE_NAME" ]; then
NOTEBOOK_ARGS="--cookie-name=$JPY_COOKIE_NAME $NOTEBOOK_ARGS"
fi
if [ ! -z "$JPY_BASE_URL" ]; then
NOTEBOOK_ARGS="--base-url=$JPY_BASE_URL $NOTEBOOK_ARGS"
fi
if [ ! -z "$JPY_HUB_PREFIX" ]; then
NOTEBOOK_ARGS="--hub-prefix=$JPY_HUB_PREFIX $NOTEBOOK_ARGS"
fi
if [ ! -z "$JPY_HUB_API_URL" ]; then
NOTEBOOK_ARGS="--hub-api-url=$JPY_HUB_API_URL $NOTEBOOK_ARGS"
fi
NOTEBOOK_BIN="jupyterhub-singleuser"
. /usr/local/bin/start.sh $NOTEBOOK_BIN $NOTEBOOK_ARGS "$@"
#!/bin/bash
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
set -e
# Exec the specified command or fall back on bash
if [ $# -eq 0 ]; then
cmd=( "bash" )
else
cmd=( "$@" )
fi
run-hooks () {
# Source scripts or run executable files in a directory
if [[ ! -d "$1" ]] ; then
return
fi
echo "$0: running hooks in $1"
for f in "$1/"*; do
case "$f" in
*.sh)
echo "$0: running $f"
source "$f"
;;
*)
if [[ -x "$f" ]] ; then
echo "$0: running $f"
"$f"
else
echo "$0: ignoring $f"
fi
;;
esac
done
echo "$0: done running hooks in $1"
}
run-hooks /usr/local/bin/start-notebook.d
# Handle special flags if we're root
if [ $(id -u) == 0 ] ; then
# Only attempt to change the jovyan username if it exists
if id jovyan &> /dev/null ; then
echo "Set username to: $NB_USER"
usermod -d /home/$NB_USER -l $NB_USER jovyan
fi
# handle home and working directory if the username changed
if [[ "$NB_USER" != "jovyan" ]]; then
# changing username, make sure homedir exists
# (it could be mounted, and we shouldn't create it if it already exists)
if [[ ! -e "/home/$NB_USER" ]]; then
echo "Relocating home dir to /home/$NB_USER"
mv /home/jovyan "/home/$NB_USER" || ln -s /home/jovyan "/home/$NB_USER"
fi
# if workdir is in /home/jovyan, cd to /home/$NB_USER
if [[ "$PWD/" == "/home/jovyan/"* ]]; then
newcwd="/home/$NB_USER/${PWD:13}"
echo "Setting CWD to $newcwd"
cd "$newcwd"
fi
fi
# Handle case where provisioned storage does not have the correct permissions by default
# Ex: default NFS/EFS (no auto-uid/gid)
if [[ "$CHOWN_HOME" == "1" || "$CHOWN_HOME" == 'yes' ]]; then
echo "Changing ownership of /home/$NB_USER to $NB_UID:$NB_GID with options '${CHOWN_HOME_OPTS}'"
chown $CHOWN_HOME_OPTS $NB_UID:$NB_GID /home/$NB_USER
fi
if [ ! -z "$CHOWN_EXTRA" ]; then
for extra_dir in $(echo $CHOWN_EXTRA | tr ',' ' '); do
echo "Changing ownership of ${extra_dir} to $NB_UID:$NB_GID with options '${CHOWN_EXTRA_OPTS}'"
chown $CHOWN_EXTRA_OPTS $NB_UID:$NB_GID $extra_dir
done
fi
# Change UID:GID of NB_USER to NB_UID:NB_GID if it does not match
if [ "$NB_UID" != $(id -u $NB_USER) ] || [ "$NB_GID" != $(id -g $NB_USER) ]; then
echo "Set user $NB_USER UID:GID to: $NB_UID:$NB_GID"
if [ "$NB_GID" != $(id -g $NB_USER) ]; then
groupadd -f -g $NB_GID -o ${NB_GROUP:-${NB_USER}}
fi
userdel $NB_USER
useradd --home /home/$NB_USER -u $NB_UID -g $NB_GID -G 100 -l $NB_USER
fi
# Enable sudo if requested
if [[ "$GRANT_SUDO" == "1" || "$GRANT_SUDO" == 'yes' ]]; then
echo "Granting $NB_USER sudo access and appending $CONDA_DIR/bin to sudo PATH"
echo "$NB_USER ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/notebook
fi
# Add $CONDA_DIR/bin to sudo secure_path
sed -r "s#Defaults\s+secure_path\s*=\s*\"?([^\"]+)\"?#Defaults secure_path=\"\1:$CONDA_DIR/bin\"#" /etc/sudoers | grep secure_path > /etc/sudoers.d/path
# Exec the command as NB_USER with the PATH and the rest of
# the environment preserved
run-hooks /usr/local/bin/before-notebook.d
echo "Executing the command: ${cmd[@]}"
exec sudo -E -H -u $NB_USER PATH=$PATH XDG_CACHE_HOME=/home/$NB_USER/.cache PYTHONPATH=${PYTHONPATH:-} "${cmd[@]}"
else
if [[ "$NB_UID" == "$(id -u jovyan 2>/dev/null)" && "$NB_GID" == "$(id -g jovyan 2>/dev/null)" ]]; then
# User is not attempting to override user/group via environment
# variables, but they could still have overridden the uid/gid that
# container runs as. Check that the user has an entry in the passwd
# file and if not add an entry.
STATUS=0 && whoami &> /dev/null || STATUS=$? && true
if [[ "$STATUS" != "0" ]]; then
if [[ -w /etc/passwd ]]; then
echo "Adding passwd file entry for $(id -u)"
cat /etc/passwd | sed -e "s/^jovyan:/nayvoj:/" > /tmp/passwd
echo "jovyan:x:$(id -u):$(id -g):,,,:/home/jovyan:/bin/bash" >> /tmp/passwd
cat /tmp/passwd > /etc/passwd
rm /tmp/passwd
else
echo 'Container must be run with group "root" to update passwd file'
fi
fi
# Warn if the user isn't going to be able to write files to $HOME.
if [[ ! -w /home/jovyan ]]; then
echo 'Container must be run with group "users" to update files'
fi
else
# Warn if looks like user want to override uid/gid but hasn't
# run the container as root.
if [[ ! -z "$NB_UID" && "$NB_UID" != "$(id -u)" ]]; then
echo 'Container must be run as root to set $NB_UID'
fi
if [[ ! -z "$NB_GID" && "$NB_GID" != "$(id -g)" ]]; then
echo 'Container must be run as root to set $NB_GID'
fi
fi
# Warn if looks like user want to run in sudo mode but hasn't run
# the container as root.
if [[ "$GRANT_SUDO" == "1" || "$GRANT_SUDO" == 'yes' ]]; then
echo 'Container must be run as root to grant sudo permissions'
fi
# Execute the command
run-hooks /usr/local/bin/before-notebook.d
echo "Executing the command: ${cmd[@]}"
exec "${cmd[@]}"
fi
# Build all libraries which come from external sources like other subversion or git repositories
# Exclude dowload of external packages. The external packages are not needed
# to run the code format checker and slows down the execution.
# Without the externals it isn't possible to build CbmRoot so don't switch
# it off execept for the checker.
Option(DOWNLOAD_EXTERNALS "Download the code from the external repositories." ON)
if(DOWNLOAD_EXTERNALS)
download_project_if_needed(PROJECT cppzmq
GIT_REPOSITORY "https://github.com/zeromq/cppzmq/"
GIT_TAG "05a0256d0eeea8063690fde6a156e14b70ed2280"
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/cppzmq
TEST_FILE zmq.hpp
)
download_project_if_needed(PROJECT flib_dpb_20
GIT_REPOSITORY "https://git.cbm.gsi.de/f.uhlig/flib_dpb_20.git"
GIT_TAG "9ce7c7c0ccc7c9ea2f8c396c35d7fe39ac9aafb6"
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/flib_dpb_20
TEST_FILE CMakeLists.txt
)
Set(IPC_INCLUDE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ipc/ipc/lib/fles_ipc PARENT_SCOPE)
Set(IPCLOG_INCLUDE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ipc/ipc/lib/logging PARENT_SCOPE)
Add_Subdirectory(ipc)
Add_Subdirectory(ipc_legacy)
Add_Subdirectory(flib_dpb)
Add_Subdirectory(flib_dpb_20)
Add_Subdirectory(spadic)
Include(InstallVC.cmake)
Include(InstallKFParticle.cmake)
Include(InstallDataTree.cmake)
Include(InstallDataTreeQA.cmake)
Include(InstallNicaFemto.cmake)
Include(InstallAnalysisTree.cmake)
# Include(InstallAnalysisTreeQA.cmake)
Include(InstallParameter.cmake)
Include(InstallInput.cmake)
Include(InstallGeometry.cmake)
else()
# Define targets which are needed by CbmRoot but are not available
# whithout the external packages
add_library(DATATREE SHARED IMPORTED GLOBAL)
add_library(ANALYSISTREE SHARED IMPORTED GLOBAL)
add_library(NICAFEMTO SHARED IMPORTED GLOBAL)
add_library(KFPARTICLE SHARED IMPORTED GLOBAL)
endif()
......@@ -3,17 +3,13 @@ ARG BASE_IMAGE=elavrik/fairroot:latest
FROM $BASE_IMAGE
ARG GIT_REPO=https://git.cbm.gsi.de/computing/cbmroot.git