Files
llm-wiki/raw/branch-notes/feature-runtime-health-lifecycle-contract.md
T

61 KiB
Raw Blame History

title, source_type, status, branch, parent_branch, related_projects, governing_docs, tags, created, target_merge, status_label, id, kind, project, work_item, inherits, refines, overrides, depends_on, contract_packet, contract_packet_sha256
title source_type status branch parent_branch related_projects governing_docs tags created target_merge status_label id kind project work_item inherits refines overrides depends_on contract_packet contract_packet_sha256
branch / feature-runtime-health-lifecycle-contract branch-note raw feature-runtime-health-lifecycle-contract
ca-skeleton
wiki/projects/ca-tmpl/runtime-container-health-migration
branch
ca-skeleton
runtime
health
lifecycle
2026-05-21 in-progress BR-CA-SKELETON-OPERATIONAL-CONTRACT-013 project-work-item ca-skeleton-operational-contract WI-CA-SKELETON-OPERATIONAL-CONTRACT-013
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MIGRATION-001@1
1 3924b8c0f447ff7dd65b9e109da6dadaac2055bb945f0db17844ea22b8e8e0fd

branch: feature-runtime-health-lifecycle-contract

Layer: raw/branch-notes/ — runtime health와 application lifecycle 실패 계약을 정의합니다.

부모 (필수)

ca-skeleton 은 별도 root branch 없이 project-note 가 SSOT 역할. 본 feature branch 는 project-note 의 운영 계약 중 해당 영역 (§<관련 섹션>) 의 결정/근거/금지 사항을 정제한다.

브랜치 계약 패킷

  • 생성 시 프로젝트 개정: 1
  • 패킷 스키마: contract_packet: 1
  • 완료 조건: startup·readiness·shutdown lifecycle test가 통과한다

상속한 프로젝트 결정

Decision Ref Project Summary Branch Application Source
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MIGRATION-001@1 Flyway는 startup에서 실행하고 migration 완료 후에만 readiness를 healthy로 전환한다 Work Item 완료 조건에 적용 raw/project-notes/ca-skeleton-operational-contract

브랜치 지역 결정

기존 branch-local 결정은 아래 ## Decision Evidence Map / 결정-근거 매핑의 D-row가 소유하며 이 packet에서 복제하지 않는다.

Decision ID Decision Relation Supporting Claims Status

선언한 예외

Override ID Overrides Reason Approval Status

목표

서비스는 요청 처리 중에만 실패하지 않습니다. startup, migration, readiness, graceful shutdown, scheduler, async executor, resource exhaustion 같은 lifecycle 표면도 skeleton 기본 기준에 포함되어야 합니다.

  • 이슈:
  • PR:

범위

포함 범위

  • actuator health/readiness/liveness 기준.
  • graceful shutdown 기준.
  • startup validation 기준.
  • scheduled job 실패 기준.
  • async executor/thread pool rejection 기준.
  • resource exhaustion 분류.
  • system clock/timezone 기준.

제외 범위

  • Kubernetes manifest 작성.
  • cloud provider specific health check.
  • scheduler business job 구현.

근거 (필수, 최소 1개+)

본 branch의 결정 근거. 상세 비교는 §외부 근거 / 대안 조사 (있다면) 참조.

