Files
llm-wiki/vault/30-knowledge/projects/ca-tmpl/runtime-container-health-migration.md
T

12 KiB

title, source_type, status, confidence, tags, related_projects, last_reviewed
title source_type status confidence tags related_projects last_reviewed
ca-tmpl - Runtime / Container / Health / Migration 결정 project verified high
ca-tmpl
runtime
container
kubernetes
flyway
actually-implemented
locally-verified
ca-tmpl
2026-07-02

ca-tmpl - Runtime / Container / Health / Migration 결정

Layer: wiki/projects/ — 내 프로젝트 사실. 일반 개념은 wiki/concepts/runtime-container-health-migration 참조.

프로젝트 컨텍스트

ca-tmpl은 Clean Architecture 기반 백엔드 skeleton 프로젝트다. 운영 계약(operational contract) 단계에서 JVM 서비스의 runtime baseline을 세 축으로 묶어 단일 운영 계약으로 통합하는 결정을 했다.

  • Container: Eclipse Temurin (Adoptium) JRE slim + JVM ergonomics (-XX:MaxRAMPercentage=75, -XX:+UseContainerSupport, -XX:+ExitOnOutOfMemoryError) + UTC / UTF-8 locale 고정.
  • Health: Kubernetes Probes 3종 (liveness / readiness / startup) 분리 + Spring Boot Actuator Health Groups + Required / Optional Dependency Matrix.
  • Migration: Flyway forward-only migration을 readiness-gated로 실행 + repair / baselineOnMigrate / outOfOrder 모두 prod forbidden + 표준 startup exit code 78 / 70 / 71 / 72 매핑.
  • Graceful shutdown budget: app 20s + preStop 5s + terminationGracePeriodSeconds 35s (10s margin).

현재 진행 상태:

  • C2 구현 + 로컬 검증 완료src/Dockerfile, runtime safety/startup validators, Actuator health group contract, Flyway prod safety guard, startup exit-code mapping, graceful shutdown settings가 코드화되어 있다. 2026-07-02 ./gradlew check 통과로 로컬 검증했다. Kubernetes manifest와 운영 rolling update 실측은 없다.

문서/설계 산출물만 존재하며, 코드/검증/측정은 전무하다.

실제 구현 내용 (actually-implemented)

  • src/Dockerfile과 runtime settings가 존재한다.
  • app-bootstrapRuntimeSafetyConfig, RuntimeSafetySettings, RuntimeNumericBoundsValidator, OpenInViewSafetyValidator, HikariPoolConstraintValidator가 startup/runtime guard를 구성한다.
  • MigrationStartupConfig, MigrationStartupRunner, FlywayProdSafetyValidator, StartupFailureException, StartupErrorCode가 migration readiness-gate와 exit code mapping을 구성한다.
  • adapter-webHealthcheckControllerapp-bootstrap health group contract가 liveness/readiness/startup 구분을 검증한다.

로컬/dev 검증 (locally-verified)

  • ./gradlew check 통과(2026-07-02, BUILD SUCCESSFUL, 114 tasks).
  • RuntimeHealthLifecycleContractTest가 liveness/readiness/startup group membership과 readiness-vs-liveness 분리를 검증한다.
  • FlywayProdSafetyValidatorTest, MigrationStartupRunnerTest, RequiredEnvironmentValidatorTest, StartupErrorCodeTest, StartupFailureExceptionTest가 migration/startup failure contract와 exit code를 검증한다.
  • ContainerRuntimeOomContractTest, OperationalContractRuntimeTest, RuntimeNumericBoundsValidatorTest, OpenInViewSafetyValidatorTest, HikariPoolConstraintValidatorTest가 runtime/container/startup guard를 검증한다.

운영 검증 (prod-verified)

없음. 운영 환경 검증 없음. K8s rolling update 동작, graceful shutdown 실측, cold start latency, migration 실패 복구 모두 없음.

문서/계획만 존재 (documented-only / planned)

