Files
tech-log-backend/docker-compose.yml
T

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://git.learn.hyeonworks.com/donghyeon.kang/tech-log-backend}"
image: tech-log-backend:${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: