blob: f2f43cd199375f3c63cf5e4d0cb2ac9c79fe89c2 [file]
#
# 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.
#
# Builder: Gradle 8.5 + JDK 21, matching the fork's local build setup. The
# gradle wrapper (8.7) is intentionally not used: its distribution is fetched
# from services.gradle.org, which is unreachable on this fork's network.
# Dependencies resolve against the Maven repositories configured in settings.gradle / build.gradle.
# Build-stage runs as root (default for the gradle base image); no shipped
# artifact is affected -- the runtime stage below drops to a non-root user.
FROM gradle:8.5.0-jdk21 AS builder
USER root
WORKDIR /build
COPY . .
# `dist` builds every subproject jar and assembles dist/{apps,lib,conf,bin};
# `installPlugin` lays out dist/plugin/<type>/<name>/ (storage + protocol only;
# connectors are filtered out — they ship in the connector image). Neither runs
# tests nor style gates (jar does not depend on check/test).
RUN gradle clean dist --no-daemon \
&& gradle installPlugin --no-daemon
# Runtime: EventMeshApplication boots traffic HTTP + admin HTTP (+ opt-in
# WebSocket) as one JVM. Storage/protocol plugins are discovered from ./plugin/
# by the SPI JarExtensionClassLoader, so only conf/apps/lib need the classpath.
# bin/start.sh assembles the classpath and maps ENV vars to -D flags.
FROM eclipse-temurin:21-jre
# procps ships pgrep (container introspection / debug). The base image already
# provides the C.UTF-8 locale, so no locales package / localedef is needed
# (issue #5350: the previous en_US.UTF-8 generation was never selected by any
# ENV, leaving the JVM on POSIX anyway).
RUN apt-get update && apt-get install -y --no-install-recommends procps \
&& rm -rf /var/lib/apt/lists/*
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8
# Run the JVM as a dedicated non-root user (issue #5350). uid 10001, nologin;
# home points at the app dir only so `~` resolves sensibly in shells.
RUN groupadd --gid 10001 eventmesh \
&& useradd --uid 10001 --gid eventmesh --home-dir /data/app/eventmesh --shell /usr/sbin/nologin eventmesh
WORKDIR /data/app/eventmesh
COPY --from=builder --chown=eventmesh:eventmesh /build/dist ./
RUN chmod +x bin/*.sh \
# Create the writable state dir up front: a bare VOLUME would be auto-created
# by the docker daemon as root, leaving the uid-10001 process unable to open
# the RocksDB offset store inside it (EACCES at boot).
&& mkdir -p /data/app/eventmesh/data /data/app/eventmesh/logs \
&& chown eventmesh:eventmesh /data/app/eventmesh/data /data/app/eventmesh/logs
# 10105 = traffic HTTP (/events/* CloudEvents + legacy /eventmesh/*).
# 10106 = independent admin port (UniAdminServer /admin/*, incl. GET /admin/health).
# 10107 = WebSocket push transport (opt-in via -Deventmesh.ws.port in JAVA_OPTS).
# 10108 = A2A gateway demos (Experimental, not started by bin/start.sh).
# 10205 = RESERVED for the future gRPC protocol port (the 1.x default; the uni
# runtime does not ship a gRPC server yet - do not bind it to another
# service so gRPC can land on its historical number).
# Note (#5380 deployment modes, see docs/feature/deployment.md, deployment modes): this image is the
# CORE Runtime. The A2A Gateway (A2AGatewayServer, EXPERIMENTAL) and the v2 streaming
# session layer are NOT started by bin/start.sh; they require a dedicated launcher /
# embedder wiring and are documented as separate deployment modes.
EXPOSE 10105 10106 10107
# Liveness probe against the admin health endpoint (issue #5350). Uses bash's
# /dev/tcp redirection so no curl/wget layer is added; any non-200 first line
# (connection refused, 5xx) fails the check.
HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \
CMD bash -c 'exec 3<>/dev/tcp/127.0.0.1/${EVENTMESH_ADMIN_PORT:-10106} \
&& printf "GET /admin/health HTTP/1.0\r\nHost: localhost\r\n\r\n" >&3 \
&& head -n1 <&3 | grep -q " 200 "' || exit 1
ENV DOCKER=true
ENV EVENTMESH_HOME=/data/app/eventmesh
ENV EVENTMESH_LOG_HOME=/data/app/eventmesh/logs
ENV CONFPATH=/data/app/eventmesh/conf
# memory ships as the zero-dependency default for dev/CI/quick start - no broker
# needed. Override with -e EVENTMESH_STORAGE_TYPE=kafka|rocketmq|rocketmq5 and
# supply the backend address via conf/eventmesh.properties (mount a volume over
ENV EVENTMESH_STORAGE_TYPE=memory
ENV EVENTMESH_HTTP_PORT=10105
ENV EVENTMESH_ADMIN_PORT=10106
ENV EVENTMESH_OFFSET_PATH=/data/app/eventmesh/data/offset
# Extra -D flags: TLS keystore, ws.port, meta.type/meta.addr for clustering, …
# For container memory limits consider -XX:MaxRAMPercentage=70.
ENV JAVA_OPTS=""
# Persistent state: the RocksDB offset store under data/ holds the delivery
# progress that makes at-least-once survive restarts (issue #5350). Declaring
# VOLUME makes the persistence requirement visible and keeps forgotten mounts
# on an anonymous volume instead of the container's writable layer.
VOLUME ["/data/app/eventmesh/data"]
USER eventmesh
CMD ["bash", "bin/start.sh"]