운영 계약 문서(canonical §15 Runtime / Lifecycle Contract, §29 G-D)와 3 branch-note에 다음이 설계 수준으로만 기록되어 있다.

Container (actually-implemented / locally-verified)

  • Base image: Eclipse Temurin JRE slim 채택 (distroless / alpine+musl / GraalVM native 대안 모두 검토 후 보류).
  • JVM ergonomics: -XX:+UseContainerSupport (JDK 10+ default 명시) + -XX:MaxRAMPercentage=75 + -XX:+ExitOnOutOfMemoryError + -XX:HeapDumpPath.
  • Locale: UTC / UTF-8 고정 (env TZ=UTC, LANG=C.UTF-8).
  • 대안 검토: distroless (보안 surface 축소 vs 디버깅 손실), alpine+musl (image 크기 vs glibc 호환성 risk), GraalVM native-image (cold start vs reflection/peak throughput 손실, hybrid 사례) — branch-note feature-container-runtime-contract.

Health (actually-implemented / locally-verified)

  • K8s Probes 3 endpoint 분리: /livez, /readyz, /startupz (or Actuator /actuator/health/{liveness,readiness} + startup variant).
  • Spring Boot Actuator Health Groups로 endpoint별 HealthIndicator set 분리.
  • Required / Optional Dependency Matrix — DB·broker는 readiness 필수, 외부 cache는 optional 등 dependency 범위 명시.
  • 대안 검토: single /health (legacy, restart loop risk), custom HealthIndicator only (default readiness 외부 dependency 미포함), Istio mesh-based health (sidecar/app 구분 모호) — branch-note feature-runtime-health-lifecycle-contract.

Migration (actually-implemented / locally-verified)

  • Flyway forward-only + readiness-gated: migration 완료 전 readiness probe false.
  • prod forbidden: flyway.repair, flyway.baselineOnMigrate, flyway.outOfOrder 모두 prod에서 사용 금지.
  • 표준 startup exit code 매핑 (sysexits.h 관례):
    • 78 — config error (env / property 누락·잘못된 값)
    • 70 — internal software error (예상 외 application failure)
    • 71 — OS error (system call / resource 실패)
    • 72 — critical OS file missing
  • 대안 검토: Liquibase (DB-agnostic + rollback, XML/YAML verbose), Hibernate hbm2ddl=update (anti-pattern), Atlas (declarative, JVM 외부), K8s Init Container (replica race) vs Job + migration lock — branch-note feature-migration-startup-contract.

Graceful Shutdown (partially-implemented)

  • App SIGTERM 수신 후 in-flight 처리 20s + preStop hook 5s drain + K8s terminationGracePeriodSeconds 35s (10s margin).
  • Spring Boot server.shutdown=graceful + spring.lifecycle.timeout-per-shutdown-phase 설정 예정.

Kubernetes manifest와 실제 rolling update/drain 실측은 아직 없다. 따라서 local/runtime guard와 운영 가정의 경계를 분리해서 말해야 한다.

면접에서 말할 수 있는 범위

자신 있게 답할 수 있는 (개념·설계 의도)

  • JRE slim vs distroless 선택 근거 — 운영/디버깅 친숙도 vs 보안 surface trade-off.
  • liveness / readiness / startup 3 probe 분리 이유 — single /health로 묶으면 dependency 일시 outage가 container restart loop를 유발하고, startup 단계 liveness 오판이 긴 migration/warmup을 죽일 수 있다.
  • Graceful shutdown 단계 — SIGTERM → app drain 20s → preStop 5s → grace 35s. 각 timeout이 sync되지 않으면 SIGKILL로 inflight 요청 유실.
  • Flyway repair가 prod에서 위험한 이유 — 실제 schema 변경 없이 metadata만 수정. 공식이 직접 위험성 경고. baselineOnMigrate는 누락 migration skip, outOfOrder는 협업 일관성 깨짐.
  • Exit code 78/70/71/72 의미 — sysexits.h 관례. config error / internal / OS / critical OS file missing 진단 분리.

