본문 바로가기
서버연구 블로그 서버연구 블로그

OOM Killer는 왜 프로세스를 강제 종료할까? Linux 메모리 부족 분석

읽는 시간 약 17분

Linux 서버를 운영하다 보면 분명히 서비스가 정상적으로 동작하고 있었는데 갑자기 Java, MySQL, Redis, Nginx 같은 프로세스가 사라지는 경우가 있습니다.

애플리케이션 로그를 확인해도 정상적인 종료 기록이 없고, 프로세스가 종료되기 직전의 에러 메시지도 찾기 어렵습니다. 이런 상황에서 반드시 확인해야 하는 것이 Linux의 **OOM Killer(Out Of Memory Killer)**입니다.

OOM Killer는 단순히 메모리를 많이 사용하는 프로세스를 종료하는 기능이 아닙니다. Linux Kernel이 더 이상 정상적인 메모리 할당을 지속하기 어렵다고 판단했을 때 시스템 전체가 멈추는 것을 막기 위해 특정 프로세스를 희생시키는 마지막 방어 장치입니다.

따라서 OOM 문제를 제대로 분석하려면 단순히 “RAM이 부족했다”에서 끝내면 안 됩니다. 실제 메모리 사용량과 available memory, swap, page reclaim, 메모리 할당 실패, 프로세스별 메모리 사용 구조까지 함께 살펴봐야 합니다.

OOM Killer는 왜 실행되는가?

프로세스는 실행되는 동안 계속해서 메모리를 요청합니다.

웹 애플리케이션이라면 요청을 처리하기 위한 버퍼와 객체가 만들어지고, 데이터베이스는 캐시와 쿼리 처리 공간을 사용하며, Redis와 같은 인메모리 시스템은 데이터 자체를 메모리에 저장합니다.

문제는 서버가 사용할 수 있는 메모리가 무한하지 않다는 것입니다.

예를 들어 물리 메모리가 16GB인 서버에서 여러 프로세스가 동시에 메모리를 소비한다고 가정해보겠습니다.

Linux는 메모리가 부족해지기 시작했다고 해서 즉시 프로세스를 종료하지 않습니다. 먼저 사용 가능한 페이지를 확보하기 위한 작업을 수행합니다.

파일 캐시를 회수하거나, 사용하지 않는 메모리 페이지를 정리하고, swap이 설정되어 있다면 일부 페이지를 swap 영역으로 이동시키는 등의 작업이 먼저 진행됩니다.

하지만 이런 작업을 수행하고도 새로운 메모리 요청을 만족시킬 수 없는 상황이 발생할 수 있습니다.

이때 Kernel 입장에서는 선택지가 많지 않습니다.

메모리 할당을 계속 실패시키면 핵심 시스템 프로세스까지 정상적으로 동작하지 못하면서 서버 전체가 사실상 멈출 수 있습니다.

Linux는 이러한 상황을 피하기 위해 일부 프로세스를 종료하여 메모리를 확보합니다.

이것이 OOM Killer가 존재하는 이유입니다.

즉,

메모리 사용 증가
        ↓
사용 가능한 메모리 감소
        ↓
Page Reclaim 시도
        ↓
Swap 사용 또는 캐시 회수
        ↓
메모리 확보 실패
        ↓
OOM 상황 발생
        ↓
희생할 프로세스 선택
        ↓
프로세스 강제 종료
        ↓
메모리 확보

라는 흐름으로 이해할 수 있습니다.

OOM Killer의 목적은 특정 애플리케이션을 보호하는 것이 아니라 Linux 시스템 자체가 완전히 멈추는 것을 막는 것입니다.

Linux는 어떤 프로세스를 종료할까?

OOM이 발생했다고 해서 Linux가 무조건 가장 많은 메모리를 사용하는 프로세스를 종료하는 것은 아닙니다.

Kernel은 각 프로세스에 대해 OOM 상황에서 얼마나 종료 대상으로 적합한지를 판단합니다.

이를 확인할 때 중요한 값 중 하나가 다음입니다.

cat /proc/<PID>/oom_score

예를 들어 특정 Java 프로세스의 PID가 3251이라면 다음과 같이 확인할 수 있습니다.

cat /proc/3251/oom_score

일반적으로 이 값이 높을수록 OOM Killer의 종료 후보가 될 가능성이 높습니다.

관리자는 oom_score_adj를 통해 특정 프로세스의 OOM 대상 우선순위에 영향을 줄 수도 있습니다.

cat /proc/3251/oom_score_adj

oom_score_adj의 범위는 -1000부터 1000까지입니다.

