Files
llm-wiki/wiki/concepts/runtime-container-health-migration.md
T

10 KiB

title, source_type, status, confidence, tags, related_projects, last_reviewed
title source_type status confidence tags related_projects last_reviewed
Runtime / Container / Health / Migration Baseline llm-generated draft medium
runtime
container
kubernetes
health
migration
flyway
ca-skeleton
2026-05-22

Runtime / Container / Health / Migration Baseline

Layer: wiki/concepts/ — JVM 서비스의 container runtime · runtime health · migration startup 세 sub-topic을 한 문서로 통합한 baseline. 내 프로젝트 사실은 project-template 사용.

Summary

JVM 서비스의 runtime baseline은 세 축으로 구성된다.

  1. Container: Eclipse Temurin (Adoptium) JRE slim + JVM ergonomics (-XX:MaxRAMPercentage=75, -XX:+UseContainerSupport).
  2. Health: Kubernetes Probes (liveness/readiness/startup)를 세 endpoint로 분리 + Spring Boot Actuator Health Groups로 dependency 범위를 명시.
  3. Migration: Flyway forward-only migration을 readiness gated로 실행 + 표준 startup exit code (sysexits 계열 78/70/71/72).

세 축은 graceful shutdown 35s budget (app 20s + preStop 5s + grace 10s margin)으로 묶인다.

Standard (공식 정의)

Container

  • Eclipse Temurin (Adoptium) — JEP/JCK 인증 OpenJDK 빌드. JRE slim 이미지는 JDK 대비 footprint 작고 production runtime에 권장.
  • OCI Image spec — base image, layer, label 표준. Dockerfile은 OCI 호환 image를 산출.
  • JVM container ergonomics:
    • -XX:+UseContainerSupport — JDK 10+ default. cgroup memory/cpu limit을 JVM이 인식.
    • -XX:MaxRAMPercentage=<N> — container memory limit의 N%를 max heap으로 사용. 절대값 -Xmx보다 container 환경에서 안전.
    • -XX:+ExitOnOutOfMemoryError — JVM OutOfMemoryError 발생 시 즉시 process exit (137).
    • -XX:HeapDumpPath=... — OOM 진단용 heap dump.

Health

  • Kubernetes Probes (kubelet 공식 모델):
    • liveness — process가 살아있는가. 실패 시 container restart.
    • readiness — traffic을 받을 수 있는가. 실패 시 Service endpoint 제거 (drain).
    • startup — startup이 끝났는가. startup probe가 success할 때까지 liveness/readiness 비활성. 긴 migration/warmup 시 liveness 오판 방지.
    • probe 분리는 K8s 공식 권장. single /health로 묶지 않는다.
  • Spring Boot Actuator Health Groupsmanagement.endpoint.health.group.liveness.include, .readiness.include로 endpoint별 HealthIndicator set을 분리.
    • Spring default readiness는 외부 dependency 미포함이므로 DB/broker 등 required dependency는 명시적 group 등록 필요.

Migration

  • Flyway 공식:
    • forward-only versioned migration이 기본 model.
    • flyway.repair — checksum/state 수정 도구. prod 사용은 공식이 직접 위험성 경고 (실제 schema 변경 없이 metadata만 수정).
    • flyway.baselineOnMigrate — 기존 DB에 처음 Flyway 적용 시. 잘못 켜면 누락 migration이 skip된 채 baseline.
    • flyway.outOfOrder — version 순서 외 migration 허용. 협업 환경에서 일관성 깨짐.
  • sysexits.h (BSD sysexits.h, 1990s) — Unix 관례적 exit code 의미.
    • 64 — usage error
    • 70 — internal software error
    • 71 — OS error
    • 72 — critical OS file missing
    • 78 — config error
    • 표준이 강제하는 enum은 아니지만 ops/CI 진단에 관례적으로 사용.

한계 / 주의점