Source 정당화하는 결정
raw/official-docs/runtime-health-k8s-probes-official K8s liveness/readiness/startup probe 공식
raw/official-docs/runtime-health-spring-actuator-groups Spring Boot Actuator Health Groups 공식 (ca-tmpl 결정과 정합
raw/official-docs/runtime-health-istio-mesh-health-check mTLS 환경 편의성 vs sidecar/app 살아있음 구분 불명확
raw/company-tech-blogs/runtime-health-datadog-engineering-graceful-shutdown Datadog preStop 5s + drain 20s + grace 35s 비율 보강
raw/official-docs/k8s-configure-probes-task-page D5 startup probe budget 산식 (failureThreshold × periodSeconds) verbatim + D11 startup validation scope (legacy / slow-starting 분리) 정당화
raw/official-docs/k8s-pod-lifecycle-probes-concept D7 timeoutSeconds vs periodSeconds 의미 구분 — 4가지 probe 메커니즘이 "단일 호출" 단위임을 정의 + probe outcome 정의
raw/official-docs/rfc3339-datetime-utc D12 UTC 강제의 IETF Standards Track 근거 ("Z" suffix 의미 + UTC interoperability 권고)
raw/official-docs/spring-smartlifecycle-reference D4 graceful shutdown 의 phase ordering (ascending start / descending stop) + stop(Runnable) async + DefaultLifecycleProcessor phase-level timeout 메커니즘

외부 근거 / 대안 조사 (2026-05-22 — Group G-D: Runtime Health Lifecycle)

본 branch의 liveness/readiness/startup probe 3-endpoint 분리 + Required vs Optional Dependency Matrix + UTC + NTP drift >5s readiness fail 결정에 대한 외부 source.

TODO

TODO drained 2026-05-22 — 결정은 아래 "결정 사항" / "Health Endpoint Contract" / "Required vs Optional Dependency Matrix" / "Startup Validation Scope" / "Decisionized Work Items" 참조. actuator endpoints/graceful shutdown/startup validation/scheduler/executor/resource exhaustion/timezone-clock 모두 표 또는 결정 라인으로 반영됨. 잔존 TODO 없음.

Work Item Contract

각 TODO는 아래 판정 단위로 재작성되어야 canonical 승급 가능합니다. TODO가 단순히 기준 작성으로 남아 있으면 이 branch는 완료로 보지 않습니다.

field required rule
Decision yes 구현자가 선택해야 하는 기본값
Allowed yes 허용되는 예외와 조건
Forbidden yes 절대 금지되는 구현/문서 상태
Required registry update conditional error/env/header/log/metric/capability 변경 시 필수
Required contract test yes 계약 위반 시 실패해야 하는 테스트
Failure condition yes review/build에서 실패로 판정할 상태
Canonical extraction target yes wiki/projects 승급 위치

진행 중 메모

  • readiness 실패와 liveness 실패는 운영 의미가 다릅니다.

결정 사항

  • 2026-05-21: runtime lifecycle도 non-business operational contract에 포함.
  • 2026-05-22: endpoint shape owner는 이 branch. management actuator security branch는 exposure/auth policy만 소유.
  • 2026-05-22: startup probe를 별도로 두고 migration/startup validation 중 readiness/liveness 오판을 막음.
  • 2026-05-22: graceful shutdown timeout은 app runtime과 deployment manifest sync table에서 같은 값을 사용.
  • 2026-05-22: startup probe timeout = initialDelaySeconds=10, periodSeconds=5, failureThreshold=30 (최대 150s, migration 포함). 초과 시 K8s가 SIGKILL.
  • 2026-05-22: graceful shutdown total budget = 35s (terminationGracePeriodSeconds). app shutdown timeout = 20s, preStop sleep = 5s, safety margin = 10s.
  • 2026-05-22: startup probe single-call timeout 30s × failureThreshold 30 × periodSeconds 5s = total budget 150s. container-runtime의 single timeout 30s는 single probe call 한도. 150s는 startup 전체 한도(migration 포함). 두 수치는 다른 축.
  • 2026-05-22: multi-instance claim parsing SSOT는 feature-env-driven-runtime-configurationAPP_MULTI_INSTANCE_ENABLED flag. 본 branch는 readiness probe 시 이 flag와 distributed lock contract test 결과의 일치 verify (consume only).

결정-근거 매핑

각 결정이 어떤 raw source claim 으로 뒷받침되는지 명시한다. Decision ID 는 이 branch-note 안에서 안정적으로 유지한다.

Decision ID Decision Supporting Claims Evidence Strength Open Risk
D1 runtime lifecycle 도 non-business operational contract 에 포함 UNSUPPORTED_DECISION (scope 결정은 내부 운영 정책) N/A branch scope 결정 — 외부 표준 인용 대상 아님
D2 endpoint shape owner = 본 branch, management actuator security branch 는 exposure/auth policy 만 소유 UNSUPPORTED_DECISION (SSOT ownership 분할) N/A branch ownership 정책
D3 startup probe 별도 endpoint — migration/startup validation 중 readiness/liveness 오판 방지 raw/official-docs/runtime-health-k8s-probes-official.md#K8S-PROBE-C4, raw/official-docs/runtime-health-k8s-probes-official.md#K8S-PROBE-C5 (startup probe 가 성공할 때까지 liveness/readiness 실행 안 함 + startup 실패 시 kubelet kill) official-vendor-doc (K8s 공식 — startup probe 가 느린 초기화 보호) K8S-PROBE-C4 Usage Boundary: startup probe 미설정 시 동작은 본 인용 범위 밖. Spring Boot 가 startup 전용 group 을 default 제공하는지는 SB-HEALTH-C1 에 명시 없음 (liveness + readiness 만)
D4 graceful shutdown timeout = app runtime ↔ deployment manifest sync table 동일값 Mechanism SUPPORTED: raw/official-docs/spring-smartlifecycle-reference.md#SPRING-SMARTLC-C3 (startup ascending / shutdown descending phase 순서 — web server 보다 outbound 컴포넌트가 먼저 stop 되는 mechanism), raw/official-docs/spring-smartlifecycle-reference.md#SPRING-SMARTLC-C7 (stop(Runnable) async + DefaultLifecycleProcessor 의 phase-level timeout 대기 mechanism). Quantitative stays UNSUPPORTED: app runtime ↔ deployment manifest 의 동일값 강제 + 35s/20s/5s/10s 조합 자체는 인용 자료에 직접 spec 없음. company-tech-blog RH-DD-C1~C4needs-confirmation (verbatim 미확보) official-vendor-doc (Spring Framework — mechanism only) + UNSUPPORTED (quantitative sync 값) SPRING-SMARTLC-C7 Does not prove: DefaultLifecycleProcessor 의 timeout default 값 (30s) 은 본 인용 범위 밖. company-tech-blog 자체가 needs-confirmation — official best practice 표현 금지. 35s/20s/5s/10s 가 "Datadog 권장 범위 내" 진술은 검증 실패. CODE DRIFT: ca-tmpl 실측값은 executor await 19s + server phase timeout 30s — §Audit SHUTDOWN_BUDGET_DRIFT 참조
D5 startup probe timeout = initialDelaySeconds=10, periodSeconds=5, failureThreshold=30 (최대 150s, migration 포함) Mechanism SUPPORTED: raw/official-docs/k8s-configure-probes-task-page.md#K8S-PROBE-TASK-C2 (startup probe maximum budget = failureThreshold × periodSeconds 의 단일 문장 verbatim — "30 * 10 = 300s" 예시로 산식 직접 명시) + raw/official-docs/runtime-health-k8s-probes-official.md#K8S-PROBE-C6 (periodSeconds default 10s — ca-tmpl 은 5s 로 override). Quantitative stays UNSUPPORTED: ca-tmpl 의 구체 값 initialDelaySeconds=10 / periodSeconds=5 / failureThreshold=30 자체는 내부 운영 가정 — 인용 자료의 예시는 30 × 10 = 300s 이며 ca-tmpl 의 30 × 5 = 150s 가 Spring Boot 콜드스타트를 cover 한다는 실측 부재 (Claims To Verify 참조) official-vendor-doc (산식 mechanism) + UNSUPPORTED (정량 10/5/30) K8S-PROBE-TASK-C2 Does not prove: initialDelaySeconds 가 budget 에 포함되는지는 본 인용 단독으로 명시 안 됨 (C4 권고와 조합 필요). failureThreshold / timeoutSeconds / initialDelaySeconds default 값도 본 capture 에서 직접 증명 안 됨 — ca-tmpl 의 10/5/30 은 외부 표준이 아닌 ca-tmpl 운영 가정
D6 graceful shutdown total budget = 35s (terminationGracePeriodSeconds), app shutdown timeout = 20s, preStop sleep = 5s, safety margin = 10s UNSUPPORTED_DECISION (인용 자료에 35s/20s/5s/10s 정량 spec 직접 근거 없음 — RH-DD-C2 의 "510s preStop + 1030s drain + 3060s grace" 도 verbatim 미확인, needs-confirmation) company-case-study (Datadog blog — verbatim 미확인) 정량 값은 ca-tmpl 운영 가정. company-tech-blog 의 "510s/1030s/3060s" 도 needs-confirmation — official 권장 아님. CODE DRIFT: 코드는 app shutdown 20s 가 아니라 executor await 19s (AsyncExecutorConfig:46 "container 20s budget 1s cleanup margin") + server phase timeout 30s (APP_SERVER_SHUTDOWN_TIMEOUT default) — §Audit SHUTDOWN_BUDGET_DRIFT
D7 startup probe single-call timeout 30s vs total budget 150s = 다른 축 명시 Mechanism SUPPORTED: raw/official-docs/k8s-pod-lifecycle-probes-concept.md#K8S-POD-LC-C4 (httpGet probe = 단일 HTTP GET 호출 — timeout 적용 단위), K8S-POD-LC-C5 (exec probe = 단일 명령 실행), K8S-POD-LC-C6 (tcpSocket probe = 단일 TCP 연결), K8S-POD-LC-C7 (grpc probe = 단일 RPC 호출). 4가지 probe 메커니즘 모두 "단일 호출의 결과를 평가" 하므로 timeout 은 호출 단위, period 는 반복 주기라는 두 축 구분이 메커니즘 정의로부터 함의됨 + raw/official-docs/runtime-health-k8s-probes-official.md#K8S-PROBE-C6 (periodSeconds default 10s — period 축 근거). Quantitative stays UNSUPPORTED: single-call timeout 30s 의 정량 값은 본 branch 인용 자료에 직접 verbatim 없음 — container-runtime spec 별도 필요 official-vendor-doc (timeout vs period 축 구분 mechanism) + UNSUPPORTED (30s 단일 값) K8S-POD-LC-C4~C7 Does not prove: periodSeconds / timeoutSeconds단일 문장 verbatim 정의 — 본 capture 의 configuration fields 섹션이 truncate (concept 페이지 NOTE 참조). 의미 구분은 메커니즘 정의로부터 간접 정당화
D8 multi-instance claim parsing SSOT = feature-env-driven-runtime-configuration consume only UNSUPPORTED_DECISION (SSOT 분할은 내부 운영 정책) N/A branch ownership 분할. CODE 정합: StartupSafetyValidator:59 validateMultiInstance()APP_MULTI_INSTANCE_ENABLED=true 시 5개 coordination bean (distributedLockProvider 등) 존재를 assert — owner 는 feature-env-driven-runtime-configuration + feature-distributed-lock-contract (§엣지·실패·의존)
D9 liveness = JVM process can continue, readiness = traffic + required deps ready, startup = startup/migration validation 완료 raw/official-docs/runtime-health-spring-actuator-groups.md#SB-HEALTH-C1, raw/official-docs/runtime-health-spring-actuator-groups.md#SB-HEALTH-C2, raw/official-docs/runtime-health-spring-actuator-groups.md#SB-HEALTH-C3, raw/official-docs/runtime-health-spring-actuator-groups.md#SB-HEALTH-C6, raw/official-docs/runtime-health-k8s-probes-official.md#K8S-PROBE-C1, raw/official-docs/runtime-health-k8s-probes-official.md#K8S-PROBE-C3 official-vendor-doc (Spring Actuator + K8s 공식) SB-HEALTH-C3 Does not prove: readiness 가 자동으로 외부 의존성 실패에 반응하는 것 아님 — application code 가 publish 해야 함. ca-tmpl 의 "readiness 에 외부 dependency 포함" 은 SB-HEALTH-C8 (needs-confirmation) — default 모델과 어긋날 가능성
D10 Required vs Optional Dependency Matrix (primary DB required / primary cache conditional / message broker optional·fail-open / notification adapter optional) raw/official-docs/runtime-health-spring-actuator-groups.md#SB-HEALTH-C7 (health group 의 CompositeHealthContributor include/exclude 메커니즘 존재) official-vendor-doc (부분) SB-HEALTH-C7 Does not prove: 외부 dependency 를 readiness 에 포함시키는 권장/비권장 정책은 본 인용 범위 밖. SB-HEALTH-C8needs-confirmation — Spring 의 "default readiness 는 외부 의존성 미포함" verbatim 부재
D11 Startup validation = env var presence + DB schema migration history + required adapter bean — external endpoint reachability 는 startup-time 검사하지 않음 Mechanism SUPPORTED: raw/official-docs/k8s-configure-probes-task-page.md#K8S-PROBE-TASK-C1 (startup probe 의 일차 use case = legacy / slow-starting 워크로드 보호 — startup validation 의 외부 dependency 는 startup probe 가 cover 한다는 분리 정당화), K8S-PROBE-TASK-C4 ("If your container usually starts in more than initialDelaySeconds + failureThreshold × periodSeconds, you should specify a startup probe that checks the same endpoint as the liveness probe" — runtime probe 로 외부 reachability 위임하는 메커니즘 정당화). Scope decision stays partially UNSUPPORTED: env var presence / DB migration history / adapter bean 의 각 항목이 startup validation 에 포함되어야 한다는 공식 spec 없음 — ca-tmpl 운영 가정 official-vendor-doc (startup probe ↔ runtime probe 분리 mechanism) + UNSUPPORTED (validation 항목 구성) K8S-PROBE-TASK-C1 Does not prove: startup probe 가 모든 워크로드 default 라는 뜻 아님 — 본 인용은 "legacy applications" 한정. K8S-PROBE-TASK-C4 의 "should... the same endpoint" 는 권고 — startup probe endpoint 가 liveness 와 반드시 같아야 하거나 달라야 한다는 강제 아님. "startup-time 외부 endpoint 검사 anti-pattern" 의 공식 경고 자체는 본 capture 에 없음. CODE 정합: 실 구현은 sibling feature-migration-startup-contract (RequiredEnvironmentValidator/MigrationStartupRunner/StartupSafetyValidator, exit 78/70/71/72) — 본 branch 는 scope policy owner, 코드/에러코드는 위임 (§Audit OWNERSHIP_DRIFT)
D12 JVM timezone UTC 강제 + NTP drift > 5초 시 readiness fail 검토 UTC part SUPPORTED: raw/official-docs/rfc3339-datetime-utc.md#RFC3339-C1 ("Z" suffix = UTC offset 00:00, ICAO "Zulu" 정의), RFC3339-C2 ("true interoperability is best achieved by using Coordinated Universal Time (UTC)" — local timezone rule 의 daylight saving 복잡성으로 인한 IETF Standards Track 권고). 5s drift stays UNSUPPORTED: NTP drift > 5초 threshold 의 정량 값은 RFC 3339 범위 밖 — NTP (RFC 5905) / NIST 별도 raw 필요. health endpoint timestamp 가 readiness 에 미치는 영향의 mechanism 도 본 RFC 범위 밖 official-standard (UTC 권고 — IETF RFC 3339 Standards Track) + UNSUPPORTED (5s threshold + readiness 연동) RFC3339-C2 Does not prove: "UTC 만 허용" strict MUST 아님 — best achieved by 는 권고 (numeric offset 도 syntactically valid). NTP drift threshold 의 정량 spec 자체는 본 RFC 범위 밖 — Claims To Verify 의 NTP 5초 threshold 검증 항목 참조. CODE 정합: Clock.systemUTC()IdempotencyConfig:27 에 실재 (UTC clock actually-implemented). JVM -Duser.timezone=UTC / TZ=UTC 는 container env (owner feature-container-runtime-contract). NTP-drift readiness check 는 코드 부재 = planned
D13 Service mesh-based health (Istio) 대안 거부 raw/official-docs/runtime-health-istio-mesh-health-check.md#RH-IST-C1, raw/official-docs/runtime-health-istio-mesh-health-check.md#RH-IST-C2, raw/official-docs/runtime-health-istio-mesh-health-check.md#RH-IST-C3 (mTLS + httpGet probe 실패 / probe rewrite default 활성화 / sidecar 가 response body strip) official-vendor-doc (Istio 공식) RH-IST-C2 Does not prove: probe rewrite 가 application 자체의 deadlock 을 감지한다는 뜻 아님 — sidecar→app HTTP probe 통과만 확인. ca-tmpl 의 "sidecar/app 살아있음 구분 불명확" 평가 와 정합

Health Endpoint Contract

endpoint shape owner default meaning failure condition
/actuator/health/liveness runtime-health JVM process can continue dependency outage alone fails liveness
/actuator/health/readiness runtime-health can receive traffic and required deps ready migration/startup validation 중 healthy
/actuator/health/startup runtime-health startup/migration validation completed absent startup gate in deployable profile

⚠️ 구현 상태 = planned: ca-tmpl 코드에는 현재 custom GET /healthcheck (HealthcheckController:16, {"status":"UP"}) 만 존재하며, 위 3개 actuator probe endpoint + Spring Boot Actuator Health Groups 설정은 미작성이다. 상세 + reconcile 권고는 §Audit HEALTH_ENDPOINT_NOT_IMPLEMENTED, 구현 절차는 §구현 가이드 1 참조.

Required vs Optional Dependency Matrix

이 branch는 dependency taxonomy 표만 owns. 실제 dependency 분류는 integration-adapter-templates와 cross-link.

dependency type required startup validation readiness 영향
primary DB yes connection + migration history unavailable → readiness fail
primary cache (Redis enabled 시) conditional ping unavailable → degraded ready (cache-aside fallback)
message broker (Kafka, outbox publish) no — fail-open none (producer lazy) unavailable → degrade (outbox 가 DB 보존 후 retry; readiness 미반영)
notification adapter (Slack/Email) no none unavailable → degrade

dependency taxonomy 표의 owner는 본 branch. 실제 adapter별 분류(Kafka/Redis/Slack/Email 등)와 fail-open/closed 정책 SSOT는 integration-adapter-templates branch consume. 양방향 cross-link.

Startup Validation Scope

  • env var presence + type/range 검증.
  • DB schema migration history 일치 확인.
  • required adapter bean 등록 확인.
  • external endpoint reachability는 startup-time에 검사하지 않음 (runtime probe로 대체).
  • JVM timezone UTC 강제. NTP drift > 5초 시 readiness fail 검토 (테스트 계약 항목).

Decisionized Work Items

item Decision Allowed Forbidden Required test
graceful shutdown stop readiness first, drain inflight, then exit force stop after timeout accept new traffic while draining lifecycle smoke
scheduler failure structured error log + retry/DLQ owner mapping fail-fast for critical jobs swallow exception job failure test
executor rejection map to operational error/log with executor name shed load with 503 generic internal without context rejection test
resource exhaustion memory/disk/temp classified separately platform alert first raw OOM only resource failure mapping

구현 가이드

결정 이 "무엇" 이라면 본 §는 "어디에 어떻게" 의 사전 명세. 본 branch 는 ca-tmpl 운영 계약의 runtime health + lifecycle 표면을 owns — 단, 구체 error-code / env-key / executor 설정 / 메트릭은 sibling branch 가 SSOT (registry owner_branch 기준). 따라서 아래 sub-section 은 본 branch 가 정하는 것 (endpoint shape, dependency taxonomy, startup validation scope, shutdown ordering, clock readiness policy) 만 명세하고, sibling-owned 메커니즘은 위임 포인터(R3) 로 남긴다. ca-tmpl 코드 anchor 는 /home/donghyeon/workspace/ca-tmpl/src (read-only 대조 2026-06-14).

1. Health probe endpoint shape + readiness group membership

Trace: D3 (K8S-PROBE-C4/C5) + D9 (SB-HEALTH-C1/C2/C3/C6, K8S-PROBE-C1/C3) + D10 (SB-HEALTH-C7). Health Endpoint Contract 표가 owner.

  • UNSUPPORTED_IMPL_DECISION: (a) readiness group include 멤버의 정확한 indicator 이름 집합 — SB-HEALTH-C8needs-confirmation 이라 "Spring default readiness 가 외부 dependency 미포함"의 verbatim 미확보 → 어떤 indicator 를 명시 include 할지는 구현자 trade-off. (b) 기존 custom /healthcheck (HealthcheckController:16) 를 retire 할지 actuator 와 공존할지 — 두 endpoint 공존 시 운영 혼선 vs migration 비용 trade-off.
구현 항목 명세 상태 Anchor
actuator probe 활성화 management.endpoint.health.probes.enabled=true + management.endpoint.health.group.{liveness,readiness,startup}.include=... actually-implemented 2026-06-15 worktree ca-tmpl-runtime-health-lifecycle. spring-boot-starter-actuator 추가 + application.yml management 블록
startup group/probe startup gate 를 readiness 와 분리해 migration 중 readiness/liveness 오판 방지 actually-implemented management.endpoint.health.group.startup.include=readinessState
liveness 멤버 livenessState 만 — 외부 dependency 미포함 (outage 시 restart loop 방지) actually-implemented management.endpoint.health.group.liveness.include=livenessState
readiness 멤버 readinessState + db (primary DB — REQUIRED) — optional 의존성 제외 actually-implemented management.endpoint.health.group.readiness.include=readinessState,db
기존 endpoint custom GET /healthcheck{"status":"UP"} (actuator 미사용) actually-implemented adapter-web/.../HealthcheckController.java:16
exposure/auth policy OUT_OF_BRANCH_SCOPE (R3) — actuator 노출/인증은 raw/branch-notes/feature-management-actuator-security-contract (D2) 위임 ⚠️ §Audit PROBE_AUTH_BLOCKER (현재 probe 401) governing security-baseline-jwt-actuator-secrets

2. Required-dependency → readiness wiring (taxonomy → group membership)

Trace: D10 + §Required vs Optional Dependency Matrix. CompositeHealthContributor include/exclude 메커니즘 = SB-HEALTH-C7.

  • UNSUPPORTED_IMPL_DECISION: "degraded ready" (primary cache conditional) 를 Spring HealthStatus 로 어떻게 표현할지 (UP-with-detail vs custom status) — Spring status enum 매핑은 구현자 선택. 인용 자료에 spec 없음.
dependency readiness 멤버십 위임 owner (R3)
primary DB (required) readiness group include → unavailable=DOWN adapter 분류는 feature-integration-adapter-templates
message broker (Kafka, outbox publish) readiness 제외 — Kafka 기본 비활성(DisabledMessagePublisher) + publish 실패는 outbox retry, broker HealthIndicator 부재 (2026-06-15 런타임 확인: readiness body 에 broker component 없음) mechanism feature-domain-event-outbox-contract + fail-open/closed feature-integration-adapter-templates
primary cache (conditional) readiness 제외 → cache-aside fallback = degraded ready feature-cache-consistency-contract
notification (Slack/Email, optional) readiness 제외 → degrade only feature-integration-adapter-templates (fail-open/closed)
multi-instance 일치 readiness 시 APP_MULTI_INSTANCE_ENABLED flag ↔ distributed-lock contract test 결과 일치 verify (consume only) D8 — flag SSOT feature-env-driven-runtime-configuration, lock feature-distributed-lock-contract

3. Startup validation scope (policy owner here, 코드 위임)

Trace: D11 (K8S-PROBE-TASK-C1/C4). 본 branch = startup validation 에 무엇이 포함되는가 의 scope policy owner. 코드 + exit-code 매핑은 sibling feature-migration-startup-contract 가 SSOT (§Audit OWNERSHIP_DRIFT).

  • UNSUPPORTED_IMPL_DECISION: env presence / migration history / adapter bean 3항목 구성 자체는 ca-tmpl 운영 가정 (D11 partially-unsupported) — 공식 spec 없음.
validation 항목 (scope) 위임 구현 (sibling) exit code Anchor
env var presence (datasource) RequiredEnvironmentValidator 78 STARTUP_VALIDATION_FAILED app-bootstrap/.../runtime/startup/RequiredEnvironmentValidator.java
DB schema migration history MigrationStartupRunner (readiness-gated) 70 MIGRATION_FAILED .../runtime/startup/MigrationStartupRunner.java
prod-forbidden flyway flags FlywayProdSafetyValidator 71 PROFILE_MISMATCH .../runtime/startup/FlywayProdSafetyValidator.java
required adapter bean 등록 + prod-unsafe toggle StartupSafetyValidator 72 REQUIRED_ADAPTER_DISABLED .../runtime/StartupSafetyValidator.java:57-100
StartupPhase 라벨 (구조화 로그) StartupPhase enum: env-validation / migration / adapter-enablement / profile-check .../runtime/startup/StartupPhase.java
external endpoint reachability 금지 — startup-time 검사 안 함, runtime probe 로 위임 D11 (K8S-PROBE-TASK-C4)

4. Graceful shutdown ordering + budget sync

Trace: D4 (SPRING-SMARTLC-C3/C7 — descending stop phase + async stop(Runnable) + phase-level timeout). 본 branch = shutdown ordering invariant + budget ≤ terminationGracePeriod sync 요구 owner. 정량 값은 sibling SSOT.

  • UNSUPPORTED_IMPL_DECISION: 35s/20s/5s/10s 조합은 ca-tmpl 운영 가정 (D6 UNSUPPORTED_DECISION). RH-DD-C1~C4needs-confirmation.
  • OUT_OF_BRANCH_SCOPE (R3): terminationGracePeriodSeconds=35s + preStop sleep=5s 는 K8s manifest 값 → §범위 Out of scope. feature-container-runtime-contract 가 owner.
항목 명세 위임/상태 Anchor
ordering invariant SIGTERM → readiness DOWN (신규 traffic 차단) → server inflight drain → outbound 컴포넌트 descending stop → exit 본 branch owns (D4) SPRING-SMARTLC-C3
spring 설정 server.shutdown=graceful + spring.lifecycle.timeout-per-shutdown-phase=${APP_SERVER_SHUTDOWN_TIMEOUT} actually-implemented (config) app-bootstrap/.../application.yml:203-205, 211
server phase timeout 값 APP_SERVER_SHUTDOWN_TIMEOUT default 30s 위임 feature-env-driven-runtime-configuration env-keys.yaml (validation: ≤ k8s terminationGracePeriod)
executor await setWaitForTasksToCompleteOnShutdown(true) + setAwaitTerminationSeconds(19) ("20s budget 1s margin; 25s forbidden") 위임 feature-background-job-async-contract app-bootstrap/.../async/AsyncExecutorConfig.java:46,72-73
budget sync 요구 app shutdown budget terminationGracePeriodSeconds — 초과 시 SIGKILL → inflight 유실 본 branch invariant + container-runtime 값 §엣지·실패·의존

5. executor / resource) — taxonomy owns here, mechanism 위임

