From 875acc9d949cacfd12bdf0c1b8bb885394c5b37a Mon Sep 17 00:00:00 2001 From: Walter Oggioni Date: Sat, 25 May 2024 19:36:19 +0800 Subject: [PATCH] updated Ganymede and python images --- jupyter-ganymede/Dockerfile | 13 ++---- jupyter-python/Dockerfile | 58 +++++++++++++++---------- jupyter-python/docker_healthcheck.py | 39 +++++++++++++++++ jupyter-python/jupyter_server_config.py | 58 +++++++++++++++++++++++++ jupyter-python/start-notebook.py | 44 +++++++++++++++++++ jupyter-python/start-singleuser.py | 26 +++++++++++ 6 files changed, 205 insertions(+), 33 deletions(-) create mode 100755 jupyter-python/docker_healthcheck.py create mode 100644 jupyter-python/jupyter_server_config.py create mode 100755 jupyter-python/start-notebook.py create mode 100755 jupyter-python/start-singleuser.py diff --git a/jupyter-ganymede/Dockerfile b/jupyter-ganymede/Dockerfile index cf97e7d..fd6c59d 100644 --- a/jupyter-ganymede/Dockerfile +++ b/jupyter-ganymede/Dockerfile @@ -7,23 +7,16 @@ RUN adduser jovyan -u 1000 --system -s /bin/sh -h /home/jovyan USER jovyan WORKDIR /home/jovyan RUN python -m venv venv -RUN venv/bin/pip install numpy -RUN venv/bin/pip install matplotlib RUN venv/bin/pip install jupyter-core -RUN venv/bin/pip install jupyterhub -RUN venv/bin/pip install jupyterlab -RUN venv/bin/pip install notebook -RUN venv/bin/pip install ipywidgets -RUN venv/bin/pip install jupyterlab-lsp -RUN venv/bin/pip install python-lsp-server[yapf,rope,pyflakes] -RUN venv/bin/pip install pylsp-mypy +RUN venv/bin/pip install notebook==5.7.16 ENV PATH="/home/jovyan/venv/bin/:$PATH" RUN java -jar /ganymede-2.1.2.20230910.jar -i --copy-jar=true +RUN venv/bin/pip install jupyterhub FROM alpine:3.19 RUN --mount=type=cache,target=/var/cache/apk/,sharing=locked \ apk update &&\ - apk add python3 openjdk21-jre texlive texlive-xetex texlive-full + apk add python3 openjdk21-jre RUN adduser jovyan -u 1000 --system -s /bin/sh -h /home/jovyan USER jovyan COPY --from=build /home/jovyan /home/jovyan diff --git a/jupyter-python/Dockerfile b/jupyter-python/Dockerfile index 9d1ea45..b9c123d 100644 --- a/jupyter-python/Dockerfile +++ b/jupyter-python/Dockerfile @@ -1,31 +1,43 @@ FROM alpine:3.19 AS build RUN --mount=type=cache,target=/var/cache/apk/,sharing=locked \ apk update &&\ - apk add python3 python3-dev pipx gcc musl-dev linux-headers -RUN adduser luser --system -s /bin/sh -h /var/lib/jupyter -USER luser -WORKDIR /var/lib/jupyter -RUN pipx install jupyter-core -RUN pipx inject jupyter-core jupyterlab -RUN pipx inject jupyter-core ipywidgets -RUN pipx inject jupyter-core jupyterlab-lsp -RUN pipx inject jupyter-core python-lsp-server[yapf,rope,pyflakes] -RUN pipx inject jupyter-core pylsp-mypy -RUN pipx inject jupyter-core matplotlib -RUN pipx inject jupyter-core numpy - + apk add python3 python3-dev pipx gcc g++ musl-dev linux-headers libffi-dev +RUN adduser jovyan -u 1000 --system -s /bin/sh -h /home/jovyan +USER jovyan +WORKDIR /home/jovyan +RUN python -m venv venv +RUN venv/bin/pip install numpy +RUN venv/bin/pip install matplotlib +RUN venv/bin/pip install jupyter-core +RUN venv/bin/pip install jupyterhub +RUN venv/bin/pip install jupyterlab +RUN venv/bin/pip install notebook +RUN venv/bin/pip install ipywidgets +RUN venv/bin/pip install jupyterlab-lsp +RUN venv/bin/pip install python-lsp-server[yapf,rope,pyflakes] +RUN venv/bin/pip install pylsp-mypy +RUN venv/bin/pip install solders +RUN venv/bin/pip install solana +ENV PATH="/home/jovyan/venv/bin/:$PATH" FROM alpine:3.19 RUN --mount=type=cache,target=/var/cache/apk/,sharing=locked \ apk update &&\ - apk add python3 texlive texlive-xetex texlive-full - -RUN adduser luser --system -s /bin/sh -h /var/lib/jupyter -USER luser -WORKDIR /var/lib/jupyter -COPY --from=build /var/lib/jupyter /var/lib/jupyter -ENV PATH="/var/lib/jupyter/.local/bin/:$PATH" -ENTRYPOINT ["jupyter", "lab", "--no-browser", "--ip=0.0.0.0"] -EXPOSE 8888/tcp - + apk add python3 +RUN adduser jovyan -u 1000 --system -s /bin/sh -h /home/jovyan +USER jovyan +COPY --from=build /home/jovyan /home/jovyan +COPY jupyter_server_config.py docker_healthcheck.py /etc/jupyter/ +COPY start-notebook.py start-singleuser.py /usr/local/bin/ +RUN mkdir /home/jovyan/work +WORKDIR /home/jovyan/work +ENV JUPYTER_PORT=8888 +EXPOSE $JUPYTER_PORT +ENV PATH="/home/jovyan/venv/bin/:$PATH" +CMD ["start-notebook.py"] +# HEALTHCHECK documentation: https://docs.docker.com/engine/reference/builder/#healthcheck +# This healtcheck works well for `lab`, `notebook`, `nbclassic`, `server`, and `retro` jupyter commands +# https://github.com/jupyter/docker-stacks/issues/915#issuecomment-1068528799 +HEALTHCHECK --interval=3s --timeout=1s --start-period=3s --retries=3 \ + CMD /etc/jupyter/docker_healthcheck.py || exit 1 diff --git a/jupyter-python/docker_healthcheck.py b/jupyter-python/docker_healthcheck.py new file mode 100755 index 0000000..b0db1a8 --- /dev/null +++ b/jupyter-python/docker_healthcheck.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# Copyright (c) Jupyter Development Team. +# Distributed under the terms of the Modified BSD License. +import json +import os +import subprocess +from pathlib import Path + +import requests + +# Several operations below deliberately don't check for possible errors +# As this is a health check, it should succeed or raise an exception on error + +# Docker runs health checks using an exec +# It uses the default user configured when running the image: root for the case of a custom NB_USER or jovyan for the case of the default image user. +# We manually change HOME to make `jupyter --runtime-dir` report a correct path +# More information: +result = subprocess.run( + ["jupyter", "--runtime-dir"], + check=True, + capture_output=True, + text=True, + env=dict(os.environ) | {"HOME": "/home/" + os.environ["NB_USER"]}, +) +runtime_dir = Path(result.stdout.rstrip()) + +json_file = next(runtime_dir.glob("*server-*.json")) + +url = json.loads(json_file.read_bytes())["url"] +url = url + "api" + +proxies = { + "http": "", + "https": "", +} + +r = requests.get(url, proxies=proxies, verify=False) # request without SSL verification +r.raise_for_status() +print(r.content) diff --git a/jupyter-python/jupyter_server_config.py b/jupyter-python/jupyter_server_config.py new file mode 100644 index 0000000..c0cca3a --- /dev/null +++ b/jupyter-python/jupyter_server_config.py @@ -0,0 +1,58 @@ +# Copyright (c) Jupyter Development Team. +# Distributed under the terms of the Modified BSD License. +# mypy: ignore-errors +import os +import stat +import subprocess +from pathlib import Path + +from jupyter_core.paths import jupyter_data_dir + +c = get_config() # noqa: F821 +c.ServerApp.ip = "0.0.0.0" +c.ServerApp.open_browser = False + +# to output both image/svg+xml and application/pdf plot formats in the notebook file +c.InlineBackend.figure_formats = {"png", "jpeg", "svg", "pdf"} + +# https://github.com/jupyter/notebook/issues/3130 +c.FileContentsManager.delete_to_trash = False + +# Generate a self-signed certificate +OPENSSL_CONFIG = """\ +[req] +distinguished_name = req_distinguished_name +[req_distinguished_name] +""" +if "GEN_CERT" in os.environ: + dir_name = Path(jupyter_data_dir()) + dir_name.mkdir(parents=True, exist_ok=True) + pem_file = dir_name / "notebook.pem" + + # Generate an openssl.cnf file to set the distinguished name + cnf_file = Path(os.getenv("CONDA_DIR", "/usr/lib")) / "ssl/openssl.cnf" + if not cnf_file.exists(): + cnf_file.write_text(OPENSSL_CONFIG) + + # Generate a certificate if one doesn't exist on a 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", + f"-keyout={pem_file}", + f"-out={pem_file}", + ] + ) + # Restrict access to the file + pem_file.chmod(stat.S_IRUSR | stat.S_IWUSR) + c.ServerApp.certfile = str(pem_file) + +# Change default umask for all subprocesses of the Server if set in the environment +if "NB_UMASK" in os.environ: + os.umask(int(os.environ["NB_UMASK"], 8)) diff --git a/jupyter-python/start-notebook.py b/jupyter-python/start-notebook.py new file mode 100755 index 0000000..973da5a --- /dev/null +++ b/jupyter-python/start-notebook.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# Copyright (c) Jupyter Development Team. +# Distributed under the terms of the Modified BSD License. +import os +import shlex +import sys + +# If we are in a JupyterHub, we pass on to `start-singleuser.py` instead so it does the right thing +if "JUPYTERHUB_API_TOKEN" in os.environ: + print( + "WARNING: using start-singleuser.py instead of start-notebook.py to start a server associated with JupyterHub." + ) + command = ["/usr/local/bin/start-singleuser.py"] + sys.argv[1:] + os.execvp(command[0], command) + + +# Entrypoint is start.sh +command = [] + +# If we want to survive restarts, launch the command using `run-one-constantly` +if os.environ.get("RESTARTABLE") == "yes": + command.append("run-one-constantly") + +# We always launch a jupyter subcommand from this script +command.append("jupyter") + +# Launch the configured subcommand. +# Note that this should be a single string, so we don't split it. +# We default to `lab`. +jupyter_command = os.environ.get("DOCKER_STACKS_JUPYTER_CMD", "lab") +command.append(jupyter_command) + +# Append any optional NOTEBOOK_ARGS we were passed in. +# This is supposed to be multiple args passed on to the notebook command, +# so we split it correctly with shlex +if "NOTEBOOK_ARGS" in os.environ: + command += shlex.split(os.environ["NOTEBOOK_ARGS"]) + +# Pass through any other args we were passed on the command line +command += sys.argv[1:] + +# Execute the command! +print("Executing: " + " ".join(command)) +os.execvp(command[0], command) diff --git a/jupyter-python/start-singleuser.py b/jupyter-python/start-singleuser.py new file mode 100755 index 0000000..c80339f --- /dev/null +++ b/jupyter-python/start-singleuser.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# Copyright (c) Jupyter Development Team. +# Distributed under the terms of the Modified BSD License. +import os +import shlex +import sys + +# Entrypoint is start.sh +command = ["jupyterhub-singleuser"] + +# set default ip to 0.0.0.0 +if "--ip=" not in os.environ.get("NOTEBOOK_ARGS", ""): + command.append("--ip=0.0.0.0") + +# Append any optional NOTEBOOK_ARGS we were passed in. +# This is supposed to be multiple args passed on to the notebook command, +# so we split it correctly with shlex +if "NOTEBOOK_ARGS" in os.environ: + command += shlex.split(os.environ["NOTEBOOK_ARGS"]) + +# Pass any other args we have been passed through +command += sys.argv[1:] + +# Execute the command! +print("Executing: " + " ".join(command)) +os.execvp(command[0], command)