값이 높아질수록 종료 대상이 될 가능성이 커지고, -1000은 해당 프로세스를 OOM Killer 대상으로부터 사실상 제외하는 특별한 값입니다.

하지만 중요한 시스템이라고 해서 무조건 -1000을 설정하는 것은 좋은 해결책이 아닙니다.

모든 중요한 프로세스를 보호해버리면 정작 OOM이 발생했을 때 Kernel이 종료할 적절한 프로세스를 찾지 못하면서 시스템 전체의 안정성이 더 나빠질 수 있기 때문입니다.

따라서 oom_score_adj는 OOM 문제 자체를 해결하는 방법이라기보다 OOM 발생 시 어떤 프로세스를 우선적으로 보호할 것인지 결정하는 운영 정책에 가깝습니다.

실제 서버에서는 어떤 증상이 나타날까?

OOM 문제의 가장 대표적인 특징은 애플리케이션 로그만 보면 원인을 찾기 어렵다는 것입니다.

예를 들어 Java 애플리케이션이 갑자기 종료되었다고 가정해보겠습니다.

애플리케이션 로그에는 마지막 요청 처리 기록까지만 남아 있고 그 이후에는 아무런 기록도 없을 수 있습니다.

정상적인 종료라면 shutdown hook이나 종료 로그가 남을 수 있지만 OOM Killer에 의해 강제로 종료된 경우 애플리케이션이 스스로 종료 절차를 수행할 기회를 얻지 못합니다.

운영자는 다음과 같은 현상을 경험할 수 있습니다.

서비스 응답 지연
→ 일부 요청 Timeout
→ 특정 프로세스 갑자기 종료
→ systemd 등에 의해 프로세스 재시작
→ 잠시 후 서비스 정상화

이 경우 단순히 애플리케이션 장애로 판단하면 원인을 놓칠 수 있습니다.

특히 자동 재시작이 설정되어 있다면 문제가 더 감춰집니다.

프로세스가 OOM으로 종료된 후 몇 초 뒤 다시 실행되면서 서비스가 정상화되기 때문입니다.

운영자는 “잠깐 장애가 발생했다가 복구됐다”고 생각할 수 있지만 실제로는 서버가 심각한 메모리 압박 상태에 도달했던 것입니다.

OOM 발생 여부는 어디에서 확인해야 할까?

OOM을 의심한다면 애플리케이션 로그보다 먼저 Kernel 로그를 확인해야 합니다.

대표적으로 다음 명령을 사용할 수 있습니다.

dmesg -T | grep -i -E "oom|killed process|out of memory"

이 명령을 사용하는 이유는 OOM Killer가 애플리케이션 기능이 아니라 Kernel의 메모리 관리 기능이기 때문입니다.

OOM이 실제로 발생했다면 환경과 Kernel 버전에 따라 형태는 다르지만 다음과 비슷한 메시지를 볼 수 있습니다.

Out of memory: Killed process 3251 (java)

systemd 기반 서버라면 journal에서도 확인할 수 있습니다.

journalctl -k | grep -i -E "oom|killed process|out of memory"

여기서 중요한 것은 단순히 Killed process 한 줄만 보는 것이 아닙니다.

OOM 직전 로그에는 당시 시스템 메모리 상태와 프로세스 정보 등이 함께 기록될 수 있습니다.

따라서 실제 장애 분석에서는 종료된 프로세스뿐 아니라 왜 Kernel이 메모리를 확보하지 못했는지까지 확인해야 합니다.

free 명령에서 free 메모리만 보면 안 되는 이유

메모리 문제를 분석할 때 가장 흔히 사용하는 명령이 다음입니다.

free -h

하지만 여기서 free 값만 보고 서버 메모리가 부족하다고 판단하면 안 됩니다.

Linux는 사용하지 않는 RAM을 파일 캐시 등에 적극적으로 활용합니다.

따라서 free memory가 적은 것은 정상적인 상황에서도 발생할 수 있습니다.

운영자가 더 중요하게 봐야 하는 값은 available입니다.

예를 들어 다음과 같은 상황이 있다고 가정해보겠습니다.

              total    used    free    shared    buff/cache    available
Mem:           15Gi     11Gi    300Mi    200Mi        3.7Gi        3.1Gi

free가 300MB밖에 없다고 해서 곧바로 OOM 상태인 것은 아닙니다.

Kernel이 필요할 때 회수할 수 있는 캐시 등을 포함하면 실제 애플리케이션이 추가로 사용할 수 있는 메모리가 약 3.1GB 존재할 수 있기 때문입니다.

따라서 메모리 장애를 분석할 때는

free

보다

available

의 변화를 함께 관찰하는 것이 훨씬 중요합니다.

