지난 2월 9일 오후 1시 41분. 호스트 시스템에서 Python 워커 프로세스 하나가 갑자기 죽었다. 원인은 OOM killer — 메모리 부족으로 시스템이 스스로 프로세스를 강제 종료한 것이다.
문제의 신호들
커널 로그를 펼쳐보니 상황은 심각했다.
메모리 상태:
- 활성 익명 메모리: 6.7GB 점유
- Swap 메모리: 완전 소진 (0kB)
- 시스템 여유: 약 31MB (극도로 위험)
Python 워커들의 점유:
단일 프로세스가 1.6GB씩 점유하는 상황이었다. 그리고 더 충격적인 건, 이건 새로운 코드 배포 이후부터 시작된 문제라는 것.
근본 원인: 코드 메모리 누수
개발자가 새로운 소스 코드를 적용한 지 얼마 지나지 않아 발생한 현상이다. Python 워커 프로세스들이 지속적으로 메모리를 할당하지만 해제하지 않는 전형적인 메모리 누수 패턴.
이미지 처리나 데이터 변환 같은 무거운 작업을 반복할 때 자주 발생하는 문제다.
응급 대응: 재부팅
이 시점에서 선택지는 두 가지였다.
- 빠른 복구: 시스템 재부팅 → 즉시 정상화
- 근본 분석: 메모리 덤프 수집 후 분석 → 시간 소요, 서비스 중단
인프라 엔지니어의 원칙은 명확하다. 빠른 복구가 최우선. 원인 분석은 나중이다. 그래서 재부팅했다.
교훈: 3가지 수준의 방어선
이 상황에서 배운 점들:
1. 메모리 모니터링의 중요성
초기 징후를 놓쳤다면 OOM killer까지 가지 않았을 것이다. 필수 조치:
- 크론잡 기반 정기 모니터링 (5분마다 체크)
- 임계값 도달 시 자동 알림 설정
- 메모리 사용 추세 추적
2. 프로세스 제한 설정
메모리 제한이 있었다면, 단일 프로세스가 1.6GB를 점유하지 못했을 것이다. 여러 방법이 있다.
방법 1: systemd service 파일 (권장)
| |
방법 2: /etc/security/limits.conf
| |
방법 3: cgroup 직접 설정
| |
방법 4: Docker 컨테이너라면
| |
오라클 DB에서 많이 본 건 아마 **방법 2 (limits.conf)**일 것이다. 가장 범용적이고 오래된 방식이기 때문이다.
3. 개발자와의 협력
- 새 코드 배포 후 모니터링 기간 필수
- 성능 테스트 환경에서 장시간 부하 테스트 필요
- 메모리 프로파일링 도구 활용 (memory_profiler, tracemalloc 등)
현재 상태
- 시스템: 재부팅으로 정상화됨
- 개발자: 메모리 누수 원인 분석 중
- 인프라: 메모리 모니터링 크론잡 추가 예정
결론
OOM killer를 만났다는 건 시스템이 마지막 자기방어 메커니즘을 발동했다는 뜻이다. 그 전에 감지하고 멈추는 게 진정한 모니터링이다.
다음부터는 메모리 한계 전에 손을 쓰자. 🛡️