Trace: §Decisionized Work Items. 본 branch = 실패 표면 분류 policy owner. 구체 error-code / executor 설정 / scheduler 코드 / 메트릭은 sibling SSOT (§Audit OWNERSHIP_DRIFT).

  • UNSUPPORTED_IMPL_DECISION: resource exhaustion (memory/disk/temp) 분류는 본 branch policy 지만 대응 registry error-code 가 부재 (error-codes.yaml NOT FOUND) → RESOURCE_* 코드는 "신규 제안" / planned.
실패 표면 policy (본 branch) 위임 mechanism (sibling) Anchor
scheduler failure structured error log + retry/DLQ owner mapping; critical=fail-fast; swallow 금지 OutboxRelayScheduler.relay() — 모든 Exception catch + ERROR 로그 + 다음 tick 재시도 (thread 생존) app-bootstrap/.../outbox/OutboxRelayScheduler.java:66-87 (feature-domain-event-outbox-contract)
executor rejection executor name 포함 operational error/log + 503 shed; context 없는 generic internal 금지 LoggingAbortPolicyOperationalError.JOB_EXECUTOR_REJECTED (TRANSIENT_DEPENDENCY / 503 / retryable) + 메트릭 executor.rejected.total·executor.saturation AsyncExecutorConfig.java:71, shared-contract/.../OperationalError.java:148, error-codes.yaml (feature-background-job-async-contract)
resource exhaustion memory/disk/temp 별도 분류; platform alert first; raw OOM only 금지 planned — 대응 RESOURCE_* error-code 미존재 (신규 제안 필요) error-codes.yaml NOT FOUND

