
코드가 아닌 컨테이너 밖에서 물이 새고 있었다! JVM 힙 메모리 옵션과 Page Cache의 숨겨진 비밀
쿠버네티스 환경에서 Spring Boot 애플리케이션을 운영하는 엔지니어라면 한 번쯤 마주쳤을 메모리 포화 현상의 진범을 밝힙니다. 코드 레벨의 메모리 누수가 아니라 JDK 11 G1 GC의 힙 반환 한계와 Logback 파일 로깅으로 인한 Linux Page Cache 비대화라는 원인을 분석해 냅니다. 구체적인 cgroup 실측 지표 수집 기법부터 컨테이너 환경의 로그 아키텍처 최적화 전략까지 단계별 해결 가이드를 공유합니다.
컨테이너 환경에서 JVM을 가동할 때는 힙 상한을 최대 60% 수준으로 보수적으로 정의하고, 중앙 집중식 로그 스택이 갖춰졌다면 불필요한 로컬 파일 로그 생성을 철저히 비활성화해야 메모리 병목을 피할 수 있습니다.
Kubernetes 환경에서 운영 중인 프로모션 계산 서비스의 메모리 사용률(usage/limits)이 지속적으로 99%에 달했으나, OOM 장애나 Pod 재시작 현상이 발생하지 않아 원인 파악 및 최적화에 난항을 겪고 있었습니다.
JVM 힙 메모리 예약 정책(Initial/MaxRAMPercentage)을 하향 조정하고, Fluent-bit 기반 통합 로그 아키텍처 환경에서 중복으로 존재하던 Logback RollingFileAppender 설정을 제거하여 로컬 파일 출력을 전면 중단했습니다.
Logback을 통해 생성되던 약 1GiB 크기의 리눅스 Page Cache가 11MB 수준으로 소거되었고, 최종적으로 전체 메모리 사용률이 99%에서 60%대의 안정적인 평탄 상태로 완벽하게 안정화되었습니다.
Trade-off
Pod 로컬 내부 경로에 생성되던 파일 로그가 사라짐에 따라 컨테이너 내부 셸 접속을 통한 직접적인 로그 확인은 불가해졌으나, 중앙화된 외부 로그 분석 플랫폼으로 상시 조회가 가능해 실무적인 제약 사항은 최소화되었습니다.
리눅스 커널 수준에서 프로세스 그룹 전체가 점유하는 메모리 총량으로, 실제 애플리케이션의 메모리 외에 캐시 영역과 가상 메모리 매핑 데이터까지 모두 합산되어 모니터링 시스템에 수집되는 기본 규격입니다.
운영체제가 파일에 대한 반복적 읽기/쓰기 성능을 비약적으로 끌어올리기 위해 남는 물리적 메모리에 저장하는 디스크 복사본이자 파일 버퍼 공간입니다.
JVM이 필요에 따라 OS 측으로부터 할당받아 즉각적인 자바 객체 생성이 가능하도록 예약해 확보해 둔 가용 메모리 영역입니다.




