This gives you a way to run any jupyter kernel in a new container using a notebook server on your host. One reason for this is to customize one notebook server, and use it on many docker images.
Docker running a new container for each ipython kernel
- Add ipykernel to docker image. For example:
RUN virtualenv /opt/bash; \
VIRTUAL_ENV_DISABLE_PROMPT=1; \
source /opt/bash/bin/activate; \
pip --no-cache-dir install ipykernel- Add new kernel.json
{
"display_name": "docker_test",
"argv": [
"/home/andy/note2/bin/python", "-m", "docker_proxy_kernel",
"-f", "{connection_file}",
"--image", "mykernel:latest",
"--docker", "nvidia-docker",
"--cmd", "['/opt/bash/bin/python', '-m', 'ipykernel']"
],
"env": {},
"language": "python"
}
A non-docker specific proof of concept has been added. If --image is not
specified, then it is assumed the docker command is not being called. Instead
the --cmd is executed directly. (The --docker argument will have no
meaning).
This example will use docker-compose, but this could be any script that does
whatever you need instead
-
Example
kernel.jsonDocker-compose -f "${C3D_CWD}/docker-compose-main.yml"
run -T --service-ports ipykernel
pipenv run python -m ipykernel_launcher ${@+"${@}"} > /dev/null
{
"display_name": "docker_compose_test",
"argv": [
"python", "-m", "docker_proxy_kernel",
"-f", "{connection_file}",
"--cmd", "['docker-compose', 'run', '-T', '--service-ports', 'ipykernel', 'python', '-m' 'ipykernel_launcher']"
],
"env": {"COMPOSE_FILE": "/opt/project/docker-compose.yml"},
"language": "python"
}Note 'python' can be replaced with 'pipenv', 'run', 'python' if you are using a pipenv environment
- Write your
docker-compose.ymlfile similar to:
version: '2.3'
services:
example: &example
image: jupyter/base-notebook # just something with ipykernel installed
ipykernel:
<<: *example
ports:
- "${JUPYTER_CONTROL_PORT_HOST}:${JUPYTER_CONTROL_PORT}"
- "${JUPYTER_HB_PORT_HOST}:${JUPYTER_HB_PORT}"
- "${JUPYTER_IOPUB_PORT_HOST}:${JUPYTER_IOPUB_PORT}"
- "${JUPYTER_SHELL_PORT_HOST}:${JUPYTER_SHELL_PORT}"
- "${JUPYTER_STDIN_PORT_HOST}:${JUPYTER_STDIN_PORT}"
# This is only important if you use `docker-compose up --no-start`, and then
# used `docker start -ai {container id}`
# stdin_open: true- Run your kernel!
How does this work? docker_proxy_kernel will execute your cmd command and set the values for the arguments:
--control--hb--iopub--shell--stdin--ip=0.0.0.0--transport--Session.signature_scheme--Session.key
And also set the following environment variables:
JUPYTER_CONTROL_PORT_HOSTJUPYTER_CONTROL_PORTJUPYTER_HB_PORT_HOSTJUPYTER_HB_PORTJUPYTER_IOPUB_PORT_HOSTJUPYTER_IOPUB_PORTJUPYTER_SHELL_PORT_HOSTJUPYTER_SHELL_PORTJUPYTER_STDIN_PORT_HOSTJUPYTER_STDIN_PORT
And the rest is normal docker-compose execution
-h/--help- Show help message and exit-f CONNECTION_FILE- Connection file--image IMAGE- Name of docker image to be run--control CONTROL- Docker control port--hb HB- Docker heart beat port--iopub IOPUB- Docker IO publish port--shell SHELL- Docker shell port--stdin STDIN- Docker stdin port--docker DOCKER- Docker executable used for running docker commands. Can be full path, or point to a wrapper script to add arguments before therundocker sub-command.--cmd CMD- The command executed in a docker. This argument must be a python representation of a list of strings. e.g. "['sleep', '1']"-- [OTHER] [DOCKER] [ARGUMENTS]- Set of additional docker arguments that will be inserted after the run sub-command but before the image name. This will include anything you need to create your environment including port forwards, mount directories, and etc...
See remote_kernel or rk projects.
-
Connect to a running container (via
docker execorimport docker). I thought about this some, and since the notebook decides which ports it expects to use docker exec on its own is impossible and using docker-py is no easier than using docker cli configuration layer in kernel.json. In order to get docker exec working, either:- Expose 5 ports before starting the container. And upon starting docker proxy kernel, start come tcp redirector to tunnel the traffic to the container
Connect to the container, and use stdin/stdout to multiplex the 5 tcp ports in. Crazy for a number of reasons, flushing to begin withUse iptables to redirect the write port
-
Support
docker-compose. This would be less than straight forward. A new docker-compose.yml file will need to be created on the fly to add the 5 tcp ports that need exposing, and then start the container. -
Add a
managelikerkandremote_kernel
- "I always have to change the kernel every time I want to change one of the parameters." Yeah... I can't find any way to have notebook prompt you for a question upon starting a kernel, and without that I can't come up with a better experience.
- Cannot use python3/newer ipykernel on the docker side, bug in command line argument parser