6. Clock / timezone readiness

Trace: D12 (RFC3339-C1/C2 — UTC interoperability 권고).

  • UNSUPPORTED_IMPL_DECISION: NTP drift > 5초 threshold + readiness-gating 메커니즘은 무출처 (RFC 3339 범위 밖). 2026-06-14 자동조사 결론: 어떤 공식 표준(RFC 5905/7519, NIST SC-45, K8s)도 app-readiness 의 NTP-drift 임계값을 정의하지 않으며, readiness 를 clock skew 로 gating 하면 동일 노드 모든 pod 의 동시 readiness fail(cascade) 위험 → Alt 2(clock-agnostic readiness + 인프라 계층 모니터링 위임) 권고. 본 sub-section 의 "NTP readiness" 행은 사용자 D12 개정 확정 전까지 planned 유지. 상세 §Audit NTP_READINESS_ANTIPATTERN.
  • OUT_OF_BRANCH_SCOPE (R3): JVM -Duser.timezone=UTC / TZ=UTC 는 container env → feature-container-runtime-contract (governing doc: TZ=UTC, LANG=C.UTF-8).
항목 명세 상태 Anchor
UTC clock Clock.systemUTC() bean (timestamp 생성 UTC 고정) actually-implemented app-bootstrap/.../idempotency/IdempotencyConfig.java:27
JVM timezone TZ=UTC container env 강제 (production) + -Duser.timezone=UTC test JVM arg (test pinning) container env 위임 feature-container-runtime-contract; test arg actually-implemented 2026-06-15 (app-bootstrap/build.gradle tasks.named('test')) RuntimeHealthLifecycleContractTest#jvm_default_timezone_is_utc 로 검증
NTP drift readiness drift > 5초 시 readiness fail planned (무출처, 코드 부재) D12 / Claims To Verify / §Audit 자동조사

