diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..74ca1971cf --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +.git +build +dist +documentation +doxygen +lib +obj* +tests +userproject +!userproject/include +**/*.so +**/*.lib diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..96953c017d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,44 @@ +ARG BASE=11.5.0-devel-ubuntu20.04 +FROM nvidia/cuda:${BASE} + +LABEL maintainer="J.C.Knight@sussex.ac.uk" +LABEL version="4.8.0" +LABEL org.opencontainers.image.documentation="https://genn-team.github.io/" +LABEL org.opencontainers.image.source="https://github.com/genn-team/genn" +LABEL org.opencontainers.image.title="GeNN Docker image" + +# Update APT database and upgrade any outdated packages +RUN apt-get update && \ + apt-get upgrade -y + +# Install Python, pip and swig +RUN apt-get install -yq --no-install-recommends python3-dev python3-pip swig gosu nano + +# Set CUDA environment variable +ENV CUDA_PATH=/usr/local/cuda-11.5 + +ENV GENN_PATH=/opt/genn + +# Upgrade pip itself +RUN pip install --upgrade pip + +# Install numpy and jupyter +RUN pip install numpy jupyter matplotlib + +# Copy GeNN into /opt +COPY . ${GENN_PATH} + +# Use this as working directory +WORKDIR ${GENN_PATH} + +# Install GeNN and PyGeNN +RUN make install -j `lscpu -p | egrep -v '^#' | sort -u -t, -k 2,4 | wc -l` +RUN make DYNAMIC=1 LIBRARY_DIRECTORY=${GENN_PATH}/pygenn/genn_wrapper/ -j `lscpu -p | egrep -v '^#' | sort -u -t, -k 2,4 | wc -l` +RUN python3 setup.py develop + +# Default command will be to launch bash +CMD ["/bin/bash"] + +# Start entrypoint +# **NOTE** in 'exec' mode shell arguments aren't expanded so can't use environment variables +ENTRYPOINT ["/opt/genn/bin/genn-docker-entrypoint.sh"] diff --git a/Makefile b/Makefile index 6047458b34..b787652621 100644 --- a/Makefile +++ b/Makefile @@ -69,3 +69,7 @@ clean: @# Delete libGeNN @rm -f $(LIBGENN) @rm -f $(BACKEND_LIBS) + +.PHONY docker-build: +docker-build: + @docker build -t genn:latest . diff --git a/README.md b/README.md index ef059644fa..87dc22ed05 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,8 @@ GeNN is a GPU-enhanced Neuronal Network simulation environment based on code gen ## Installation -You can download GeNN either as a zip file of a stable release or a -snapshot of the most recent stable version or the unstable development -version using the Git version control system. +You can download GeNN either as a zip file of a stable release, checkout the development +version using the Git version control system or use our Docker container. ### Downloading a release Point your browser to https://github.com/genn-team/genn/releases @@ -102,6 +101,54 @@ environment already set up by navigating to Start - All Programs - Microsoft Visual Studio - Visual Studio Tools - x64 Native Tools Command Prompt. You may also wish to create a shortcut for this tool on the desktop, for convenience. +### Docker +You can also use GeNN through our CUDA-enabled docker container which comes with GeNN pre-installed. +To work with such CUDA-enabled containers, you need to first install CUDA on your host system as described above and then install docker and the NVIDIA Container Toolkit as described in https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#docker. +You can then build the GeNN container yourself or download it from Dockerhub. + +### Building the container +The following command can be used from the GeNN source directory to build the GeNN container: + +```bash +make docker-build +``` + +This builds a container tagged as ``genn:latest`` so, to use this container rather than downloading the prebuild one from dockerhub, just replace ``gennteam/genn:latest`` with ``genn:latest`` in the following instructions. + +### Interactive mode +If you wish to use GeNN or PyGeNN interactively, you can launch a bash shell in the GeNN container using the following command: +```bash +docker run -it --gpus=all gennteam/genn:latest +``` +You can also provide a final argument to launch a different executable e.g. ``/bin/sh`` to launch a dash shell. + +### Accessing your files +When using the GeNN container you often want to access files on your host system. +This can be easily achieved by using the ``-v`` option to mount a local directory into the container. For example: +```bash +docker run -it --gpus=all -v $HOME:/local_home gennteam/genn:latest +``` +mounts the local user's home directory into ``/local_home`` within the container. +However, all of the commands provided by the GeNN container operate using a non-elevated, internal user called 'genn' who, by default, won't have the correct permissions to create files in volumes mounted into the container. +This can be resolved by setting the ``LOCAL_USER_ID`` and ``LOCAL_GROUP_ID`` environment variables when running the container like: +```bash +docker run -it --gpus=all -e LOCAL_USER_ID=`id -u $USER` -e LOCAL_GROUP_ID=`id -g $USER` -v $HOME:/local_home gennteam/genn:latest +``` +which will ensure that that 'genn' user has the same UID and GID as the local user, meaning that they will have the same permissions to access the files mounted into ``/local_home``. + +### Running Jupyter Notebooks +A Jupyter Notebook environment running in the container can be launched using the ``notebook`` command. Typically, you would combine this with the ``-p 8080:8080`` option to 'publish' port 8080, allowing the notebook server to be accessed on the host. By default, notebooks are created in the home directory of the 'genn' user inside the container. However, to create notebooks which persist beyond the lifetime of the container, the notebook command needs to be combined with the options discussed previously. For example: +```bash +docker run --gpus=all -p 8080:8080 -e LOCAL_USER_ID=`id -u $USER` -e LOCAL_GROUP_ID=`id -g $USER` -v $HOME:/local_home gennteam/genn:latest notebook /local_home +``` +will create notebooks in the current users home directory. + +### Running PyGeNN scripts +Assuming they have no additional dependencies, PyGeNN scripts can be run directly using the container with the ``script`` command. As scripts are likely to be located outside of the container, the script command is often combined with the options discussed previously. For example, to run a script called ``test.py`` in your home directory, the script command could be invoked with: +```bash +docker run --gpus=all -e LOCAL_USER_ID=`id -u $USER` -e LOCAL_GROUP_ID=`id -g $USER` -v $HOME:/local_home gennteam/genn:latest script /local_home/test.py +``` + ## Usage ### Sample projects diff --git a/bin/genn-docker-entrypoint.sh b/bin/genn-docker-entrypoint.sh new file mode 100755 index 0000000000..0f3aaaf636 --- /dev/null +++ b/bin/genn-docker-entrypoint.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# Read desired user and group ID from environment varibles (typically set on docker command line with -e) +USER_ID=${LOCAL_USER_ID:-9001} +GROUP_ID=${LOCAL_GROUP_ID:-$USER_ID} + +# Add GeNN user with matching user and group ID +groupadd -g $GROUP_ID genn +useradd --shell /bin/bash -u $USER_ID -g genn -o -c "" -m genn +export HOME=/home/genn + +# If script command passed +if [[ "$1" = "script" ]]; then + # Shift script command itself off arguments + shift + + # Change to directory script is in and launch + # **YUCK** this should not really be necessary but PyGeNN does + # not work nicely running scripts not in working directory + CWD=$(dirname "$1") + cd "$CWD" + exec gosu genn:genn python3 "$@" +# Otherwise, if notebook is passes +elif [[ "$1" = "notebook" ]]; then + # Extract notebook directory from next command line argument, otherwise use home + CWD=${2:-$HOME} + exec gosu genn:genn /usr/local/bin/jupyter-notebook --ip=0.0.0.0 --port=8080 --no-browser --notebook-dir="$CWD" +# Otherwise, change directory to home directory and execute arguments +else + cd $HOME + exec gosu genn:genn "$@" +fi