Swap이 있으면 OOM이 발생하지 않을까?

그렇지 않습니다.

Swap은 OOM을 방지하는 절대적인 해결책이 아닙니다.

Swap 공간이 존재하면 Kernel은 당장 사용하지 않는 메모리 페이지 일부를 디스크 영역으로 이동시켜 물리 메모리를 확보할 수 있습니다.

하지만 swap 역시 크기가 제한되어 있습니다.

또한 디스크는 RAM보다 훨씬 느리기 때문에 swap 사용량이 과도하게 증가하면 OOM이 발생하기 전에 심각한 성능 저하가 먼저 나타날 수 있습니다.

예를 들어 다음 명령으로 메모리와 swap 변화를 확인할 수 있습니다.

vmstat 1

여기서 특히 확인할 값은 si와 so입니다.

si = swap in
so = swap out

이 값이 지속적으로 발생하면서 CPU의 wa까지 높아진다면 서버가 메모리 압박으로 인해 디스크 I/O를 과도하게 사용하고 있을 가능성을 의심해야 합니다.

즉,

메모리 부족
→ Swap 사용 증가
→ 디스크 I/O 증가
→ 애플리케이션 응답 지연
→ Timeout 증가
→ 메모리 압박 지속
→ OOM 발생

처럼 장애가 진행될 수 있습니다.

따라서 “swap이 있으니 안전하다”는 접근은 위험합니다.

어떤 프로세스가 메모리를 많이 사용하는지 찾아야 한다

OOM이 발생했다는 사실만 확인해서는 장애 분석이 끝나지 않습니다.

다음 단계는 메모리를 소비한 주체를 찾는 것입니다.

간단하게는 다음 명령을 사용할 수 있습니다.

ps aux --sort=-%mem | head

이 명령은 메모리 사용 비율이 높은 프로세스를 빠르게 확인할 때 유용합니다.

또는 top이나 htop을 이용해 실시간으로 확인할 수도 있습니다.

하지만 여기서도 단순히 %MEM만 보는 것은 충분하지 않습니다.

프로세스 메모리를 분석할 때 자주 등장하는 값이 VIRT와 RES입니다.

VIRT는 프로세스가 확보한 가상 주소 공간을 의미하며 실제 RAM 사용량과 동일하지 않습니다.

반면 RES는 현재 실제 물리 메모리에 올라와 있는 영역을 이해하는 데 더 유용합니다.

따라서 VIRT 값이 매우 크다고 해서 곧바로 메모리 누수라고 판단해서는 안 됩니다.

Java, 데이터베이스, mmap을 적극적으로 사용하는 프로그램 등은 가상 주소 공간을 크게 확보할 수 있습니다.

OOM 직전 상태를 잡는 것이 중요한 이유

OOM 분석에서 가장 어려운 부분은 사고가 발생한 뒤 명령어를 실행하면 이미 상황이 달라져 있다는 점입니다.

예를 들어 10GB를 사용하던 프로세스가 OOM Killer에 의해 종료됐다고 생각해보겠습니다.

프로세스가 종료되면 해당 프로세스가 사용하던 메모리가 반환됩니다.

그 후 운영자가 서버에 접속해 free -h를 실행하면 오히려 메모리가 충분해 보일 수 있습니다.

OOM 발생 직전

Available Memory
100MB

        ↓

10GB 프로세스 종료

        ↓

운영자 접속

        ↓

Available Memory
10GB 이상

현재 상태만 보면 아무 문제가 없어 보입니다.

그래서 메모리 장애는 실시간 모니터링 데이터가 매우 중요합니다.

최소한 다음 지표의 시간 흐름을 확보하는 것이 좋습니다.

Memory Available
Swap Used
Swap In / Out
Process RSS
Page Fault
Load Average
Disk I/O

Prometheus, Grafana 등의 모니터링 환경을 운영한다면 장애 발생 시점을 기준으로 몇 분 전부터 메모리 지표가 어떻게 변했는지를 확인해야 합니다.

OOM은 대부분 “발생한 순간”보다 발생하기 전 메모리가 어떻게 소진됐는지가 더 중요한 분석 자료가 됩니다.

해결 방법은 메모리를 추가하는 것만이 아니다

OOM이 발생하면 가장 단순한 대응은 RAM을 늘리는 것입니다.

물론 실제 메모리가 부족한 서버라면 증설이 필요할 수 있습니다.

하지만 원인을 분석하지 않고 메모리만 늘리면 같은 문제가 더 늦게 다시 발생할 수도 있습니다.

예를 들어 애플리케이션에 메모리 누수가 있다면

8GB 서버
→ 6시간 후 OOM