엣지·실패·의존

R4(깊이 게이트) 캡처용. 정상 경로 외 구현 중 부딪힐 실패/엣지/타 계약 의존.

  • 실패·엣지 경로:
    • readiness flip race: migration 진행 중 startup probe 통과 전까지 readiness 는 DOWN 이어야 함 (D3). readiness 가 migration 완료 전 UP 되면 un-migrated 인스턴스로 traffic 유입.
    • liveness ≠ dependency outage: DB outage → liveness 200 / readiness 503 (D9, K8S-PROBE-C1). liveness 가 외부 의존성 실패로 죽으면 cascading restart loop.
    • graceful shutdown race: app shutdown budget > terminationGracePeriodSeconds → SIGKILL → inflight 유실 (D4/D6 budget sync invariant). executor await 19s + server phase 30s 가 grace 35s 안에 drain 완료해야 함.
    • executor rejection under load: queue capacity 200 초과 → LoggingAbortPolicy → 503 (TRANSIENT_DEPENDENCY). executor-name context 없이 shed 하면 금지 (Decisionized Work Items).
    • scheduler 침묵 swallow: OutboxRelayScheduler 가 모든 Exception catch + 생존 — business 실패가 조용히 삼켜지면 안 됨 (status 전이는 use case 에서 로깅).
    • clock skew 미감지: NTP drift 미감지 시 JWT exp 검증 / distributed-lock TTL / idempotency timestamp 왜곡 (ca-tmpl audit report 의 "clock drift 노드가 readiness UP 유지" 격리 갭).
    • startup-time 외부 reachability 미검사: 필수 외부 의존성이 boot 시 down 이어도 인스턴스는 ready 가 됨 (D11) → runtime readiness probe 가 잡아야 함.
  • 다른 계약 의존:

Audit & Findings (ca-tmpl ground-truth 대조 2026-06-14)

