Skip to content

Commit

Permalink
Renaming & adding python kernel
Browse files Browse the repository at this point in the history
  • Loading branch information
david.fischak@eodc.eu committed Feb 27, 2025
1 parent 454cb6e commit cc6a138
Show file tree
Hide file tree
Showing 24 changed files with 3,366 additions and 0 deletions.
37 changes: 37 additions & 0 deletions kernel-python/container/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Ubuntu 18.04.1 LTS Bionic
ARG BASE_CONTAINER=quay.io/jupyter/scipy-notebook:2025-02-24
FROM $BASE_CONTAINER

ENV PATH=$PATH:$CONDA_DIR/bin

# Add debugger support
RUN pip install --upgrade ipykernel

RUN conda install --quiet --yes \
cffi \
future \
pycryptodomex && \
conda clean --all && \
fix-permissions $CONDA_DIR && \
fix-permissions /home/$NB_USER

ADD jupyter_enterprise_gateway_kernel_image_files*.tar.gz /usr/local/bin/

USER root

RUN apt-get update && apt-get install -yq --no-install-recommends \
libkrb5-dev \
&& rm -rf /var/lib/apt/lists/*

RUN chown jovyan:users /usr/local/bin/bootstrap-kernel.sh && \
chmod 0755 /usr/local/bin/bootstrap-kernel.sh && \
chown -R jovyan:users /usr/local/bin/kernel-launchers

USER jovyan

ENV KERNEL_LANGUAGE python

# Disble healthcheck inherited from notebook image
HEALTHCHECK NONE

CMD /usr/local/bin/bootstrap-kernel.sh
126 changes: 126 additions & 0 deletions kernel-python/container/bootstrap-kernel.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#!/bin/bash

PORT_RANGE=${PORT_RANGE:-${EG_PORT_RANGE:-0..0}}
RESPONSE_ADDRESS=${RESPONSE_ADDRESS:-${EG_RESPONSE_ADDRESS}}
PUBLIC_KEY=${PUBLIC_KEY:-${EG_PUBLIC_KEY}}
KERNEL_LAUNCHERS_DIR=${KERNEL_LAUNCHERS_DIR:-/usr/local/bin/kernel-launchers}
KERNEL_SPARK_CONTEXT_INIT_MODE=${KERNEL_SPARK_CONTEXT_INIT_MODE:-none}
KERNEL_CLASS_NAME=${KERNEL_CLASS_NAME}

echo $0 env: `env`

launch_python_kernel() {
# Launch the python kernel launcher - which embeds the IPython kernel and listens for interrupts
# and shutdown requests from Enterprise Gateway.

export JPY_PARENT_PID=$$ # Force reset of parent pid since we're detached

if [ -z "${KERNEL_CLASS_NAME}" ]
then
kernel_class_option=""
else
kernel_class_option="--kernel-class-name ${KERNEL_CLASS_NAME}"
fi

set -x
python ${KERNEL_LAUNCHERS_DIR}/python/scripts/launch_ipykernel.py --kernel-id ${KERNEL_ID} \
--port-range ${PORT_RANGE} --response-address ${RESPONSE_ADDRESS} --public-key ${PUBLIC_KEY} \
--spark-context-initialization-mode ${KERNEL_SPARK_CONTEXT_INIT_MODE} ${kernel_class_option}
{ set +x; } 2>/dev/null
}

launch_R_kernel() {
# Launch the R kernel launcher - which embeds the IRkernel kernel and listens for interrupts
# and shutdown requests from Enterprise Gateway.

set -x
Rscript ${KERNEL_LAUNCHERS_DIR}/R/scripts/launch_IRkernel.R --kernel-id ${KERNEL_ID} --port-range ${PORT_RANGE} \
--response-address ${RESPONSE_ADDRESS} --public-key ${PUBLIC_KEY} \
--spark-context-initialization-mode ${KERNEL_SPARK_CONTEXT_INIT_MODE}
{ set +x; } 2>/dev/null
}

launch_julia_kernel() {
# Launch the Julia kernel launcher - which embeds the IJulia kernel and listens for interrupts
# and shutdown requests from Enterprise Gateway.

set -x
export JULIA_NUM_THREADS=$(( ($(nproc) - 2) > 0 ? $(nproc) - 2 : 1 )) # Use all but 2 cores for Julia

julia --debug-info=1 --optimize=1 \
${KERNEL_LAUNCHERS_DIR}/julia/scripts/launch_ijuliakernel.jl \
--kernel-id ${KERNEL_ID} --port-range ${PORT_RANGE} \
--response-address ${RESPONSE_ADDRESS} --public-key ${PUBLIC_KEY}

{ set +x; } 2>/dev/null
}

launch_scala_kernel() {
# Launch the scala kernel launcher - which embeds the Apache Toree kernel and listens for interrupts
# and shutdown requests from Enterprise Gateway. This kernel is currenly always launched using
# spark-submit, so additional setup is required.

PROG_HOME=${KERNEL_LAUNCHERS_DIR}/scala
KERNEL_ASSEMBLY=`(cd "${PROG_HOME}/lib"; ls -1 toree-assembly-*.jar;)`
TOREE_ASSEMBLY="${PROG_HOME}/lib/${KERNEL_ASSEMBLY}"
if [ ! -f ${TOREE_ASSEMBLY} ]; then
echo "Toree assembly '${PROG_HOME}/lib/toree-assembly-*.jar' is missing. Exiting..."
exit 1
fi

# Toree launcher jar path, plus required lib jars (toree-assembly)
JARS="${TOREE_ASSEMBLY}"
# Toree launcher app path
LAUNCHER_JAR=`(cd "${PROG_HOME}/lib"; ls -1 toree-launcher*.jar;)`
LAUNCHER_APP="${PROG_HOME}/lib/${LAUNCHER_JAR}"
if [ ! -f ${LAUNCHER_APP} ]; then
echo "Scala launcher jar '${PROG_HOME}/lib/toree-launcher*.jar' is missing. Exiting..."
exit 1
fi

SPARK_OPTS="--name ${KERNEL_USERNAME}-${KERNEL_ID}"
TOREE_OPTS="--alternate-sigint USR2"

set -x
eval exec \
"${SPARK_HOME}/bin/spark-submit" \
"${SPARK_OPTS}" \
--jars "${JARS}" \
--class launcher.ToreeLauncher \
"${LAUNCHER_APP}" \
"${TOREE_OPTS}" \
"--kernel-id ${KERNEL_ID} --port-range ${PORT_RANGE} --response-address ${RESPONSE_ADDRESS} --public-key ${PUBLIC_KEY} --spark-context-initialization-mode ${KERNEL_SPARK_CONTEXT_INIT_MODE}"
{ set +x; } 2>/dev/null
}

# Ensure that required envs are present, check language before the dynamic values
if [ -z "${KERNEL_LANGUAGE+x}" ]
then
echo "KERNEL_LANGUAGE is required. Set this value in the image or when starting container."
exit 1
fi
if [ -z "${KERNEL_ID+x}" ] || [ -z "${RESPONSE_ADDRESS+x}" ] || [ -z "${PUBLIC_KEY+x}" ]
then
echo "Environment variables, KERNEL_ID, RESPONSE_ADDRESS, and PUBLIC_KEY are required."
exit 1
fi

# Invoke appropriate launcher based on KERNEL_LANGUAGE (case-insensitive)

if [[ "${KERNEL_LANGUAGE,,}" == "python" ]]
then
launch_python_kernel
elif [[ "${KERNEL_LANGUAGE,,}" == "julia" ]]
then
launch_julia_kernel
elif [[ "${KERNEL_LANGUAGE,,}" == "scala" ]]
then
launch_scala_kernel
elif [[ "${KERNEL_LANGUAGE,,}" == "r" ]]
then
launch_R_kernel
else
echo "Unrecognized value for KERNEL_LANGUAGE: '${KERNEL_LANGUAGE}'!"
exit 1
fi
exit 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# This file defines the Kubernetes objects necessary for kernels to run witihin Kubernetes.
# Substitution parameters are processed by the launch_kubernetes.py code located in the
# same directory. Some values are factory values, while others (typically prefixed with 'kernel_') can be
# provided by the client.
#
# This file can be customized as needed. No changes are required to launch_kubernetes.py provided kernel_
# values are used - which be automatically set from corresponding KERNEL_ env values. Updates will be required
# to launch_kubernetes.py if new document sections (i.e., new k8s 'kind' objects) are introduced.
#
apiVersion: v1
kind: Pod
metadata:
name: "{{ kernel_pod_name }}"
namespace: "{{ kernel_namespace }}"
labels:
kernel_id: "{{ kernel_id }}"
app: enterprise-gateway
component: kernel
source: kernel-pod.yaml
annotations:
cluster-autoscaler.kubernetes.io/safe-to-evict: "false"
spec:
restartPolicy: Never
serviceAccountName: "{{ kernel_service_account_name }}"
# NOTE: that using runAsGroup requires that feature-gate RunAsGroup be enabled.
# WARNING: Only using runAsUser w/o runAsGroup or NOT enabling the RunAsGroup feature-gate
# will result in the new kernel pod's effective group of 0 (root)! although the user will
# correspond to the runAsUser value. As a result, BOTH should be uncommented AND the feature-gate
# should be enabled to ensure expected behavior. In addition, 'fsGroup: 100' is recommended so
# that /home/jovyan can be written to via the 'users' group (gid: 100) irrespective of the
# "kernel_uid" and "kernel_gid" values.
{% if kernel_uid is defined or kernel_gid is defined %}
securityContext:
{% if kernel_uid is defined %}
runAsUser: {{ kernel_uid | int }}
{% endif %}
{% if kernel_gid is defined %}
runAsGroup: {{ kernel_gid | int }}
{% endif %}
fsGroup: 100
{% endif %}
containers:
- image: "{{ kernel_image }}"
name: "{{ kernel_pod_name }}"
env:
# Add any custom envs here that aren't already configured for the kernel's environment
# - name: MY_CUSTOM_ENV
# value: "my_custom_value"
{% if kernel_cpus is defined or kernel_memory is defined or kernel_gpus is defined or kernel_cpus_limit is defined or kernel_memory_limit is defined or kernel_gpus_limit is defined %}
resources:
{% if kernel_cpus is defined or kernel_memory is defined or kernel_gpus is defined %}
requests:
{% if kernel_cpus is defined %}
cpu: "{{ kernel_cpus }}"
{% endif %}
{% if kernel_memory is defined %}
memory: "{{ kernel_memory }}"
{% endif %}
{% if kernel_gpus is defined %}
nvidia.com/gpu: "{{ kernel_gpus }}"
{% endif %}
{% endif %}
{% if kernel_cpus_limit is defined or kernel_memory_limit is defined or kernel_gpus_limit is defined %}
limits:
{% if kernel_cpus_limit is defined %}
cpu: "{{ kernel_cpus_limit }}"
{% endif %}
{% if kernel_memory_limit is defined %}
memory: "{{ kernel_memory_limit }}"
{% endif %}
{% if kernel_gpus_limit is defined %}
nvidia.com/gpu: "{{ kernel_gpus_limit }}"
{% endif %}
{% endif %}
{% endif %}
{% if kernel_working_dir %}
workingDir: "{{ kernel_working_dir }}"
{% endif %}
volumeMounts:
# Define any "unconditional" mounts here, followed by "conditional" mounts that vary per client
{% if kernel_volume_mounts %}
{% for volume_mount in kernel_volume_mounts %}
- {{ volume_mount }}
{% endfor %}
{% endif %}
volumes:
# Define any "unconditional" volumes here, followed by "conditional" volumes that vary per client
{% if kernel_volumes %}
{% for volume in kernel_volumes %}
- {{ volume }}
{% endfor %}
{% endif %}
Loading

0 comments on commit cc6a138

Please sign in to comment.