16GB 서버
→ 12시간 후 OOM

32GB 서버
→ 24시간 후 OOM

처럼 장애 발생 시간만 늦춰질 수 있습니다.

따라서 먼저 메모리 사용량이 정상적으로 증가한 것인지 비정상적으로 증가한 것인지 구분해야 합니다.

트래픽 증가로 인해 동시 요청 수가 늘어난 것인지, Java Heap 크기가 지나치게 크게 설정되어 있는지, DB Buffer Pool과 다른 프로세스 메모리를 합산했을 때 물리 메모리를 초과하는지, 메모리 누수가 발생하고 있는지 등을 각각 확인해야 합니다.

컨테이너 환경이라면 한 가지를 더 확인해야 합니다.

Docker나 Kubernetes에서는 호스트 전체 메모리가 남아 있어도 컨테이너에 설정된 memory limit을 초과하면 해당 컨테이너 내부 프로세스가 종료될 수 있습니다.

즉,

Host RAM 부족

과

Container Memory Limit 초과

는 서로 다른 문제입니다.

Kubernetes에서 OOMKilled가 나타났다고 해서 반드시 물리 서버 전체의 RAM이 고갈되었다는 뜻은 아닙니다.

컨테이너 단위의 메모리 제한과 실제 사용량을 함께 분석해야 합니다.

운영 환경에서는 OOM을 어떻게 예방해야 할까?

가장 좋은 방법은 OOM Killer가 동작한 후 원인을 찾는 것이 아니라 메모리 압박이 위험 수준에 도달하기 전에 감지하는 것입니다.

예를 들어 available memory가 지속적으로 감소하거나 swap 사용량이 비정상적으로 증가하는 시점에 경고를 발생시킬 수 있습니다.

특정 프로세스의 RSS가 시간에 따라 계속 증가하는지도 중요합니다.

정상적인 애플리케이션이라면 트래픽에 따라 메모리가 증가하고 감소하는 패턴을 보일 수 있습니다.

하지만 트래픽이 일정한데도

2GB
→ 3GB
→ 4GB
→ 5GB
→ 6GB
→ OOM

처럼 장시간 한 방향으로만 증가한다면 메모리 누수를 의심할 수 있습니다.

또한 서버의 메모리 용량을 계산할 때 애플리케이션의 최대 메모리 설정만 더해서는 안 됩니다.

Kernel, page cache, system daemon, monitoring agent, 네트워크 버퍼 등도 메모리를 사용하기 때문입니다.

따라서 물리 메모리를 100% 애플리케이션에 할당하는 방식은 위험합니다.

운영 서버에는 반드시 일정 수준의 메모리 여유 공간이 필요합니다.

정리

OOM Killer는 Linux 서버에 문제가 생겨 무작위로 프로세스를 종료하는 기능이 아닙니다.

오히려 시스템 전체가 메모리 부족으로 멈추는 최악의 상황을 피하기 위해 Kernel이 선택하는 마지막 방어 수단입니다.

중요한 것은 OOM 메시지를 발견하는 것에서 분석을 끝내지 않는 것입니다.

운영자는 다음 흐름을 따라가야 합니다.

프로세스 비정상 종료
        ↓
Kernel OOM 로그 확인
        ↓
종료된 프로세스 확인
        ↓
장애 직전 available memory 확인
        ↓
Swap 및 reclaim 상태 확인
        ↓
프로세스별 메모리 사용량 분석
        ↓
메모리 증가 원인 확인
        ↓
설정·애플리케이션·서버 용량 개선

특히 OOM 발생 후의 free -h 결과만 보고 “메모리는 충분한데 왜 죽었지?”라고 판단해서는 안 됩니다. 이미 OOM Killer가 대형 프로세스를 종료해 메모리를 확보한 뒤일 수 있기 때문입니다.

결국 OOM 장애 분석의 핵심은 현재 메모리가 얼마나 남았는지가 아니라, 장애 직전 어떤 프로세스가 어떤 속도로 메모리를 소비했고 Kernel이 왜 더 이상 메모리를 확보할 수 없었는지를 추적하는 것입니다.

이 관점이 잡히면 OOM Killer는 단순한 프로세스 강제 종료 현상이 아니라 Linux 메모리 관리 전체를 이해할 수 있는 중요한 장애 신호가 됩니다.

instone
함께 보면 좋은 글

댓글 0

첫 댓글을 남겨보세요.

error: Content is protected !!

광고 차단 알림

광고 클릭 제한을 초과하여 광고가 차단되었습니다.

단시간에 반복적인 광고 클릭은 시스템에 의해 감지되며, IP가 수집되어 사이트 관리자가 확인 가능합니다.