This is an automated email from the ASF dual-hosted git repository.
pan3793 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/celeborn.git
The following commit(s) were added to refs/heads/main by this push:
new 4e20a611c [CELEBORN-2390] Add docker-compose for local cluster
4e20a611c is described below
commit 4e20a611c9953bc817b83712caaee233a7b1db10
Author: yew1eb <[email protected]>
AuthorDate: Mon Aug 24 16:50:48 2026 +0800
[CELEBORN-2390] Add docker-compose for local cluster
### What changes were proposed in this pull request?
Add a `docker-compose.yaml` under `docker/` to run a minimal local Celeborn
cluster (1 master + 3 workers) for development and debugging, with a
`build-image.sh` wrapper and a `conf/`. The existing `docker/Dockerfile` is
unchanged.
### Why are the changes needed?
There is no lightweight local deployment path today — only the K8s Helm
chart or running master/worker manually on the host.
### Does this PR resolve a correctness bug?
- [ ] Yes
### Does this PR introduce _any_ user-facing change?
- [ ] Yes
### How was this patch tested?
Ran the cluster locally with `docker compose up -d`:
> $ docker compose -f docker/docker-compose.yaml up -d
> $ docker compose -f docker/docker-compose.yaml ps
<img width="1354" height="132" alt="image"
src="https://github.com/user-attachments/assets/86b8e10b-9058-498f-b532-15ab2ce5f3fc"
/>
<img width="1510" height="119" alt="image"
src="https://github.com/user-attachments/assets/96cd905d-be6a-4c46-9c62-70f77927a3d1"
/>
Closes #3768 from yew1eb/add-docker-compose.
Authored-by: yew1eb <[email protected]>
Signed-off-by: Cheng Pan <[email protected]>
---
.gitignore | 2 +
docker/README.md | 107 +++++++++++++++++++++++++++
docker/build-image.sh | 107 +++++++++++++++++++++++++++
docker/conf/celeborn-defaults.conf | 72 ++++++++++++++++++
docker/conf/log4j2.xml | 129 ++++++++++++++++++++++++++++++++
docker/conf/metrics.properties | 20 +++++
docker/docker-compose.yaml | 134 ++++++++++++++++++++++++++++++++++
docker/spark/Dockerfile | 40 ++++++++++
docker/spark/conf/spark-defaults.conf | 43 +++++++++++
9 files changed, 654 insertions(+)
diff --git a/.gitignore b/.gitignore
index 970fea471..847ea21f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,3 +76,5 @@ spark-warehouse/
logs
pids
tmp/
+
+*.jar
diff --git a/docker/README.md b/docker/README.md
new file mode 100644
index 000000000..3187d0d5d
--- /dev/null
+++ b/docker/README.md
@@ -0,0 +1,107 @@
+<!---
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+# Celeborn Local Cluster (docker-compose)
+
+A local Celeborn cluster — 1 master (HA off) + 3 workers — for development and
+debugging, optionally accompanied by an example Spark cluster (1 master + 2
+workers) whose shuffle data is served by Celeborn, so the whole stack can be
+exercised end-to-end.
+
+## Prerequisites
+
+- Docker (daemon running) and the `docker compose` plugin.
+- The repo bundles `build/mvn` used by `make-distribution.sh`. No system Maven
+ required, but a JDK is needed to run the build.
+
+## 1. Build the images
+
+```bash
+# Celeborn only (no Spark example cluster):
+./docker/build-image.sh
+
+# Also build the Spark example image (celeborn-spark:dev):
+./docker/build-image.sh -Pspark-3.5 # Spark 3 client
+./docker/build-image.sh -Pspark-4.0 # or Spark 4 client
+```
+
+This runs `./build/make-distribution.sh` (producing `dist/`) and then:
+
+- `docker build -t celeborn:dev -f dist/docker/Dockerfile dist/` — the
+ Celeborn master/worker image, always built.
+- If a Spark client profile was passed (e.g. `-Pspark-3.5` or `-Pspark-4.0`),
+ the distribution also produces
`dist/spark/celeborn-client-spark-*-shaded_*.jar`;
+ `build-image.sh` copies it into `docker/spark/` and runs
+ `docker build -t celeborn-spark:dev docker/spark/` — a Spark image with the
+ Celeborn client jar baked in. Without a Spark profile, the Spark image is
+ skipped and only the Celeborn cluster is available.
+
+## 2. Start the cluster
+
+```bash
+docker compose -f docker/docker-compose.yaml up -d
+docker compose -f docker/docker-compose.yaml ps
+```
+
+The Celeborn master has a healthcheck; the 3 workers start once the master is
+healthy and register with it. When `celeborn-spark:dev` is present, the Spark
+master and 2 workers start after the Celeborn master is healthy.
+
+## 3. Verify
+
+```bash
+# Master HTTP (expect HTTP 200)
+curl -s -o /dev/null -w '%{http_code}\n' http://localhost:9098/metrics/json
+
+# Tail logs; look for "Registered worker" on the master
+docker compose -f docker/docker-compose.yaml logs -f celeborn-master
+```
+
+## 4. Run a Spark job that shuffles through Celeborn
+
+Spark is pre-wired to use Celeborn via `docker/spark/conf/spark-defaults.conf`
+(`spark.shuffle.manager=...CelebornShuffleManager`, master endpoints
+`celeborn-master:9097`), so no extra `--conf` flags are needed. Submit Spark's
+built-in `GroupByTest`, which forces a shuffle:
+
+```bash
+docker exec celeborn-spark-master /opt/spark/bin/run-example \
+ --master spark://celeborn-spark-master:7077 \
+ GroupByTest 10 100 1000 10
+```
+
+The job should finish with `final status: SUCCEEDED`. While it runs, watch the
+shuffle traffic land on Celeborn workers (push/fetch requests) and confirm the
+shuffle really went through Celeborn rather than the local Spark shuffle:
+
+```bash
+docker compose -f docker/docker-compose.yaml logs -f celeborn-worker
+# Master HTTP metrics: the worker push/fetch counters should increase.
+curl -s http://localhost:9098/metrics/json | grep -i shuffle
+```
+
+The Spark master Web UI is at http://localhost:8080/.
+
+## 5. Stop
+
+```bash
+docker compose -f docker/docker-compose.yaml down
+```
+
+`down` removes the containers (Celeborn and Spark) and the `shared` volume, so
+worker shuffle data (in the containers' writable layer) is discarded.
+`restart` keeps it.
diff --git a/docker/build-image.sh b/docker/build-image.sh
new file mode 100755
index 000000000..ea09bd93e
--- /dev/null
+++ b/docker/build-image.sh
@@ -0,0 +1,107 @@
+#!/usr/bin/env bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Build the local Celeborn docker image (celeborn:dev) for docker-compose.
+#
+# Two steps:
+# 1. ./build/make-distribution.sh -> produces dist/ (bin/sbin/conf/jars/...
+# plus dist/docker/Dockerfile copied by the distribution script itself)
+# 2. docker build -> builds the image with context = dist/
+# (the Dockerfile COPYs bin/sbin/... relative to the context root)
+#
+# The image is NOT built via the compose `build:` key, because
make-distribution
+# must run before docker build and dist/ may not exist yet when invoking
+# `docker compose up`.
+#
+# When a Spark client profile is passed (e.g. -Pspark-3.5 or -Pspark-4.0),
+# make-distribution also drops the client shaded jar at dist/spark/. Step 3
+# then builds an additional Spark image (celeborn-spark:dev) on top of
+# apache/spark with that jar baked in, so docker-compose can run an end-to-end
+# Spark cluster whose shuffle is served by Celeborn.
+#
+# Usage:
+# ./docker/build-image.sh # core master/worker/cli
+# ./docker/build-image.sh -Pspark-3.5 # also build Spark 3 client +
image
+# ./docker/build-image.sh -Pspark-4.0 # also build Spark 4 client +
image
+# CELEBORN_IMAGE_TAG=celeborn:dev ./docker/build-image.sh
+# CELEBORN_SPARK_IMAGE_TAG=celeborn-spark:dev ./docker/build-image.sh
-Pspark-3.5
+# CELEBORN_SPARK_BASE_TAG=3.5.9 ./docker/build-image.sh -Pspark-3.5
+#
+# Requirements: docker on PATH; build/mvn is bundled in the repo.
+
+set -euo pipefail
+
+# Resolve repo root (parent of this script's directory).
+cd "$(dirname "$0")/.."
+
+IMAGE_TAG="${CELEBORN_IMAGE_TAG:-celeborn:dev}"
+SPARK_IMAGE_TAG="${CELEBORN_SPARK_IMAGE_TAG:-celeborn-spark:dev}"
+# Base apache/spark image tag. Renamed away from SPARK_IMAGE_TAG because that
+# env var is commonly exported by Spark's own tooling and would otherwise leak
+# in here as the *output* image name (e.g. celeborn-spark:dev), producing an
+# invalid "apache/spark:celeborn-spark:dev" FROM reference. When unset, the
+# default is derived from the built client jar's Spark major version below.
+BASE_SPARK_IMAGE_TAG="${CELEBORN_SPARK_BASE_TAG:-}"
+
+echo "==> Building distribution (./build/make-distribution.sh --sbt-enabled
$*) ..."
+./build/make-distribution.sh --sbt-enabled "$@"
+
+echo "==> Building docker image ${IMAGE_TAG} from dist/ ..."
+if [ ! -f dist/docker/Dockerfile ]; then
+ echo "ERROR: dist/docker/Dockerfile not found. make-distribution may have
failed." >&2
+ exit 1
+fi
+docker build -t "${IMAGE_TAG}" -f dist/docker/Dockerfile dist/
+
+# --- Spark image (optional)
---------------------------------------------------
+# Only build it when make-distribution produced a Spark client shaded jar,
which
+# happens when a -Pspark-3.x/-Pspark-4.x profile was passed. If absent, skip
+# gracefully so the Celeborn-only workflow keeps working.
+SPARK_CLIENT_JAR="$(ls dist/spark/celeborn-client-spark-*-shaded_*.jar
2>/dev/null || true)"
+
+if [ -n "${SPARK_CLIENT_JAR}" ]; then
+ echo "==> Found Spark client jar: $(basename "${SPARK_CLIENT_JAR}")"
+ # Derive the default base image tag from the jar's Spark major version, e.g.
+ # celeborn-client-spark-4-shaded_2.13-<version>.jar -> 4 ->
apache/spark:4.0.4.
+ SPARK_MAJOR_VERSION="$(basename "${SPARK_CLIENT_JAR}" | sed -E
's/celeborn-client-spark-([0-9]+)-shaded_.*/\1/')"
+ if [ -z "${BASE_SPARK_IMAGE_TAG}" ]; then
+ case "${SPARK_MAJOR_VERSION}" in
+ 3) BASE_SPARK_IMAGE_TAG="3.5.9" ;;
+ 4) BASE_SPARK_IMAGE_TAG="4.0.4" ;;
+ *)
+ echo "ERROR: unsupported Spark major version
'${SPARK_MAJOR_VERSION}'," >&2
+ echo " set CELEBORN_SPARK_BASE_TAG explicitly." >&2
+ exit 1
+ ;;
+ esac
+ fi
+ # Copy into the Spark image build context (excluded from git via .gitignore).
+ cp "${SPARK_CLIENT_JAR}" docker/spark/
+ echo "==> Building Spark docker image ${SPARK_IMAGE_TAG} (base
apache/spark:${BASE_SPARK_IMAGE_TAG}) ..."
+ docker build -t "${SPARK_IMAGE_TAG}" \
+ --build-arg spark_image_tag="${BASE_SPARK_IMAGE_TAG}" \
+ docker/spark/
+ echo "==> Done. Images: ${IMAGE_TAG}, ${SPARK_IMAGE_TAG}"
+ echo "Start the cluster (Celeborn + Spark):"
+ echo " docker compose -f docker/docker-compose.yaml up -d"
+else
+ echo "==> No Spark client jar under dist/spark/ (pass e.g. -Pspark-3.5 or
-Pspark-4.0 to build one)."
+ echo "==> Skipped Spark image. Done. Image: ${IMAGE_TAG}"
+ echo "Start the Celeborn-only cluster:"
+ echo " docker compose -f docker/docker-compose.yaml up -d"
+fi
diff --git a/docker/conf/celeborn-defaults.conf
b/docker/conf/celeborn-defaults.conf
new file mode 100644
index 000000000..a55234202
--- /dev/null
+++ b/docker/conf/celeborn-defaults.conf
@@ -0,0 +1,72 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# This is a docker-compose local cluster configuration for Apache Celeborn.
+# Topology: 1 master (HA disabled) + 3 workers.
+# Config values are aligned with charts/celeborn/values.yaml, with local
+# downsizing (single master, 1 disk x 10G, smaller memory via compose env).
+#
+# Mounted (read-only) into /opt/celeborn/conf/ by docker-compose.yaml, which
+# overrides the image's conf (the image only ships *.template files that are
+# NOT loaded by default -- see Utils.getDefaultPropertiesFile).
+
+celeborn.metrics.enabled true
+
+# ----------------------------------------------------------------------------
+# Master (single node, HA disabled)
+# ----------------------------------------------------------------------------
+# Bind/advertise host = compose service name, so workers/clients can reach
+# master via celeborn-master:9097 on the compose network.
+celeborn.master.host celeborn-master
+celeborn.master.port 9097
+celeborn.master.http.port 9098
+celeborn.master.endpoints celeborn-master:9097
+celeborn.master.ha.enabled false
+celeborn.master.heartbeat.worker.timeout 120s
+celeborn.master.heartbeat.application.timeout 300s
+
+# ----------------------------------------------------------------------------
+# Worker
+# ----------------------------------------------------------------------------
+celeborn.worker.http.port 9096
+# Store shuffle data in the image-writable /opt/celeborn/data (owned by the
+# celeborn user). No host bind-mount; data lives in the container's writable
+# layer, so it survives restarts but is discarded when the container is removed
+# (e.g. `docker compose down`).
+celeborn.worker.storage.dirs
/opt/celeborn/data:disktype=SSD:capacity=10G
+celeborn.worker.storage.disk.reserve.size 1G
+# Containers have no real /sys/block disk info; disable device monitor to
+# avoid workers being wrongly excluded as unhealthy.
+celeborn.worker.monitor.disk.enabled false
+celeborn.worker.fetch.io.threads 32
+celeborn.worker.push.io.threads 32
+celeborn.worker.heartbeat.timeout 120s
+
+# ----------------------------------------------------------------------------
+# Network
+# ----------------------------------------------------------------------------
+celeborn.rpc.io.serverThreads 64
+celeborn.rpc.io.numConnectionsPerPeer 2
+celeborn.rpc.io.clientThreads 64
+celeborn.rpc.dispatcher.numThreads 4
+
+# ----------------------------------------------------------------------------
+# Shuffle / Client
+# ----------------------------------------------------------------------------
+celeborn.shuffle.chunk.size 8m
+celeborn.client.push.stageEnd.timeout 120s
+
diff --git a/docker/conf/log4j2.xml b/docker/conf/log4j2.xml
new file mode 100644
index 000000000..68a304c3e
--- /dev/null
+++ b/docker/conf/log4j2.xml
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!--
+ ~ Extra logging related to initialization of Log4j.
+ ~ Set to debug or trace if log4j initialization is failing.
+ -->
+<Configuration status="INFO">
+ <Appenders>
+ <Console name="stdout" target="SYSTEM_OUT">
+ <!--
+ ~ In the pattern layout configuration below, we specify an
explicit `%ex` conversion
+ ~ pattern for logging Throwables. If this was omitted, then (by
default) Log4J would
+ ~ implicitly add an `%xEx` conversion pattern which logs
stacktraces with additional
+ ~ class packaging information. That extra information can
sometimes add a substantial
+ ~ performance overhead, so we disable it in our default logging
config.
+ -->
+ <PatternLayout pattern="%d{yy/MM/dd HH:mm:ss,SSS} %p [%t] %c{1}:
%m%n%ex"/>
+ </Console>
+ <RollingRandomAccessFile name="file"
fileName="${env:CELEBORN_LOG_DIR}/celeborn.log"
+
filePattern="${env:CELEBORN_LOG_DIR}/celeborn.log.%d-%i">
+ <PatternLayout pattern="%d{yy/MM/dd HH:mm:ss,SSS} %p [%t] %c{1}:
%m%n%ex"/>
+ <Policies>
+ <SizeBasedTriggeringPolicy size="200 MB"/>
+ </Policies>
+ <DefaultRolloverStrategy max="7">
+ <Delete basePath="${env:CELEBORN_LOG_DIR}" maxDepth="1">
+ <IfFileName glob="celeborn.log*">
+ <IfAny>
+ <IfAccumulatedFileSize exceeds="1 GB"/>
+ <IfAccumulatedFileCount exceeds="10"/>
+ </IfAny>
+ </IfFileName>
+ </Delete>
+ </DefaultRolloverStrategy>
+ </RollingRandomAccessFile>
+ <RollingRandomAccessFile name="restAuditFile"
fileName="${env:CELEBORN_LOG_DIR}/audit/rest-audit.log"
+
filePattern="${env:CELEBORN_LOG_DIR}/audit/rest-audit.log.%d-%i">
+ <PatternLayout pattern="%d{yy/MM/dd HH:mm:ss,SSS} %p [%t] %c{1}:
%m%n%ex"/>
+ <Policies>
+ <SizeBasedTriggeringPolicy size="200 MB"/>
+ </Policies>
+ <DefaultRolloverStrategy max="7">
+ <Delete basePath="${env:CELEBORN_LOG_DIR}/audit" maxDepth="1">
+ <IfFileName glob="rest-audit.log*">
+ <IfAny>
+ <IfAccumulatedFileSize exceeds="1 GB"/>
+ <IfAccumulatedFileCount exceeds="10"/>
+ </IfAny>
+ </IfFileName>
+ </Delete>
+ </DefaultRolloverStrategy>
+ </RollingRandomAccessFile>
+ <RollingRandomAccessFile name="shuffleAuditFile"
fileName="${env:CELEBORN_LOG_DIR}/audit/shuffle-audit.log"
+
filePattern="${env:CELEBORN_LOG_DIR}/audit/shuffle-audit.log.%d-%i">
+ <PatternLayout pattern="%d{yy/MM/dd HH:mm:ss,SSS} %p [%t] %c{1}:
%m%n%ex"/>
+ <Policies>
+ <SizeBasedTriggeringPolicy size="200 MB"/>
+ </Policies>
+ <DefaultRolloverStrategy max="7">
+ <Delete basePath="${env:CELEBORN_LOG_DIR}/audit" maxDepth="1">
+ <IfFileName glob="shuffle-audit.log*">
+ <IfAny>
+ <IfAccumulatedFileSize exceeds="1 GB"/>
+ <IfAccumulatedFileCount exceeds="10"/>
+ </IfAny>
+ </IfFileName>
+ </Delete>
+ </DefaultRolloverStrategy>
+ </RollingRandomAccessFile>
+ <RollingRandomAccessFile name="metricsAuditFile"
fileName="${env:CELEBORN_LOG_DIR}/audit/metrics-audit.log"
+
filePattern="${env:CELEBORN_LOG_DIR}/audit/metrics-audit.log.%d-%i">
+ <PatternLayout pattern="%d{yy/MM/dd HH:mm:ss,SSS} %p [%t] %c{1}:
%m%n%ex"/>
+ <Policies>
+ <SizeBasedTriggeringPolicy size="200 MB"/>
+ </Policies>
+ <DefaultRolloverStrategy max="7">
+ <Delete basePath="${env:CELEBORN_LOG_DIR}/audit" maxDepth="1">
+ <IfFileName glob="metrics-audit.log*">
+ <IfAny>
+ <IfAccumulatedFileSize exceeds="1 GB"/>
+ <IfAccumulatedFileCount exceeds="10"/>
+ </IfAny>
+ </IfFileName>
+ </Delete>
+ </DefaultRolloverStrategy>
+ </RollingRandomAccessFile>
+ </Appenders>
+
+ <Loggers>
+ <Root level="INFO">
+ <AppenderRef ref="stdout"/>
+ <AppenderRef ref="file"/>
+ </Root>
+ <Logger name="org.apache.hadoop.hdfs" level="WARN" additivity="false">
+ <Appender-ref ref="stdout" level="WARN"/>
+ <Appender-ref ref="file" level="WARN"/>
+ </Logger>
+ <Logger name="org.apache.ratis.server.RaftServerConfigKeys"
level="WARN" additivity="false">
+ <Appender-ref ref="stdout" level="WARN"/>
+ <Appender-ref ref="file" level="WARN"/>
+ </Logger>
+ <Logger name="org.apache.celeborn.server.common.http.RestAuditLogger"
level="INFO" additivity="false">
+ <Appender-ref ref="restAuditFile" level="INFO"/>
+ </Logger>
+ <Logger
name="org.apache.celeborn.service.deploy.master.audit.ShuffleAuditLogger"
level="INFO"
+ additivity="false">
+ <Appender-ref ref="shuffleAuditFile" level="INFO"/>
+ </Logger>
+ <Logger name="org.apache.celeborn.common.metrics.sink.LoggerSink"
level="INFO" additivity="false">
+ <Appender-ref ref="metricsAuditFile" level="INFO"/>
+ </Logger>
+ </Loggers>
+</Configuration>
diff --git a/docker/conf/metrics.properties b/docker/conf/metrics.properties
new file mode 100644
index 000000000..e3b521369
--- /dev/null
+++ b/docker/conf/metrics.properties
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+*.sink.prometheusServlet.class=org.apache.celeborn.common.metrics.sink.PrometheusServlet
+*.sink.jsonServlet.class=org.apache.celeborn.common.metrics.sink.JsonServlet
+*.sink.loggerSink.class=org.apache.celeborn.common.metrics.sink.LoggerSink
diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml
new file mode 100644
index 000000000..a6f310c5e
--- /dev/null
+++ b/docker/docker-compose.yaml
@@ -0,0 +1,134 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Local docker-compose cluster for Apache Celeborn.
+# Topology: 1 master (HA disabled) + 3 workers (via replicas)
+# + an example Spark cluster (1 master + 2 workers) whose shuffle
+# data is served by Celeborn, so the whole stack can be exercised
+# end-to-end without exposing any extra ports.
+#
+# Prerequisites: build the images first ->
+# ./docker/build-image.sh -Pspark-3.5
+# (produces celeborn:dev and celeborn-spark:dev)
+#
+# Usage:
+# docker compose -f docker/docker-compose.yaml up -d
+# docker compose -f docker/docker-compose.yaml logs -f
+# docker compose -f docker/docker-compose.yaml down
+
+name: celeborn
+
+services:
+ celeborn-master:
+ image: celeborn:dev
+ container_name: celeborn-master
+ hostname: celeborn-master
+ command: /opt/celeborn/sbin/start-master.sh
+ environment:
+ # Foreground mode so the JVM stays as PID 1 (else nohup & container
exits).
+ CELEBORN_NO_DAEMONIZE: "true"
+ CELEBORN_MASTER_MEMORY: 1g
+ ports:
+ - "9097:9097" # RPC
+ - "9098:9098" # HTTP REST / metrics
+ volumes:
+ - ./conf:/opt/celeborn/conf:ro
+ networks:
+ - celeborn-net
+ healthcheck:
+ # Master binds to its container IP (via celeborn-master host), not
127.0.0.1.
+ test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/celeborn-master/9098' ||
exit 1"]
+ interval: 10s
+ timeout: 3s
+ retries: 6
+ start_period: 40s
+ restart: unless-stopped
+
+ celeborn-worker:
+ image: celeborn:dev
+ command: /opt/celeborn/sbin/start-worker.sh
+ deploy:
+ replicas: 3
+ environment:
+ CELEBORN_NO_DAEMONIZE: "true"
+ CELEBORN_WORKER_MEMORY: 1g
+ CELEBORN_WORKER_OFFHEAP_MEMORY: 1g
+ volumes:
+ - ./conf:/opt/celeborn/conf:ro
+ networks:
+ - celeborn-net
+ depends_on:
+ celeborn-master:
+ condition: service_healthy
+ restart: unless-stopped
+
+ spark-master:
+ image: celeborn-spark:dev
+ container_name: celeborn-spark-master
+ hostname: celeborn-spark-master
+ command: /opt/spark/bin/spark-class org.apache.spark.deploy.master.Master
-h celeborn-spark-master
+ environment:
+ SPARK_MASTER_HOST: celeborn-spark-master
+ SPARK_MASTER_PORT: 7077
+ SPARK_MASTER_WEBUI_PORT: 8080
+ SPARK_PUBLIC_DNS: localhost
+ volumes:
+ # Pre-wire Spark to use Celeborn shuffle (see spark-defaults.conf).
+ - ./spark/conf/spark-defaults.conf:/opt/spark/conf/spark-defaults.conf:ro
+ - shared:/shared:rw
+ ports:
+ - "4040:4040" # Spark application UI (when a job runs on the master)
+ - "7077:7077" # Spark master RPC
+ - "8080:8080" # Spark master Web UI
+ networks:
+ - celeborn-net
+ depends_on:
+ celeborn-master:
+ condition: service_healthy
+ healthcheck:
+ test: ["CMD-SHELL", "bash -c 'echo >
/dev/tcp/celeborn-spark-master/8080' || exit 1"]
+ interval: 10s
+ timeout: 3s
+ retries: 6
+ start_period: 20s
+ restart: unless-stopped
+
+ spark-worker:
+ image: celeborn-spark:dev
+ command: /opt/spark/bin/spark-class org.apache.spark.deploy.worker.Worker
spark://celeborn-spark-master:7077
+ deploy:
+ replicas: 2
+ environment:
+ SPARK_WORKER_CORES: 1
+ SPARK_WORKER_MEMORY: 1g
+ SPARK_WORKER_WEBUI_PORT: 8081
+ SPARK_PUBLIC_DNS: localhost
+ volumes:
+ - ./spark/conf/spark-defaults.conf:/opt/spark/conf/spark-defaults.conf:ro
+ - shared:/shared:rw
+ networks:
+ - celeborn-net
+ depends_on:
+ spark-master:
+ condition: service_healthy
+ restart: unless-stopped
+
+networks:
+ celeborn-net:
+ driver: bridge
+
+volumes:
+ shared:
diff --git a/docker/spark/Dockerfile b/docker/spark/Dockerfile
new file mode 100644
index 000000000..355475eb4
--- /dev/null
+++ b/docker/spark/Dockerfile
@@ -0,0 +1,40 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Spark image pre-wired to use Celeborn as its shuffle service.
+#
+# Built by docker/build-image.sh, which copies the Celeborn client shaded jar
+# produced by make-distribution.sh (under dist/spark/) into this directory so
+# it can be COPYed into /opt/spark/jars. The jar is not committed to the repo.
+#
+# Usage (built by build-image.sh, not directly):
+# docker build -t celeborn-spark:dev docker/spark/
+
+ARG spark_image_tag=3.5.9
+FROM apache/spark:${spark_image_tag}
+
+ENV PATH="${PATH}:/opt/spark/bin"
+
+USER root
+RUN mkdir -p /home/spark /shared && chown spark:spark /home/spark /shared
+
+# Celeborn client shaded jar enables
spark.shuffle.manager=...CelebornShuffleManager
+COPY celeborn-client-spark-*-shaded_*.jar /opt/spark/jars/
+
+VOLUME /shared
+
+USER spark
diff --git a/docker/spark/conf/spark-defaults.conf
b/docker/spark/conf/spark-defaults.conf
new file mode 100644
index 000000000..583f7b1b5
--- /dev/null
+++ b/docker/spark/conf/spark-defaults.conf
@@ -0,0 +1,43 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Spark defaults that route shuffle data through the Celeborn cluster running
+# alongside this Spark container in docker-compose. Mounted into
+# /opt/spark/conf/spark-defaults.conf so spark-submit/spark-shell pick it up
+# automatically (no need to repeat --conf flags on every invocation).
+#
+# See README.md "Configure Spark" of the Celeborn repo and
+# tests/spark-it/.../SparkTestBase.scala for the authoritative option set.
+
+# Route shuffle through Celeborn. The shuffle manager class name changed in
+# Celeborn 0.3.0 (was RssShuffleManager); this repo is >= 0.3.0.
+spark.shuffle.manager
org.apache.spark.shuffle.celeborn.SparkShuffleManager
+# Kryo is required: the shaded client relocates packages, which the Java
+# serializer cannot handle.
+spark.serializer
org.apache.spark.serializer.KryoSerializer
+
+# Connect to the Celeborn master started by docker-compose (RPC port 9097).
+# Spark only talks to the master; worker push/fetch ports are random and
+# handed out by the master, so workers need no exposed ports.
+spark.celeborn.master.endpoints celeborn-master:9097
+spark.shuffle.service.enabled false
+spark.celeborn.client.spark.shuffle.writer hash
+spark.celeborn.client.push.replicate.enabled true
+
+# AQE local shuffle reader is incompatible with remote (Celeborn) shuffle.
+spark.sql.adaptive.enabled true
+spark.sql.adaptive.localShuffleReader.enabled false