Container 선택 트레이드오프

  • Temurin JRE slim (base):
    • 운영/디버깅 친숙도 우위 (shell, JDK tools 가용).
    • security surface는 distroless보다 크다 (apt, libc 등 OS 패키지 포함).
  • Distroless (Google):
    • OS 패키지 제거 → 보안 surface 축소 + image 크기 감소.
    • shell·debug tool 없음 → in-container 디버깅 손실. 별도 sidecar/ephemeral container 필요.
  • Alpine + musl libc:
    • image 크기 작음.
    • musl libc는 glibc 호환성 risk (DNS resolver 차이, native lib 미지원 등). Java 일부 native lib는 alpine에서 동작 미보장.
  • GraalVM Native Image / Spring Boot Native:
    • cold start/메모리 우위 (수십 MB heap, ms 단위 startup).
    • reflection·dynamic proxy는 build-time metadata 필요. peak throughput은 HotSpot JIT보다 손실.
    • Spring Boot Native는 Spring 6+ + Spring Boot 3+ AOT compile 의존.
    • 우아한형제들 도입기는 전체 native 전환이 아닌 hybrid 채택 결론.

Health 분리의 한계

  • Single /health endpoint (legacy):
    • liveness/readiness 구분 불가.
    • K8s rolling update 시 dependency 일시 outage가 container restart loop 유발 가능. traffic 유실 risk.
  • Custom HealthIndicator만 사용:
    • Spring default readiness는 외부 dependency 미포함. DB/broker 등은 명시적으로 readiness group에 묶지 않으면 readiness가 traffic 가능 여부를 반영하지 않음.
  • Service mesh-based health (Istio sidecar):
    • mTLS 환경에서 편의성. 단 sidecar 살아있음 / app 살아있음 구분이 mesh layer에서 불명확.
    • 추가 infra 의존 (sidecar 주입, mesh control plane).

Migration tool 트레이드오프

  • Liquibase (XML/YAML changelog):
    • DB-agnostic + rollback 기능.
    • XML/YAML 기반은 SQL 대비 verbose. migration speed Flyway 대비 느림 (changelog parser 오버헤드).
    • rollback 안전 보장 없음 (rollback script 사람이 작성).
  • Hibernate hbm2ddl=update:
    • 공식 anti-pattern. prod 사용 금지가 일반 권고. schema drift 추적 불가.
  • Atlas / Tern (schema-as-code):
    • declarative + integrity hash 강점.
    • Java/Spring 생태계 성숙도 부족. JVM 외부 CLI tool.
  • K8s Init Container 패턴:
    • replica마다 init container 실행 → multi-instance migration race.
    • K8s Job + migration lock이 race 회피에 구조적 우월.
  • Flyway 자체 한계:
    • repair / baselineOnMigrate / outOfOrder는 잘못 쓰면 schema state corruption. 공식이 직접 위험 경고.
    • forward-only 모델이라 rollback은 별도 forward migration으로 처리.

Exit code 한계

  • sysexits.h는 관례. POSIX 강제 표준 아님. 조직 표준으로 명시적 enum 필요.

Project Application

Interview Questions

  • JRE slim과 distroless 중 어떤 base image를 선택하고, 그 근거는 무엇인가?
  • -XX:MaxRAMPercentage=75로 설정한 이유는 무엇이고, 절대값 -Xmx와 어떤 차이가 있는가?
  • liveness / readiness / startup 세 probe를 분리하는 이유는 무엇인가? single /health로 묶으면 어떤 운영 문제가 생기는가?
  • graceful shutdown을 app 20s + preStop 5s + terminationGracePeriodSeconds 35s로 잡았다면 각 단계가 어떤 의미를 가지는가?
  • Flyway repair가 prod에서 위험하다고 보는 근거는? 어떤 대안 경로가 있는가?
  • startup exit code 78 / 70 / 71 / 72로 분리하면 어떤 진단상 이점이 생기는가? (config error / internal error / OS error / critical OS file missing)

Do Not Overclaim

  • "GraalVM native-image가 곧 standard"라고 단정하지 말 것. reflection-heavy 코드와 peak throughput 손실은 실측 trade-off. 우아한형제들 사례도 hybrid 채택.
  • "Flyway가 항상 우월"이라고 단정하지 말 것. 조직이 XML/YAML 기반 schema-as-doc을 요구하거나 DB-agnostic이 강제일 때는 Liquibase가 합리.
  • "distroless가 보안상 무조건 정답"이라고 단정하지 말 것. in-container 디버깅 손실은 incident 대응 시간을 늘릴 수 있다.
  • "K8s probe만 있으면 graceful shutdown은 자동"이라고 말하지 말 것. app shutdown timeout과 manifest grace period가 sync되지 않으면 SIGKILL로 inflight 요청 유실.
  • "exit code 70/78은 표준"이라고 말하지 말 것. sysexits.h는 관례이고 조직 enum 명시가 필요.

Sources