Kubernetes에서 JVM 애플리케이션은 시작이 느리다. Pod가 뜨고 컨테이너가 Running 상태가 되어도, JVM이 준비되기까지는 시간이 더 걸린다. 이 간극을 무시하면 초기 요청이 실패한다.
Karpenter 노드 정리로 Pod가 재스케줄링될 때마다 초기 API 요청이 타임아웃으로 실패하는 문제가 있었다. ReadinessProbe만으로는 부족했다. StartupProbe와 전용 엔드포인트를 도입해서 해결한 과정을 공유한다.
새벽에 Karpenter가 비용 최적화를 위해 유휴 노드를 정리하면서 Pod가 다른 노드로 이동했다. Pod 재시작 자체는 정상이었지만, 재시작 직후 들어온 요청들이 실패했다.
Kubernetes에서 애플리케이션을 운영할 때, 결국 핵심은 Pod의 시작과 끝이다. 얼마나 따뜻하게 시작하고, 얼마나 우아하게 끝내느냐. Probe와 Warmup으로 시작을 제어하고, Graceful Shutdown으로 끝을 제어한다. 이 두 가지가 안정적인 서비스 운영의 기반이다.
Probe 설정은 이전 글에서 다뤘다. 이번에는 끝에 대한 이야기다. 배포할 때마다 502 에러가 발생하는 서비스가 있었다. 원인은 Pod 종료 전략의 부재였다. terminationGracePeriodSeconds와 preStop hook을 적용해서 해결한 과정을 공유한다.
Spring Boot API 서버를 배포할 때마다 간헐적으로 502 에러가 발생했다.
Connection Pool은 연결을 재사용해서 성능을 올려주지만, 풀 안의 연결이 죽었는지 살았는지는 별개 문제다.
EKS 업그레이드 중 Redis pod가 재시작되면서 Pool 안의 연결이 전부 끊겼는데, Pool이 유효성 검증 없이 끊긴 연결을 그대로 반환했다. Lettuce에 auto-reconnect이 있는데도 복구가 안 된 이유와, testOnBorrow/testWhileIdle 설정의 차이를 정리한다.
| 항목 |
내용 |
| 증상 |
RedisSystemException, pod restart 전까지 복구 불가 |
| 원인 |
Connection Pool의 testOnBorrow 미설정 |
| 스택 |
Spring Boot 3.5.0 + Lettuce 6.5.5 + Commons Pool2 |
| 해결 |
testOnBorrow=true 또는 testWhileIdle=true 추가 |
| 재발 조건 |
Redis pod와 앱 pod가 동시에 재생성되는 경우 |
order-api는 Redis 연결을 Lettuce + Commons Pool2 기반 Connection Pool로 관리하고 있었다.