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

159 lines
10 KiB
Markdown

---
title: Runtime / Container / Health / Migration Baseline
source_type: llm-generated
status: draft
confidence: medium
tags: [runtime, container, kubernetes, health, migration, flyway]
related_projects: [ca-skeleton]
last_reviewed: 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 Groups** — `management.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
- [[wiki/projects/ca-tmpl/runtime-container-health-migration]] — ca-tmpl 의사결정 기록 (현재 `documented-only`, Phase C2 미진입). 실제 구현 여부는 project 문서 참조.
- [[raw/branch-notes/feature-container-runtime-contract]] — container runtime 결정 (Temurin JRE slim, `MaxRAMPercentage=75`, UTC/UTF-8, graceful shutdown 35s).
- [[raw/branch-notes/feature-runtime-health-lifecycle-contract]] — liveness/readiness/startup 3-endpoint 분리, Required vs Optional Dependency Matrix.
- [[raw/branch-notes/feature-migration-startup-contract]] — Flyway baseline + readiness gated + exit code 78/70/71/72.
- [[raw/project-notes/ca-skeleton-operational-contract]] (§15 Runtime / Lifecycle Contract).
## 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
- [Eclipse Temurin / Adoptium project](https://adoptium.net/) — 공식 OpenJDK 배포.
- [Kubernetes — Configure Liveness, Readiness and Startup Probes (공식)](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)
- [Spring Boot Actuator — Health (공식)](https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.health)
- [Flyway — Concepts / Repair (공식)](https://documentation.red-gate.com/flyway/) — repair / baseline_on_migrate / out_of_order 위험성 경고 명시.
- [sysexits.h — BSD man page](https://man.freebsd.org/cgi/man.cgi?sysexits) — 64/70/71/72/78 등 관례적 exit code.
- [[raw/official-docs/container-distroless-google-github]] — Distroless 보안 surface vs 디버깅 손실.
- [[raw/official-docs/container-alpine-java-musl-tradeoffs]] — Alpine + musl libc 호환성 risk.
- [[raw/official-docs/container-graalvm-native-image-spring-boot]] — GraalVM native-image / Spring Boot Native AOT 비용·이득.
- [[raw/company-tech-blogs/container-woowahan-spring-native-tradeoffs]] — 우아한형제들 Spring Native 도입기 (hybrid 채택).
- [[raw/official-docs/runtime-health-k8s-probes-official]] — K8s liveness/readiness/startup 공식.
- [[raw/official-docs/runtime-health-spring-actuator-groups]] — Spring Boot Actuator Health Groups.
- [[raw/official-docs/runtime-health-istio-mesh-health-check]] — Istio mesh health 대안과 한계.
- [[raw/company-tech-blogs/runtime-health-datadog-engineering-graceful-shutdown]] — Datadog graceful shutdown preStop/drain/grace 비율 사례.
- [[raw/official-docs/migration-flyway-official-concepts-and-repair]] — Flyway 공식 repair/baseline_on_migrate/out_of_order 위험성.
- [[raw/official-docs/migration-liquibase-official-changelog-xml-yaml]] — Liquibase XML/YAML changelog.
- [[raw/official-docs/migration-atlas-schema-as-code]] — Atlas schema-as-code 대안.
- [[raw/official-docs/migration-k8s-init-container-job-pattern]] — K8s Init Container vs Job 패턴 비교.
- [[raw/project-notes/ca-skeleton-operational-contract]] — §15 Runtime / Lifecycle Contract.