From b1b0a6c955c49e280ddc0395d2d0b9cf04ffd0b4 Mon Sep 17 00:00:00 2001 From: tsuz <6927131+tsuz@users.noreply.github.com> Date: Mon, 25 May 2026 22:04:09 +0900 Subject: [PATCH] fit all components in one --- Dockerfile-all-in-one | 40 ++++++++++++++++ docker-compose.all-in-one.yml | 90 +++++++++++++++++++++++++++++++++++ scripts/start-all-in-one.sh | 30 ++++++++++++ 3 files changed, 160 insertions(+) create mode 100644 Dockerfile-all-in-one create mode 100644 docker-compose.all-in-one.yml create mode 100755 scripts/start-all-in-one.sh diff --git a/Dockerfile-all-in-one b/Dockerfile-all-in-one new file mode 100644 index 0000000..f82193b --- /dev/null +++ b/Dockerfile-all-in-one @@ -0,0 +1,40 @@ +# All-in-one image: builds and runs every Flightdeck JVM service in one container. +# +# Build context MUST be the repo root: +# docker build -f Dockerfile-all-in-one -t flightdeck-all-in-one . +# +# Kafka is NOT included — run it as its own container or use a managed broker, +# and point KAFKA_BOOTSTRAP_SERVERS at it. + +# ---- Build stage: compile every module's JAR ---- +FROM maven:3.9-eclipse-temurin-17 AS build +WORKDIR /src +COPY . . +RUN mvn -q -Dmaven.test.skip=true -f api/chat-api/pom.xml package \ + && mvn -q -Dmaven.test.skip=true -f think/think-consumer/pom.xml package \ + && mvn -q -Dmaven.test.skip=true -f processor-apps/processing/pom.xml package \ + && mvn -q -Dmaven.test.skip=true -f memoir/update-memoir-consumer/pom.xml package \ + && mvn -q -Dmaven.test.skip=true -f monitoring/logging-consumer/pom.xml package + +# ---- Runtime stage: all JARs in one image ---- +FROM eclipse-temurin:17-jre +WORKDIR /app + +COPY --from=build /src/api/chat-api/target/chat-api-0.1.0-SNAPSHOT.jar api.jar +COPY --from=build /src/think/think-consumer/target/think-consumer-0.1.0-SNAPSHOT.jar think.jar +COPY --from=build /src/processor-apps/processing/target/flightdeck-streams-0.1.0-SNAPSHOT.jar processing.jar +COPY --from=build /src/memoir/update-memoir-consumer/target/update-memoir-consumer-0.1.0-SNAPSHOT.jar memoir.jar +COPY --from=build /src/monitoring/logging-consumer/target/monitoring-consumer-0.1.0-SNAPSHOT.jar monitoring.jar +COPY scripts/start-all-in-one.sh ./start-all-in-one.sh +RUN chmod +x ./start-all-in-one.sh + +# Shared defaults; override per service via -e / compose environment. +ENV KAFKA_BOOTSTRAP_SERVERS=kafka:29092 \ + PORT=8000 \ + WS_PORT=8001 \ + MEMOIR_ENABLED=true \ + CLAUDE_MODEL=claude-sonnet-4-20250514 \ + TOOLS_JSON_FILE=/app/tools.json + +EXPOSE 8000 8001 +ENTRYPOINT ["./start-all-in-one.sh"] diff --git a/docker-compose.all-in-one.yml b/docker-compose.all-in-one.yml new file mode 100644 index 0000000..b3698f3 --- /dev/null +++ b/docker-compose.all-in-one.yml @@ -0,0 +1,90 @@ +# All-in-one compose: Kafka + every Flightdeck JVM service in a single app container. +# +# docker compose -f docker-compose.all-in-one.yml up --build +# +# Requires CLAUDE_API_KEY in the environment (or a .env file next to this one). +services: + + # ─── Infrastructure ────────────────────────────────────────────────────────── + + kafka: + image: apache/kafka:4.1.1 + hostname: kafka + ports: + - "9092:9092" + volumes: + - kafka-data:/var/lib/kafka/data + environment: + KAFKA_NODE_ID: 1 + KAFKA_PROCESS_ROLES: broker,controller + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT + KAFKA_LISTENERS: INTERNAL://kafka:29092,EXTERNAL://0.0.0.0:9092,CONTROLLER://kafka:9093 + KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka:29092,EXTERNAL://localhost:9092 + KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL + KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER + KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093 + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 + KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0 + KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true" + KAFKA_LOG_DIRS: /var/lib/kafka/data + CLUSTER_ID: MkU3OEVBNTcwNTJENDM2Qk + healthcheck: + test: ["CMD", "/opt/kafka/bin/kafka-topics.sh", "--bootstrap-server", "localhost:9092", "--list"] + interval: 10s + timeout: 10s + retries: 10 + start_period: 30s + + # ─── All-in-one application container (api + think + processing + memoir + monitoring) ── + + flightdeck: + build: + context: . + dockerfile: Dockerfile-all-in-one + ports: + - "8000:8000" + - "8001:8001" + # nginx in the frontend image proxies to the hostname "api"; expose that alias + # so it resolves to this all-in-one container. + networks: + default: + aliases: + - api + volumes: + - ./examples/lead-followup-agent/tools.json:/app/tools.json:ro + - ./examples/lead-followup-agent/system-prompt.txt:/app/system-prompt.txt:ro + # Supplies CLAUDE_API_KEY (and any other example vars) directly to the container. + # Note: vars listed under `environment:` below override anything from env_file, + # so CLAUDE_API_KEY is intentionally NOT repeated there. + env_file: + - ./examples/lead-followup-agent/.env + environment: + KAFKA_BOOTSTRAP_SERVERS: kafka:29092 + AGENT_NAME: ${AGENT_NAME:-lead-followup} + PORT: 8000 + WS_PORT: 8001 + MEMOIR_ENABLED: ${MEMOIR_ENABLED:-false} + CLAUDE_MODEL: ${CLAUDE_MODEL:-claude-haiku-4-5-20251001} + CLAUDE_MAX_TOKENS: ${CLAUDE_MAX_TOKENS:-8096} + INPUT_TOKEN_PRICE: ${INPUT_TOKEN_PRICE:-1} + OUTPUT_TOKEN_PRICE: ${OUTPUT_TOKEN_PRICE:-5} + BUDGET_PRICE_PER_SESSION: ${BUDGET_PRICE_PER_SESSION:-0.5} + TOOLS_JSON_FILE: /app/tools.json + SYSTEM_PROMPT_FILE: /app/system-prompt.txt + depends_on: + kafka: + condition: service_healthy + + # ─── Frontend ──────────────────────────────────────────────────────────────── + + frontend: + image: ghcr.io/tsuz/flightdeck/frontend:${FLIGHTDECK_VERSION:-latest} + ports: + - "80:80" + depends_on: + - flightdeck + +volumes: + kafka-data: diff --git a/scripts/start-all-in-one.sh b/scripts/start-all-in-one.sh new file mode 100755 index 0000000..63ce426 --- /dev/null +++ b/scripts/start-all-in-one.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# +# Launches every Flightdeck JVM service inside a single container. +# Used by Dockerfile-all-in-one. Each JVM runs in the background with a +# tagged log prefix; if any one exits the whole container exits so Docker +# can restart the bundle. +# +set -euo pipefail + +# Per-JVM heap cap so 5 JVMs don't balloon the container. Override with JAVA_OPTS. +JAVA_OPTS="${JAVA_OPTS:--XX:MaxRAMPercentage=15.0}" + +run() { + local name="$1" jar="$2" + # stdbuf -oL forces sed to line-buffer; without it sed block-buffers when its + # stdout is a pipe (as under Docker), so low-volume services' logs — including + # startup banners and errors — would never surface. + # shellcheck disable=SC2086 + java $JAVA_OPTS -jar "/app/${jar}" 2>&1 | stdbuf -oL sed "s/^/[${name}] /" & +} + +run processing processing.jar +run think think.jar +run memoir memoir.jar +run monitoring monitoring.jar +run api api.jar + +# If ANY child process exits, stop the container with its exit code. +wait -n +exit $?