/branch-spec §2 — branch-note 의 명칭/매핑이 registry/코드 enum 과 어긋날 때 surface. 사용자 작성 결정 영역은 auto-rewrite 하지 않고 정합 권고만 기록.

  • HEALTH_ENDPOINT_NOT_IMPLEMENTED (정합 권고)2026-06-15 해소: feature-runtime-health-lifecycle-contract worktree 에서 spring-boot-starter-actuator 추가 + management.endpoint.health.probes.enabled=true + 3개 group include 설정 완료 (actually-implemented). custom GET /healthcheck (HealthcheckController:16) 는 공존 — task 명세가 retire 금지를 명시함. actuator probe 는 별도 경로(/actuator/health/{liveness,readiness,startup})로 추가됨. exposure/auth policy 는 parallel feature-management-actuator-security-contract 소유 (unchanged).
  • SHUTDOWN_BUDGET_DRIFT (정합 권고): D6/§결정사항 의 "app shutdown timeout = 20s" 가 코드와 어긋남 — 코드 실측은 (a) executor await setAwaitTerminationSeconds(19) (AsyncExecutorConfig:46, "container 20s budget 1s cleanup margin; 25s forbidden"), (b) server phase timeout APP_SERVER_SHUTDOWN_TIMEOUT default 30s. 노트가 executor-await(19s)와 server-phase-timeout(30s) 두 축을 "20s" 하나로 뭉갬. → 권고: D6 를 executor await 19s / server phase 30s / terminationGracePeriod 35s(manifest) 세 축으로 분리. (사용자 결정 영역 — auto-rewrite 안 함.)
  • OWNERSHIP_DRIFT (정합 — 위임 확인): 본 노트가 표로 다루는 일부 계약값의 registry owner_branch 는 sibling 임 (D2/D8 의 "shape/scope/policy 만 owns" 와 정합):
    • JOB_EXECUTOR_REJECTED (TRANSIENT_DEPENDENCY/503/retryable) → feature-background-job-async-contract (error-codes.yaml, OperationalError.java:148).
    • STARTUP_VALIDATION_FAILED(78)/MIGRATION_FAILED(70)/PROFILE_MISMATCH(71)/REQUIRED_ADAPTER_DISABLED(72)feature-migration-startup-contract.
    • executor.saturation/executor.rejected.totalfeature-background-job-async-contract (metrics.yaml).
    • APP_SERVER_SHUTDOWN/APP_SERVER_SHUTDOWN_TIMEOUT/APP_MULTI_INSTANCE_ENABLEDfeature-env-driven-runtime-configuration (env-keys.yaml).
    • StartupSafetyValidator:35distributedLockProvider multi-instance 주석은 raw/branch-notes/feature-distributed-lock-contract (D1/D3) 로 reassign 됨. → 조치: 이 코드/키들을 본 branch 가 소유한다고 주장하지 않음. §구현 가이드 의 위임 포인터(R3) 유지.
  • GOVERNING_DOC_STALE (Advisory): governing wiki/projects/ca-tmpl/runtime-container-health-migration.md (last_reviewed 2026-05-22) 은 "C2 미진입 / 코드 없음" 으로 기술하나, startup validators + async executor + outbox scheduler 는 현재 코드 존재 (sibling-owned). Health endpoint 슬라이스는 여전히 planned (정합). → 본 branch health 슬라이스 착수 시 governing doc refresh 권고. 비차단.
  • RESOURCE_CODE_ABSENT (planned): resource-exhaustion 분류(memory/disk/temp)에 대응하는 registry error-code 가 error-codes.yaml없음. 별도 operational code 가 필요하면 owner_branch=본 branch 로 "신규 제안" row 등록 (§구현 가이드 5).
  • NTP_READINESS_ANTIPATTERN (정합 권고 — 자동조사 2026-06-14): D12 의 "NTP drift > 5초 시 readiness fail" 은 wiki-decision-researcher 조사 결과 어떤 공식 표준에도 근거 없음 — RFC 5905(STEPT 125ms / PANICT 1000s, app readiness 임계값 아님)·RFC 7519(JWT leeway "a few minutes", 숫자 없음)·NIST SP 800-53 SC-45(org-defined 위임)·K8s 공식(클럭을 readiness 사유로 미정의). "5초" 는 무출처 운영 가정으로 확정. 또한 readiness 를 clock-skew 로 gating 하면 동일 노드의 모든 pod 이 동시에 readiness fail → cascade failure 위험(AWS EKS prescriptive guidance). 조사 권고 = Alt 2: readiness 는 clock-agnostic, clock-skew 모니터링은 인프라 계층(Prometheus node_timex_offset_seconds + K8s NodeProblemDetector NTPProblem NodeCondition)에 위임. → 권고(사용자 결정 영역 — auto-rewrite 안 함): D12 의 readiness-gating 부분을 제거하고 (a) UTC 강제(유지, RFC3339-C1/C2 + Clock.systemUTC()), (b) clock-skew = 인프라 위임으로 분리. 채택 시 raw 4건 archive(RFC 5905 / RFC 7519 / K8s NPD / node-exporter mixin) 후 §Sources·§Decision Evidence Map 갱신. 미채택(Alt 3 startup-only sanity check) 선택지도 조사에 포함 — 결정 전 확인 필요: ca-tmpl 의 실제 JWT leeway / 분산락 TTL(허용 드리프트 역산), NPD·node-exporter 배포 여부.
  • PROBE_AUTH_BLOCKER (⚠️ 차단 의존 — 2026-06-15 런타임 검증; 2026-06-15 sentinel BLOCKED): worktree 부팅 후 unauthenticated curl 결과 /actuator/health + /actuator/health/{liveness,readiness,startup} 전부 HTTP 401 AUTH_TOKEN_MISSING (/api/healthcheck 만 200). 원인: SECURITY_PUBLIC_PATHS=/api/healthcheck + 코드에 management/actuator SecurityFilterChain 부재(EndpointRequest/toAnyEndpoint 검색 0건). K8s kubelet 은 JWT 없이 probe 를 호출하므로 liveness 401=restart loop / readiness 401=never-ready / startup 401=kill → probe end-to-end 비동작. → 조치(sibling 코드): feature-management-actuator-security-contract/actuator/health/liveness·/actuator/health/readiness 를 unauthenticated 허용(EndpointRequest.to("health") permitAll 또는 별도 management.server.port). 본 branch 코드 변경 아님(D2 exposure/auth 위임). → 2026-06-15 interim 시도 후 revert: src/.envSECURITY_PUBLIC_PATHS 에 3개 sub-path 를 interim 추가했으나 ca-architect-sentinelnot-ready(blocking:1) 판정 — verifyPublicPathSnapshot 스냅샷 미갱신 + 이 branch scope 밖(actuator 인증/노출은 feature-management-actuator-security-contract + 별도 management.server.port=9001 에서 처리되므로 8080 SECURITY_PUBLIC_PATHS 에 추가하는 것이 의미상 잘못됨). → revert 완료(2026-06-15): SECURITY_PUBLIC_PATHS=/api/healthcheck 단일값으로 복원. verifyPublicPathSnapshot PASS. src/.env = HEAD~1 identical. 현재 상태: probe shape actually-implemented, probe auth = 여전히 sibling BLOCKERfeature-management-actuator-security-contract 정식 구현(별도 management.server.port=9001 또는 EndpointRequest.to("health").permitAll()) 전까지 kubelet probe 401 은 expected in this branch.
  • BROKER_READINESS_DRIFT (정합 — 2026-06-15 코드 대조 후 노트 정정 완료): §Dependency Matrix(D10) 가 broker 를 "required(publish) → readiness fail" 로 기술했으나 구현은 broker 를 readiness 에서 제외(readiness.include=readinessState,db). 코드 ground truth: Kafka 기본 비활성(DisabledMessagePublisher) + fail-open(publish 실패는 outbox 흡수) + broker HealthIndicator 부재. transactional outbox 설계상 broker 가용성이 readiness 를 gating 하면 안 됨 → 코드가 옳음, 노트가 stale. → 본 세션에서 D10 + §Matrix + §구현 가이드 2 를 broker=optional·fail-open 으로 정정. 코드 변경 불필요.
  • ACTUATOR_METERREGISTRY_SIDEEFFECT (확인 필요 — 2026-06-15): 본 branch 가 spring-boot-starter-actuator 를 classpath 에 추가 → 여태 "no Actuator → no-op" 이던 MeterRegistry 가 actuator autoconfiguration 으로 활성화(tracing/metrics/outbox/lock 의 ObjectProvider<MeterRegistry> no-op fallback 이 실제 등록으로 전환). 부팅 로그에 SimpleMeterRegistry — A MeterFilter is being configured after a Meter has been registered WARN 2건(cardinality filter ordering — 일부 early meter 에 미적용 가능). → 확인(metrics 브랜치): (a) metrics dormant→active 가 의도된 통합 시점인지, (b) MetricsCardinalityMeterFilter/MetricsContractConfig filter 설치를 meter 등록 이전 으로 당겨 WARN 해소. feature-metrics-alerting-contract 소유 — 본 branch 코드 변경 아님(actuator 의존은 health probe 에 필수).

테스트 계약

  • required dependency가 unavailable이면 readiness가 실패해야 함.
  • graceful shutdown 중 신규 요청 처리 정책이 명시되어야 함.
  • scheduler failure가 조용히 삼켜지면 실패.
  • async executor rejection이 INTERNAL without context로 뭉개지면 실패.
  • startup probe 없이 migration/readiness race가 가능하면 실패.
  • JVM timezone이 UTC가 아니면 실패.
  • NTP drift > 5초 상태에서 readiness가 ready를 유지하면 실패 (검토 대상).

검증해야 할 주장

