OOM Killer (Out-of-Memory Killer) 작동 우선순위 점수
핵심 인사이트 (3줄 요약)
- 본질: OOM(Out of Memory) Killer는 리눅스 시스템에서 물리 메모리(RAM)와 스왑(Swap) 공간마저 100% 고갈되어 커널이 질식사(Kernel Panic)하기 직전, 시스템 전체를 구하기 위해 가장 램을 많이 잡아먹는 프로세스 하나를 골라 총으로 쏴 죽이는(SIGKILL) 최후의 암살 데몬이다.
- 가치: 어떤 놈을 쏴 죽일지 무작위로 고르지 않고, 프로세스가 먹고 있는 램 크기와 루트 권한 여부 등을 계산해
oom_score라는 살생부 점수를 매겨, 가장 희생양으로 적합한 뚱뚱한 유저 앱부터 자비 없이 처형하여 스래싱(Thrashing)의 늪에서 서버를 기적적으로 구출해 낸다.- 융합: 실무 엔지니어는 커널의 이 무자비한 심판을 피하기 위해, 데이터베이스(DB) 같은 핵심 프로세스에 **
oom_score_adj(면책 특권 점수)**를 부여하여 OOM Killer의 총구를 다른 잉여 앱으로 돌리게 만드는 고도화된 아키텍처 보호 전술을 융합한다.
Ⅰ. 개요 및 필요성 (Context & Necessity)
-
개념: OOM Killer는 리눅스 커널의 자가방어 메커니즘이다. 운영체제는 'Overcommit(초과 할당)' 정책에 따라 램이 없어도 일단 앱들에게 램을 주겠다고 사기(가상 주소 발급)를 쳐놨다. 앱들이 진짜로 램을 내놓으라고 몰려와서 남은 램과 스왑 공간이 단 1바이트도 없을 때, 커널은 이 뱅크런 사태를 막기 위해 법정(Score)을 열고 사형수를 한 명 뽑아 목을 날려버리고 그놈이 물고 있던 램을 압수한다.
-
필요성: 만약 OOM Killer가 없다면? 램 100% 상태에서 마우스 드라이버나 시스템 코어가 메모리를 4KB 요구했는데 램이 없다. 커널은 그 자리에서 치명적 에러를 뿜으며 컴퓨터를 파란 화면(Kernel Panic)으로 멈춰버리거나 영원한 렉(Deadlock)에 빠져버린다. "다 같이 사이좋게 죽느니, 그냥 제일 뚱뚱한 시민 하나를 절벽으로 밀어버리고 남은 사람이라도 살리자!"는 트롤리 딜레마의 공학적 결단이 OOM Killer를 리눅스의 핏줄에 새겨 넣었다.
-
💡 비유: OOM Killer는 타이타닉호의 무자비한 구명보트 선장이다. 구명보트(RAM)에 정원 10명이 넘는 15명이 타버려서 보트가 가라앉기 직전이다(시스템 패닉). 선장(OS 커널)은 다 같이 수장되는 걸 막기 위해 총을 빼 들고 뱃머리로 나간다. 그리고 승객들을 쓱 훑어보고 **가장 몸무게가 많이 나가고(메모리 독식), 짐이 많고, VIP가 아닌 일반 승객 1명(oom_score 1등)**의 멱살을 잡아 바다로 던져버린다. 배는 가벼워져 다시 떠오르고 남은 사람들은 무사히 항해를 계속한다.
-
등장 배경 및 Overcommit의 부메랑:
- 요구 페이징의 뻥카: 램 16GB 서버에서 프로세스들이
malloc으로 30GB를 요구해도 OS는 "OK"라며 I 비트를 찍어 보냈다(Overcommit). - 뻥카의 들통: 앱들이 가짜 주소에 진짜 데이터를 쓰기 시작하며 물리 프레임을 요구하자 스왑마저 다 털려버림.
- 사형 집행인의 등장: 뻥카를 쳐서 시스템을 위기에 빠뜨린 OS가 뒷수습을 하기 위해 가장 램을 많이 요구한 놈부터 총살시켜 증거 인멸(?)을 하는 아키텍처가 확립되었다.
- 요구 페이징의 뻥카: 램 16GB 서버에서 프로세스들이
┌────────────────────────────────────────────────────────────────────┐
│ OOM Killer가 총을 뽑아 드는 절망적 런타임 시각화 │
├────────────────────────────────────────────────────────────────────┤
│ │
│ [ 상황: RAM 16GB + Swap 8GB 모두 사용률 100% 도달! ] │
│ ┌───────────────┬────────────────┬──────────────┐ │
│ │ MySQL (15GB) │ 파이썬 봇 (8GB) │ SSH 접속 (1GB)│ │
│ └───────────────┴────────────────┴──────────────┘ │
│ │
│ ▶ 1. 최후의 순간 (Page Fault) │
│ SSH 접속 데몬이 "나 로그인 처리할 4KB만 줘!" 라고 외침. │
│ OS 커널: "빈방 0개... 캐시도 다 지움... 스왑도 꽉 참... 좆됐다" │
│ │
│ ▶ 2. 재판장 (OOM Score 계산 발동) │
│ 커널이 [ oom_score ] 살생부 랭킹을 매김. │
│ - MySQL (15GB 쳐먹음) -> 800점 │
│ - 파이썬 봇 (8GB 쳐먹음) -> 500점 │
│ - SSH 데몬 (VIP 권한 + 1GB) -> 10점 │
│ │
│ ▶ 3. 처형 (Execution) │
│ OOM Killer: "MySQL 네 이놈! 네가 점수 1등이다! 죽어라!" │
│ 💥 SIGKILL 발사 -> MySQL 즉사 -> 15GB 램 허공에 확 풀림! │
│ ✅ SSH 데몬은 무사히 램을 받아 로깅 성공. 서버 커널 생존! │
└────────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 초보 개발자들은 MySQL이 갑자기 죽어버린 걸 보고 "리눅스가 미쳤어!"라고 원망하지만, 사실 리눅스는 MySQL을 죽여서 SSH(터미널)와 서버 심장부를 살려낸 다크나이트다. 만약 OOM 킬러가 저 총을 안 쐈다면 당신은 서버에 SSH로 접속조차 못 하고 AWS 콘솔에서 하드웨어 강제 리부팅 버튼을 눌러야만 했을 것이다.
- 📢 섹션 요약 비유: 풍선(RAM)에 바람(데이터)을 계속 불어넣어 터지기 0.001초 직전입니다. 풍선 전체가 터져서(서버 패닉) 다 죽는 걸 막기 위해, 엄마(OOM Killer)가 바늘을 들고 달려와서 바람을 가장 많이 차지하고 있던 거대한 방 하나를 콕 찔러 바람을 쫙 빼버림(프로세스 사살)으로써 풍선 전체가 터지는 걸 기적처럼 막아내는 긴급 조치입니다.
Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)
OOM Score (살생부 점수)의 수학적 계산법
리눅스 커널은 아무나 막 쏘지 않는다. /proc/<pid>/oom_score 에 명시된 점수(0 ~ 1000점)를 철저히 계산하여 가장 높은 놈을 죽인다. 점수가 높을수록 죽을 확률이 높다.
[ 점수를 올리는 죄악 (Bad) ]
- 물리 램(RSS)과 스왑 점유율: 절대적인 평가 기준이다. 가장 뚱뚱하게 램을 많이 깔고 앉은 프로세스가 기본적으로 수백 점의 점수를 먹고 1순위 타겟이 된다. (램을 많이 토해낼 놈을 죽여야 가성비가 좋으니까).
- 자식 프로세스가 많은 놈:
fork()로 새끼를 쳐서 메모리를 갉아먹는 악성 스크립트들을 싹 쓸어버리기 위해 자식들의 메모리 합까지 연좌제로 물어 점수를 올린다.
[ 점수를 깎아주는 선행 (Good) ]
- 루트(Root) 권한:
sudo나root로 띄운 프로세스는 커널의 주요 관리 데몬일 확률이 높으므로 묻지도 따지지도 않고 점수를 깎아 살려준다. (SSH, Systemd 등 보호). - 하드웨어 직접 접근: 디스크 컨트롤러(DMA) 등 하드웨어 락을 쥐고 있는 앱을 죽이면 기계가 뻗어버릴 수 있으므로 점수를 대폭 깎아 방어해 준다.
- 오래 살아남은 놈 (Uptime): 1년 동안 안 터지고 잘 돌아간 앱은 시스템의 핵심 코어일 확률이 높으므로 점수를 깎아준다. (새로 들어와 램을 먹어대는 뜨내기 파이썬 스크립트를 우선 사살함).
OOM Killer의 처형 방식 (SIGKILL)
점수 1등이 결정되면 커널은 해당 프로세스에게 SIGKILL (Signal 9)을 자비 없이 발사한다.
-
일반적인 종료 신호(
SIGTERM)는 프로세스에게 "너 메모리 정리하고 유서 쓰고 예쁘게 죽어라"라고 시간을 준다. -
하지만 OOM 상태는 1바이트의 램도 없어 유서를 쓸 램 공간조차 없는 위급 상황이다.
-
SIGKILL은 앱이 저항하거나 에러 로그를 남길 틈조차 주지 않고, 커널이 직접 뇌를 박살 내고 램을 즉시 강제 압수해버리는 무식하고 확실한 처형이다. 이 때문에 앱 내부 로그에는 아무런 에러 메시지도 남지 않고 조용히 증발하며, 개발자는 리눅스 커널 시스템 로그(/var/log/messages나dmesg)를 뒤져봐야만 "아... OOM 킬러한테 총 맞았구나" 하고 사인을 알 수 있게 된다. -
📢 섹션 요약 비유: OOM 킬러는 재판부(점수표)의 판결이 떨어지면, 피고인(앱)에게 마지막 인사할 시간조차 주지 않고 뒤통수에 소음기 달린 권총(SIGKILL)을 쏴서 즉사시킨 뒤, 그가 들고 있던 빵(메모리)을 빼앗아 굶주린 시민(커널)에게 즉각 뿌리는 피도 눈물도 없는 사형 집행인입니다.
Ⅲ. 융합 비교 및 다각도 분석
비교 1: 스와핑(Swapping) vs 컴팩션(Compaction) vs OOM Killer
메모리가 모자랄 때 OS가 뽑아 드는 3단계 방어 무기의 한계 비교.
| 방어 단계 | 방어 무기 (Mechanism) | 시스템 체감 (UX) | 한계 및 부작용 |
|---|---|---|---|
| 1단계 (경고) | Swapping / Paging (안 쓰는 놈 디스크로) | 앱 켤 때 1~2초씩 버벅거림 | 디스크 스래싱(Thrashing) 터지면 끝남 |
| 2단계 (위험) | Memory Compaction (램 구석으로 밀어 압축) | 화면이 멈추고 렉이 심하게 걸림 (STW) | 빈 공간 자체가 아예 없으면 실패함 |
| 3단계 (사형) | OOM Killer (뚱뚱한 앱 사살) | 갑자기 돌아가던 게임(DB)이 팍 튕겨버림 | 앱의 데이터가 저장 안 되고 다 날아감 (Data Loss) |
Overcommit (초과 할당) 철학의 논쟁
리눅스 철학의 뜨거운 감자다.
- Strict Overcommit (보수적): "물리 램 + 스왑 공간 이상으로는 절대
malloc안 받아줘! 애초에 OOM 킬러가 뜰 일을 만들지 마!" -> 안정적이지만 램 낭비가 뼈아프다. (윈도우가 선호). - Heuristic Overcommit (진취적, 리눅스 디폴트): "야, 쟤들
malloc(1GB)해놓고 실제론 10MB밖에 안 쓰는 허풍쟁이들인 거 다 알아. 램 16GB여도 30GB어치 수표 팍팍 남발해서 다 받아줘! 혹시 나중에 수표 막을 돈 부족해지면? 그때 가서 OOM 킬러로 한 놈 쏴 죽이고 무마하면 돼!" - 리눅스는 이 야만적인 Overcommit과 OOM Killer의 콜라보 덕분에 적은 램으로도 어마어마하게 많은 컨테이너를 띄우며 클라우드 서버 생태계를 압살할 수 있었다.
┌──────────┬────────────┬────────────┬──────────────────────────────────────────────┐
│ Overcommit│ 램 10GB일때 할당량│ OOM 킬러 출동 빈도│ 다중 프로그래밍 밀도 │
├──────────┼────────────┼────────────┼──────────────────────────────────────────────┤
│ 비활성화 (0)│ 딱 10GB만 허가│ 절대 출동 안함 (평화)│ 낮음 (보수적 낭비) │
│ 활성화 (1) │ 30GB 이상 뻥튀기│ 가끔 탕! 쏘고 감 (야생)│ **최상 (클라우드 최적화)**│
└──────────┴────────────┴────────────┴──────────────────────────────────────────────┘
[매트릭스 해설] OOM 킬러는 리눅스가 무능해서 만든 에러 코드가 아니다. 오히려 리눅스가 램 자본주의를 극한으로 쥐어짜기(Overcommit) 위해 의도적으로 배치해 둔 '폭력적인 시장 조정자'에 가깝다. 이 살생부가 있기에 리눅스 커널은 배짱 있게 메모리 장사를 할 수 있다.
- 📢 섹션 요약 비유: 비행기 표 100장을 팔 때, 취소표를 예상하고 110장(Overcommit)을 파는 짓과 같습니다. 110명이 다 공항에 오면 자리가 없어 폭동(패닉)이 날 텐데, 이때 공항 경찰(OOM Killer)을 불러서 가장 덩치 큰 10명을 강제로 질질 끌어내 비행기 밖으로 내쫓아버리고 이륙을 감행하는 것이 리눅스 항공사의 영업 비결입니다.
Ⅳ. 실무 적용 및 기술사적 판단 (Strategy & Decision)
실무 시나리오: oom_score_adj 로 면책 특권(방탄조끼) 부여하기
- 문제의 발단: 회사 수익을 100% 담당하는 **오라클 데이터베이스(DB)**가 돌아가는 서버에 로그를 파싱하는 별도 앱을 띄웠다. DB는 당연히 램을 제일 많이 쳐먹고(15GB) 있으니 OOM 점수 1등이다. 로그 앱이 실수로 램을 다 긁어먹는 순간, OOM 킬러가 튀어나와 1초의 망설임도 없이 회사 매출의 심장인 오라클 DB의 머리에 총을 쏴서 죽여버렸다. (엔지니어 사유서 제출).
- 방탄조끼 튜닝 (
oom_score_adj):- 실력 있는 백엔드 엔지니어는 DB 서버를 켤 때 시스템 설정에 방탄조끼를 입힌다.
- 프로세스 고유의 살생부 가중치인
/proc/<pid>/oom_score_adj파일에 들어가서 **-1000(면책 특권 수치)**을 박아 넣는다. - 이 수치가
-1000이 되면 커널의 살생부 점수 계산식에서 무조건 음수나 0점이 되어, **"아무리 뚱뚱하고 램을 다 쳐먹어도 OOM 킬러가 절대 쏘지 않는 영구 까임 방지권"**을 획득하게 된다.
- 결과: 나중에 램이 폭발해도, OOM 킬러는 오라클 DB는 건드리지 못하고 옆에서 찌질거리던 로그 파싱 앱(점수 2등)의 머리통을 날려버린다. 핵심 서비스는 살아남고 보조 서비스만 죽는 완벽한 장애 통제(Graceful Degradation)가 완성된다.
K8s (Kubernetes)의 QoS 클래스와 OOM
쿠버네티스는 컨테이너를 띄울 때 파드(Pod)에 3가지 계급(QoS)을 부여하며, 이 계급이 곧 OOM 킬러의 1순위 타겟 지표가 된다.
-
Guaranteed (귀족): Request와 Limit 램 용량이 정확히 같게 깐깐하게 세팅된 파드.
oom_score_adj가 낮게 설정되어 절대 안 죽음. -
Burstable (평민): Request보다 Limit을 높게 잡아 뻥카를 친 파드. 위험할 때 죽을 수 있음.
-
BestEffort (천민): 램 세팅을 아예 안 하고 띄운 파드. OOM 터지면 0순위로 모가지가 썰려 나가는 희생양이다. K8s의 오케스트레이션은 이 OOM 킬러 점수를 뒤에서 조작하는 거대한 인형극이다.
-
📢 섹션 요약 비유: 왕(커널)이 식량이 모자라 백성을 죽여 식량을 아끼려 할 때(OOM), 똑똑한 귀족(DB 엔지니어)은 미리 왕에게 뇌물을 줘서(oom_score_adj=-1000) 자기 가문을 살생부에서 완벽히 파내고, 아무 빽도 없는 가난한 평민(로그 수집기)들만 무참히 학살당하게 만드는 철저한 권력 다툼의 현장입니다.
Ⅴ. 기대효과 및 결론 (Future & Standard)
정량/정성 기대효과
| 구분 | 내용 |
|---|---|
| 커널 패닉(Kernel Panic) 원천 차단 | 램 잔고 0원이라는 최악의 교착 상태(Deadlock)에서 프로세스 1개를 쳐냄으로써, 서버 전체가 먹통 되어 강제 리부팅하는 사태를 방지 |
| Overcommit (초과 할당) 아키텍처 성립 | OOM 킬러라는 든든한 뒤처리꾼이 존재하기에, OS가 마음껏 뻥튀기 가상 메모리를 팔아 서버 다중 프로그래밍 스루풋을 극대화 |
| 마이크로서비스(MSA) 우선순위 통제 | 쿠버네티스 환경에서 QoS와 oom_score_adj 튜닝을 통해, 죽어도 되는 컨테이너와 살려야 할 컨테이너의 생사를 클라우드 설계자가 완벽하게 통제 |
결론 및 미래 전망
OOM Killer (Out-of-Memory Killer)는 "가상 메모리의 환상이 물리적 현실의 벽에 부딪혔을 때, 운영체제가 보여줄 수 있는 가장 폭력적이고도 이성적인 구조조정"이다. 초보 개발자들에게는 내 코드를 알림도 없이 찢어 죽이는 공포의 대상이지만, 베테랑 시스템 엔지니어에게는 이 킬러의 총구를 누구에게 돌릴지(oom_score_adj) 세팅함으로써 시스템의 코어를 지켜내는 고도화된 스케줄링의 체스판이 된다. 클라우드와 도커(Docker)의 시대가 도래하며 단일 서버가 수백 개의 앱을 품는 오버커밋(Overcommit)이 당연해짐에 따라, 이 살생부 데몬의 역할은 단순한 오류 처리기를 넘어 마이크로서비스 생태계의 숲을 가꾸는 '산불(자연 도태)'과 같은 필수 불가결한 정화 시스템으로 영원히 작동할 것이다.
- 📢 섹션 요약 비유: 기차(서버)가 산비탈을 올라가는데 엔진 힘(램)이 모자라 뒤로 미끄러져 낭떠러지로 떨어질 위기(커널 패닉)입니다. 이때 차장(OOM 킬러)이 화물칸으로 달려가 제일 무거운 황금 금고(가장 뚱뚱한 프로세스) 하나를 냅다 밖으로 발로 차버립니다. 황금은 잃었지만(데이터 손실) 가벼워진 기차는 무사히 언덕을 넘어 수백 명의 승객(시스템 전체)을 살려냅니다. 이것이 OS가 택한 대의를 위한 소수의 희생입니다.
📌 관련 개념 맵 (Knowledge Graph)
- Overcommit (초과 할당) | 물리 램은 16GB인데 앱들에겐 30GB를 줬다고 뻥을 치는 기법으로, OOM 킬러가 존재해야만 성립하는 리눅스의 도박
- 스래싱 (Thrashing) | 램이 모자라 디스크 스왑만 치면서 컴퓨터가 뻗는 현상으로, OOM 킬러가 총을 빼들게 만드는 가장 직접적인 위기 상태
- oom_score_adj | 개발자가 "얘는 죽이지 마라(-1000)" 혹은 "얘부터 무조건 죽여라(+1000)"라고 커널 살생부 점수를 강제로 조작하는 실무 마법의 튜닝값
- SIGKILL (Signal 9) | OOM 킬러가 앱을 쏠 때 쓰는 처형 명령어로, 앱이 에러 로그를 남기거나 반항할 틈도 주지 않고 뇌를 즉사시키는 무자비한 신호
- cgroups (Control Groups) | 도커 컨테이너 환경에서 전체 시스템 OOM이 터지기 전에, 좁은 철창 안에서 Local OOM을 터뜨려 샌드박스를 지키는 방파제
👶 어린이를 위한 3줄 비유 설명
- OOM 킬러가 뭔가요? 놀이방(메모리)에 장난감이 너무 꽉 차서 발 디딜 틈도 없이 숨이 막혀 터지기 일보 직전에, 갑자기 무서운 청소부 로봇이 나타나서 제일 큰 장난감 하나를 통째로 쓰레기통에 냅다 버리는 거예요.
- 왜 내 소중한 장난감을 버려요? 그 큰 장난감을 안 버리면 놀이방 자체가 쾅! 하고 폭발해서(시스템 멈춤) 놀이방 안에 있는 모든 친구가 영원히 갇혀 죽게 되니까, 어쩔 수 없이 하나를 희생시키는 거랍니다.
- 내 장난감을 지키려면요? 엄마한테 미리 "이 로봇은 1000만 원짜리 황금 로봇(oom_score_adj)이니까 절대 청소기가 버리지 못하게 딱지를 붙여줘!"라고 단단히 약속을 해둬야만 살아남을 수 있어요.