167 lines
11 KiB
Markdown
167 lines
11 KiB
Markdown
---
|
|
title: Runtime 설정 오류를 Startup에서 실패시키기
|
|
source_type: blog
|
|
status: verified
|
|
confidence: high
|
|
tags: [blog, ca-tmpl, runtime, container, health, migration]
|
|
related_projects: [ca-tmpl]
|
|
last_reviewed: 2026-07-03
|
|
canonical_sources:
|
|
- wiki/projects/ca-tmpl/runtime-container-health-migration
|
|
audience: backend-engineer
|
|
target_publish:
|
|
status_label: ready
|
|
---
|
|
|
|
# Runtime 설정 오류를 Startup에서 실패시키기
|
|
|
|
## Parent / 부모 (필수)
|
|
|
|
- 핵심 canonical: [[wiki/projects/ca-tmpl/runtime-container-health-migration]]
|
|
- 관련 개념 문서: [[wiki/concepts/runtime-container-health-migration]] - 일반 runtime/container/health/migration 개념. 현재 이 글의 구현 사실 근거는 verified project canonical에 둔다.
|
|
|
|
## 타깃 독자 / Target reader
|
|
|
|
- 독자 profile: container readiness, health check, migration, runtime config guard를 skeleton에 넣고 싶은 엔지니어.
|
|
- 이미 안다고 가정하는 것: Docker/Kubernetes probe, Flyway, env config, Spring Boot Actuator.
|
|
- 처음 듣는다고 가정하는 것: runtime safety를 startup validator, health group, migration strategy, exit code로 묶는 방식.
|
|
|
|
## 도입 / Hook
|
|
|
|
운영 설정 오류는 배포 뒤에 늦게 발견될수록 비쌉니다. pool size가 음수로 들어가거나, `open-in-view`가 켜지거나, prod profile에서 Flyway safety option이 풀린 상태로 애플리케이션이 올라오면, 문제는 요청을 받기 시작한 뒤에 드러날 수 있습니다.
|
|
|
|
ca-tmpl은 이런 오류를 startup 단계에서 실패시키는 방향으로 runtime contract를 잡았습니다. env-driven configuration을 쓰되 잘못된 값은 lenient default로 숨기지 않고, health group은 liveness/readiness/startup 역할을 나누며, Flyway migration 실패는 startup failure와 exit code로 드러냅니다. 이 글은 ca-tmpl에 실제로 구현된 runtime/container/health/migration baseline과 아직 운영 검증으로 말하면 안 되는 범위를 정리합니다.
|
|
|
|
## 본문 outline / Body outline
|
|
|
|
1. startup fail-fast의 가치.
|
|
2. runtime numeric bounds와 OSIV/Hikari guard.
|
|
3. health probe group split과 readiness gate.
|
|
4. Flyway migration startup contract와 exit code.
|
|
5. container image/runtime baseline.
|
|
6. Kubernetes cluster rollout 검증은 없음.
|
|
|
|
## 본문 / Body
|
|
|
|
runtime 설정은 코드보다 덜 중요해 보이지만, 실제로는 애플리케이션의 동작 경계를 바꿉니다. DB pool max size, Tomcat thread count, shutdown timeout, Flyway option, Actuator exposure는 모두 장애 양상을 바꿀 수 있습니다. 그래서 ca-tmpl은 “값이 이상하면 프레임워크 기본값으로 알아서 흘러가게 둔다”보다 “startup에서 실패한다”는 쪽을 택했습니다.
|
|
|
|
`RuntimeNumericBoundsValidator`는 대표적인 예입니다. `spring.datasource.hikari.maximum-pool-size`, `server.tomcat.threads.max`, `server.tomcat.max-connections` 같은 값은 1 이상이어야 하고, minimum idle이나 accept count처럼 0을 허용하는 값은 0 이상이어야 합니다. key가 없으면 framework default에 맡기지만, key가 있는데 범위를 벗어나면 `IllegalStateException`으로 startup을 막습니다. env-driven 설정을 쓰면서도 잘못된 env 값을 조용히 묻지 않는 장치입니다.
|
|
|
|
OSIV와 Hikari 설정도 별도 guard로 다룹니다. `OpenInViewSafetyValidator`는 `spring.jpa.open-in-view=true`를 거부합니다. Hikari validator는 `connection-timeout`, `validation-timeout`, `keepalive-time`, `max-lifetime`, `leak-detection-threshold`의 상호 관계를 검사합니다. 예를 들어 validation timeout이 connection timeout보다 길면 pool 동작을 예측하기 어려워집니다. ca-tmpl은 이런 값을 요청 처리 뒤의 증상으로 발견하기보다 startup에서 configuration error로 드러내려 합니다.
|
|
|
|
health check는 endpoint 하나로 뭉개지 않습니다. `application.yml`에는 Actuator health group이 `liveness`, `readiness`, `startup`으로 나뉘어 있습니다. liveness는 JVM이 계속 살아갈 수 있는지를 보며 dependency health를 포함하지 않습니다. DB가 잠깐 내려갔다고 pod를 재시작하는 것은 보통 원하는 동작이 아니기 때문입니다. readiness는 traffic을 받아도 되는지를 판단하므로 `readinessState,db`를 포함합니다. startup은 context initialization과 migration 완료 후 준비 상태를 드러내는 gate로 둡니다.
|
|
|
|
Flyway도 startup contract의 일부입니다. `MigrationStartupRunner`는 context refresh 중 Flyway migration을 수행하고, 실패하면 `MigrationFailedException` 계열로 바꿔 exit code 70에 연결합니다. `StartupErrorCode`에는 startup validation, migration failure, profile mismatch, required adapter disabled가 각각 다른 exit code와 phase로 정의되어 있습니다. 실패 원인을 process exit status와 structured startup log에서 분리해 보려는 설계입니다.
|
|
|
|
prod profile의 Flyway safety guard도 들어 있습니다. `FlywayProdSafetyValidator`는 prod에서 `baseline-on-migrate=true`, `out-of-order=true`, `clean-disabled=false` 같은 위험한 override를 막습니다. 여기서 중요한 것은 “Flyway를 쓰면 안전하다”가 아닙니다. migration 도구를 쓰더라도 prod에서 안전망을 푸는 설정이 들어오면 애플리케이션이 올라오지 않게 만드는 것입니다.
|
|
|
|
container/runtime baseline도 project canonical에 포함되어 있습니다. `src/Dockerfile`, graceful shutdown 설정, Actuator health group, startup validator, migration strategy가 묶여 있습니다. 그러나 실제 Kubernetes manifest, rolling update, probe tuning, cluster에서의 rollout incident 검증은 없습니다. 따라서 이 글은 “Kubernetes 운영에서 검증된 lifecycle 설계”가 아니라 “ca-tmpl이 startup fail-fast와 health/migration baseline을 코드와 설정으로 고정하고 로컬 검증했다”까지 말합니다.
|
|
|
|
## 코드 예제 / Code samples (있다면)
|
|
|
|
```java
|
|
// 출처: [[wiki/projects/ca-tmpl/runtime-container-health-migration]]
|
|
// 실제 파일: app-bootstrap/.../RuntimeNumericBoundsValidator.java, ca-tmpl @f6fbd4e196b4
|
|
static final List<Bound> BOUNDS =
|
|
List.of(
|
|
new Bound("spring.datasource.hikari.maximum-pool-size", "APP_DATASOURCE_POOL_MAX_SIZE", 1),
|
|
new Bound("server.tomcat.threads.max", "APP_SERVER_TOMCAT_MAX_THREADS", 1),
|
|
new Bound("server.tomcat.max-connections", "APP_SERVER_TOMCAT_MAX_CONNECTIONS", 1),
|
|
new Bound("spring.datasource.hikari.minimum-idle", "APP_DATASOURCE_POOL_MIN_IDLE", 0),
|
|
new Bound("server.tomcat.accept-count", "APP_SERVER_TOMCAT_ACCEPT_COUNT", 0));
|
|
```
|
|
|
|
```java
|
|
// 출처: [[wiki/projects/ca-tmpl/runtime-container-health-migration]]
|
|
// 실제 파일: app-bootstrap/.../StartupErrorCode.java, ca-tmpl @f6fbd4e196b4
|
|
public enum StartupErrorCode {
|
|
STARTUP_VALIDATION_FAILED(78, StartupPhase.ENV_VALIDATION),
|
|
MIGRATION_FAILED(70, StartupPhase.MIGRATION),
|
|
PROFILE_MISMATCH(71, StartupPhase.PROFILE_CHECK),
|
|
REQUIRED_ADAPTER_DISABLED(72, StartupPhase.ADAPTER_ENABLEMENT);
|
|
}
|
|
```
|
|
|
|
```java
|
|
// 출처: [[wiki/projects/ca-tmpl/runtime-container-health-migration]]
|
|
// 실제 파일: app-bootstrap/.../FlywayProdSafetyValidator.java, ca-tmpl @f6fbd4e196b4
|
|
if (isTrue(BASELINE_ON_MIGRATE_KEY)) {
|
|
violations.add(BASELINE_ON_MIGRATE_KEY + "=true (removes the missing-migration safety net)");
|
|
}
|
|
if (isTrue(OUT_OF_ORDER_KEY)) {
|
|
violations.add(OUT_OF_ORDER_KEY + "=true (breaks migration ordering consistency)");
|
|
}
|
|
if (isFalse(CLEAN_DISABLED_KEY)) {
|
|
violations.add(CLEAN_DISABLED_KEY + "=false (re-arms destructive Flyway clean)");
|
|
}
|
|
```
|
|
|
|
```java
|
|
// 출처: [[wiki/projects/ca-tmpl/runtime-container-health-migration]]
|
|
// 실제 파일: app-bootstrap/.../MigrationStartupRunner.java, ca-tmpl @f6fbd4e196b4
|
|
try {
|
|
MigrateResult result = flyway.migrate();
|
|
int executed = (result != null) ? result.migrationsExecuted : 0;
|
|
log.info("startup phase {}: migration complete, {} migration(s) applied",
|
|
kv("startup.phase", StartupPhase.MIGRATION.wireName()), executed);
|
|
} catch (FlywayException e) {
|
|
throw StartupFailures.migrationFailed("Flyway forward-only migration failed during startup", e);
|
|
}
|
|
```
|
|
|
|
```yaml
|
|
# 출처: [[wiki/projects/ca-tmpl/runtime-container-health-migration]]
|
|
# 실제 파일: app-bootstrap/src/main/resources/application.yml, ca-tmpl @f6fbd4e196b4
|
|
management:
|
|
endpoint:
|
|
health:
|
|
probes:
|
|
enabled: true
|
|
group:
|
|
liveness:
|
|
include: livenessState
|
|
readiness:
|
|
include: readinessState,db
|
|
startup:
|
|
include: readinessState
|
|
```
|
|
|
|
## Sources / 근거 (canonical 인용 필수, derived layer 의무)
|
|
|
|
- [[wiki/projects/ca-tmpl/runtime-container-health-migration]] - 이 글의 1차 canonical. startup validators, Actuator health group, Flyway startup migration/safety guard, exit code mapping, local verification, 운영 미검증 경계를 따른다.
|
|
- [[wiki/concepts/runtime-container-health-migration]] - 관련 개념 문서. container lifecycle, health probe, migration strategy 일반 배경으로만 둔다.
|
|
|
|
## 사실 vs 의견 / Fact vs opinion 구분
|
|
|
|
- 사실: ca-tmpl에는 runtime numeric bounds validator, OSIV/Hikari safety validator, Actuator health group 설정, Flyway startup migration strategy, prod safety validator, startup exit code mapping이 존재한다. 근거: [[wiki/projects/ca-tmpl/runtime-container-health-migration]]
|
|
- 사실: startup validation과 migration failure는 local/dev verification 범위로 기록되어 있다. 근거: [[wiki/projects/ca-tmpl/runtime-container-health-migration]]
|
|
- 사실: Kubernetes manifest, rolling update, production probe tuning, cluster-level incident 검증은 없다. 근거: [[wiki/projects/ca-tmpl/runtime-container-health-migration]]
|
|
- 의견: skeleton에서는 잘못된 runtime env를 lenient default로 흘리는 것보다 startup에서 실패시키는 쪽이 학습과 운영 설명에 유리하다.
|
|
- 알지 못하는 것: 실제 orchestrator rollout behavior, migration lock contention, production shutdown latency.
|
|
|
|
## 답할 수 있는 범위 / Answer boundary
|
|
|
|
- 자신 있게 답할 수 있는 후속 질문:
|
|
- ca-tmpl은 어떤 runtime config 오류를 startup에서 막는가?
|
|
- liveness와 readiness health group을 왜 나누는가?
|
|
- Flyway migration 실패가 어떻게 startup failure와 exit code로 연결되는가?
|
|
- 다음 글로 넘길 부분:
|
|
- Kubernetes production probe tuning.
|
|
- rolling update와 graceful shutdown 실측.
|
|
- DB migration 운영 runbook과 장애 복구 사례.
|
|
|
|
## 게시 체크리스트 / Publish checklist
|
|
|
|
- [x] 모든 사실 주장에 canonical 링크 있음
|
|
- [x] 사실 vs 의견 분리 명시됨
|
|
- [x] 금지 마케팅 표현 없음
|
|
- [x] 코드 예제 출처 명시
|
|
- [x] 타깃 독자 가정과 톤 일치
|
|
- [x] `/lint` 통과
|
|
- [ ] 게시 URL 기록 (게시 후):
|
|
|
|
## Related / 관련
|
|
|
|
- 후속 글 후보: [[wiki/blog/ca-tmpl-config-and-adapter-templates-2026-07-02]]
|
|
- 후속 글 후보: [[wiki/blog/ca-tmpl-devops-ci-supply-chain-dx-2026-07-02]]
|