Claim Why uncertain How to verify Status
Spring Boot 의 default readiness group 이 외부 의존성 (DB/Kafka) 을 포함하지 않음 → ca-tmpl 이 명시적 management.endpoint.health.group.readiness.include 필요 SB-HEALTH-C8needs-confirmation — verbatim 미확보 Spring Boot reference 의 actuator.endpoints.health.groups 페이지 별도 fetch + application.yml config 검증 needs-confirmation
startup probe total budget = failureThreshold × periodSeconds 산식의 K8s 공식 verbatim K8S-PROBE-C7needs-confirmation — task 페이지 truncate task 페이지 #define-startup-probes sub-URL 직접 fetch needs-confirmation
ca-tmpl 의 startup 30 × 5s = 150s 가 Spring Boot 콜드스타트 + JVM warmup + 외부 의존성 wiring 시간 cover 실측 부재 k8s deployment 실측 (startup 시간 분포 + p99) planned
readiness fail → EndpointSlice 제거 → drain → preStop sleep → SIGTERM → shutdown timeout 의 e2e timing 이 ca-tmpl 의 PreStop 5s + grace 35s 와 정합 K8S-PROBE-C3 Does not prove: EndpointSlice 제거 propagation delay 본 인용 범위 밖 chaos test — readiness fail 시 inflight request loss rate 측정 planned
liveness probe 가 dependency outage 로 인해 실패하지 않음 (cascading restart 방지) K8S-PROBE-C1 Usage Boundary: liveness 가 모든 hang 검출하지 않음. ca-tmpl 의 "JVM process can continue" 정의 와 정합 검증 필요 contract test: DB outage fixture → liveness 200 / readiness 503 planned
Spring Boot graceful shutdown 시 readiness 자동 DOWN 전환 메커니즘 본 branch 인용 자료에 verbatim 부재 (SB-HEALTH Usage Boundary) Spring Boot features/graceful-shutdown.html 별도 fetch needs-confirmation
Datadog 의 preStop 5s + drain 20s + grace 35s 비율이 실제 Datadog 공식 권장 RH-DD-C1~C4 모두 needs-confirmation — verbatim 미확보 Datadog Engineering blog 원본 URL 재 fetch 또는 ca-tmpl 정책으로만 표현 needs-confirmation
Istio probe rewrite 환경에서도 ca-tmpl 의 3-endpoint 분리 가 동작 RH-IST-C2 는 probe rewrite 가 sidecar→app HTTP 만 — group 별 endpoint 가 sidecar 에서 어떻게 보이는지 별도 Istio sandbox 환경 통합 test planned
NTP drift > 5초 readiness fail 의 정량 threshold (5초) 출처 + readiness-gating 이 anti-pattern 인지 UNSUPPORTED_DECISION — 외부 spec 인용 없음 조사 완료 (2026-06-14 wiki-decision-researcher): RFC 5905(STEPT 125ms/PANICT 1000s)·RFC 7519(JWT leeway "a few minutes")·NIST SP 800-53 SC-45(org-defined)·K8s 공식 어디에도 app readiness 의 NTP-drift 임계값 정의 없음 → "5초" 는 무출처 운영 가정 확정. readiness-gating 은 cascade-failure 위험(AWS EKS guidance) — Alt 2 권고: readiness 는 clock-agnostic, clock-skew 는 인프라 계층(Prometheus node_timex_offset_seconds + K8s NodeProblemDetector NTPProblem)에 위임. 채택 시 별도 raw 4건(RFC 5905·RFC 7519·K8s NPD·node-exporter mixin) archive. §Audit NTP_READINESS_ANTIPATTERN resolved (no authoritative standard) — D12 readiness-gating 부분은 사용자 확정 후 Alt 2 로 개정 권고
ca-tmpl 실 코드의 graceful shutdown 정량값 (executor await 19s + server phase 30s) 이 terminationGracePeriod 35s 안에서 inflight drain 완료 AsyncGracefulShutdownBehaviorTest 는 behaviour test (19s-vs-25s 정확한 수치는 증명 안 함) k8s 실측 또는 통합 lifecycle test 로 drain 완료 시간 측정 planned
3-endpoint actuator config (management.endpoint.health.probes.enabled + group include) 가 실제로 /actuator/health/{liveness,readiness,startup} 노출 2026-06-15 actually-implementedapplication.yml management 블록 추가 + spring-boot-starter-actuator 의존성. HTTP-level endpoint 노출 검증은 feature-management-actuator-security-contract 가 exposure/security config 완료 후 통합 테스트 가능 locally-verified (group config shape 레벨)

관심사 커버리지 (coverage-auditor 자동 생성 — 있을 때)

/coverage 가 채우는 생성물 — 손으로 유지하지 않는다. governing 문서(frontmatter governing_docs)가 요구하는 관심사를 이 브랜치가 빠짐없이 덮는지의 결과. 기준: rules/coverage-gate.md. governing_docs: wiki/projects/ca-tmpl/runtime-container-health-migration (§Health + §Graceful Shutdown 슬라이스). 마지막 감사: 2026-06-14 (coverage-auditor) → Covered (Blocking 0 / Should-fix 2 = 위임 링크 보강으로 해소 / Advisory 1). Container 슬라이스(base image·JVM ergonomics·locale)와 Migration 슬라이스(Flyway·exit code)의 4개 관심사는 본 슬라이스 범위 밖 — 각각 feature-container-runtime-contract / feature-migration-startup-contract 소유(dropped, governing doc §Container·§Migration).

관심사 상태 owner 심각도 근거
liveness/readiness/startup 3-probe 분리 covered-here D3, D9
Spring Boot Actuator Health Groups covered-here D9, D10
Required vs Optional Dependency Matrix covered-here D10 + §Dependency Matrix
graceful shutdown ordering covered-here D4 (§구현 가이드 4)
graceful shutdown 정량값 (APP_SERVER_SHUTDOWN* / executor await) delegated raw/branch-notes/feature-env-driven-runtime-configuration, raw/branch-notes/feature-background-job-async-contract, raw/branch-notes/feature-container-runtime-contract (grace=35s manifest) OK §Audit OWNERSHIP_DRIFT + §엣지·실패·의존
startup validation scope covered-here D11
startup validators 코드 + exit code 78/70/71/72 delegated raw/branch-notes/feature-migration-startup-contract OK §구현 가이드 3 + §Audit OWNERSHIP_DRIFT
scheduled job 실패 정책 covered-here §Decisionized Work Items
scheduler 실 mechanism (OutboxRelayScheduler) delegated raw/branch-notes/feature-domain-event-outbox-contract OK §구현 가이드 5
async executor rejection 정책 covered-here §Decisionized Work Items
executor 설정·JOB_EXECUTOR_REJECTED·메트릭 delegated raw/branch-notes/feature-background-job-async-contract OK §구현 가이드 5 + §Audit OWNERSHIP_DRIFT
resource exhaustion 분류 covered-here Advisory §Decisionized Work Items — RESOURCE_* code 부재(planned, §Audit RESOURCE_CODE_ABSENT)
system clock/timezone (UTC) covered-here D12 (Clock.systemUTC())
JVM timezone TZ=UTC (container env) delegated raw/branch-notes/feature-container-runtime-contract OK §구현 가이드 6
NTP drift readiness covered-here 🟡 Should-fix→해소 D12 planned — 2026-06-14 조사: 무출처, Alt 2(clock-agnostic readiness + 인프라 모니터링 위임) 권고. §Audit NTP_READINESS_ANTIPATTERN
actuator endpoint exposure/auth delegated raw/branch-notes/feature-management-actuator-security-contract 🟡 Should-fix (probe 401 — §Audit PROBE_AUTH_BLOCKER) D2 + §구현 가이드 1
multi-instance readiness 일관성 delegated raw/branch-notes/feature-env-driven-runtime-configuration (D8), raw/branch-notes/feature-distributed-lock-contract (D1/D3) OK §구현 가이드 2

구현 진행 기록 (2026-06-15 — CA Implementer)

