-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile
More file actions
131 lines (110 loc) · 5.45 KB
/
Copy pathDockerfile
File metadata and controls
131 lines (110 loc) · 5.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
## Multi-arch Jupyter + R (rsafd) environment
## Targets: linux/amd64 and linux/arm64 (works on Intel & Apple Silicon macOS, Linux, Windows WSL)
## Build example:
## docker buildx build \
## --platform linux/amd64,linux/arm64 \
## -t ghcr.io/OWNER/rsafd-docker:latest \
## --push .
## Replace OWNER with your GitHub org/user. Ensure you are logged in: echo $GHCR_PAT | docker login ghcr.io -u USERNAME --password-stdin
ARG UBUNTU_VERSION=24.04
ARG GIT_SHA=unknown
FROM ubuntu:${UBUNTU_VERSION}
ARG DEBIAN_FRONTEND=noninteractive
ARG TZ=Etc/UTC
ARG USERNAME=developer
ARG USERID=1000
ARG GROUPID=1000
ARG GH_OWNER="OWNER"
LABEL org.opencontainers.image.source="https://github.com/${GH_OWNER}/rsafd-docker" \
org.opencontainers.image.description="Multi-arch Jupyter (Python + R) with rsafd, tensorflow, keras, reticulate" \
org.opencontainers.image.licenses="MIT" \
org.opencontainers.image.revision="${GIT_SHA}"
## Use bash for RUN
SHELL ["/bin/bash", "-c"]
## System packages (prioritize distro native binaries for arch-specific optimization)
RUN apt-get update -y && \
apt-get install -y --no-install-recommends \
software-properties-common gnupg ca-certificates curl wget git build-essential openssl \
python3 python3-pip python3-venv python3-dev libpython3-dev \
nodejs \
r-base r-base-dev libcurl4-openssl-dev libssl-dev libxml2-dev \
libopenblas-dev gfortran libpng-dev libjpeg-dev libfreetype6-dev pkg-config \
libgl1-mesa-dev libglu1-mesa-dev libx11-dev libxext-dev libxrender-dev libxt-dev libxmu-dev libxi-dev \
locales sudo tini libgmp-dev libglpk-dev && \
locale-gen en_US.UTF-8 && \
apt-get clean && rm -rf /var/lib/apt/lists/*
ENV LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8
## Create non-root user (matching host UID/GID optionally for mounted home)
RUN set -euo pipefail; \
if getent group ${GROUPID} >/dev/null; then \
EXISTING_GROUP_NAME=$(getent group ${GROUPID} | cut -d: -f1); \
echo "GID ${GROUPID} already exists as ${EXISTING_GROUP_NAME}"; \
else \
groupadd -g ${GROUPID} ${USERNAME}; \
fi; \
TARGET_GROUP_NAME=$(getent group ${GROUPID} | cut -d: -f1); \
if id -u ${USERID} >/dev/null 2>&1; then \
echo "UID ${USERID} already exists, will re-use and ensure username ${USERNAME}"; \
if ! id -u ${USERNAME} >/dev/null 2>&1; then \
usermod -l ${USERNAME} $(getent passwd ${USERID} | cut -d: -f1) || true; \
usermod -d /home/${USERNAME} -m ${USERNAME} || true; \
fi; \
elif id -u ${USERNAME} >/dev/null 2>&1; then \
echo "User ${USERNAME} exists with different UID; not modifying UID"; \
else \
useradd -m -s /bin/bash -u ${USERID} -g ${TARGET_GROUP_NAME} ${USERNAME}; \
fi; \
usermod -aG sudo ${USERNAME} && echo "%sudo ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/99-nopasswd
WORKDIR /workspace
## Python virtual environment (PEP 668 friendly) and scientific stack
RUN python3 -m venv /opt/venv && \
/opt/venv/bin/pip install --no-cache-dir --upgrade pip setuptools wheel && \
/opt/venv/bin/pip install --no-cache-dir \
notebook jupyterlab ipykernel \
numpy pandas scipy matplotlib seaborn scikit-learn \
tensorflow keras && \
/opt/venv/bin/python -m ipykernel install --name python3 --display-name "Python 3 (venv)" --sys-prefix
## Environment variables for reticulate to find venv python
ENV PATH="/opt/venv/bin:$PATH" \
RETICULATE_PYTHON=/opt/venv/bin/python
## R package installation (IRkernel for Jupyter, reticulate bridge, Rsafd copied from repo)
RUN R -q -e "install.packages(c('IRkernel','remotes','reticulate'), repos='https://cloud.r-project.org', dependencies=TRUE)" && \
R -q -e "install.packages('RcppEigen', repos='https://cloud.r-project.org', dependencies=TRUE)" && \
R -q -e "install.packages('igraph', repos='https://cloud.r-project.org', dependencies=TRUE)" && \
R -q -e "install.packages(c('timeDate','quadprog','quantreg','plot3D','robustbase','scatterplot3d','tseries','glasso','qgraph','keras','rgl','glmnet'), repos='https://cloud.r-project.org', dependencies=TRUE)" && \
R -q -e "IRkernel::installspec(user = FALSE, name='ir', displayname='R')" && \
git clone --depth=1 https://github.com/princetonuniversity/rsafd /usr/local/lib/R/site-library/Rsafd && \
rm -rf /usr/local/lib/R/site-library/Rsafd/.git && \
R -q -e "library(Rsafd); cat('Rsafd loaded (copied)\n')"
## (Optional) Basic import tests to fail early if something is broken
RUN /opt/venv/bin/python - <<'PYTEST' && \
R -q -e 'library(Rsafd); cat("Rsafd loaded successfully (smoke test)\n")' && \
R -q -e 'library(reticulate); tf <- import("tensorflow"); cat(tf[["__version__"]], "\n")'
import tensorflow as tf
import keras
print('TensorFlow:', tf.__version__)
print('Keras:', keras.__version__)
PYTEST
## Expose Jupyter default port
EXPOSE 8888
## Make sure notebooks & data written with correct ownership when running as non-root
RUN mkdir -p /workspace/notebooks && chown -R ${USERNAME}:${GROUPID} /workspace
COPY start-jupyter.sh /usr/local/bin/start-jupyter
RUN chmod +x /usr/local/bin/start-jupyter
# Copy healthcheck script
COPY healthcheck.sh /usr/local/bin/healthcheck
RUN chmod +x /usr/local/bin/healthcheck
USER ${USERNAME}
ENV HOME=/home/${USERNAME}
ENV RSAFD_IMAGE_SHA=${GIT_SHA} \
JUPYTER_PORT=8888 \
JUPYTER_DISABLE_TOKEN=0 \
JUPYTER_TOKEN="" \
JUPYTER_LAB_ARGS="" \
JUPYTER_PLAIN_URL=0 \
JUPYTER_DISABLE_LSP=1 \
JUPYTER_LINK_ONLY=0 \
JUPYTER_UI=notebook
ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["/usr/local/bin/start-jupyter"]
HEALTHCHECK --interval=2m --timeout=20s --start-period=30s --retries=3 CMD /usr/local/bin/healthcheck || exit 1