100 lines
5.0 KiB
YAML
100 lines
5.0 KiB
YAML
# =============================================================================
|
|
# feature-container-runtime-contract — base docker-compose service definition
|
|
#
|
|
# Usage:
|
|
# docker compose up # base only
|
|
# docker compose -f docker-compose.yml -f docker-compose.dev.yml up # dev override
|
|
# docker compose -f docker-compose.yml -f docker-compose.local.yml up # local override
|
|
#
|
|
# Graceful-shutdown sync table (feature-container-runtime-contract D5):
|
|
# App shutdown timeout : 30s (APP_SERVER_SHUTDOWN_TIMEOUT — SSOT: docs/registries/
|
|
# env-keys.yaml, owner feature-env-driven-runtime-configuration)
|
|
# Container preStop : 5s (handled by the orchestrator / stop_grace_period offset)
|
|
# stop_grace_period : 40s (30s app drain + 5s preStop + 5s safety margin)
|
|
#
|
|
# Ownership: env-driven-config owns the app shutdown *value* (30s); this contract owns the
|
|
# *relationship* (stop_grace_period >= app timeout + preStop + margin). The grace is therefore
|
|
# sized UP to the env-keys SSOT value (30s) — you do not shrink the app drain window to fit an
|
|
# arbitrary grace. This resolves the earlier 20s/35s drift, which had assumed an app-timeout
|
|
# value this branch does not own (env-keys.yaml is the SSOT and sets 30s).
|
|
#
|
|
# Memory limit (D4):
|
|
# The container memory limit MUST be set for -XX:MaxRAMPercentage=75 to be meaningful.
|
|
# Default 512m suits a small service; adjust per deployment.
|
|
# =============================================================================
|
|
|
|
services:
|
|
app:
|
|
build:
|
|
context: src/
|
|
dockerfile: Dockerfile
|
|
args:
|
|
RELEASE_VERSION: "${RELEASE_VERSION:-0.0.1}"
|
|
BUILD_VERSION: "${BUILD_VERSION:-0.0.1+0000000}"
|
|
GIT_SHA: "${GIT_SHA:-0000000}"
|
|
SOURCE_URL: "${SOURCE_URL:-https://example.invalid/ca-tmpl}"
|
|
image: caskeleton:${BUILD_VERSION:-0.0.1_local_0000000}
|
|
ports:
|
|
- "${APP_SERVER_PORT:-8080}:8080"
|
|
- "9001:9001"
|
|
environment:
|
|
TZ: "UTC"
|
|
LANG: "C.UTF-8"
|
|
LC_ALL: "C.UTF-8"
|
|
# Graceful shutdown — fallback matches the env-keys.yaml SSOT default (30s).
|
|
# Override via .env or docker compose --env-file if you need a different value.
|
|
APP_SERVER_SHUTDOWN_TIMEOUT: "${APP_SERVER_SHUTDOWN_TIMEOUT:-30s}"
|
|
APP_SERVER_SHUTDOWN: "graceful"
|
|
# ---- Read-only root filesystem (D2) -------------------------------------
|
|
# The application must write only to the tmpfs mounts declared below.
|
|
# /tmp — Tomcat basedir (see -Dserver.tomcat.basedir=/tmp in Dockerfile).
|
|
# /var/tmp/heap — heap dump landing zone (see -XX:HeapDumpPath=/var/tmp/heap).
|
|
read_only: true
|
|
tmpfs:
|
|
- /tmp:mode=1777,size=128m
|
|
- /var/tmp/heap:mode=1777,size=512m
|
|
# ---- Fileserver storage volume ------------------------------------------
|
|
# A named volume, not a tmpfs and not the read-only root. The Fileserver platform's default
|
|
# storage root is /var/lib/backend/files, and with a read-only root and no mount there was
|
|
# nowhere on the image it could legally write: enabling the capability failed on its first
|
|
# upload rather than at startup. The volume is declared unconditionally because a volume
|
|
# nobody writes to costs nothing, while a missing one costs an outage.
|
|
#
|
|
# Ownership: the image runs as uid/gid 1000 (see src/Dockerfile). Docker initialises a fresh
|
|
# named volume from the image path's ownership, so the directory is created in the image with
|
|
# that owner; a pre-existing volume or a host bind mount must be chowned to 1000:1000 by the
|
|
# operator, or every write is refused with a permission error the application reports as
|
|
# STORAGE_UNAVAILABLE.
|
|
volumes:
|
|
- fileserver-data:/var/lib/backend/files
|
|
# ---- Memory limit (D4) --------------------------------------------------
|
|
# Must be set so -XX:MaxRAMPercentage=75 can compute a meaningful heap bound.
|
|
mem_limit: 512m
|
|
memswap_limit: 512m
|
|
# ---- Graceful shutdown (D5) ---------------------------------------------
|
|
# stop_grace_period = app drain (30s, env-keys SSOT) + preStop (5s) + margin (5s) = 40s.
|
|
stop_grace_period: 40s
|
|
# ---- Health check -------------------------------------------------------
|
|
# Targets the actuator readiness probe on the management port (9001).
|
|
# CROSS-FEATURE COUPLING: /actuator/health/readiness is implemented by the
|
|
# parallel runtime-health + actuator branches. This healthcheck will report
|
|
# UNHEALTHY in this worktree until those branches are merged.
|
|
healthcheck:
|
|
test:
|
|
- "CMD"
|
|
- "wget"
|
|
- "--no-verbose"
|
|
- "--tries=1"
|
|
- "--spider"
|
|
- "http://localhost:9001/actuator/health/readiness"
|
|
interval: 30s
|
|
timeout: 5s
|
|
start_period: 60s
|
|
retries: 3
|
|
restart: unless-stopped
|
|
|
|
volumes:
|
|
# Survives container replacement, which is the point: published content outlives the process
|
|
# that wrote it. Back this with real storage in any deployment that keeps files.
|
|
fileserver-data:
|