Skip to content

Commit e40fc92

Browse files
Add containers (stfc#580)
Co-authored-by: Elliott Kasoar <[email protected]>
1 parent b87a674 commit e40fc92

15 files changed

+818
-1
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: ghcr-jupyter
2+
on:
3+
push:
4+
branches: [ "main" ]
5+
6+
jobs:
7+
build-amd64:
8+
runs-on: ubuntu-latest
9+
if: github.repository == 'stfc/janus-core'
10+
permissions:
11+
packages: write
12+
contents: read
13+
steps:
14+
- name: 'clone the repo'
15+
uses: actions/checkout@v4
16+
- name: 'login to ghcr'
17+
uses: docker/login-action@v3
18+
with:
19+
registry: ghcr.io
20+
username: ${{github.actor}}
21+
password: ${{secrets.GITHUB_TOKEN}}
22+
- name: Set up Docker Buildx
23+
uses: docker/setup-buildx-action@v3
24+
25+
- name: Build and push Docker image
26+
uses: docker/build-push-action@v6
27+
with:
28+
context: ./containers
29+
file: ./containers/Dockerfile.jupyter
30+
push: true
31+
tags: |
32+
ghcr.io/stfc/janus-core/jupyter:amd64-${{ github.sha }}
33+
ghcr.io/stfc/janus-core/jupyter:amd64-latest
34+
build-arm64:
35+
runs-on: ubuntu-24.04-arm
36+
if: github.repository == 'stfc/janus-core'
37+
permissions:
38+
packages: write
39+
contents: read
40+
steps:
41+
- name: 'clone the repo'
42+
uses: actions/checkout@v4
43+
- name: 'login to ghcr'
44+
uses: docker/login-action@v3
45+
with:
46+
registry: ghcr.io
47+
username: ${{github.actor}}
48+
password: ${{secrets.GITHUB_TOKEN}}
49+
- name: Set up Docker Buildx
50+
uses: docker/setup-buildx-action@v3
51+
52+
- name: Build and push Docker image
53+
uses: docker/build-push-action@v6
54+
with:
55+
context: ./containers
56+
file: ./containers/Dockerfile.jupyter
57+
push: true
58+
tags: |
59+
ghcr.io/stfc/janus-core/jupyter:arm64-${{ github.sha }}
60+
ghcr.io/stfc/janus-core/jupyter:arm64-latest
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: ghcr-marimo
2+
on:
3+
push:
4+
branches: [ "main" ]
5+
6+
jobs:
7+
build-amd64:
8+
runs-on: ubuntu-latest
9+
if: github.repository == 'stfc/janus-core'
10+
permissions:
11+
packages: write
12+
contents: read
13+
steps:
14+
- name: 'clone the repo'
15+
uses: actions/checkout@v4
16+
- name: 'login to ghcr'
17+
uses: docker/login-action@v3
18+
with:
19+
registry: ghcr.io
20+
username: ${{github.actor}}
21+
password: ${{secrets.GITHUB_TOKEN}}
22+
- name: Set up Docker Buildx
23+
uses: docker/setup-buildx-action@v3
24+
25+
- name: Build and push Docker image
26+
uses: docker/build-push-action@v6
27+
with:
28+
context: ./containers
29+
file: ./containers/Dockerfile.marimo
30+
push: true
31+
tags: |
32+
ghcr.io/stfc/janus-core/marimo:amd64-${{ github.sha }}
33+
ghcr.io/stfc/janus-core/marimo:amd64-latest
34+
build-arm64:
35+
runs-on: ubuntu-24.04-arm
36+
if: github.repository == 'stfc/janus-core'
37+
permissions:
38+
packages: write
39+
contents: read
40+
steps:
41+
- name: 'clone the repo'
42+
uses: actions/checkout@v4
43+
- name: 'login to ghcr'
44+
uses: docker/login-action@v3
45+
with:
46+
registry: ghcr.io
47+
username: ${{github.actor}}
48+
password: ${{secrets.GITHUB_TOKEN}}
49+
- name: Set up Docker Buildx
50+
uses: docker/setup-buildx-action@v3
51+
52+
- name: Build and push Docker image
53+
uses: docker/build-push-action@v6
54+
with:
55+
context: ./containers
56+
file: ./containers/Dockerfile.marimo
57+
push: true
58+
tags: |
59+
ghcr.io/stfc/janus-core/marimo:arm64-${{ github.sha }}
60+
ghcr.io/stfc/janus-core/marimo:arm64-latest

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Tools for machine learnt interatomic potentials
1717
- [Features](#features)
1818
- [Python interface](#python-interface)
1919
- [Command line interface](#command-line-interface)
20+
- [Docker/Podman images](#dockerpodman-images)
2021
- [Development](#development)
2122
- [License](#license)
2223
- [Funding](#funding)
@@ -294,6 +295,41 @@ This will run a singlepoint energy calculation on `KCl.cif` using the [MACE-MP](
294295
Minimal and full example configuration files for all calculations can be found
295296
[here](https://stfc.github.io/janus-core/examples/index.html).
296297

298+
## Docker/Podman images
299+
300+
You can use `janus_core` in a JupyterHub or marimo environment using [docker](https://www.docker.com) or [podman](https://podman.io/). We provide regularly updated docker/podman images, which can be dowloaded by running:
301+
302+
```shell
303+
docker pull ghcr.io/stfc/janus-core/jupyter:amd64-latest
304+
305+
docker pull ghcr.io/stfc/janus-core/marimo:amd64-latest
306+
```
307+
or using podman
308+
309+
```shell
310+
podman pull ghcr.io/stfc/janus-core/jupyter-amd64:latest
311+
312+
podman pull ghcr.io/stfc/janus-core/marimo-amd64:latest
313+
```
314+
315+
for amd64 architecture, if you require arm64 replace amd64 with arm64 above, and next instructions.
316+
317+
To start, for marimo run:
318+
319+
```shell
320+
321+
podman run --rm --security-opt seccomp=unconfined -p 8842:8842 ghcr.io/stfc/janus-core/marimo:amd64-latest
322+
323+
```
324+
or for JupyterHub, run:
325+
326+
```
327+
podman run --rm --security-opt seccomp=unconfined -p 8888:8888 ghcr.io/stfc/janus-core/jupyter:amd64-latest
328+
```
329+
330+
For more details on how to share your filesystem and so on you can refer to this documentation: https://summer.ccp5.ac.uk/introduction.html#run-locally.
331+
332+
297333

298334
## Development
299335

containers/Dockerfile.jupyter

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
# Copyright (c) Jupyter Development Team.
2+
# Distributed under the terms of the Modified BSD License.
3+
# this is adapted from above.
4+
5+
ARG ROOT_CONTAINER=ubuntu:25.04
6+
7+
FROM $ROOT_CONTAINER
8+
9+
LABEL maintainer="Alin Elena <[email protected]>"
10+
ARG NB_USER="jovyan"
11+
ARG NB_UID="1001"
12+
ARG NB_GID="100"
13+
14+
USER root
15+
16+
# Install all OS dependencies for notebook server that starts but lacks all
17+
# features (e.g., download as all possible file formats)
18+
ENV DEBIAN_FRONTEND=noninteractive
19+
RUN apt update --yes && \
20+
# - apt-get upgrade is run to patch known vulnerabilities in apt-get packages as
21+
# the ubuntu base image is rebuilt too seldom sometimes (less than once a month)
22+
apt upgrade --yes && \
23+
apt install --yes --no-install-recommends \
24+
ca-certificates \
25+
fonts-liberation \
26+
locales \
27+
# - pandoc is used to convert notebooks to html files
28+
# it's not present in arm64 ubuntu image, so we install it here
29+
pandoc \
30+
# - run-one - a wrapper script that runs no more
31+
# than one unique instance of some command with a unique set of arguments,
32+
# we use `run-one-constantly` to support `RESTARTABLE` option
33+
run-one \
34+
sudo \
35+
# - tini is installed as a helpful container entrypoint that reaps zombie
36+
# processes and such of the actual executable we want to start, see
37+
# https://github.com/krallin/tini#why-tini for details.
38+
tini \
39+
wget bzip2 && \
40+
apt clean && rm -rf /var/lib/apt/lists/* && \
41+
echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && \
42+
locale-gen
43+
44+
# Configure environment
45+
ENV CONDA_DIR=/opt/conda \
46+
SHELL=/bin/bash \
47+
NB_USER="${NB_USER}" \
48+
NB_UID=${NB_UID} \
49+
NB_GID=${NB_GID} \
50+
LC_ALL=en_US.UTF-8 \
51+
LANG=en_US.UTF-8 \
52+
LANGUAGE=en_US.UTF-8
53+
ENV PATH="${CONDA_DIR}/bin:${PATH}" \
54+
HOME="/home/${NB_USER}"
55+
56+
# Copy a script that we will use to correct permissions after running certain commands
57+
COPY fix-permissions /usr/local/bin/fix-permissions
58+
RUN chmod a+rx /usr/local/bin/fix-permissions
59+
60+
# Enable prompt color in the skeleton .bashrc before creating the default NB_USER
61+
# hadolint ignore=SC2016
62+
RUN sed -i 's/^#force_color_prompt=yes/force_color_prompt=yes/' /etc/skel/.bashrc && \
63+
# Add call to conda init script see https://stackoverflow.com/a/58081608/4413446
64+
echo 'eval "$(command conda shell.bash hook 2> /dev/null)"' >> /etc/skel/.bashrc
65+
66+
# Create NB_USER with name jovyan user with UID=1000 and in the 'users' group
67+
# and make sure these dirs are writable by the `users` group.
68+
RUN echo "auth requisite pam_deny.so" >> /etc/pam.d/su && \
69+
sed -i.bak -e 's/^%admin/#%admin/' /etc/sudoers && \
70+
sed -i.bak -e 's/^%sudo/#%sudo/' /etc/sudoers && \
71+
useradd -l -m -s /bin/bash -N -u "${NB_UID}" "${NB_USER}" && \
72+
mkdir -p "${CONDA_DIR}" && \
73+
chown "${NB_USER}:${NB_GID}" "${CONDA_DIR}" && \
74+
chmod g+w /etc/passwd && \
75+
fix-permissions "${HOME}" && \
76+
fix-permissions "${CONDA_DIR}"
77+
78+
USER ${NB_UID}
79+
ARG PYTHON_VERSION=3.12
80+
81+
# Setup work directory for backward-compatibility
82+
RUN mkdir "/home/${NB_USER}/work" && \
83+
fix-permissions "/home/${NB_USER}"
84+
85+
# Install conda as jovyan and check the sha256 sum provided on the download site
86+
WORKDIR /tmp
87+
88+
RUN set -x && \
89+
arch=$(uname -m) && \
90+
if [ "${arch}" = "x86_64" ]; then arch="64"; fi && \
91+
wget --progress=dot:giga -O - "https://micro.mamba.pm/api/micromamba/linux-${arch}/latest" | tar -xvj bin/micromamba && \
92+
PYTHON_SPECIFIER="python=${PYTHON_VERSION}" && \
93+
if [ "${PYTHON_VERSION}" = "default" ]; then PYTHON_SPECIFIER="python"; fi && \
94+
./bin/micromamba install \
95+
--root-prefix="${CONDA_DIR}" \
96+
--prefix="${CONDA_DIR}" \
97+
--yes \
98+
'jupyter_core' \
99+
'conda' \
100+
'mamba' \
101+
"${PYTHON_SPECIFIER}" && \
102+
rm -rf /tmp/bin/ && \
103+
mamba list --full-name 'python' | awk 'END{sub("[^.]*$", "*", $2); print $1 " " $2}' >> "${CONDA_DIR}/conda-meta/pinned" && \
104+
mamba clean --all -f -y && \
105+
fix-permissions "${CONDA_DIR}" && \
106+
fix-permissions "/home/${NB_USER}"
107+
108+
# setup the notebook... pretty basic stuff
109+
USER ${NB_UID}
110+
111+
WORKDIR /tmp
112+
RUN mamba install --yes \
113+
'jupyterhub-singleuser' \
114+
'jupyterlab' \
115+
'nbclassic' \
116+
# Sometimes, when the new version of `jupyterlab` is released, latest `notebook` might not support it for some time
117+
# Old versions of `notebook` (<v7) didn't have a restriction on the `jupyterlab` version, and old `notebook` is getting installed
118+
# That's why we have to pin the minimum notebook version
119+
# More info: https://github.com/jupyter/docker-stacks/pull/2167
120+
'notebook>=7.2.2' && \
121+
jupyter server --generate-config && \
122+
mamba clean --all -f -y && \
123+
jupyter lab clean && \
124+
rm -rf "/home/${NB_USER}/.cache/yarn" && \
125+
fix-permissions "${CONDA_DIR}" && \
126+
fix-permissions "/home/${NB_USER}"
127+
128+
ENV JUPYTER_PORT=8888
129+
EXPOSE $JUPYTER_PORT
130+
131+
# Configure container startup
132+
CMD ["start-notebook.py"]
133+
134+
# Copy local files as late as possible to avoid cache busting
135+
COPY start-notebook.py start-notebook.sh start-singleuser.py start-singleuser.sh /usr/local/bin/
136+
COPY jupyter_server_config.py /etc/jupyter/
137+
138+
# Fix permissions on /etc/jupyter as root
139+
USER root
140+
RUN fix-permissions /etc/jupyter/
141+
142+
RUN rm -rf "/home/${NB_USER}/.cache/"
143+
144+
# Switch back to jovyan to avoid accidental container runs as root
145+
USER ${NB_UID}
146+
147+
#============
148+
#minimal image now
149+
150+
USER root
151+
152+
RUN apt update --yes && \
153+
apt install --yes --no-install-recommends \
154+
cm-super \
155+
dvipng && \
156+
apt clean && rm -rf /var/lib/apt/lists/*
157+
158+
USER ${NB_UID}
159+
160+
# Install Python 3 packages
161+
RUN mamba install --yes \
162+
'altair' \
163+
'beautifulsoup4' \
164+
'bokeh' \
165+
'bottleneck' \
166+
'cloudpickle' \
167+
'conda-forge::blas=*=openblas' \
168+
'conda-forge::gfortran' \
169+
'conda-forge::hdf5' \
170+
'cython' \
171+
'dask' \
172+
'dill' \
173+
'h5py' \
174+
'ipympl'\
175+
'ipywidgets' \
176+
'matplotlib-base' \
177+
'numba' \
178+
'numexpr' \
179+
'pandas' \
180+
'openpyxl' \
181+
'patsy' \
182+
'protobuf' \
183+
'pytables' \
184+
'scikit-image' \
185+
'scikit-learn' \
186+
'scipy' \
187+
'seaborn' \
188+
'sqlalchemy' \
189+
'statsmodels' \
190+
'sympy' \
191+
'widgetsnbextension'\
192+
'xlrd' \
193+
'gxx' \
194+
'spglib' 'jupyter-server-proxy' 'jupyter-collaboration' && \
195+
mamba clean --all -f -y && \
196+
fix-permissions "${CONDA_DIR}" && \
197+
fix-permissions "/home/${NB_USER}"
198+
199+
# Import matplotlib the first time to build the font cache.
200+
ENV XDG_CACHE_HOME="/home/${NB_USER}/.cache/"
201+
202+
RUN MPLBACKEND=Agg python -c "import matplotlib.pyplot" && \
203+
fix-permissions "/home/${NB_USER}"
204+
205+
USER root
206+
207+
RUN apt -y update && apt -y upgrade\
208+
&& apt install -y git pkg-config cmake && \
209+
apt clean && rm -rf /var/lib/apt/lists/*
210+
211+
RUN chown -R $NB_UID:$NB_GID $HOME
212+
213+
COPY --chown=$NB_UID:$NB_GID environment.yml /tmp
214+
USER $NB_USER
215+
RUN . /opt/conda/bin/activate && \
216+
mamba env update --quiet --file /tmp/environment.yml && \
217+
mamba clean --all -f -y && \
218+
rm -rf "/home/${NB_USER}/.cache"
219+
220+
EXPOSE 8888
221+
222+
WORKDIR "${HOME}"

0 commit comments

Comments
 (0)