This is an automated email from the ASF dual-hosted git repository.
hello-stephen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new b3b1eab90ca [feat](docker) add an all-in-one image for 4.1 integration
testing (#66983)
b3b1eab90ca is described below
commit b3b1eab90ca567246d0564452e325d6e19688821
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Thu Aug 20 15:57:41 2026 +0800
[feat](docker) add an all-in-one image for 4.1 integration testing (#66983)
## Proposed changes
Downstream projects that want to run integration tests against Doris
need one
container they can start and connect to, not a compose file with an FE,
a BE
and a wait loop. This adds that image for the **4.1 release line** under
`docker/runtime/all-in-one/4.1/`.
It also removes the old `docker/runtime/all-in-one/` recipe, which no
longer
worked: it pinned JDK 8 (so 3.0+ would not start), carried a literal
`apache-doris-x.x.x-bin-` placeholder that had to be hand-edited, and
laid its
`resource/` tree out incompatibly with what `docker-build.sh` produces.
Its
entrypoint also called `stop_fe.sh` / `stop_be.sh`, which cannot work
under
`--console` (neither leaves a usable pid file).
### Assembly, not repackaging
`fe/` and `be/` are copied straight out of the official
`apache/doris:fe-<v>`
and `:be-<v>` images, so a new Doris release needs no work here. buildx
resolves those per target platform, which is what lets one tag be
multi-arch
without staging per-arch directories by hand. A locally built `./output`
and an
extracted release tarball are accepted too; all three normalise into the
same
artifact stage.
### Two tags
| tag | covers | size |
|---|---|---|
| `all-in-one-<v>` | internal tables, Hive, Iceberg (incl. system
tables), Paimon, JDBC catalogs, external-table writeback, Java UDF |
2.46 GB |
| `all-in-one-<v>-full` | the above plus Hudi, Trino connector,
MaxCompute | 2.99 GB |
against 4.9 GB for the same payload untouched.
The largest single saving is not a connector. `apache/doris:be-4.1.3`
ships
`doris_be` with debug info; `strip --strip-debug` takes it from **2213
MB to
430 MB** while keeping `.symtab`, so crash backtraces still resolve
function
names and only lose file:line. Build with `--strip none` to keep the
binary
byte-identical to the official image.
Both the strip and the pruning happen in the artifact stage. Doing
either after
the final `COPY` would only add a whiteout layer and the bytes would
still ship.
### What must never be pruned
`resource/prune.txt` is the only file that needs to track a release, and
it
records the three directories that look like external-table extras but
are the
JNI baseline every such read goes through:
- `be/lib/java_extensions/preload-extensions` — no Java sources at all;
it is
the parquet-hadoop-bundle / hadoop-common / libthrift / arrow / AWS SDK
dependency bundle. Preloaded by `bin/start_be.sh`, and
`DORIS_PRELOAD_JAR`
must stay first on the classpath.
- `be/lib/java_extensions/java-udf` — preloaded alongside it.
- `be/lib/hadoop_hdfs` — the JVM side of libhdfs (157 jars). libhdfs is
linked
statically into `doris_be` in 4.1, hence the empty `native/`, but it is
still
a JNI wrapper and cannot open an HDFS file without them.
FE is left alone: branch-4.1 has no FE plugin split, so all 634 jars in
`fe/lib` are on the startup classpath.
### Startup is a sequence, not a sleep
FE → poll until ready → BE → register the backend → poll until FE
reports it
alive → flag ready. FE readiness is a metadata query, deliberately
**not**
`select 1`: Nereids picks a backend as the scan node for that, which
cannot
succeed before the BE the entrypoint has not started yet.
`HEALTHCHECK` asks FE's `/api/health`, whose `online_backend_num`
answers both
"is FE ready" and "is the BE alive" in one request, so downstream can
use
`depends_on: condition: service_healthy` instead of sleeping.
If either process exits, the container exits non-zero — a dead FE
becomes a
failed job rather than a job timeout. Shutdown signals process groups,
since
`--console` leaves no usable pid file for the stop scripts.
### Usage
```yaml
services:
doris:
image: apache/doris:all-in-one-4.1.3
ports: ["9030:9030", "8030:8030", "8040:8040"]
integration-test:
image: my-project-tests:latest
depends_on:
doris:
condition: service_healthy
```
## Testing
`resource/smoke-test.sh <tag> [base|full]` starts the image, waits for
`healthy`, asserts the JNI baseline and the flavor payload, then runs
create / insert / stream load / aggregate / schema change. Everything
goes
through `docker exec`, so the host needs only docker.
Both tags come up healthy in ~17 s and pass on **arm64**. Multi-arch
tags were
built and verified locally (one index, `linux/amd64` + `linux/arm64`).
**Not yet verified, and worth a reviewer's attention:**
1. The **amd64** half is verified structurally only — correct ELF, JDK
path and
payload, and `doris_be --version` byte-identical to the official binary
— but
never actually run. Doris BE does not survive emulation on an Apple
Silicon
host, and neither does the stock `apache/doris:be-4.1.3` amd64 image, so
this
needs a run on real x86 hardware.
2. **Reading a real external table** has not been exercised. The base
tag rests
on Hive/Iceberg data reads needing no format-specific JNI scanner — the
set
of scanner classes BE can construct is closed and neither appears in it
— but
that is still an argument from code, not a test. It needs the fixtures
under
`docker/thirdparties`.
## Notes
- No CI wiring in this PR; `build.sh` is meant to be run by hand for
now.
- The directory targets 4.1 only. Other release lines differ enough in
payload
layout to deserve their own directory rather than a version switch.
- Naming: this uses `all-in-one-<version>` to line up with
`fe-<version>` /
`be-<version>`. The repo already has one `doris-all-in-one-2.1.0` tag
under
the older convention; happy to switch if maintainers prefer it.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01QhGHox19pMRy2cvs7A6xsq
---------
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
docker/runtime/all-in-one/4.1/Dockerfile | 179 +++++++++++++++
.../runtime/all-in-one/4.1/Dockerfile.dockerignore | 27 +++
docker/runtime/all-in-one/4.1/README.md | 251 +++++++++++++++++++++
docker/runtime/all-in-one/4.1/build.sh | 243 ++++++++++++++++++++
.../all-in-one/4.1/resource/conf/be_ci.conf | 37 +++
.../all-in-one/4.1/resource/conf/fe_ci.conf | 41 ++++
.../runtime/all-in-one/4.1/resource/entrypoint.sh | 194 ++++++++++++++++
.../all-in-one/4.1/resource/health_check.sh | 41 ++++
docker/runtime/all-in-one/4.1/resource/prune.txt | 64 ++++++
.../runtime/all-in-one/4.1/resource/smoke-test.sh | 153 +++++++++++++
docker/runtime/all-in-one/Dockerfile | 54 -----
docker/runtime/all-in-one/resource/entry_point.sh | 188 ---------------
12 files changed, 1230 insertions(+), 242 deletions(-)
diff --git a/docker/runtime/all-in-one/4.1/Dockerfile
b/docker/runtime/all-in-one/4.1/Dockerfile
new file mode 100644
index 00000000000..31c3c829bf8
--- /dev/null
+++ b/docker/runtime/all-in-one/4.1/Dockerfile
@@ -0,0 +1,179 @@
+# 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.
+
+# ============================================================================
+# Apache Doris all-in-one image for integration testing -- branch-4.1 only.
+#
+# A single container running one FE and one BE, sized for use as a test
+# fixture in downstream CI. Build it with ./build.sh; the commands below are
+# what that script ends up running.
+#
+# base flavor (default):
+# docker buildx build --build-arg DORIS_VERSION=4.1.3 \
+# -f docker/runtime/all-in-one/4.1/Dockerfile \
+# -t apache/doris:all-in-one-4.1.3 .
+#
+# full flavor (adds hudi / trino / maxcompute):
+# ... --build-arg FLAVOR=full -t apache/doris:all-in-one-4.1.3-full .
+#
+# The build context is the repository root (so ARTIFACT_SOURCE=local can pick
+# up ./output); Dockerfile.dockerignore narrows it down to a few KB.
+# ============================================================================
+
+# ---- global args: must be declared before the first FROM ----
+ARG DORIS_VERSION=4.1.3
+ARG ARTIFACT_SOURCE=image
+ARG FE_IMAGE=apache/doris:fe-${DORIS_VERSION}
+ARG BE_IMAGE=apache/doris:be-${DORIS_VERSION}
+ARG BASE_IMAGE=ubuntu:22.04
+
+# ============================ artifact sources ==============================
+# Three interchangeable ways to get fe/ and be/ into /artifacts. Everything
+# downstream only knows about /artifacts, never about where it came from.
+
+FROM ${FE_IMAGE} AS fe-src
+FROM ${BE_IMAGE} AS be-src
+
+# strip(1) lives here, so this is ubuntu rather than busybox.
+FROM ${BASE_IMAGE} AS artifacts-base
+RUN apt-get update -y && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ -o Acquire::Retries=3 binutils && \
+ rm -rf /var/lib/apt/lists/*
+
+# A. the official component images (default)
+FROM artifacts-base AS artifacts-image
+COPY --from=fe-src /opt/apache-doris/fe /artifacts/fe
+COPY --from=be-src /opt/apache-doris/be /artifacts/be
+
+# B. a locally built ./output, for developers testing their own build
+FROM artifacts-base AS artifacts-local
+ARG LOCAL_OUTPUT=output
+COPY ${LOCAL_OUTPUT}/fe /artifacts/fe
+COPY ${LOCAL_OUTPUT}/be /artifacts/be
+
+# C. an extracted release tarball
+FROM artifacts-base AS artifacts-tarball
+ARG TARBALL_DIR
+COPY ${TARBALL_DIR}/fe /artifacts/fe
+COPY ${TARBALL_DIR}/be /artifacts/be
+
+# ========================= strip + flavor pruning ===========================
+# Both have to happen HERE, before the final COPY. Doing them in the runtime
+# stage would only add a whiteout layer -- the bytes would still ship.
+
+FROM artifacts-${ARTIFACT_SOURCE} AS artifacts
+ARG CTX_PREFIX=docker/runtime/all-in-one/4.1
+ARG FLAVOR=base
+ARG STRIP_BE=debug
+
+COPY ${CTX_PREFIX}/resource/prune.txt /tmp/prune.txt
+
+RUN set -eux; \
+ case "${FLAVOR}" in base|full) ;; *) echo "bad FLAVOR=${FLAVOR}" >&2; exit
1 ;; esac; \
+ # 4.1.3 ships doris_be with debug info: 2213M -> 430M, and --strip-debug
+ # keeps .symtab so crash backtraces still resolve function names.
+ case "${STRIP_BE}" in \
+ debug) strip --strip-debug /artifacts/be/lib/doris_be ;; \
+ full) strip -s /artifacts/be/lib/doris_be ;; \
+ none) : ;; \
+ *) echo "bad STRIP_BE=${STRIP_BE}" >&2; exit 1 ;; \
+ esac; \
+ # "always" rows apply to both flavors, "base" rows only to the base flavor.
+ awk -v f="${FLAVOR}" '$1=="always" || $1==f {print $2}' /tmp/prune.txt \
+ | while IFS= read -r p; do echo "prune: ${p}"; rm -rf "/artifacts/${p}";
done; \
+ rm -f /tmp/prune.txt; \
+ rm -rf /artifacts/fe/log /artifacts/be/log; \
+ mkdir -p /artifacts/fe/log /artifacts/be/log \
+ /artifacts/fe/doris-meta /artifacts/be/storage; \
+ echo "=== artifact size (${FLAVOR}) ==="; du -sh /artifacts/fe
/artifacts/be
+
+# ================================ runtime ===================================
+
+FROM ${BASE_IMAGE} AS runtime
+ARG TARGETARCH
+ARG DORIS_VERSION
+ARG FLAVOR=base
+ARG CTX_PREFIX=docker/runtime/all-in-one/4.1
+ARG JDK_PKG=openjdk-17-jre-headless
+# FE ships -Xmx8192m, far more than a CI runner can spare; BE ships -Xmx2048m
+# for its JNI-side JVM. Both are rewritten in place below.
+ARG FE_HEAP=2048m
+ARG BE_HEAP=1024m
+
+ENV DORIS_HOME=/opt/apache-doris \
+ CI_HOME=/opt/doris-ci \
+ LANG=C.UTF-8 \
+ TZ=UTC
+
+RUN set -eux; \
+ apt-get update -y; \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ -o Acquire::Retries=3 \
+ ${JDK_PKG} \
+ mysql-client-core-8.0 \
+ curl \
+ tini \
+ procps \
+ tzdata \
+ ca-certificates; \
+ ln -sfn "/usr/lib/jvm/java-17-openjdk-${TARGETARCH:-amd64}"
/usr/lib/jvm/java; \
+ ln -sf /usr/share/zoneinfo/UTC /etc/localtime; \
+ rm -rf /var/lib/apt/lists/* /usr/share/doc/* /usr/share/man/*
+
+# SKIP_CHECK_ULIMIT: start_be.sh checks vm.max_map_count / swap / ulimit -n and
+# exits 1 when they are too low. vm.max_map_count is not a namespaced sysctl,
so
+# a container cannot fix it -- the check has to be skipped here.
+ENV JAVA_HOME=/usr/lib/jvm/java \
+
PATH=/usr/lib/jvm/java/bin:/opt/apache-doris/fe/bin:/opt/apache-doris/be/bin:$PATH
\
+ SKIP_CHECK_ULIMIT=true
+
+COPY --from=artifacts /artifacts/fe ${DORIS_HOME}/fe
+COPY --from=artifacts /artifacts/be ${DORIS_HOME}/be
+COPY ${CTX_PREFIX}/resource/ ${CI_HOME}/
+
+RUN set -eux; \
+ # Integration-test defaults are appended, never edited in: the upstream
+ # values stay visible above them and the last assignment wins for both the
+ # shell-sourced ALL_CAPS vars and the property-style lowercase keys.
+ cat "${CI_HOME}/conf/fe_ci.conf" >> "${DORIS_HOME}/fe/conf/fe.conf"; \
+ cat "${CI_HOME}/conf/be_ci.conf" >> "${DORIS_HOME}/be/conf/be.conf"; \
+ # The heap lives inside a long JAVA_OPTS_FOR_JDK_17 line that also carries
+ # every --add-opens FE needs on JDK 17. Rewriting just the -Xmx/-Xms tokens
+ # keeps the rest of that line exactly as upstream shipped it.
+ sed -i -E "s/-Xmx[0-9]+[kKmMgG]/-Xmx${FE_HEAP}/g;
s/-Xms[0-9]+[kKmMgG]/-Xms${FE_HEAP}/g" \
+ "${DORIS_HOME}/fe/conf/fe.conf"; \
+ sed -i -E "s/-Xmx[0-9]+[kKmMgG]/-Xmx${BE_HEAP}/g;
s/-Xms[0-9]+[kKmMgG]/-Xms${BE_HEAP}/g" \
+ "${DORIS_HOME}/be/conf/be.conf"; \
+ chmod +x "${CI_HOME}"/*.sh
+
+LABEL org.opencontainers.image.title="Apache Doris all-in-one" \
+ org.opencontainers.image.description="Single-container Doris (FE+BE) for
integration testing" \
+ org.opencontainers.image.version="${DORIS_VERSION}" \
+ org.opencontainers.image.source="https://github.com/apache/doris" \
+ org.apache.doris.allinone.flavor="${FLAVOR}" \
+ org.apache.doris.allinone.branch="4.1"
+
+# 8030 FE http | 9030 FE mysql | 8040 BE http (stream load) | 9050 BE heartbeat
+EXPOSE 8030 9030 8040 9050
+
+WORKDIR ${DORIS_HOME}
+
+HEALTHCHECK --interval=5s --timeout=5s --start-period=20s --retries=90 \
+ CMD /opt/doris-ci/health_check.sh
+
+ENTRYPOINT ["/usr/bin/tini", "--", "/opt/doris-ci/entrypoint.sh"]
diff --git a/docker/runtime/all-in-one/4.1/Dockerfile.dockerignore
b/docker/runtime/all-in-one/4.1/Dockerfile.dockerignore
new file mode 100644
index 00000000000..379b506fd84
--- /dev/null
+++ b/docker/runtime/all-in-one/4.1/Dockerfile.dockerignore
@@ -0,0 +1,27 @@
+# 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.
+
+# BuildKit reads this in preference to the repository root .dockerignore when
+# building with -f docker/runtime/all-in-one/4.1/Dockerfile.
+#
+# The context has to be the repository root so that ARTIFACT_SOURCE=local can
+# reach ./output, but nothing else in the tree is needed. Exclude everything,
+# then add back the two paths the build actually reads.
+*
+!docker/runtime/all-in-one/4.1/resource
+!output/fe
+!output/be
diff --git a/docker/runtime/all-in-one/4.1/README.md
b/docker/runtime/all-in-one/4.1/README.md
new file mode 100644
index 00000000000..49d8d1d6888
--- /dev/null
+++ b/docker/runtime/all-in-one/4.1/README.md
@@ -0,0 +1,251 @@
+<!--
+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.
+-->
+
+# Doris all-in-one image (branch-4.1)
+
+One FE and one BE in a single container, sized to be a test fixture in a
+downstream project's CI. It is assembled from the official `apache/doris:fe-*`
+and `apache/doris:be-*` images, so a new Doris release needs no repackaging of
+anything here.
+
+This directory targets the **4.1 release line only**. Other lines differ enough
+in payload layout to deserve their own directory rather than a version switch.
+
+## Two tags
+
+| tag | covers | size |
+|---|---|---|
+| `apache/doris:all-in-one-<version>` | internal tables, Hive, Iceberg
(including system tables), Paimon, JDBC catalogs, external-table writeback,
Java UDF | 2.46 GB |
+| `apache/doris:all-in-one-<version>-full` | the above plus Hudi, Trino
connector, MaxCompute | 2.99 GB |
+
+Pick `-full` only if the tests touch Hudi, the Trino connector or MaxCompute.
+
+Both come up `healthy` in under 20 seconds. Sizes are the uncompressed layer
+sum measured on 4.1.3/arm64, against 4.9 GB for the same payload untouched.
+`docker image inspect --format '{{.Size}}'` reports 1.61 / 2.06 GiB for them;
+`docker images` can print a much larger figure when the containerd image store
+is enabled, because it adds the compressed blobs to the unpacked snapshot.
+
+## Build
+
+A plain run builds for the host architecture only. Multi-arch needs an explicit
+`--platform`; the header the script prints says which it is doing.
+
+```shell
+# both tags, from the official 4.1.3 component images, host architecture only
+./build.sh -v 4.1.3
+
+# base tag only, then smoke test it
+./build.sh -v 4.1.3 -f base -t
+
+# from a locally built ./output instead
+./build.sh -v dev -s local
+```
+
+`./build.sh --help` lists the rest. The build context is the repository root,
+narrowed to a few KB by `Dockerfile.dockerignore`; run the script from
anywhere.
+
+## Multi-architecture
+
+One tag can serve both architectures, the way `apache/doris:fe-4.1.3` does: the
+tag points at an OCI image index listing one manifest per platform, and a pull
+picks the matching entry.
+
+```
+apache/doris:all-in-one-4.1.3 -> index
+ |- linux/amd64
+ \- linux/arm64
+```
+
+Nothing in the Dockerfile is architecture-specific beyond one JDK symlink;
+`COPY --from=apache/doris:be-4.1.3` resolves to the target platform's variant
on
+its own, so no per-arch directories have to be staged by hand.
+
+**Both architectures on one host.** Simple, but slow for whichever architecture
+is foreign to the machine:
+
+```shell
+./build.sh -v 4.1.3 --platform linux/amd64,linux/arm64 --push
+```
+
+Keeping the result locally instead of pushing works only with the containerd
+image store, which is where Docker has somewhere to put a second architecture.
+
+**One architecture per host, joined afterwards.** No emulation, so this is much
+faster and is the sane choice for a release:
+
+```shell
+# on an x86 host
+./build.sh -v 4.1.3 --platform linux/amd64 --push -i myrepo/doris # then
retag ...-amd64
+# on an arm host
+./build.sh -v 4.1.3 --platform linux/arm64 --push -i myrepo/doris # then
retag ...-arm64
+# anywhere
+docker buildx imagetools create -t myrepo/doris:all-in-one-4.1.3 \
+ myrepo/doris:all-in-one-4.1.3-amd64 myrepo/doris:all-in-one-4.1.3-arm64
+```
+
+**What emulation costs.** Measured on an Apple Silicon host building amd64:
+the two `apt-get install` layers take about 18 minutes between them, plus a
+one-off pull of that architecture's `be` image. Everything else is cheap --
+the `COPY --from` steps are plain file copies at 8 seconds total and `strip`
+takes under 2 seconds even emulated. If that ever needs to come down, the
+runtime `apt` is the only target worth attacking, e.g. by basing the runtime
+stage on a multi-arch image that already carries a JDK.
+
+Note that a Doris BE built for a foreign architecture generally will not *run*
+under emulation -- the official `apache/doris:be-4.1.3` amd64 image segfaults
on
+an Apple Silicon host just as this image does. Build for the other architecture
+freely; test it on real hardware.
+
+## Use
+
+The image ships a `HEALTHCHECK`, so nothing downstream has to sleep.
+
+```yaml
+# docker compose
+services:
+ doris:
+ image: apache/doris:all-in-one-4.1.3
+ ports: ["9030:9030", "8030:8030", "8040:8040"]
+
+ integration-test:
+ image: my-project-tests:latest
+ depends_on:
+ doris:
+ condition: service_healthy
+```
+
+```yaml
+# GitHub Actions -- service containers are started and waited on automatically
+services:
+ doris:
+ image: apache/doris:all-in-one-4.1.3
+ ports: ['9030:9030', '8030:8030', '8040:8040']
+```
+
+| | |
+|---|---|
+| MySQL protocol | `9030`, user `root`, no password |
+| FE HTTP | `8030`, `/api/health` needs no auth |
+| BE HTTP | `8040`, stream load endpoint |
+| readiness | docker health status turns `healthy` once FE is up and the BE is
registered and alive |
+| replicas | forced to 1, so `CREATE TABLE` needs no `replication_num` |
+| persistence | none by default; mount `/opt/apache-doris/fe/doris-meta` and
`/opt/apache-doris/be/storage` to keep state, the startup path is idempotent |
+| config override | `-e FE_CONFIG_EXTRA=...` / `-e BE_CONFIG_EXTRA=...`,
appended to the respective conf at startup |
+| exit behaviour | fail-fast: if FE or BE exits, the container exits non-zero.
`docker stop` shuts BE then FE down gracefully and exits 0 |
+| logs | `docker logs` carries FE's console stream; the full logs live in
`fe/log/fe.log` and `be/log/be.INFO` |
+
+Heap and memory are tuned down for CI runners (FE `-Xmx2048m`, BE JNI heap
+`-Xmx1024m`, BE `mem_limit = 40%`). On a larger machine, raise them with
+`-e BE_CONFIG_EXTRA="mem_limit = 80%"` or rebuild with `--build-arg
FE_HEAP=8192m`.
+
+Heavy workloads may also need `vm.max_map_count` raised **on the host**
+(`sysctl -w vm.max_map_count=2000000`); it is not a namespaced sysctl, so the
+container cannot set it and `start_be.sh`'s check for it is skipped.
+
+## How the two tags differ
+
+BE loads JNI scanners by enumerating the directories under
+`be/lib/java_extensions/` (`ScannerLoader.loadAllScannerJars`, called from
+`be/src/util/jni-util.cpp` when the BE JVM starts). There is no list and no
+config key, so a tag supports exactly the formats whose directory it ships.
+
+The set of scanner classes BE can construct is closed — every one of them is
+named as a string literal under `be/src/format_v2/jni/` and
+`be/src/format/table/`:
+
+| class | directory | tag |
+|---|---|---|
+| `PaimonJniScanner` | `paimon-scanner` | both |
+| `IcebergSysTableJniScanner` | `iceberg-metadata-scanner` | both |
+| `JdbcJniScanner` | `jdbc-scanner` | both |
+| (`JniWriter`) | `java-writer` | both |
+| `HadoopHudiJniScanner` | `hadoop-hudi-scanner` | `-full` |
+| `TrinoConnectorJniScanner` | `trino-connector-scanner` | `-full` |
+| `MaxComputeJniScanner` | `max-compute-connector` | `-full` |
+
+Hive and Iceberg **data** reads are not in that table: their column decoding
+runs in BE's native parquet/orc reader. They still go through JNI for file
+system and catalog access, which is what the next section is about.
+
+## The JNI baseline — do not prune these
+
+Three directories look like external-table extras and are not:
+
+- **`be/lib/java_extensions/preload-extensions`** (254 MB) — no Java sources at
+ all, just a dependency bundle: parquet-hadoop-bundle, hadoop-common,
+ hadoop-cos, libthrift, arrow, and the AWS SDK including
+ `s3-tables-catalog-for-iceberg`. Every external-table read passes through it.
+ `bin/start_be.sh` preloads it and requires `DORIS_PRELOAD_JAR` to stay first
+ on the classpath.
+- **`be/lib/java_extensions/java-udf`** (237 MB) — preloaded alongside it.
+- **`be/lib/hadoop_hdfs`** (99 MB) — the JVM side of libhdfs, 157 jars. In 4.1
+ libhdfs is linked statically into `doris_be` (hence the empty `native/`
+ directory), but it is still a JNI wrapper and cannot open an HDFS file
+ without them.
+
+Together they are ~590 MB, a quarter of the base tag. `resource/prune.txt`
+deliberately omits them and says so.
+
+## What the build actually removes
+
+| | base | -full |
+|---|---|---|
+| `strip --strip-debug` on `doris_be` | 2213 MB → 430 MB (450 MB on arm64) |
same |
+| `be/lib/meta_tool`, `be/lib/cdc_client`, `fe/arthas` | removed | removed |
+| hudi / trino / maxcompute scanners | removed | kept |
+
+Stripping is the single largest saving — more than every connector combined —
+and `--strip-debug` keeps `.symtab`, so crash backtraces still resolve function
+names and only lose file:line. Build with `--strip none` to keep the binary
+byte-identical to the official image.
+
+Both the strip and the pruning happen in the artifact stage, before the final
+`COPY`. Doing either in the runtime stage would only add a whiteout layer and
+the bytes would still ship.
+
+FE is left alone: branch-4.1 has no FE plugin split, so all 634 jars in
+`fe/lib` are on the startup classpath and pruning by jar name would be fragile
+for little gain.
+
+## Verifying a change to `resource/prune.txt`
+
+`resource/smoke-test.sh <image:tag> [base|full]` starts the image, waits for
+`healthy`, asserts the JNI baseline and the flavor payload are what they should
+be, then exercises create / insert / stream load / aggregate / schema change.
+It runs everything through `docker exec`, so the host needs only docker.
+
+It does **not** read a real external table. To check that end of things, point
+the built image at the fixtures under `docker/thirdparties` and run an Iceberg
+or Hive query by hand.
+
+## Layout
+
+```
+4.1/
+├── Dockerfile three artifact sources -> strip + prune ->
runtime
+├── Dockerfile.dockerignore keeps the repo-root context to a few KB
+├── build.sh the only entry point you need
+└── resource/
+ ├── entrypoint.sh FE -> readiness -> BE -> register -> fail-fast
wait
+ ├── health_check.sh backs HEALTHCHECK
+ ├── smoke-test.sh guards prune.txt
+ ├── prune.txt what each flavor drops, and what must never be
dropped
+ └── conf/{fe_ci.conf,be_ci.conf} appended to the upstream conf at build
time
+```
diff --git a/docker/runtime/all-in-one/4.1/build.sh
b/docker/runtime/all-in-one/4.1/build.sh
new file mode 100755
index 00000000000..55f509e1c85
--- /dev/null
+++ b/docker/runtime/all-in-one/4.1/build.sh
@@ -0,0 +1,243 @@
+#!/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.
+#
+# Builds the Doris 4.1.x all-in-one images locally.
+
+set -euo pipefail
+
+HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${HERE}/../../../.." && pwd)"
+DOCKERFILE="docker/runtime/all-in-one/4.1/Dockerfile"
+
+IMAGE="${IMAGE:-apache/doris}"
+VERSION=""
+SOURCE="image"
+FLAVORS=""
+STRIP_BE="debug"
+PLATFORM=""
+LOCAL_OUTPUT="output"
+TARBALL_DIR=""
+RUN_TEST=false
+NO_CACHE=false
+PUSH=false
+
+usage() {
+ cat <<'USAGE'
+Usage: ./build.sh -v <doris-version> [options]
+
+Builds one container image per flavor. Both flavors share every layer up to the
+artifact stage, so building the second one after the first is cheap.
+
+ base internal tables, Hive, Iceberg (incl. system tables), Paimon,
+ JDBC catalogs, external-table writeback, Java UDF -> :all-in-one-<v>
+ full the above plus Hudi, Trino connector, MaxCompute ->
:all-in-one-<v>-full
+
+Options:
+ -v, --version <v> Doris version, e.g. 4.1.3. Required.
+ -f, --flavor <f> base | full | both (default: both)
+ -s, --source <s> image | local | tarball (default: image)
+ image -> apache/doris:fe-<v> and :be-<v>
+ local -> ./output/{fe,be} from a local build
+ tarball -> --tarball-dir
+ --tarball-dir <d> Extracted release package holding fe/ and be/,
+ as a path relative to the repository root.
+ --local-output <d> Override the ./output path for --source local.
+ --strip <mode> debug | full | none (default: debug)
+ debug -> strip --strip-debug, keeps .symtab
+ full -> strip -s
+ none -> ship doris_be as-is (+1.8 GB)
+ --platform <p> Target platform(s), e.g. linux/amd64 or
+ linux/amd64,linux/arm64. Comma-separated values produce
+ one multi-arch tag (an OCI image index) that resolves
to
+ the right image per host, the way apache/doris:fe-*
does.
+ Keeping a multi-platform result locally needs the
+ containerd image store; otherwise pass --push.
+ --push Push instead of loading into the local image store.
+ -i, --image <name> Image name without tag (default: apache/doris)
+ --no-cache Pass --no-cache to docker build.
+ -t, --test Run resource/smoke-test.sh against each image built.
+ -h, --help This message.
+
+Examples:
+ ./build.sh -v 4.1.3 # both flavors from the official
images
+ ./build.sh -v 4.1.3 -f base -t # base only, then smoke test it
+ ./build.sh -v dev -s local -f full # from a local ./output
+ ./build.sh -v 4.1.3 --platform linux/amd64,linux/arm64 --push
+ # one multi-arch tag for both
+
+Building a foreign architecture goes through emulation, and the two apt layers
+dominate: roughly 18 minutes per foreign arch on an Apple Silicon host, plus
the
+one-off pull of that architecture's be image. Where both architectures matter,
+building each one natively and joining them afterwards is far faster:
+
+ # on an x86 host
+ ./build.sh -v 4.1.3 --push -i myrepo/doris --platform linux/amd64 # tag it
-amd64 by hand
+ # on an arm host
+ ./build.sh -v 4.1.3 --push -i myrepo/doris --platform linux/arm64 # tag it
-arm64 by hand
+ # then, anywhere
+ docker buildx imagetools create -t myrepo/doris:all-in-one-4.1.3 \
+ myrepo/doris:all-in-one-4.1.3-amd64 myrepo/doris:all-in-one-4.1.3-arm64
+USAGE
+}
+
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ -v|--version) VERSION="$2"; shift 2 ;;
+ -f|--flavor) FLAVORS="$2"; shift 2 ;;
+ -s|--source) SOURCE="$2"; shift 2 ;;
+ --tarball-dir) TARBALL_DIR="$2"; shift 2 ;;
+ --local-output) LOCAL_OUTPUT="$2"; shift 2 ;;
+ --strip) STRIP_BE="$2"; shift 2 ;;
+ --platform) PLATFORM="$2"; shift 2 ;;
+ -i|--image) IMAGE="$2"; shift 2 ;;
+ --no-cache) NO_CACHE=true; shift ;;
+ --push) PUSH=true; shift ;;
+ -t|--test) RUN_TEST=true; shift ;;
+ -h|--help) usage; exit 0 ;;
+ *) echo "unknown option: $1" >&2; usage; exit 1 ;;
+ esac
+done
+
+[[ -n "${VERSION}" ]] || { echo "error: -v/--version is required" >&2; usage;
exit 1; }
+
+case "${FLAVORS:-both}" in
+ both|"") FLAVORS="base full" ;;
+ base) FLAVORS="base" ;;
+ full) FLAVORS="full" ;;
+ *) echo "error: bad --flavor '${FLAVORS}', expected base|full|both" >&2;
exit 1 ;;
+esac
+
+case "${SOURCE}" in
+ image|local|tarball) ;;
+ *) echo "error: bad --source '${SOURCE}'" >&2; exit 1 ;;
+esac
+case "${STRIP_BE}" in
+ debug|full|none) ;;
+ *) echo "error: bad --strip '${STRIP_BE}'" >&2; exit 1 ;;
+esac
+
+if [[ "${SOURCE}" == tarball && -z "${TARBALL_DIR}" ]]; then
+ echo "error: --source tarball needs --tarball-dir" >&2; exit 1
+fi
+if [[ "${SOURCE}" == local && ! -d "${REPO_ROOT}/${LOCAL_OUTPUT}/be" ]]; then
+ echo "error: ${REPO_ROOT}/${LOCAL_OUTPUT}/be not found; build Doris first"
>&2; exit 1
+fi
+
+command -v docker >/dev/null || { echo "error: docker not found" >&2; exit 1; }
+
+# A comma in --platform means one tag carrying an OCI image index. Docker can
+# only hold that locally with the containerd image store; the classic store has
+# nowhere to put a second architecture, so the build has to go straight to a
+# registry.
+MULTI_PLATFORM=false
+case "${PLATFORM}" in *,*) MULTI_PLATFORM=true ;; esac
+if [[ "${MULTI_PLATFORM}" == true && "${PUSH}" != true ]]; then
+ if ! docker info --format '{{range .DriverStatus}}{{.}}{{end}}'
2>/dev/null | grep -q containerd; then
+ echo "error: a multi-platform build needs --push, or the containerd
image store" >&2
+ echo " (Docker Desktop: Settings > General > Use containerd for
pulling and storing images)" >&2
+ exit 1
+ fi
+fi
+if [[ "${MULTI_PLATFORM}" == true && "${RUN_TEST}" == true ]]; then
+ echo "error: --test cannot run against a multi-platform tag; build one
platform at a time" >&2
+ exit 1
+fi
+
+builder="docker buildx build"
+docker buildx version >/dev/null 2>&1 || builder="docker build"
+
+# Say which platforms are being built. Leaving --platform unset means the host
+# architecture only, which is easy to mistake for a multi-arch build.
+if [[ -n "${PLATFORM}" ]]; then
+ platform_note="${PLATFORM}"
+else
+ platform_note="$(docker version --format '{{.Server.Os}}/{{.Server.Arch}}'
2>/dev/null || echo host)"
+ platform_note="${platform_note} (host only -- pass --platform for
multi-arch)"
+fi
+
+echo "repository root : ${REPO_ROOT}"
+echo "doris version : ${VERSION}"
+echo "artifact source : ${SOURCE}"
+echo "strip mode : ${STRIP_BE}"
+echo "flavors : ${FLAVORS}"
+echo "platform(s) : ${platform_note}"
+echo "output : $([[ "${PUSH}" == true ]] && echo 'push to registry'
|| echo 'load into local image store')"
+echo
+
+built_tags=()
+
+for flavor in ${FLAVORS}; do
+ suffix=""
+ [[ "${flavor}" == full ]] && suffix="-full"
+ tag="${IMAGE}:all-in-one-${VERSION}${suffix}"
+
+ args=(
+ --build-arg "DORIS_VERSION=${VERSION}"
+ --build-arg "ARTIFACT_SOURCE=${SOURCE}"
+ --build-arg "FLAVOR=${flavor}"
+ --build-arg "STRIP_BE=${STRIP_BE}"
+ --build-arg "LOCAL_OUTPUT=${LOCAL_OUTPUT}"
+ )
+ [[ -n "${TARBALL_DIR}" ]] && args+=(--build-arg
"TARBALL_DIR=${TARBALL_DIR}")
+ [[ -n "${PLATFORM}" ]] && args+=(--platform "${PLATFORM}")
+ [[ "${NO_CACHE}" == true ]] && args+=(--no-cache)
+ if [[ "${PUSH}" == true ]]; then
+ args+=(--push)
+ elif [[ "${builder}" == "docker buildx build" ]]; then
+ # buildx leaves the result in the build cache by default; --load puts
it
+ # in the local image store where docker run can see it.
+ args+=(--load)
+ fi
+
+ echo ">>> building ${tag} (flavor=${flavor})"
+ DOCKER_BUILDKIT=1 ${builder} "${args[@]}" \
+ -f "${DOCKERFILE}" -t "${tag}" "${REPO_ROOT}"
+
+ built_tags+=("${tag}:${flavor}")
+ echo
+done
+
+echo "=== built images ==="
+if [[ "${PUSH}" == true ]]; then
+ for entry in "${built_tags[@]}"; do
+ tag="${entry%:*}"
+ echo " pushed ${tag}"
+ docker buildx imagetools inspect "${tag}" 2>/dev/null \
+ | grep -E "^(Name|MediaType)|Platform:" | sed 's/^/ /' || true
+ done
+ exit 0
+fi
+# Reported by docker image inspect. Note that `docker images` can print a much
+# larger number when the containerd image store is enabled: it adds the
+# compressed blobs to the unpacked snapshot instead of reporting one of them.
+for entry in "${built_tags[@]}"; do
+ tag="${entry%:*}"
+ bytes="$(docker image inspect "${tag}" --format '{{.Size}}' 2>/dev/null ||
echo 0)"
+ awk -v t="${tag}" -v b="${bytes}" \
+ 'BEGIN { printf " %-42s %.2f GiB\n", t, b / 1073741824 }'
+done
+
+if [[ "${RUN_TEST}" == true ]]; then
+ for entry in "${built_tags[@]}"; do
+ tag="${entry%:*}"
+ flavor="${entry##*:}"
+ echo
+ echo ">>> smoke test ${tag} (${flavor})"
+ "${HERE}/resource/smoke-test.sh" "${tag}" "${flavor}"
+ done
+fi
diff --git a/docker/runtime/all-in-one/4.1/resource/conf/be_ci.conf
b/docker/runtime/all-in-one/4.1/resource/conf/be_ci.conf
new file mode 100644
index 00000000000..8e26b7cb095
--- /dev/null
+++ b/docker/runtime/all-in-one/4.1/resource/conf/be_ci.conf
@@ -0,0 +1,37 @@
+# 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.
+
+#### appended by the all-in-one image -- integration-test defaults ####
+#
+# be.conf is read the same way fe.conf is, and the last assignment wins.
+# The JNI-side heap is not set here; the Dockerfile rewrites -Xmx/-Xms inside
+# the upstream JAVA_OPTS_FOR_JDK_17 line.
+
+priority_networks = 127.0.0.1/32
+
+# Upstream default is 90%, measured against the cgroup limit when there is one
+# and against host memory when there is not. An unconstrained container on a
+# 7 GB CI runner would leave nothing for FE, so cap it well below that.
+# Raise it with -e BE_CONFIG_EXTRA="mem_limit = 80%" when the box is bigger.
+mem_limit = 40%
+
+# A test fixture does not need compaction throughput, and these threads are
+# per disk, so the defaults are pure overhead here.
+base_compaction_num_threads_per_disk = 1
+cumulative_compaction_num_threads_per_disk = 1
+
+sys_log_roll_num = 2
diff --git a/docker/runtime/all-in-one/4.1/resource/conf/fe_ci.conf
b/docker/runtime/all-in-one/4.1/resource/conf/fe_ci.conf
new file mode 100644
index 00000000000..374fe60d120
--- /dev/null
+++ b/docker/runtime/all-in-one/4.1/resource/conf/fe_ci.conf
@@ -0,0 +1,41 @@
+# 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.
+
+#### appended by the all-in-one image -- integration-test defaults ####
+#
+# fe.conf is consumed twice: bin/start_fe.sh exports the ALL_CAPS keys as
+# environment variables, and FE parses the whole file as properties. Both take
+# the last assignment, so appending overrides without editing anything above.
+#
+# The heap is not set here; the Dockerfile rewrites the -Xmx/-Xms tokens inside
+# the upstream JAVA_OPTS_FOR_JDK_17 line so the --add-opens flags stay intact.
+
+# One BE means one replica. Saves every downstream CREATE TABLE from having to
+# spell out replication_num=1. Config.java describes this as test-environment
+# only, which is exactly what this image is.
+force_olap_table_replication_num = 1
+
+# Everything lives on the container loopback. Pinning IP mode as well keeps a
+# restart under a different container hostname from invalidating the metadata.
+priority_networks = 127.0.0.1/32
+enable_fqdn_mode = false
+
+# Nothing to balance across a single backend.
+disable_balance = true
+
+# Keep the log directory bounded over a long CI run.
+sys_log_roll_num = 2
diff --git a/docker/runtime/all-in-one/4.1/resource/entrypoint.sh
b/docker/runtime/all-in-one/4.1/resource/entrypoint.sh
new file mode 100755
index 00000000000..1ce68ee0efa
--- /dev/null
+++ b/docker/runtime/all-in-one/4.1/resource/entrypoint.sh
@@ -0,0 +1,194 @@
+#!/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.
+#
+# Brings up one FE and one BE inside a single container and keeps them there.
+#
+# Fail-fast by design: if either process exits, so does the container, with a
+# non-zero status. A test fixture that quietly restarts a dead FE turns a
+# two-second failure into a job timeout.
+
+set -Eeuo pipefail
+
+# Job control, so each child lands in its own process group. This is what makes
+# shutdown work: start_fe.sh / start_be.sh run the real java / doris_be process
+# in the foreground and do not forward signals, and neither writes a usable pid
+# file under --console, so stop_fe.sh / stop_be.sh cannot help us. Signalling
+# the whole group reaches the actual process.
+set -m
+
+DORIS_HOME="${DORIS_HOME:-/opt/apache-doris}"
+FE_HOME="${DORIS_HOME}/fe"
+BE_HOME="${DORIS_HOME}/be"
+READY_FLAG="${DORIS_HOME}/.ready"
+
+HOST=127.0.0.1
+FE_HTTP_PORT="${FE_HTTP_PORT:-8030}"
+FE_QUERY_PORT="${FE_QUERY_PORT:-9030}"
+BE_HEARTBEAT_PORT="${BE_HEARTBEAT_PORT:-9050}"
+START_TIMEOUT="${START_TIMEOUT:-300}"
+STOP_TIMEOUT="${STOP_TIMEOUT:-30}"
+
+FE_PID=
+BE_PID=
+
+log() { printf '%s [%-5s] [entrypoint] %s\n' "$(date -Iseconds)" "$1"
"${*:2}"; }
+info() { log INFO "$@"; }
+warn() { log WARN "$@" >&2; }
+die() { log ERROR "$@" >&2; exit 1; }
+
+sql() {
+ mysql -uroot -h"${HOST}" -P"${FE_QUERY_PORT}" -N --batch
--connect-timeout=2 -e "$1" 2>/dev/null
+}
+
+fe_health() {
+ # Public endpoint (HealthAction): 503 until FE is ready, otherwise a body
+ # carrying online_backend_num. curl -f turns the 503 into a non-zero exit.
+ curl -fsS --max-time 4 "http://${HOST}:${FE_HTTP_PORT}/api/health"
2>/dev/null
+}
+
+# ---------------------------------------------------------------- config ----
+# The image already carries the integration-test defaults; this is the runtime
+# escape hatch for downstream projects that need one knob changed.
+apply_env_overrides() {
+ if [[ -n "${FE_CONFIG_EXTRA:-}" ]]; then
+ info "appending FE_CONFIG_EXTRA to fe.conf"
+ printf '\n# --- FE_CONFIG_EXTRA ---\n%s\n' "${FE_CONFIG_EXTRA}"
>>"${FE_HOME}/conf/fe.conf"
+ fi
+ if [[ -n "${BE_CONFIG_EXTRA:-}" ]]; then
+ info "appending BE_CONFIG_EXTRA to be.conf"
+ printf '\n# --- BE_CONFIG_EXTRA ---\n%s\n' "${BE_CONFIG_EXTRA}"
>>"${BE_HOME}/conf/be.conf"
+ fi
+}
+
+# ------------------------------------------------------------------- FE -----
+start_fe() {
+ info "starting FE"
+ "${FE_HOME}/bin/start_fe.sh" --console &
+ FE_PID=$!
+}
+
+wait_fe_ready() {
+ local deadline=$((SECONDS + START_TIMEOUT))
+ while ((SECONDS < deadline)); do
+ kill -0 "${FE_PID}" 2>/dev/null \
+ || die "FE exited during startup, see ${FE_HOME}/log/fe.log"
+ # Two gates: the HTTP endpoint reports FE readiness, and a metadata
+ # query proves the MySQL port is actually serving. It has to be a
+ # metadata query -- `select 1` goes through Nereids, which picks a
+ # backend as its scan node and fails with "No backend available" until
+ # one is registered. The BE is not started yet at this point, so using
+ # it here would deadlock the two waits against each other.
+ if fe_health >/dev/null && sql 'show frontends' | grep -q "${HOST}";
then
+ info "FE is ready after ${SECONDS}s"
+ return 0
+ fi
+ sleep 1
+ done
+ die "FE did not become ready within ${START_TIMEOUT}s, see
${FE_HOME}/log/fe.log"
+}
+
+# ------------------------------------------------------------------- BE -----
+start_be() {
+ info "starting BE"
+ "${BE_HOME}/bin/start_be.sh" --console &
+ BE_PID=$!
+}
+
+register_be() {
+ # Idempotent: a container restarted on a mounted doris-meta already has the
+ # backend in its metadata.
+ if sql 'show backends' | grep -qE
"[[:space:]]${HOST}[[:space:]]+${BE_HEARTBEAT_PORT}[[:space:]]"; then
+ info "backend ${HOST}:${BE_HEARTBEAT_PORT} already registered"
+ else
+ info "registering backend ${HOST}:${BE_HEARTBEAT_PORT}"
+ sql "alter system add backend '${HOST}:${BE_HEARTBEAT_PORT}'" \
+ || die "ALTER SYSTEM ADD BACKEND failed"
+ fi
+}
+
+wait_be_alive() {
+ local deadline=$((SECONDS + START_TIMEOUT))
+ while ((SECONDS < deadline)); do
+ kill -0 "${BE_PID}" 2>/dev/null \
+ || die "BE exited during startup, see ${BE_HOME}/log/be.INFO and
${BE_HOME}/log/be.out"
+ # FE reports how many backends it considers alive, so one request
+ # answers both "is the BE up" and "did FE notice".
+ if fe_health | grep -qE
'"online_backend_num"[[:space:]]*:[[:space:]]*[1-9]'; then
+ info "backend is alive after ${SECONDS}s"
+ return 0
+ fi
+ sleep 1
+ done
+ die "backend did not come alive within ${START_TIMEOUT}s"
+}
+
+# -------------------------------------------------------------- shutdown ----
+stop_one() {
+ local name=$1 pid=$2
+ [[ -n "${pid}" ]] || return 0
+ kill -0 "${pid}" 2>/dev/null || return 0
+ info "stopping ${name}"
+ # Negative pid signals the whole process group, which is where the real
+ # java / doris_be process lives.
+ kill -TERM -"${pid}" 2>/dev/null || kill -TERM "${pid}" 2>/dev/null || true
+ local deadline=$((SECONDS + STOP_TIMEOUT))
+ while ((SECONDS < deadline)); do
+ kill -0 "${pid}" 2>/dev/null || { info "${name} stopped"; return 0; }
+ sleep 1
+ done
+ warn "${name} did not stop within ${STOP_TIMEOUT}s, killing"
+ kill -KILL -"${pid}" 2>/dev/null || kill -KILL "${pid}" 2>/dev/null || true
+}
+
+shutdown() {
+ trap - SIGTERM SIGINT
+ rm -f "${READY_FLAG}"
+ # BE first, so it stops reporting to an FE that is about to go away.
+ stop_one BE "${BE_PID}"
+ stop_one FE "${FE_PID}"
+ exit 0
+}
+
+# ------------------------------------------------------------------ main ----
+main() {
+ trap shutdown SIGTERM SIGINT
+ rm -f "${READY_FLAG}"
+
+ apply_env_overrides
+ start_fe
+ wait_fe_ready
+ start_be
+ register_be
+ wait_be_alive
+
+ touch "${READY_FLAG}"
+ info "cluster is ready -- mysql -uroot -h127.0.0.1 -P${FE_QUERY_PORT}"
+
+ # Park here until something dies. `wait -n` also returns when a trap fires,
+ # so the explicit re-check below distinguishes the two cases.
+ local rc=0
+ wait -n "${FE_PID}" "${BE_PID}" || rc=$?
+ rm -f "${READY_FLAG}"
+
+ if ! kill -0 "${FE_PID}" 2>/dev/null; then
+ die "FE exited (rc=${rc}), see ${FE_HOME}/log/fe.log"
+ fi
+ die "BE exited (rc=${rc}), see ${BE_HOME}/log/be.INFO and
${BE_HOME}/log/be.out"
+}
+
+main "$@"
diff --git a/docker/runtime/all-in-one/4.1/resource/health_check.sh
b/docker/runtime/all-in-one/4.1/resource/health_check.sh
new file mode 100755
index 00000000000..16eaeebc18f
--- /dev/null
+++ b/docker/runtime/all-in-one/4.1/resource/health_check.sh
@@ -0,0 +1,41 @@
+#!/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.
+#
+# Backs the image HEALTHCHECK. Downstream CI waits on the resulting docker
+# health status instead of sleeping.
+
+set -uo pipefail
+
+DORIS_HOME="${DORIS_HOME:-/opt/apache-doris}"
+HOST=127.0.0.1
+FE_HTTP_PORT="${FE_HTTP_PORT:-8030}"
+BE_HTTP_PORT="${BE_HTTP_PORT:-8040}"
+
+# Bootstrap not finished yet: FE may answer while the backend is still being
+# registered, and a test that connects then sees a cluster with no capacity.
+[[ -f "${DORIS_HOME}/.ready" ]] || exit 1
+
+# FE readiness and backend liveness in one request: HealthAction returns 503
+# until FE is ready, and online_backend_num once it is.
+curl -fsS --max-time 4 "http://${HOST}:${FE_HTTP_PORT}/api/health" 2>/dev/null
\
+ | grep -qE '"online_backend_num"[[:space:]]*:[[:space:]]*[1-9]' || exit 1
+
+# The BE http port also serves stream load, so check it directly.
+curl -fsS --max-time 4 "http://${HOST}:${BE_HTTP_PORT}/api/health" >/dev/null
2>&1 || exit 1
+
+exit 0
diff --git a/docker/runtime/all-in-one/4.1/resource/prune.txt
b/docker/runtime/all-in-one/4.1/resource/prune.txt
new file mode 100644
index 00000000000..54be8d707ca
--- /dev/null
+++ b/docker/runtime/all-in-one/4.1/resource/prune.txt
@@ -0,0 +1,64 @@
+# 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.
+#
+# What each flavor drops. Format:
+#
+# <flavor> <path relative to /artifacts>
+#
+# always removed from both tags
+# base removed from the base tag only, kept in -full
+#
+# Paths that do not exist are skipped silently, so a directory that a future
+# 4.1.x drops needs no change here. Run smoke-test.sh after editing.
+
+# ---- offline tooling and CDC: no tag needs these ----
+always be/lib/meta_tool
+always be/lib/cdc_client
+always fe/arthas
+
+# ---- format-specific JNI scanners kept only in the -full tag ----
+# Each maps to exactly one class name BE can construct, see the table in
+# README.md. Dropping one disables that format and nothing else.
+base be/lib/java_extensions/hadoop-hudi-scanner
+base be/lib/java_extensions/trino-connector-scanner
+base be/lib/java_extensions/max-compute-connector
+
+# ---- NEVER add these: they are the JNI baseline, not "external table extras"
+#
+# be/lib/java_extensions/preload-extensions
+# Zero Java sources -- a pure dependency bundle (parquet-hadoop-bundle,
+# hadoop-common, hadoop-cos, libthrift, arrow, AWS SDK incl.
+# s3-tables-catalog-for-iceberg). Every external-table read goes through
+# it. Preloaded by bin/start_be.sh, and DORIS_PRELOAD_JAR must stay first
+# on the classpath.
+#
+# be/lib/java_extensions/java-udf
+# Preloaded alongside it by bin/start_be.sh.
+#
+# be/lib/hadoop_hdfs
+# The JVM side of libhdfs (157 jars). libhdfs itself is linked statically
+# into doris_be in 4.1 -- hence the empty native/ directory -- but it is
+# still a JNI wrapper and cannot open an HDFS file without these.
+#
+# be/lib/java_extensions/{paimon-scanner,iceberg-metadata-scanner,
+# jdbc-scanner,java-writer}
+# Deliberately in the base tag: 172M total buys Paimon, Iceberg system
+# tables, JDBC catalogs and external-table writeback.
+#
+# fe/lib
+# branch-4.1 has no FE plugin split; all 634 jars are on the startup
+# classpath. Pruning by jar name is fragile and saves little.
diff --git a/docker/runtime/all-in-one/4.1/resource/smoke-test.sh
b/docker/runtime/all-in-one/4.1/resource/smoke-test.sh
new file mode 100755
index 00000000000..1109db5d0fb
--- /dev/null
+++ b/docker/runtime/all-in-one/4.1/resource/smoke-test.sh
@@ -0,0 +1,153 @@
+#!/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.
+#
+# smoke-test.sh <image:tag> [base|full]
+#
+# DOCKER_RUN_OPTS passes extra flags to docker run, e.g. to reproduce a
+# memory-constrained CI runner:
+# DOCKER_RUN_OPTS='--memory 6g' smoke-test.sh apache/doris:all-in-one-4.1.3
+#
+# Guards the prune list. Everything runs through `docker exec`, using the
+# client tools already inside the image, so the host needs nothing but docker
+# and no ports have to be published.
+#
+# What it does not cover: real external-table reads. Those need the fixtures
+# under docker/thirdparties and are a separate exercise -- see README.md.
+
+set -euo pipefail
+
+IMAGE=${1:?usage: smoke-test.sh <image:tag> [base|full]}
+FLAVOR=${2:-base}
+WAIT_SECONDS=${WAIT_SECONDS:-300}
+
+CID=
+
+cleanup() {
+ local rc=$?
+ if [[ -n "${CID}" ]]; then
+ if ((rc != 0)); then
+ echo "--- container log (tail) ---" >&2
+ docker logs --tail 120 "${CID}" 2>&1 >&2 || true
+ fi
+ docker rm -f "${CID}" >/dev/null 2>&1 || true
+ fi
+ exit "${rc}"
+}
+trap cleanup EXIT
+
+step() { printf '\n== %s\n' "$*"; }
+fail() { echo "FAIL: $*" >&2; exit 1; }
+
+q() { docker exec "${CID}" mysql -uroot -h127.0.0.1 -P9030 -N --batch -e "$1";
}
+
+in_image() { docker exec "${CID}" test -d "/opt/apache-doris/$1"; }
+
+step "starting ${IMAGE}"
+CID=$(docker run -d ${DOCKER_RUN_OPTS:-} "${IMAGE}")
+# Record what was actually exercised: the image is multi-arch capable and this
+# is the one line that says which variant this run proves anything about.
+echo " image arch : $(docker image inspect "${IMAGE}" --format
'{{.Os}}/{{.Architecture}}')"
+echo " host arch : $(uname -m)"
+echo " container : $(docker exec "${CID}" uname -m 2>/dev/null || echo '?')"
+
+step "waiting for health status"
+deadline=$((SECONDS + WAIT_SECONDS))
+while ((SECONDS < deadline)); do
+ status=$(docker inspect -f '{{.State.Health.Status}}' "${CID}" 2>/dev/null
|| echo missing)
+ [[ "${status}" == healthy ]] && break
+ if [[ "$(docker inspect -f '{{.State.Running}}' "${CID}" 2>/dev/null)" !=
"true" ]]; then
+ fail "container exited before becoming healthy"
+ fi
+ sleep 2
+done
+[[ "$(docker inspect -f '{{.State.Health.Status}}' "${CID}")" == healthy ]] \
+ || fail "never became healthy within ${WAIT_SECONDS}s"
+echo "healthy after ${SECONDS}s"
+
+step "JNI baseline is present"
+# These three are what every external-table read goes through. If a future
+# edit to prune.txt takes one out, this is where it shows up.
+for d in be/lib/java_extensions/preload-extensions \
+ be/lib/java_extensions/java-udf \
+ be/lib/hadoop_hdfs; do
+ in_image "${d}" || fail "missing JNI baseline directory: ${d}"
+ echo " ok ${d}"
+done
+
+step "flavor payload matches ${FLAVOR}"
+# Present in both tags.
+for d in paimon-scanner iceberg-metadata-scanner jdbc-scanner java-writer; do
+ in_image "be/lib/java_extensions/${d}" || fail "missing from base payload:
${d}"
+ echo " ok ${d}"
+done
+# Present only in -full.
+for d in hadoop-hudi-scanner trino-connector-scanner max-compute-connector; do
+ if [[ "${FLAVOR}" == full ]]; then
+ in_image "be/lib/java_extensions/${d}" || fail "full flavor is missing
${d}"
+ echo " ok ${d} (full)"
+ else
+ ! in_image "be/lib/java_extensions/${d}" || fail "base flavor should
not ship ${d}"
+ echo " ok ${d} absent (base)"
+ fi
+done
+# Dropped from both tags.
+for d in be/lib/meta_tool be/lib/cdc_client fe/arthas; do
+ ! in_image "${d}" || fail "${d} should have been pruned"
+ echo " ok ${d} absent"
+done
+
+step "create database and table"
+# No replication_num: force_olap_table_replication_num must supply it.
+q "create database if not exists smoke"
+q "drop table if exists smoke.t"
+q "create table smoke.t (k int, v varchar(32), d date)
+ duplicate key(k) distributed by hash(k) buckets 3"
+[[ "$(q "show create table smoke.t" | grep -c 'replication_allocation')" -ge 1
]] \
+ || echo " note: replication_allocation not shown, continuing"
+
+step "insert and read back"
+q "insert into smoke.t values (1,'a','2026-01-01'),(2,'b','2026-01-02')"
+[[ "$(q 'select count(*) from smoke.t')" == "2" ]] || fail "insert/select
mismatch"
+echo " 2 rows"
+
+step "stream load"
+printf '3,c,2026-01-03\n' >/tmp/smoke_load.csv
+docker cp /tmp/smoke_load.csv "${CID}:/tmp/smoke_load.csv" >/dev/null
+rm -f /tmp/smoke_load.csv
+docker exec "${CID}" curl -sS -u root: \
+ -H "column_separator:," -H "Expect:100-continue" \
+ -T /tmp/smoke_load.csv \
+ -XPUT "http://127.0.0.1:8040/api/smoke/t/_stream_load" | grep -q
'"Status": *"Success"' \
+ || fail "stream load did not report Success"
+[[ "$(q 'select count(*) from smoke.t')" == "3" ]] || fail "row count after
stream load is wrong"
+echo " 3 rows"
+
+step "aggregation and schema change"
+[[ "$(q "select count(distinct v) from smoke.t")" == "3" ]] || fail
"aggregation result is wrong"
+q "alter table smoke.t add column c1 int default '0'"
+for _ in $(seq 1 60); do
+ [[ "$(q "show alter table column from smoke order by CreateTime desc limit
1" | awk '{print $10}')" == "FINISHED" ]] && break
+ sleep 2
+done
+q "select k, c1 from smoke.t order by k limit 1" >/dev/null || fail "cannot
read after schema change"
+echo " schema change applied"
+
+step "cleanup"
+q "drop database smoke"
+
+printf '\nsmoke test passed: %s (%s)\n' "${IMAGE}" "${FLAVOR}"
diff --git a/docker/runtime/all-in-one/Dockerfile
b/docker/runtime/all-in-one/Dockerfile
deleted file mode 100644
index d0832c49ad6..00000000000
--- a/docker/runtime/all-in-one/Dockerfile
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/bin/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.
-
-# how to use Dockerfile.
-# this is dockerfile for build doris fe image on amd64.
-# when build youself image.
-# 1. pull binary from official website and decompress into resource directory
that the level equals with Dockerfile_be_ubuntu.
-# 2. untar xxxx.tar.gz in resource directory, update the dockerfile field
`apache-doris-xxx`, replace with real version.
-# 3. run commad docker build -t xxx.doris.be:xx -f Dockerfile_be_ubuntu.
-
-# we have support buildx for amd64 and arm64 architecture image build.
-# get the binary from doris github and utar into resource, update the
directory as apache-`version(example:2.0.1)`-bin-`architecture(amd64/arm64)`
mode.
-
-# choose a base image
-FROM ubuntu:22.04
-
-ARG TARGETARCH
-
-RUN apt-get update -y && DEBIAN_FRONTEND=noninteractive apt-get install -y
--no-install-recommends \
- patchelf gdb binutils binutils-common mysql-client \
- curl wget less vim htop iproute2 numactl jq iotop sysstat \
- tcpdump iputils-ping dnsutils strace lsof blktrace tzdata \
- bpfcc-tools linux-headers-realtime linux-tools-realtime silversearcher-ag \
- net-tools openjdk-8-jdk && \
- rm -rf /var/lib/apt/lists/*
-
-# set environment variables
-ENV JAVA_HOME=/usr/lib/jvm/java-8-openjdk-${TARGETARCH:-amd64} \
- PATH="/opt/apache-doris/fe/bin:/opt/apache-doris/be/bin:${PATH}"
-
-# apache-doris/be from doris release xxxx.tar.gz.please update the version in
follows x.x.x.
-ADD resource/apache-doris-x.x.x-bin-${TARGETARCH:-amd64}/fe
/opt/apache-doris/fe
-ADD resource/apache-doris-x.x.x-bin-${TARGETARCH:-amd64}/be
/opt/apache-doris/be
-
-COPY resource/entry_point.sh /usr/local/bin/
-
-WORKDIR /opt/apache-doris
-
-ENTRYPOINT ["bash","entry_point.sh"]
diff --git a/docker/runtime/all-in-one/resource/entry_point.sh
b/docker/runtime/all-in-one/resource/entry_point.sh
deleted file mode 100644
index 32f38197779..00000000000
--- a/docker/runtime/all-in-one/resource/entry_point.sh
+++ /dev/null
@@ -1,188 +0,0 @@
-#!/bin/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.
-
-set -eo pipefail
-shopt -s nullglob
-
-DORIS_HOME="/opt/apache-doris"
-
-# Obtain necessary and basic information to complete initialization
-
-# logging functions
-# usage: doris_[note|warn|error] $log_meg
-# ie: doris_warn "task may be risky!"
-# out: 2023-01-08T19:08:16+08:00 [Warn] [Entrypoint]: task may be risky!
-doris_log() {
- local type="$1"
- shift
- # accept argument string or stdin
- local text="$*"
- if [ "$#" -eq 0 ]; then text="$(cat)"; fi
- local dt="$(date -Iseconds)"
- printf '%s [%s] [Entrypoint]: %s\n' "$dt" "$type" "$text"
-}
-doris_note() {
- doris_log Note "$@"
-}
-doris_warn() {
- doris_log Warn "$@" >&2
-}
-doris_error() {
- doris_log ERROR "$@" >&2
- exit 1
-}
-
-# check to see if this file is being run or sourced from another script
-_is_sourced() {
- [ "${#FUNCNAME[@]}" -ge 2 ] &&
- [ "${FUNCNAME[0]}" = '_is_sourced' ] &&
- [ "${FUNCNAME[1]}" = 'source' ]
-}
-
-docker_setup_env() {
- declare -g METADATA_FAILURE_RECOVERY MASTER_FE_IP CURRENT_BE_IP \
- CURRENT_BE_PORT DATABASE_ALREADY_EXISTS
- MASTER_FE_IP="127.0.0.1"
- CURRENT_BE_IP="127.0.0.1"
- CURRENT_BE_PORT=9050
- if [[ $RECOVERY == "true" ]]; then
- METADATA_FAILURE_RECOVERY='true'
- fi
- if [ -d "${DORIS_HOME}/fe/doris-meta/image" ]; then
- DATABASE_ALREADY_EXISTS='true'
- fi
-}
-
-# Execute sql script, passed via stdin
-docker_process_sql() {
- set +e
- mysql -uroot -P9030 -h127.0.0.1 --comments "$@" 2>&1
-}
-
-check_be_status() {
- set +e
- declare -g BE_ALREADY_EXISTS
- for i in {1..300}; do
- if [[ $1 == true ]]; then
- docker_process_sql <<<"show frontends" | grep
"[[:space:]]${MASTER_FE_IP}[[:space:]]"
- else
- docker_process_sql <<<"show backends" | grep
"[[:space:]]${CURRENT_BE_IP}[[:space:]]" | grep
"[[:space:]]${CURRENT_BE_PORT}[[:space:]]" | grep "[[:space:]]true[[:space:]]"
- fi
- be_join_status=$?
- if [[ "${be_join_status}" == 0 ]]; then
- if [[ $1 == true ]]; then
- doris_note "MASTER FE is started!"
- else
- doris_note "EntryPoint Check - Verify that BE is registered to FE
successfully"
- BE_ALREADY_EXISTS=true
- fi
- return
- fi
- if [[ $(( $i % 20 )) == 1 ]]; then
- if [[ $1 == true ]]; then
- doris_note "MASTER FE is not started. retry."
- else
- doris_note "BE is not register. retry."
- fi
- fi
- sleep 1
- done
-}
-
-add_priority_networks() {
- doris_note "add priority_networks ‘127.0.0.1/24’ to
${DORIS_HOME}/be/conf/be.conf"
- echo "priority_networks = 127.0.0.1/24" >>${DORIS_HOME}/be/conf/be.conf
-}
-
-register_be_to_fe() {
- set +e
- # check fe status
- local is_fe_start=false
- if [ -n "$DATABASE_ALREADY_EXISTS" ]; then
- check_be_status
- if [ -n "$BE_ALREADY_EXISTS" ]; then
- doris_warn "Same backend already exists! No need to register again!"
- return
- fi
- fi
- for i in {1..300}; do
- docker_process_sql <<<"alter system add backend
'${CURRENT_BE_IP}:${CURRENT_BE_PORT}'"
- register_be_status=$?
- if [[ $register_be_status == 0 ]]; then
- doris_note "BE successfully registered to FE!"
- is_fe_start=true
- return
- fi
- if [[ $(( $i % 20 )) == 1 ]]; then
- doris_note "Register BE to FE is failed. retry."
- fi
- sleep 1
- done
- if ! [[ $is_fe_start ]]; then
- doris_error "Failed to register BE to FE!Tried 30 times!Maybe FE Start
Failed!"
- fi
-}
-
-start_doris() {
- declare -g child_pid
- if [[ $METADATA_FAILURE_RECOVERY == "true" ]]; then
- doris_warn "Because \$RECOVERY = True, So Doris FE start
metadata_failure_recovery model."
- start_fe.sh --metadata_failure_recovery --console
- else
- doris_note "Start Doris FE."
- {
- set +e
- bash start_fe.sh --console 2>/dev/null
- } &
- fi
- sleep 20
- doris_note "Start Doris BE."
- {
- set +e
- bash start_be.sh --console 2>/dev/null
- } &
- child_pid=$!
-}
-
-stop_doris() {
- doris_note "Container stopped, running stop_fe & stop_be script"
- stop_be.sh
- sleep 10
- stop_fe.sh
-}
-
-_main() {
- trap 'stop_doris' SIGTERM SIGINT
- docker_setup_env
- # Check Already Exists
- if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
- add_priority_networks
- fi
- # Start Doris
- start_doris
- # register BE
- register_be_to_fe
- # keep BE started status
- wait $child_pid
- doris_note "Apache Doris is Start Successfully!"
- exec "$@"
-}
-
-if ! _is_sourced; then
- _main "$@"
-fi
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]