|
| 1 | +ARG BASE_IMAGE=debian |
| 2 | +ARG BASE_IMAGE_TAG=12 |
| 3 | +ARG BUILD_ON_IMAGE=glcr.b-data.ch/python/ver |
| 4 | +ARG PYTHON_VERSION=3.11.6 |
| 5 | +ARG CUDA_IMAGE_FLAVOR |
| 6 | + |
| 7 | +ARG NB_USER=jovyan |
| 8 | +ARG NB_UID=1000 |
| 9 | +ARG JUPYTERHUB_VERSION=4.0.2 |
| 10 | +ARG JUPYTERLAB_VERSION=4.0.9 |
| 11 | +ARG CODE_BUILTIN_EXTENSIONS_DIR=/opt/code-server/lib/vscode/extensions |
| 12 | +ARG CODE_SERVER_VERSION=4.19.1 |
| 13 | +ARG GIT_VERSION=2.43.0 |
| 14 | +ARG GIT_LFS_VERSION=3.4.0 |
| 15 | +ARG PANDOC_VERSION=3.1.1 |
| 16 | + |
| 17 | +FROM ${BUILD_ON_IMAGE}:${PYTHON_VERSION}${CUDA_IMAGE_FLAVOR:+-}${CUDA_IMAGE_FLAVOR} AS files |
| 18 | + |
| 19 | +ARG NB_UID |
| 20 | +ENV NB_GID=100 |
| 21 | + |
| 22 | +RUN mkdir /files |
| 23 | + |
| 24 | +COPY assets /files |
| 25 | +COPY conf/ipython /files |
| 26 | +COPY conf/jupyter /files |
| 27 | +COPY conf/jupyterlab /files |
| 28 | +COPY conf/user /files |
| 29 | +COPY scripts /files |
| 30 | + |
| 31 | +RUN chown -R ${NB_UID}:${NB_GID} /files/var/backups/skel \ |
| 32 | + ## Ensure file modes are correct when using CI |
| 33 | + ## Otherwise set to 777 in the target image |
| 34 | + && find /files -type d -exec chmod 755 {} \; \ |
| 35 | + && find /files -type f -exec chmod 644 {} \; \ |
| 36 | + && find /files/usr/local/bin -type f -exec chmod 755 {} \; |
| 37 | + |
| 38 | +FROM glcr.b-data.ch/git/gsi/${GIT_VERSION}/${BASE_IMAGE}:${BASE_IMAGE_TAG} AS gsi |
| 39 | +FROM glcr.b-data.ch/git-lfs/glfsi:${GIT_LFS_VERSION} AS glfsi |
| 40 | + |
| 41 | +FROM ${BUILD_ON_IMAGE}:${PYTHON_VERSION}${CUDA_IMAGE_FLAVOR:+-}${CUDA_IMAGE_FLAVOR} |
| 42 | + |
| 43 | +LABEL org.opencontainers.image.licenses="MIT" \ |
| 44 | + org.opencontainers.image.source="https://gitlab.b-data.ch/jupyterlab/python/docker-stack" \ |
| 45 | + org.opencontainers.image.vendor="b-data GmbH" \ |
| 46 | + org.opencontainers.image.authors="Olivier Benz <olivier.benz@b-data.ch>" |
| 47 | + |
| 48 | +ARG DEBIAN_FRONTEND=noninteractive |
| 49 | + |
| 50 | +ARG BUILD_ON_IMAGE |
| 51 | +ARG CUDA_IMAGE_FLAVOR |
| 52 | +ARG NB_USER |
| 53 | +ARG NB_UID |
| 54 | +ARG JUPYTERHUB_VERSION |
| 55 | +ARG JUPYTERLAB_VERSION |
| 56 | +ARG CODE_BUILTIN_EXTENSIONS_DIR |
| 57 | +ARG CODE_SERVER_VERSION |
| 58 | +ARG GIT_VERSION |
| 59 | +ARG GIT_LFS_VERSION |
| 60 | +ARG PANDOC_VERSION |
| 61 | +ARG BUILD_START |
| 62 | + |
| 63 | +ARG CODE_WORKDIR |
| 64 | + |
| 65 | +ENV PARENT_IMAGE=${BUILD_ON_IMAGE}:${PYTHON_VERSION}${CUDA_IMAGE_FLAVOR:+-}${CUDA_IMAGE_FLAVOR} \ |
| 66 | + NB_USER=${NB_USER} \ |
| 67 | + NB_UID=${NB_UID} \ |
| 68 | + JUPYTERHUB_VERSION=${JUPYTERHUB_VERSION} \ |
| 69 | + JUPYTERLAB_VERSION=${JUPYTERLAB_VERSION} \ |
| 70 | + CODE_SERVER_VERSION=${CODE_SERVER_VERSION} \ |
| 71 | + GIT_VERSION=${GIT_VERSION} \ |
| 72 | + GIT_LFS_VERSION=${GIT_LFS_VERSION} \ |
| 73 | + PANDOC_VERSION=${PANDOC_VERSION} \ |
| 74 | + BUILD_DATE=${BUILD_START} |
| 75 | + |
| 76 | +ENV NB_GID=100 |
| 77 | + |
| 78 | +## Install Git |
| 79 | +COPY --from=gsi /usr/local /usr/local |
| 80 | +## Install Git LFS |
| 81 | +COPY --from=glfsi /usr/local /usr/local |
| 82 | + |
| 83 | +USER root |
| 84 | + |
| 85 | +RUN dpkgArch="$(dpkg --print-architecture)" \ |
| 86 | + ## Unminimise if the system has been minimised |
| 87 | + && if [ $(command -v unminimize) ]; then \ |
| 88 | + yes | unminimize; \ |
| 89 | + fi \ |
| 90 | + && apt-get update \ |
| 91 | + && apt-get -y install --no-install-recommends \ |
| 92 | + bash-completion \ |
| 93 | + build-essential \ |
| 94 | + curl \ |
| 95 | + file \ |
| 96 | + fontconfig \ |
| 97 | + g++ \ |
| 98 | + gcc \ |
| 99 | + gfortran \ |
| 100 | + gnupg \ |
| 101 | + htop \ |
| 102 | + info \ |
| 103 | + jq \ |
| 104 | + libclang-dev \ |
| 105 | + man-db \ |
| 106 | + nano \ |
| 107 | + ncdu \ |
| 108 | + procps \ |
| 109 | + psmisc \ |
| 110 | + screen \ |
| 111 | + sudo \ |
| 112 | + swig \ |
| 113 | + tmux \ |
| 114 | + vim-tiny \ |
| 115 | + wget \ |
| 116 | + zsh \ |
| 117 | + ## Git: Additional runtime dependencies |
| 118 | + libcurl3-gnutls \ |
| 119 | + liberror-perl \ |
| 120 | + ## Git: Additional runtime recommendations |
| 121 | + less \ |
| 122 | + ssh-client \ |
| 123 | + ## Python: Additional dev dependencies |
| 124 | + && if [ -z "$PYTHON_VERSION" ]; then \ |
| 125 | + apt-get -y install --no-install-recommends \ |
| 126 | + python3-dev \ |
| 127 | + ## Install Python package installer |
| 128 | + ## (dep: python3-distutils, python3-setuptools and python3-wheel) |
| 129 | + python3-pip \ |
| 130 | + ## Install venv module for python3 |
| 131 | + python3-venv; \ |
| 132 | + ## make some useful symlinks that are expected to exist |
| 133 | + ## ("/usr/bin/python" and friends) |
| 134 | + for src in pydoc3 python3 python3-config; do \ |
| 135 | + dst="$(echo "$src" | tr -d 3)"; \ |
| 136 | + [ -s "/usr/bin/$src" ]; \ |
| 137 | + [ ! -e "/usr/bin/$dst" ]; \ |
| 138 | + ln -svT "$src" "/usr/bin/$dst"; \ |
| 139 | + done; \ |
| 140 | + else \ |
| 141 | + ## Force update pip, setuptools and wheel |
| 142 | + curl -sLO https://bootstrap.pypa.io/get-pip.py; \ |
| 143 | + python get-pip.py \ |
| 144 | + pip \ |
| 145 | + setuptools \ |
| 146 | + wheel; \ |
| 147 | + rm get-pip.py; \ |
| 148 | + fi \ |
| 149 | + ## Install font MesloLGS NF |
| 150 | + && mkdir -p /usr/share/fonts/truetype/meslo \ |
| 151 | + && curl -sL https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Regular.ttf -o /usr/share/fonts/truetype/meslo/MesloLGS\ NF\ Regular.ttf \ |
| 152 | + && curl -sL https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Bold.ttf -o /usr/share/fonts/truetype/meslo/MesloLGS\ NF\ Bold.ttf \ |
| 153 | + && curl -sL https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Italic.ttf -o /usr/share/fonts/truetype/meslo/MesloLGS\ NF\ Italic.ttf \ |
| 154 | + && curl -sL https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Bold%20Italic.ttf -o /usr/share/fonts/truetype/meslo/MesloLGS\ NF\ Bold\ Italic.ttf \ |
| 155 | + && fc-cache -fsv \ |
| 156 | + ## Git: Set default branch name to main |
| 157 | + && git config --system init.defaultBranch main \ |
| 158 | + ## Git: Store passwords for one hour in memory |
| 159 | + && git config --system credential.helper "cache --timeout=3600" \ |
| 160 | + ## Git: Merge the default branch from the default remote when "git pull" is run |
| 161 | + && git config --system pull.rebase false \ |
| 162 | + ## Install pandoc |
| 163 | + && curl -sLO https://github.com/jgm/pandoc/releases/download/${PANDOC_VERSION}/pandoc-${PANDOC_VERSION}-1-${dpkgArch}.deb \ |
| 164 | + && dpkg -i pandoc-${PANDOC_VERSION}-1-${dpkgArch}.deb \ |
| 165 | + && rm pandoc-${PANDOC_VERSION}-1-${dpkgArch}.deb \ |
| 166 | + ## Delete potential user with UID 1000 |
| 167 | + && if $(grep -q 1000 /etc/passwd); then \ |
| 168 | + userdel $(id -un 1000); \ |
| 169 | + fi \ |
| 170 | + ## Do not set user limits for sudo/sudo-i |
| 171 | + && sed -i 's/.*pam_limits.so/#&/g' /etc/pam.d/sudo \ |
| 172 | + && if [ -f "/etc/pam.d/sudo-i" ]; then \ |
| 173 | + sed -i 's/.*pam_limits.so/#&/g' /etc/pam.d/sudo-i; \ |
| 174 | + fi \ |
| 175 | + ## Add user |
| 176 | + && useradd -l -m -s $(which zsh) -N -u ${NB_UID} ${NB_USER} \ |
| 177 | + && mkdir -p /var/backups/skel \ |
| 178 | + && chown ${NB_UID}:${NB_GID} /var/backups/skel \ |
| 179 | + ## Install Tini |
| 180 | + && curl -sL https://github.com/krallin/tini/releases/download/v0.19.0/tini-${dpkgArch} -o /usr/local/bin/tini \ |
| 181 | + && chmod +x /usr/local/bin/tini \ |
| 182 | + ## Clean up |
| 183 | + && rm -rf /tmp/* \ |
| 184 | + && rm -rf /var/lib/apt/lists/* \ |
| 185 | + ${HOME}/.cache |
| 186 | + |
| 187 | +ENV PATH=/opt/code-server/bin:$PATH \ |
| 188 | + CS_DISABLE_GETTING_STARTED_OVERRIDE=1 |
| 189 | + |
| 190 | +## Install code-server |
| 191 | +RUN mkdir /opt/code-server \ |
| 192 | + && cd /opt/code-server \ |
| 193 | + && curl -sL https://github.com/coder/code-server/releases/download/v${CODE_SERVER_VERSION}/code-server-${CODE_SERVER_VERSION}-linux-$(dpkg --print-architecture).tar.gz | tar zxf - --no-same-owner --strip-components=1 \ |
| 194 | + && curl -sL https://upload.wikimedia.org/wikipedia/commons/9/9a/Visual_Studio_Code_1.35_icon.svg -o vscode.svg \ |
| 195 | + ## Include custom fonts |
| 196 | + && sed -i 's|</head>| <link rel="preload" href="{{BASE}}/_static/src/browser/media/fonts/MesloLGS-NF-Regular.woff2" as="font" type="font/woff2" crossorigin="anonymous">\n </head>|g' /opt/code-server/lib/vscode/out/vs/code/browser/workbench/workbench.html \ |
| 197 | + && sed -i 's|</head>| <link rel="preload" href="{{BASE}}/_static/src/browser/media/fonts/MesloLGS-NF-Italic.woff2" as="font" type="font/woff2" crossorigin="anonymous">\n </head>|g' /opt/code-server/lib/vscode/out/vs/code/browser/workbench/workbench.html \ |
| 198 | + && sed -i 's|</head>| <link rel="preload" href="{{BASE}}/_static/src/browser/media/fonts/MesloLGS-NF-Bold.woff2" as="font" type="font/woff2" crossorigin="anonymous">\n </head>|g' /opt/code-server/lib/vscode/out/vs/code/browser/workbench/workbench.html \ |
| 199 | + && sed -i 's|</head>| <link rel="preload" href="{{BASE}}/_static/src/browser/media/fonts/MesloLGS-NF-Bold-Italic.woff2" as="font" type="font/woff2" crossorigin="anonymous">\n </head>|g' /opt/code-server/lib/vscode/out/vs/code/browser/workbench/workbench.html \ |
| 200 | + && sed -i 's|</head>| <link rel="stylesheet" type="text/css" href="{{BASE}}/_static/src/browser/media/css/fonts.css">\n </head>|g' /opt/code-server/lib/vscode/out/vs/code/browser/workbench/workbench.html \ |
| 201 | + ## Install code-server extensions |
| 202 | + && cd /tmp \ |
| 203 | + && curl -sLO https://dl.b-data.ch/vsix/alefragnani.project-manager-12.7.0.vsix \ |
| 204 | + && code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension alefragnani.project-manager-12.7.0.vsix \ |
| 205 | + && curl -sLO https://dl.b-data.ch/vsix/piotrpalarz.vscode-gitignore-generator-1.0.3.vsix \ |
| 206 | + && code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension piotrpalarz.vscode-gitignore-generator-1.0.3.vsix \ |
| 207 | + && curl -sLO https://dl.b-data.ch/vsix/mutantdino.resourcemonitor-1.0.7.vsix \ |
| 208 | + && code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension mutantdino.resourcemonitor-1.0.7.vsix \ |
| 209 | + && code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension GitHub.vscode-pull-request-github \ |
| 210 | + && code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension GitLab.gitlab-workflow \ |
| 211 | + && code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension ms-python.python \ |
| 212 | + && code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension ms-toolsai.jupyter \ |
| 213 | + && code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension christian-kohler.path-intellisense \ |
| 214 | + && code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension eamodio.gitlens@11.7.0 \ |
| 215 | + && code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension mhutchie.git-graph \ |
| 216 | + && code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension redhat.vscode-yaml \ |
| 217 | + && code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension grapecity.gc-excelviewer \ |
| 218 | + && code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension editorconfig.editorconfig \ |
| 219 | + && code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension DavidAnson.vscode-markdownlint \ |
| 220 | + ## Create folders temp and tmp for Jupyter extension |
| 221 | + && cd /opt/code-server/lib/vscode/extensions/ms-toolsai.jupyter-* \ |
| 222 | + && mkdir -m 1777 temp \ |
| 223 | + && mkdir -m 1777 tmp \ |
| 224 | + ## Clean up |
| 225 | + && rm -rf /tmp/* \ |
| 226 | + ${HOME}/.config \ |
| 227 | + ${HOME}/.local |
| 228 | + |
| 229 | +## Install JupyterLab |
| 230 | +RUN export PIP_BREAK_SYSTEM_PACKAGES=1 \ |
| 231 | + && pip install \ |
| 232 | + jupyter-server-proxy \ |
| 233 | + jupyterhub==${JUPYTERHUB_VERSION} \ |
| 234 | + jupyterlab==${JUPYTERLAB_VERSION} \ |
| 235 | + jupyterlab-git \ |
| 236 | + jupyterlab-lsp \ |
| 237 | + notebook \ |
| 238 | + nbclassic \ |
| 239 | + nbconvert \ |
| 240 | + python-lsp-server[all] \ |
| 241 | + ## Include custom fonts |
| 242 | + && sed -i 's|</head>|<link rel="preload" href="{{page_config.fullStaticUrl}}/assets/fonts/MesloLGS-NF-Regular.woff2" as="font" type="font/woff2" crossorigin="anonymous"></head>|g' /usr/local/share/jupyter/lab/static/index.html \ |
| 243 | + && sed -i 's|</head>|<link rel="preload" href="{{page_config.fullStaticUrl}}/assets/fonts/MesloLGS-NF-Italic.woff2" as="font" type="font/woff2" crossorigin="anonymous"></head>|g' /usr/local/share/jupyter/lab/static/index.html \ |
| 244 | + && sed -i 's|</head>|<link rel="preload" href="{{page_config.fullStaticUrl}}/assets/fonts/MesloLGS-NF-Bold.woff2" as="font" type="font/woff2" crossorigin="anonymous"></head>|g' /usr/local/share/jupyter/lab/static/index.html \ |
| 245 | + && sed -i 's|</head>|<link rel="preload" href="{{page_config.fullStaticUrl}}/assets/fonts/MesloLGS-NF-Bold-Italic.woff2" as="font" type="font/woff2" crossorigin="anonymous"></head>|g' /usr/local/share/jupyter/lab/static/index.html \ |
| 246 | + && sed -i 's|</head>|<link rel="stylesheet" type="text/css" href="{{page_config.fullStaticUrl}}/assets/css/fonts.css"></head>|g' /usr/local/share/jupyter/lab/static/index.html \ |
| 247 | + ## Clean up |
| 248 | + && rm -rf /tmp/* \ |
| 249 | + ${HOME}/.cache |
| 250 | + |
| 251 | +## Switch back to ${NB_USER} to avoid accidental container runs as root |
| 252 | +USER ${NB_USER} |
| 253 | + |
| 254 | +ENV HOME=/home/${NB_USER} \ |
| 255 | + CODE_WORKDIR=${CODE_WORKDIR:-/home/${NB_USER}/projects} \ |
| 256 | + SHELL=/usr/bin/zsh \ |
| 257 | + TERM=xterm-256color |
| 258 | + |
| 259 | +WORKDIR ${HOME} |
| 260 | + |
| 261 | +## Install Oh My Zsh with Powerlevel10k theme |
| 262 | +RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended \ |
| 263 | + && git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ${HOME}/.oh-my-zsh/custom/themes/powerlevel10k \ |
| 264 | + && sed -i 's/ZSH="\/home\/jovyan\/.oh-my-zsh"/ZSH="$HOME\/.oh-my-zsh"/g' ${HOME}/.zshrc \ |
| 265 | + && sed -i 's/ZSH_THEME="robbyrussell"/ZSH_THEME="powerlevel10k\/powerlevel10k"/g' ${HOME}/.zshrc \ |
| 266 | + && echo "\n# set PATH so it includes user's private bin if it exists\nif [ -d \"\$HOME/bin\" ] && [[ \"\$PATH\" != *\"\$HOME/bin\"* ]] ; then\n PATH=\"\$HOME/bin:\$PATH\"\nfi" >> ${HOME}/.zshrc \ |
| 267 | + && echo "\n# set PATH so it includes user's private bin if it exists\nif [ -d \"\$HOME/.local/bin\" ] && [[ \"\$PATH\" != *\"\$HOME/.local/bin\"* ]] ; then\n PATH=\"\$HOME/.local/bin:\$PATH\"\nfi" >> ${HOME}/.zshrc \ |
| 268 | + && echo "\n# Update last-activity timestamps while in screen/tmux session\nif [ ! -z \"\$TMUX\" -o ! -z \"\$STY\" ] ; then\n busy &\nfi" >> ${HOME}/.bashrc \ |
| 269 | + && echo "\n# Update last-activity timestamps while in screen/tmux session\nif [ ! -z \"\$TMUX\" -o ! -z \"\$STY\" ] ; then\n setopt nocheckjobs\n busy &\nfi" >> ${HOME}/.zshrc \ |
| 270 | + && echo "\n# To customize prompt, run \`p10k configure\` or edit ~/.p10k.zsh." >> ${HOME}/.zshrc \ |
| 271 | + && echo "[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh" >> ${HOME}/.zshrc \ |
| 272 | + ## Create user's private bin |
| 273 | + && mkdir -p ${HOME}/.local/bin \ |
| 274 | + ## Create backup of home directory |
| 275 | + && cp -a ${HOME}/. /var/backups/skel |
| 276 | + |
| 277 | +## Copy files as late as possible to avoid cache busting |
| 278 | +COPY --from=files /files / |
| 279 | +COPY --from=files /files/var/backups/skel ${HOME} |
| 280 | + |
| 281 | +EXPOSE 8888 |
| 282 | + |
| 283 | +## Configure container startup |
| 284 | +ENTRYPOINT ["tini", "-g", "--"] |
| 285 | +CMD ["start-notebook.sh"] |
0 commit comments