Repository navigation
Expand file tree
/
Copy pathDockerfile
More file actions
531 lines (452 loc) · 22 KB
/
Copy pathDockerfile
File metadata and controls
531 lines (452 loc) · 22 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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
# syntax=docker/dockerfile:1
# Image: mspass/mspass
# Version: 1.0.0
#
# Build targets:
# runtime - standard MsPASS image, also used by the default final stage
# dev - debug build with development and documentation dependencies
# geolab - GeoLab/Kubernetes image with non-root Jupyter runtime
# mpi - standard image built with the MPI base image
# docker build --target mpi --build-arg MSPASS_BASE_IMAGE=ghcr.io/seisscoped/container-base:latest@sha256:737c7527bc43d2648ad4f7a8c11b9ba8fb93f14c06e06c5baae38e4e019fd2a5 .
# tacc - standard runtime image with TACC interactive access enabled
ARG MSPASS_BASE_IMAGE=ghcr.io/seisscoped/container-base:ubuntu22.04_jupyterlab@sha256:2902ea5d00d7b9bc71ab9c1efd9ebc1b9c4b46c987979e840e8f2aca7391feb4
ARG GEOLAB_BASE_IMAGE=ghcr.io/mspass-team/geolab-base-mirror@sha256:7aa0b713de225288188163c13519efce1ac5248ea386e734d88ad7c54b0abe27
ARG ALPINE_BASE_IMAGE=alpine:3.20@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc
ARG MSPASS_VERSION
ARG MSPASS_VCS_REF
ARG PYTHON_VERSION=3.12
ARG DASK_LABEXTENSION_VERSION=7.0.0
ARG SPARK_VERSION=3.5.8
ARG SPARK_PACKAGE=spark-${SPARK_VERSION}-bin-hadoop3
ARG SPARK_ARCHIVE=${SPARK_PACKAGE}.tgz
ARG APACHE_MIRROR=https://archive.apache.org/dist
ARG SPARK_URL=${APACHE_MIRROR}/spark/spark-${SPARK_VERSION}/${SPARK_ARCHIVE}
ARG SPARK_SHA512=5d5d2e6e111182ee1210d4a46a17ae27107c7ccc70433da0ded3d8197e027f949b00445bb5678ed0c1d5c59f12993d9b9ed12e43686d5aad89a9ec570e93a083
ARG TINI_VERSION=v0.19.0
ARG TINI_SHA256_AMD64=93dcc18adc78c65a028a84799ecf8ad40c936fdfc5f2a57b1acda5a8117fa82c
ARG TINI_SHA256_ARM64=07952557df20bfd2a95f9bef198b445e006171969499a1d361bd9e6f8e5e0e81
FROM scratch AS spark-build-assets
FROM ${ALPINE_BASE_IMAGE} AS verified-build-assets
ARG SPARK_ARCHIVE
ARG SPARK_SHA512
ARG SPARK_URL
ARG TARGETARCH
ARG TINI_VERSION
ARG TINI_SHA256_AMD64
ARG TINI_SHA256_ARM64
COPY --chmod=0755 scripts/verify-container-download.sh /usr/local/bin/
RUN apk add --no-cache ca-certificates curl
RUN --mount=type=bind,from=spark-build-assets,source=/,target=/mnt/build-assets,readonly \
set -eux; \
if [ -f "/mnt/build-assets/${SPARK_ARCHIVE}" ]; then \
cp "/mnt/build-assets/${SPARK_ARCHIVE}" /spark.tgz; \
else \
curl -fL --retry 5 --retry-all-errors --retry-delay 10 \
--connect-timeout 20 --speed-limit 1024 --speed-time 60 --max-time 900 \
-o /spark.tgz "${SPARK_URL}"; \
fi; \
verify-container-download.sh sha512 "${SPARK_SHA512}" /spark.tgz
RUN set -eux; \
case "${TARGETARCH}" in \
amd64) tini_sha256="${TINI_SHA256_AMD64}" ;; \
arm64) tini_sha256="${TINI_SHA256_ARM64}" ;; \
*) echo "Unsupported Tini architecture: ${TARGETARCH}" >&2; exit 1 ;; \
esac; \
curl -fL --retry 5 --retry-all-errors --retry-delay 10 \
--connect-timeout 20 --speed-limit 1024 --speed-time 60 --max-time 300 \
-o /tini "https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-${TARGETARCH}"; \
verify-container-download.sh sha256 "${tini_sha256}" /tini; \
chmod 0755 /tini
FROM ${MSPASS_BASE_IMAGE} AS mspass-base
ARG MSPASS_VERSION
ARG MSPASS_VCS_REF
ARG PYTHON_VERSION
LABEL maintainer="Ian Wang <yinzhi.wang.cug@gmail.com>" \
org.opencontainers.image.source="https://github.com/mspass-team/mspass" \
org.opencontainers.image.version="${MSPASS_VERSION}" \
org.opencontainers.image.revision="${MSPASS_VCS_REF}"
COPY --chmod=0755 scripts/verify-container-download.sh /usr/local/bin/
# The SCOPED base currently pins Python 3.10. GeoLab's notebook environment
# uses Python 3.12, so move every target derived from mspass-base (runtime, dev,
# mpi, and tacc) to the same Python ABI before compiling MsPASS.
RUN set -eux; \
sed -i '/^python[[:space:]]/d' /opt/conda/conda-meta/pinned; \
mamba install --yes "python=${PYTHON_VERSION}"; \
mamba clean --all --force-pkgs-dirs --yes; \
python -c "import sys; expected='${PYTHON_VERSION}'; actual=f'{sys.version_info.major}.{sys.version_info.minor}'; assert actual == expected, (actual, expected)"; \
mamba list python | grep '^python ' | tr -s ' ' | cut -d ' ' -f 1,2 >> /opt/conda/conda-meta/pinned; \
docker-clean
# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
RUN set -eux; \
groupadd --gid 999 --system mongodb; \
useradd --uid 999 --system --gid mongodb --home-dir /data/db mongodb; \
mkdir -p /data/db /data/configdb; \
chown -R mongodb:mongodb /data/db /data/configdb \
&& docker-clean
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
ca-certificates \
curl \
gnupg \
jq \
numactl \
procps \
; \
rm -rf /var/lib/apt/lists/* \
&& docker-clean
# grab "js-yaml" for parsing mongod's YAML config files (https://github.com/nodeca/js-yaml/releases)
ENV JSYAML_VERSION=3.13.1
ARG JSYAML_SHA256=01680f005cd0b602240745d28b8bd439bc41001875f92413252ec0e50983e921
RUN set -eux; \
curl -fL --retry 5 --retry-all-errors --retry-delay 10 \
--connect-timeout 20 --speed-limit 1024 --speed-time 60 --max-time 300 \
-o /js-yaml.js "https://raw.githubusercontent.com/nodeca/js-yaml/${JSYAML_VERSION}/dist/js-yaml.js"; \
verify-container-download.sh sha256 "${JSYAML_SHA256}" /js-yaml.js; \
docker-clean
RUN mkdir /docker-entrypoint-initdb.d
ARG MONGODB_KEY_URL=https://pgp.mongodb.com/server-6.0.asc
ARG MONGODB_KEY_FINGERPRINT=39BD841E4BE5FB195A65400E6A26B1AE64C3C388
RUN set -eux; \
export GNUPGHOME="$(mktemp -d)"; \
curl -fL --retry 5 --retry-all-errors --retry-delay 10 \
--connect-timeout 20 --speed-limit 1024 --speed-time 60 --max-time 300 \
-o /tmp/mongodb-server.asc "${MONGODB_KEY_URL}"; \
actual_fingerprint="$(gpg --batch --show-keys --with-colons /tmp/mongodb-server.asc | awk -F: '$1 == "fpr" { print $10; exit }')"; \
test "${actual_fingerprint}" = "${MONGODB_KEY_FINGERPRINT}"; \
mkdir -p /etc/apt/keyrings; \
gpg --batch --dearmor --output /etc/apt/keyrings/mongodb.gpg /tmp/mongodb-server.asc; \
rm -rf "${GNUPGHOME}" /tmp/mongodb-server.asc \
&& docker-clean
# Allow build-time overrides (eg. to build image with MongoDB Enterprise version)
# Options for MONGO_PACKAGE: mongodb-org OR mongodb-enterprise
# Options for MONGO_REPO: repo.mongodb.org OR repo.mongodb.com
# Example: docker build --build-arg MONGO_PACKAGE=mongodb-enterprise --build-arg MONGO_REPO=repo.mongodb.com .
ARG MONGO_PACKAGE=mongodb-org
ARG MONGO_REPO=repo.mongodb.org
ENV MONGO_PACKAGE=${MONGO_PACKAGE} MONGO_REPO=${MONGO_REPO}
ENV MONGO_MAJOR=6.0
RUN echo "deb [ signed-by=/etc/apt/keyrings/mongodb.gpg ] https://$MONGO_REPO/apt/ubuntu jammy/${MONGO_PACKAGE%-unstable}/$MONGO_MAJOR multiverse" | tee "/etc/apt/sources.list.d/${MONGO_PACKAGE%-unstable}.list"
# https://docs.mongodb.org/master/release-notes/6.0/
ENV MONGO_VERSION=6.0.5
# 03/08/2023, https://github.com/mongodb/mongo/tree/c9a99c120371d4d4c52cbb15dac34a36ce8d3b1d
RUN set -x \
# installing "mongodb-enterprise" pulls in "tzdata" which prompts for input
&& export DEBIAN_FRONTEND=noninteractive \
&& apt-get update \
&& apt-get install -y \
${MONGO_PACKAGE}=$MONGO_VERSION \
${MONGO_PACKAGE}-server=$MONGO_VERSION \
${MONGO_PACKAGE}-shell=$MONGO_VERSION \
${MONGO_PACKAGE}-mongos=$MONGO_VERSION \
${MONGO_PACKAGE}-tools=$MONGO_VERSION \
&& rm -rf /var/lib/apt/lists/* \
&& rm -rf /var/lib/mongodb \
&& mv /etc/mongod.conf /etc/mongod.conf.orig \
&& docker-clean
VOLUME /data/db /data/configdb
# ensure that if running as custom user that "mongosh" has a valid "HOME"
# https://github.com/docker-library/mongo/issues/524
ENV HOME=/data/db
COPY docker-entrypoint.sh /usr/local/bin/
EXPOSE 27017
RUN apt-get update \
&& apt-get install -y wget ssh rsync vim-tiny less graphviz \
build-essential python3-setuptools \
python3-dev python3-pip \
openjdk-8-jdk \
git cmake gfortran gdb \
libgsl-dev liblapack-dev libyaml-dev \
zip unzip \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& docker-clean
ARG TARGETARCH
# Prepare the environment
ARG SPARK_VERSION
ARG SPARK_PACKAGE=spark-${SPARK_VERSION}-bin-hadoop3
ENV JAVA_HOME=/usr/lib/jvm/java-8-openjdk-${TARGETARCH}
ENV SPARK_HOME=/usr/local/spark
ENV PYSPARK_PYTHON=python3
# Install Spark from the CI-provided build asset when present; otherwise the
# verified-build-assets stage downloads it with retries, speed limits, and checksum verification.
COPY --from=verified-build-assets /spark.tgz /tmp/spark.tgz
RUN tar -xzf /tmp/spark.tgz -C /usr/local/ \
&& cd /usr/local && ln -s ${SPARK_PACKAGE} spark \
&& rm /tmp/spark.tgz \
&& docker-clean
RUN ln -s /usr/local/spark/bin/pyspark /usr/bin/pyspark
RUN python -c "import site; print(site.getsitepackages()[0])" > site_packages_path.txt && \
PYTHON_SITE_PACKAGES_PATH=$(cat site_packages_path.txt) && \
ln -s /usr/local/spark/python/pyspark ${PYTHON_SITE_PACKAGES_PATH}/pyspark && \
unzip /usr/local/spark/python/lib/py4j-*-src.zip -d ${PYTHON_SITE_PACKAGES_PATH}/ \
&& docker-clean
# Patch pyspark for machines don't have localhost defined in /etc/hosts
RUN sed -i 's/localhost/127.0.0.1/' /usr/local/spark/python/pyspark/accumulators.py
RUN unzip /usr/local/spark/python/lib/pyspark.zip \
&& sed -i 's/localhost/127.0.0.1/' ./pyspark/accumulators.py \
&& zip /usr/local/spark/python/lib/pyspark.zip pyspark/accumulators.py \
&& rm -r ./pyspark \
&& docker-clean
RUN python -c "import pyspark; expected='${SPARK_VERSION}'; assert pyspark.__version__ == expected, (pyspark.__version__, expected)"
# Install Python tooling
RUN pip3 --no-cache-dir install --upgrade pip \
&& docker-clean
RUN if [ "$TARGETARCH" = "arm64" ]; then export CFLAGS="-O3" \
&& export DISABLE_NUMCODECS_SSE2=true && export DISABLE_NUMCODECS_AVX2=true; fi \
&& pip3 --no-cache-dir install numpy \
&& docker-clean
# Download & install pybind11
ARG PYBIND11_VERSION=2.13.6
ARG PYBIND11_URL=https://github.com/pybind/pybind11/archive/v${PYBIND11_VERSION}.tar.gz
ARG PYBIND11_SHA256=e08cb87f4773da97fa7b5f035de8763abc656d87d5773e62f6da0587d1f0ec20
RUN curl -fL --retry 5 --retry-all-errors --retry-delay 10 \
--connect-timeout 20 --speed-limit 1024 --speed-time 60 --max-time 300 \
-o /tmp/pybind11.tar.gz "${PYBIND11_URL}" \
&& verify-container-download.sh sha256 "${PYBIND11_SHA256}" /tmp/pybind11.tar.gz \
&& tar -xzf /tmp/pybind11.tar.gz -C /usr/local/ \
&& rm /tmp/pybind11.tar.gz \
&& cd /usr/local/pybind11-${PYBIND11_VERSION} \
&& mkdir build && cd build && cmake .. -DPYBIND11_TEST=OFF && make install && docker-clean
RUN rm -r /usr/local/pybind11-${PYBIND11_VERSION}
# Upgrade setuptools to enable namespace package
RUN pip3 --no-cache-dir install --upgrade setuptools \
&& docker-clean
FROM mspass-base AS mspass-source
# Add source needed by the C++ build first so Python-only changes can reuse it.
ADD cxx /mspass/cxx
ENV MSPASS_HOME=/mspass
FROM mspass-source AS runtime-package
ARG MSPASS_VERSION
ARG MSPASS_VCS_REF
# Build the Python extension and C++ library once, then install the C++ artifacts
# from the CMake build tree retained by setuptools.
ADD data /mspass/data
ADD LICENSE /mspass/LICENSE
ADD README.md /mspass/README.md
ADD setup.py /mspass/setup.py
ADD pyproject.toml /mspass/pyproject.toml
ADD python /mspass/python
RUN test -n "${MSPASS_VERSION}" \
&& test -n "${MSPASS_VCS_REF}" \
&& ln -s /opt/conda/include/yaml-cpp /usr/include/yaml-cpp \
&& SETUPTOOLS_SCM_PRETEND_VERSION_FOR_MSPASSPY="${MSPASS_VERSION}" pip3 install /mspass -v \
&& python -c "import importlib.metadata as md; expected='${MSPASS_VERSION}'; actual=md.version('mspasspy'); assert actual == expected, (actual, expected)" \
&& cmake --install /mspass/build/temp.* \
&& rm -rf /mspass/build && docker-clean
FROM mspass-source AS dev-package
ARG MSPASS_VERSION
ARG MSPASS_VCS_REF
# Build the debug Python extension and C++ library once.
ADD data /mspass/data
ADD LICENSE /mspass/LICENSE
ADD README.md /mspass/README.md
ADD setup.py /mspass/setup.py
ADD pyproject.toml /mspass/pyproject.toml
ADD python /mspass/python
RUN test -n "${MSPASS_VERSION}" \
&& test -n "${MSPASS_VCS_REF}" \
&& ln -s /opt/conda/include/yaml-cpp /usr/include/yaml-cpp \
&& SETUPTOOLS_SCM_PRETEND_VERSION_FOR_MSPASSPY="${MSPASS_VERSION}" MSPASS_CMAKE_BUILD_TYPE=Debug pip3 install '/mspass[seisbench,test]' -v \
&& python -c "import importlib.metadata as md; expected='${MSPASS_VERSION}'; actual=md.version('mspasspy'); assert actual == expected, (actual, expected)" \
&& cmake --install /mspass/build/temp.* \
&& rm -rf /mspass/build && docker-clean
# Add docs and dependencies to build docs
ADD docs /mspass/docs
RUN apt-get update && apt-get install -yq doxygen pandoc \
&& rm -rf /var/lib/apt/lists/* \
&& pip3 install -r /mspass/docs/requirements.txt \
&& docker-clean
FROM runtime-package AS runtime-common
# Install jedi
RUN pip3 --no-cache-dir install jedi==0.17.2 && docker-clean
# Tini operates as a process subreaper for jupyter.
COPY --from=verified-build-assets /tini /usr/sbin/tini
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
RUN sed -i '/set -- mongod "$@"/i [[ -d data ]] || mkdir data' /usr/local/bin/docker-entrypoint.sh
# replace localhost to 127.0.0.1 in pymongo to run on HPC
RUN python -c "import site; print(site.getsitepackages()[0])" > site_packages_path.txt && \
PYTHON_SITE_PACKAGES_PATH=$(cat site_packages_path.txt) && \
[ -f "${PYTHON_SITE_PACKAGES_PATH}/pymongo/encryption_options.py" ] && sed -i "s/localhost:27020,/127.0.0.1:27020,/g" "${PYTHON_SITE_PACKAGES_PATH}/pymongo/encryption_options.py" || true && \
[ -f "${PYTHON_SITE_PACKAGES_PATH}/pymongo/mongo_client.py" ] && sed -i 's/HOST = "localhost"/HOST = "127.0.0.1"/g' "${PYTHON_SITE_PACKAGES_PATH}/pymongo/mongo_client.py" || true && \
[ -f "${PYTHON_SITE_PACKAGES_PATH}/pymongo/pool.py" ] && sed -i "s/'localhost'/'127.0.0.1'/g" "${PYTHON_SITE_PACKAGES_PATH}/pymongo/pool.py" || true && \
rm site_packages_path.txt
ENV PATH="${SPARK_HOME}/bin:${SPARK_HOME}/sbin:${PATH}"
# Set the default behavior of this container
ENV SPARK_MASTER_PORT=7077
ENV DASK_SCHEDULER_PORT=8786
ENV MONGODB_PORT=27017
ENV JUPYTER_PORT=8888
ENV MSPASS_ROLE=all
# ENV MSPASS_SCHEDULER dask
FROM runtime-common AS runtime
ARG DASK_LABEXTENSION_VERSION
# install Dask JupyterLab extension
RUN pip3 install dask-labextension==${DASK_LABEXTENSION_VERSION} && docker-clean
# Add startup script
ADD scripts/start-mspass.sh /usr/sbin/start-mspass.sh
RUN chmod +x /usr/sbin/start-mspass.sh
ENTRYPOINT ["/usr/sbin/tini", "-s", "-g", "--", "/usr/sbin/start-mspass.sh"]
FROM runtime AS mpi
FROM ${GEOLAB_BASE_IMAGE} AS geolab
# Build a GeoLab-compatible image from the same Pangeo-style base contract used
# by EarthScope GeoLab. Non-JupyterHub commands must pass through directly so
# Dask Gateway scheduler and worker pods can run the command selected by the
# Gateway server.
USER root
ARG MSPASS_VERSION
ARG MSPASS_VCS_REF
ARG PYTHON_VERSION
LABEL org.opencontainers.image.source="https://github.com/mspass-team/mspass" \
org.opencontainers.image.version="${MSPASS_VERSION}" \
org.opencontainers.image.revision="${MSPASS_VCS_REF}"
# Keep the base GeoLab uid/gid/user identity so mounted /home/jovyan workspaces
# and Dask Gateway scheduler/worker pods match the official GeoLab runtime.
ARG NB_USER=jovyan
ARG NB_UID=1000
ARG NB_GID=1000
ARG NB_HOME=/home/jovyan
ARG MONGO_MAJOR=6.0
ARG MONGO_VERSION=6.0.5
ARG MONGODB_KEY_URL=https://pgp.mongodb.com/server-6.0.asc
ARG MONGODB_KEY_FINGERPRINT=39BD841E4BE5FB195A65400E6A26B1AE64C3C388
ENV NB_USER=${NB_USER} \
NB_UID=${NB_UID} \
NB_GID=${NB_GID} \
NB_HOME=${NB_HOME} \
HOME=${NB_HOME} \
MSPASS_WORK_DIR=${NB_HOME} \
MSPASS_WORKDIR=${NB_HOME} \
MSPASS_DB_DIR=${NB_HOME}/db \
MSPASS_LOG_DIR=${NB_HOME}/logs \
MSPASS_WORKER_DIR=${NB_HOME}/work \
MONGO_DATA_DIR=${NB_HOME}/db/data \
MONGO_LOG=${NB_HOME}/logs/mongo_log \
MSPASS_SCHEDULER=dask \
MSPASS_ENABLE_LOCAL_DASK=true \
MSPASS_DASK_WORKER_COUNT=4 \
MSPASS_DASK_WORKER_THREADS=1 \
MSPASS_DASK_WORKER_MEMORY_LIMIT=0 \
MSPASS_SCHEDULER_ADDRESS=127.0.0.1 \
MSPASS_DB_ADDRESS=127.0.0.1 \
MONGODB_PORT=27017 \
DASK_SCHEDULER_PORT=8786 \
PATH=/srv/conda/envs/notebook/bin:/srv/conda/condabin:/srv/conda/bin:${PATH}
RUN set -eux; \
export DEBIAN_FRONTEND=noninteractive; \
apt-get update; \
apt-get install -y --no-install-recommends \
ca-certificates \
curl \
gnupg \
build-essential \
cmake \
gfortran \
git \
graphviz \
libblas-dev \
libgsl-dev \
liblapack-dev \
libyaml-cpp-dev \
; \
install -d -m 0755 /etc/apt/keyrings; \
export GNUPGHOME="$(mktemp -d)"; \
curl -fsSL "${MONGODB_KEY_URL}" \
-o /tmp/mongodb-server.asc; \
actual_fingerprint="$(gpg --batch --show-keys --with-colons /tmp/mongodb-server.asc | awk -F: '$1 == "fpr" { print $10; exit }')"; \
test "${actual_fingerprint}" = "${MONGODB_KEY_FINGERPRINT}"; \
gpg --batch --dearmor -o "/etc/apt/keyrings/mongodb-server-${MONGO_MAJOR}.gpg" /tmp/mongodb-server.asc; \
echo "deb [ arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/mongodb-server-${MONGO_MAJOR}.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/${MONGO_MAJOR} multiverse" \
> "/etc/apt/sources.list.d/mongodb-org-${MONGO_MAJOR}.list"; \
apt-get update; \
apt-get install -y --no-install-recommends \
mongodb-org=${MONGO_VERSION} \
mongodb-org-server=${MONGO_VERSION} \
mongodb-org-shell=${MONGO_VERSION} \
mongodb-org-mongos=${MONGO_VERSION} \
mongodb-org-tools=${MONGO_VERSION}; \
rm -rf "${GNUPGHOME}" /tmp/mongodb-server.asc; \
rm -rf /var/lib/apt/lists/*
ADD cxx /mspass/cxx
ADD data /mspass/data
ADD LICENSE /mspass/LICENSE
ADD README.md /mspass/README.md
ADD setup.py /mspass/setup.py
ADD pyproject.toml /mspass/pyproject.toml
ADD python /mspass/python
RUN set -eux; \
test -n "${MSPASS_VERSION}"; \
test -n "${MSPASS_VCS_REF}"; \
/srv/conda/envs/notebook/bin/python -c "import sys; expected='${PYTHON_VERSION}'; actual=f'{sys.version_info.major}.{sys.version_info.minor}'; assert actual == expected, (actual, expected)"; \
printf '%s\n' \
'dask==2026.3.0' \
'distributed==2026.3.0' \
'dask-gateway==2026.3.0' \
> /tmp/geolab-dask-constraints.txt; \
/srv/conda/envs/notebook/bin/python -m pip install --no-cache-dir --upgrade pip setuptools wheel setuptools_scm; \
/srv/conda/envs/notebook/bin/python -m pip install --no-cache-dir \
--constraint /tmp/geolab-dask-constraints.txt \
'dask[complete]==2026.3.0' \
'distributed==2026.3.0' \
'dask-gateway==2026.3.0'; \
SETUPTOOLS_SCM_PRETEND_VERSION_FOR_MSPASSPY="${MSPASS_VERSION}" \
/srv/conda/envs/notebook/bin/python -m pip install --no-cache-dir -v \
--constraint /tmp/geolab-dask-constraints.txt \
/mspass; \
/srv/conda/envs/notebook/bin/python -m pip install --no-cache-dir --force-reinstall --no-deps \
dask==2026.3.0 \
distributed==2026.3.0 \
dask-gateway==2026.3.0; \
/srv/conda/envs/notebook/bin/python -c "import importlib.metadata as md; expected={'dask':'2026.3.0','distributed':'2026.3.0','dask-gateway':'2026.3.0','mspasspy':'${MSPASS_VERSION}'}; actual={name: md.version(name) for name in expected}; print(actual); assert actual == expected, actual; import mspasspy; print(mspasspy.__file__)"; \
rm -rf /mspass/build /root/.cache /tmp/geolab-dask-constraints.txt
ADD scripts/start-mspass-geolab-entrypoint.sh /usr/sbin/start-mspass-geolab-entrypoint.sh
ADD scripts/start-mspass-geolab.sh /usr/sbin/start-mspass-geolab.sh
ADD scripts/start-mspass-geolab-singleuser.py /usr/sbin/start-mspass-geolab-singleuser.py
RUN set -eux; \
singleuser=/srv/conda/envs/notebook/bin/jupyterhub-singleuser; \
test -f "$singleuser"; \
mv "$singleuser" "$singleuser.mspass-original"; \
cp /usr/sbin/start-mspass-geolab-singleuser.py "$singleuser"; \
chmod +x \
/usr/sbin/start-mspass-geolab-entrypoint.sh \
/usr/sbin/start-mspass-geolab.sh \
/usr/sbin/start-mspass-geolab-singleuser.py \
"$singleuser"; \
mkdir -p "${NB_HOME}" && \
chown -R "${NB_UID}:${NB_GID}" "${NB_HOME}" /mspass
USER ${NB_UID}:${NB_GID}
WORKDIR ${NB_HOME}
ENTRYPOINT ["/usr/sbin/start-mspass-geolab-entrypoint.sh"]
CMD ["jupyter", "lab", "--ip=0.0.0.0", "--no-browser"]
FROM dev-package AS dev
ARG DASK_LABEXTENSION_VERSION
# Install jedi
RUN pip3 --no-cache-dir install jedi==0.17.2 && docker-clean
# Tini operates as a process subreaper for jupyter.
COPY --from=verified-build-assets /tini /usr/sbin/tini
# Add startup script
ADD scripts/start-mspass.sh /usr/sbin/start-mspass.sh
RUN chmod +x /usr/sbin/start-mspass.sh
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
RUN sed -i '/set -- mongod "$@"/i [[ -d data ]] || mkdir data' /usr/local/bin/docker-entrypoint.sh
# replace localhost to 127.0.0.1 in pymongo to run on HPC
RUN python -c "import site; print(site.getsitepackages()[0])" > site_packages_path.txt && \
PYTHON_SITE_PACKAGES_PATH=$(cat site_packages_path.txt) && \
[ -f "${PYTHON_SITE_PACKAGES_PATH}/pymongo/encryption_options.py" ] && sed -i "s/localhost:27020,/127.0.0.1:27020,/g" "${PYTHON_SITE_PACKAGES_PATH}/pymongo/encryption_options.py" || true && \
[ -f "${PYTHON_SITE_PACKAGES_PATH}/pymongo/mongo_client.py" ] && sed -i 's/HOST = "localhost"/HOST = "127.0.0.1"/g' "${PYTHON_SITE_PACKAGES_PATH}/pymongo/mongo_client.py" || true && \
[ -f "${PYTHON_SITE_PACKAGES_PATH}/pymongo/pool.py" ] && sed -i "s/'localhost'/'127.0.0.1'/g" "${PYTHON_SITE_PACKAGES_PATH}/pymongo/pool.py" || true && \
rm site_packages_path.txt
# install Dask JupyterLab extension
RUN pip3 install dask-labextension==${DASK_LABEXTENSION_VERSION} && docker-clean
ENV PATH="${SPARK_HOME}/bin:${SPARK_HOME}/sbin:${PATH}"
# Set the default behavior of this container
ENV SPARK_MASTER_PORT=7077
ENV DASK_SCHEDULER_PORT=8786
ENV MONGODB_PORT=27017
ENV JUPYTER_PORT=8888
ENV MSPASS_ROLE=all
# ENV MSPASS_SCHEDULER dask
ENTRYPOINT ["/usr/sbin/tini", "-s", "-g", "--", "/usr/sbin/start-mspass.sh"]
FROM runtime AS tacc
ENV MSPASS_TACC_MODE=true
FROM runtime AS final