적당히 답할 수 있는

  • GraalVM native-image trade-off — cold start/메모리 우위 vs reflection·dynamic proxy build-time metadata 비용, peak throughput 손실. 우아한형제들도 hybrid 채택.
  • -XX:MaxRAMPercentage=75 vs 절대값 -Xmx — container memory limit 변경에 따라가는 비율 방식이 안전한 이유.

답하면 안 되는 (실측·운영 경험 없음)

  • "K8s rolling update를 운영하면서…" — 운영 경험 없음.
  • "cold start latency를 측정해보니…" — 측정 없음.
  • "DB migration이 prod에서 실패해서 복구한 경험" — 없음.
  • "liveness probe 오판으로 restart loop가 발생했을 때…" — 운영 incident 없음.
  • "graceful shutdown 35s budget이 실제로 충분했다" — 실측 없음.

과장 금지 지점

  • "GraalVM native-image가 곧 표준". reflection-heavy 코드와 peak throughput 손실은 실측 trade-off. ca-tmpl은 채택하지 않았고 hybrid 사례만 참조했다.
  • "K8s probe 동작을 운영에서 확인했다". health group contract는 로컬 테스트로 검증했지만 Kubernetes manifest/cluster 검증은 없다.
  • "Flyway readiness-gated migration이 운영에서 동작한다". startup guard와 prod forbidden option은 로컬 테스트로 검증했지만 prod migration 복구 경험은 없다.
  • "graceful shutdown 35s가 충분히 검증되었다". graceful shutdown 설정은 존재하지만 운영 drain 실측은 없다.
  • "distroless가 보안상 우월하다고 채택했다". ca-tmpl은 JRE slim 채택. distroless는 대안으로 검토만 했고 디버깅 손실을 이유로 보류.
  • "exit code 78/70/71/72가 표준이다". sysexits.h는 BSD 관례. POSIX 강제 표준 아님. 조직 enum 명시가 필요.

Blog-topic ingest: runtime 묶음 (2026-07-02)

raw/blog-topics/jvm-oom-vs-container-oomkill-exit-137-2026-07-02 는 JVM OOM과 container OOMKill이 비슷한 종료 신호로 보일 때 heap dump/native stderr/runtime signal을 어떻게 구분할지 정리하기 위한 raw seed다.

  • canonical 반영 범위: container/JVM runtime failure 해석을 runtime/container 결정 문서의 blog-topic 후보로 연결했다.
  • blogify 전 조건: 충족. 이 문서는 2026-07-02 기준 코드와 ./gradlew check로 검증됨.
  • 블로그 전 과장 방지: Kubernetes 운영 장애 대응 경험처럼 쓰지 않고, local/container evidence와 운영 가정을 분리한다.
  • raw/blog-topics/spring-actuator-health-probe-group-split-2026-07-02: Spring Actuator health group을 liveness/readiness/startup으로 분리하고 startup guard/shutdown lifecycle을 같은 운영 계약으로 보는 글감. Kubernetes end-to-end readiness 보장처럼 쓰지 않는다.
  • raw/blog-topics/java21-context-propagation-strategy-virtual-threads-2026-07-02: Java 21에서 request/security/tenant context를 ThreadLocal, Micrometer Context Propagation, ScopedValue 중 어디까지 다룰지 선택 기준을 정리하는 글감. ScopedValue 채택 경험처럼 쓰지 않고 후보/기준으로 제한한다.
  • raw/blog-topics/spring-boot-startup-exit-code-propagation-2026-06-10: startup failure exit code를 ExitCodeGenerator/Spring Boot uncaught exception path와 sysexits 관례로 분리해 설명하는 글감. POSIX 표준처럼 쓰지 않고, ca-tmpl 내부 convention과 local 검증 경계를 구분한다.
  • raw/blog-topics/spring-async-taskdecorator-bounded-executor-saturation-shutdown-2026-06-13: executor await timeout과 app shutdown / Kubernetes grace period의 계층 부등식을 다루는 글감. executor sizing 숫자를 측정값으로 쓰지 않는다.

관련 개념

Sources

Cluster / 묶음