서버 운영
서버 메모리가 줄어들 때 Node.js 프로세스에서 먼저 보이는 것들
OOM이 나기 전에 Node.js 앱이 먼저 이상한 신호를 보낸다. 어떤 순서로 증상이 나타나는지 정리했다.
서버 메모리가 부족해질 때 갑자기 프로세스가 죽는 게 아니라, 죽기 전에 몇 가지 신호가 먼저 나온다. 이 신호를 알아두면 OOM killer가 프로세스를 강제 종료하기 전에 대응할 수 있다.
초기 증상: 응답이 느려진다
Node.js는 싱글 스레드에서 이벤트 루프로 동작한다. 메모리 사용량이 늘면 GC(가비지 컬렉션)가 더 자주, 더 오래 실행된다. GC가 실행되는 동안 이벤트 루프가 멈추기 때문에 API 응답 시간이 불규칙하게 늘어나기 시작한다.
평소에 50ms 이내로 응답하던 엔드포인트가 가끔 500ms~1초씩 걸린다면 GC 압박을 의심할 수 있다.
현재 메모리 상태 확인
# 전체 메모리 현황
free -h
# 프로세스별 메모리 사용량
ps aux --sort=-%mem | head -20
# Node.js 프로세스 상세
pidstat -r -p <PID> 1
# 시스템 메모리 실시간 모니터링
vmstat 2
Node.js 앱 내부에서 확인하려면 process.memoryUsage()를 쓴다.
const mem = process.memoryUsage()
console.log({
heapUsed: Math.round(mem.heapUsed / 1024 / 1024) + 'MB',
heapTotal: Math.round(mem.heapTotal / 1024 / 1024) + 'MB',
rss: Math.round(mem.rss / 1024 / 1024) + 'MB',
})
heapUsed가 heapTotal에 가까워지면 GC가 빈번하게 발생하고 있다는 신호다.
OOM killer가 뜨면 로그에 이렇게 남는다
sudo journalctl -k | grep -i 'oom\|killed'
# 또는
dmesg | grep -i 'oom\|killed'
출력 예시:
Out of memory: Killed process 1234 (node) total-vm:2048000kB, anon-rss:1500000kB
이 메시지가 보이면 Node.js 프로세스가 OOM killer에 의해 강제 종료된 것이다.
메모리 부족 시 대응
단기 대응: swap이 없거나 부족하다면 임시로 늘린다.
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
Node.js 힙 한도 조정: 기본값은 약 1.5GB(64비트). 서버 메모리가 충분하다면 늘릴 수 있다.
node --max-old-space-size=4096 server.js
근본 원인 파악: 메모리가 계속 올라간다면 메모리 누수다. 이벤트 리스너가 해제되지 않거나, 전역 변수에 데이터가 계속 쌓이거나, 클로저가 참조를 유지하는 경우가 흔하다. --inspect 플래그로 Chrome DevTools를 연결해 힙 스냅샷을 찍어보면 어떤 객체가 메모리를 차지하는지 확인할 수 있다.