작업 트리: ca-tmpl-runtime-health-lifecycle worktree (develop 에서 fork된 격리 환경). 구현된 범위: health probe SHAPE (liveness/readiness/startup 3-group split + readiness dependency taxonomy + JVM UTC timezone pinning). management port/exposure/security 는 parallel worktree 소유.

구현 사실 (actually-implemented, locally-verified 2026-06-15)

구현 항목 파일 상태 비고
spring-boot-starter-actuator 의존성 추가 src/app-bootstrap/build.gradle actually-implemented implementation 스코프
-Duser.timezone=UTC test JVM arg src/app-bootstrap/build.gradle (tasks.named('test') 블록) actually-implemented RuntimeHealthLifecycleContractTest 의 JVM TZ 어설션 핀
management.endpoint.health.probes.enabled=true src/app-bootstrap/src/main/resources/application.yml actually-implemented management: 블록 신규 추가
management.endpoint.health.group.liveness.include=livenessState 동상 actually-implemented
management.endpoint.health.group.readiness.include=readinessState,db 동상 actually-implemented primary DB = REQUIRED 분류
management.endpoint.health.group.startup.include=readinessState 동상 actually-implemented startup gate
RuntimeHealthLifecycleContractTest (6개 테스트) src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/runtime/RuntimeHealthLifecycleContractTest.java locally-verified ApplicationContextRunner 기반 — HTTP 없음, SecurityFilterChain 없음

ca-quality-reviewer 수정 (2026-06-15 — test quality + comment accuracy)

행동 변경 없음. 테스트 품질 + 주석 정확성 수정만.

수정 항목 파일 상태 비고
health_probes_enabled_is_boundstartup_group_includes_readiness_state (메서드 리네임 + 어설션 교체) RuntimeHealthLifecycleContractTest.java locally-verified 중복 어설션(liveness/readiness isNotNull 재확인) 제거 → startup 그룹 멤버십(isMember("readinessState")) 어설션으로 교체. startup 그룹을 liveness/readiness 수준의 커버리지 동등성으로 맞춤
jvm_default_timezone_is_utc 주석 정정 — "aligns with production" 제거 RuntimeHealthLifecycleContractTest.java locally-verified 이 테스트는 UTC timezone POLICY 핀 + test-JVM 결정론 보장만. 프로덕션 UTC 강제는 feature-container-runtime-contract (TZ=UTC Dockerfile) 소유임을 명시
tasks.named('test') 블록 주석 정정 — "aligns with production" / "logging timezone default" 과장 제거 src/app-bootstrap/build.gradle locally-verified -Duser.timezone=UTC 는 TEST JVM 전용(결정론적 타임스탬프 산술). 프로덕션 UTC 는 container-runtime-contract 위임
java.util.Set / java.util.TimeZone FQN → import + 단순명 RuntimeHealthLifecycleContractTest.java locally-verified 기존 파일 나머지 코드와 일관성 맞춤

검증 명령 및 결과

  • ./gradlew :app-bootstrap:test --tests '*RuntimeHealthLifecycleContractTest'BUILD SUCCESSFUL (7/7 pass — startup_group_includes_readiness_state 포함)
  • ./gradlew :app-bootstrap:testBUILD SUCCESSFUL (전체 모듈 테스트 — 회귀 없음)

검증 명령 및 결과

  • ./gradlew :app-bootstrap:test --tests '*RuntimeHealthLifecycleContractTest'BUILD SUCCESSFUL (6/6 pass)
  • ./gradlew :app-bootstrap:testBUILD SUCCESSFUL (전체 모듈 테스트 — 회귀 없음)
  • ./gradlew verifyCleanArchitectureDependenciesBUILD SUCCESSFUL
  • ./gradlew verifyEnvKeysBUILD SUCCESSFUL (97 env keys — application.yml 에 새 env placeholder 없음)

사후 revert (2026-06-15 sentinel BLOCKED → 수정)

  • ca-architect-sentinel 판정: not-ready, blocking:1src/.envSECURITY_PUBLIC_PATHS 3개 actuator 경로 추가가 스냅샷 미갱신 + 이 branch scope 밖.
  • 조치: SECURITY_PUBLIC_PATHS=/api/healthcheck 로 revert (HEAD~1 identical).
  • ./gradlew verifyPublicPathSnapshotBUILD SUCCESSFUL ("1 public path(s) unchanged").
  • ./gradlew :app-bootstrap:test --tests '*RuntimeHealthLifecycleContractTest'BUILD SUCCESSFUL (revert 후에도 6/6 pass — test 는 public paths 에 의존하지 않음).
  • src/.env 현재 = HEAD~1 (working tree 미스테이지).

구현 중 마주친 기술적 문제

  1. AvailabilityHealthContributorAutoConfiguration 조건 오인: livenessState/readinessState 기여자는 K8s 환경 감지 조건(@ConditionalOnBooleanProperty("management.health.livenessstate.enabled")) 뒤에 있음. ApplicationContextRunner 에서 이 속성을 명시적으로 true 로 설정해야 하고 ApplicationAvailabilityAutoConfiguration 도 함께 등록해야 함.
  2. HealthEndpointGroupMembershipValidator: 그룹 include 에 명시된 기여자가 컨텍스트에 없으면 startup fail. db 기여자를 DownDbContributorConfig @Bean 으로 등록해 해소.
  3. Package 오인: 자동 완성 없이 org.springframework.boot.autoconfigure.actuate.health (잘못됨) → org.springframework.boot.actuate.autoconfigure.health (올바름) 로 수정.

마주친 문제

  • Spring Boot 3.5.x 에서 AvailabilityHealthContributorAutoConfiguration 의 조건 구조 (Kubernetes 환경 감지 + 속성 explicit enable) 가 ApplicationContextRunner 슬라이스와 상호작용하는 방식을 확인해야 했음. 해결책: management.health.livenessstate.enabled=true / management.health.readinessstate.enabled=true 속성 명시적 추가.

묶음

본 feature branch 는 leaf — Phase C2 실 코드 작성 완료 (health probe SHAPE 슬라이스).

오류 기록 (본 feature 작업 중 발생)

  • Spring actuator autoconfig package 오인: org.springframework.boot.autoconfigure.actuate.health.* 는 존재하지 않음. 올바른 패키지는 org.springframework.boot.actuate.autoconfigure.health.* (actuate 와 autoconfigure 순서 반전). ApplicationContextRunner 사용 시 jar tf 로 확인 필요.
  • AvailabilityHealthContributor 조건 gap: K8s 자동감지 없는 ApplicationContextRunner 에서 livenessState/readinessState 기여자는 비활성. management.health.livenessstate.enabled=true + management.health.readinessstate.enabled=true + ApplicationAvailabilityAutoConfiguration 등록으로 해소.

면접 준비 (이 작업에서 나올 수 있는 면접 질문)

  • Spring Boot Actuator health probe group split (liveness/readiness/startup) — 각각의 의미와 K8s 연동.
  • StatusAggregator.getDefault() — DOWN 하나가 포함되면 전체 DOWN 이 되는 이유.
  • ApplicationContextRunner vs @SpringBootTest 차이 — actuator health 테스트에서 왜 runner 를 선택했는가.
  • HealthEndpointGroupMembershipValidator 가 startup 에 실패하는 조건과 해결 패턴.

관련 일일 노트

이 브랜치를 작업한 날짜들. 양방향 nav 유지.

  • (없음 — Phase E 운영 계약 설계 단계. C2 실 구현 착수 시 daily-note 연결)

완료 후 정리

  • PR 링크:
  • 리뷰 메모:
  • 머지 결과 / 배포 환경:
  • wiki 추출 대상 (verified만, wiki/projects/로만 추출):
    • actually-implemented 항목:
    • locally-verified 항목:
    • prod-verified 항목:
  • 추출하지 않을 항목 (planned / documented-only / abandoned):