---
title: "PostgreSQL에서 OOM 킬러가 위험한 이유와 리눅스 커널의 한 글자 버그"
published: 2026-07-03T13:00:31.000Z
canonical: https://jeff.news/article/4683
---
# PostgreSQL에서 OOM 킬러가 위험한 이유와 리눅스 커널의 한 글자 버그

Ubicloud가 PostgreSQL에 엄격한 메모리 오버커밋을 쓰는 이유를 설명하다가, 리눅스 6.5 커널의 Committed_AS 누수 버그를 추적한 사례를 공개했음. 8GB 서버에서 651GB가 커밋된 것처럼 보이는 현상이 있었고, 원인은 mremap 경로의 조건문 하나가 잘못 바뀐 커널 버그였음.

## PostgreSQL은 OOM 킬러를 그냥 맞고 재시작하면 되는 프로그램이 아님

- 리눅스는 기본적으로 실제 물리 메모리보다 더 많은 가상 메모리 할당을 허용함
  - malloc() 같은 호출이 들어오면 일단 가상 주소 공간을 예약하고, 프로세스가 실제로 그 메모리를 만질 때 물리 페이지를 붙임
  - “모든 프로세스가 할당한 메모리를 동시에 다 쓰진 않을 것”이라는 가정이 깨지면 OOM killer가 프로세스를 하나 죽임

- 일반 앱은 OOM으로 죽어도 재시작하면 되는 경우가 많지만, PostgreSQL은 다름
  - Postgres의 postmaster는 연결마다 백엔드 프로세스를 fork하고, 이 백엔드들이 shared buffers, WAL buffers, lock table 같은 공유 메모리를 같이 씀
  - OOM killer는 이런 구조를 이해하지 못하고 휴리스틱으로 프로세스 하나를 골라 죽임
  - 만약 죽은 백엔드가 공유 메모리를 수정 중이었다면 OS 레벨에서는 그 상태를 트랜잭션처럼 되돌려주지 않음

- 그래서 Postgres는 자식 프로세스 하나가 비정상 종료돼도 최악을 가정함
  - 공유 메모리가 깨졌을 수 있으니 남은 백엔드를 모두 종료하고, 모든 연결을 끊고, 진행 중이던 트랜잭션을 abort함
  - 다음 시작 때 crash recovery를 돌면서 WAL을 재생함
  - 쓰기량이 많았으면 WAL replay가 길어져서 단순 OOM 한 번이 긴 장애로 이어질 수 있음

> [!WARNING]
> PostgreSQL에서 OOM kill은 “커넥션 하나 죽음”이 아니라 “서버 전체 연결 끊김 + crash recovery”로 번질 수 있음. 데이터 보호를 위한 올바른 동작이지만, 운영 영향은 꽤 큼.

## strict overcommit을 켰더니 8GB 서버에 651GB 유령 메모리가 찍힘

- Ubicloud는 PostgreSQL에 strict memory overcommit을 선호해왔음
  - 이유는 나중에 OOM killer가 랜덤하게 백엔드를 죽이는 것보다, 애초에 위험한 메모리 할당을 거절하는 편이 데이터베이스에는 낫기 때문임
  - 그런데 이번에 strict overcommit을 켠 뒤, 물리 메모리가 충분한 서버에서도 out of memory 오류가 나기 시작함

- 단서가 된 값은 /proc/meminfo의 Committed_AS였음
  - 8GB 메모리 서버에서 Committed_AS가 683,547,672KB, 즉 약 651GB로 찍힘
  - 같은 크기의 정상 서버는 2,703,940KB, 약 2.6GB 수준이었음
  - 실제 accountable memory를 /proc/*/smaps에서 합산해보니 2.43GB뿐이라, 약 648GB가 말 그대로 유령 커밋 메모리였음

- 처음 의심한 건 PostgreSQL의 shared_buffers와 huge pages였음
  - 각 백엔드 프로세스의 VSZ가 약 2GB로 보였고, 이는 8GB VM에서 설정한 2GB shared_buffers와 거의 같았음
  - 하지만 각 백엔드는 같은 2GB 공유 메모리 세그먼트를 자기 주소 공간에 매핑할 뿐이고, 물리적으로는 한 번만 존재함
  - 게다가 huge_pages=on이라 hugetlb 매핑은 Committed_AS에 잡히면 안 됨

- VMA 플래그 확인으로 huge pages 가설은 탈락함
  - /proc/<pid>/smaps에서 hugepage 매핑의 VmFlags를 봤더니 VM_ACCOUNT를 뜻하는 ac 플래그가 없었음
  - 즉 huge pages는 정상적으로 committed memory accounting에서 빠지고 있었음
  - 남은 가설은 커널의 vm_committed_as 카운터가 allocation 때 증가하고 해제 때 제대로 줄지 않는다는 쪽이었음

## 범인은 리눅스 6.5의 한 글자 조건문 버그였음

- fleet 전체를 보니 커널 버전 차이가 확실하게 드러남
  - 커널 6.5.0 서버는 Committed_AS / MemTotal 비율 평균이 24.97이었고, 최대는 3,405까지 튐
  - 커널 6.8.0 서버는 평균 0.32, 최대 1.86 수준이었음
  - 비율이 1.0을 넘는 서버도 6.5에서는 23%, 6.8에서는 1% 미만이었음
  - 통계적으로 6.5 서버가 committed memory inflation을 겪을 가능성이 52배 높았다고 함

- 버그는 리눅스 6.5의 commit 408579c에서 들어갔음
  - do_vmi_align_munmap()의 반환 규칙이 “0은 성공, 1은 lock downgraded 성공, 음수는 에러”에서 “성공은 항상 0, 음수는 에러”로 바뀜
  - 호출부들을 고치는 과정에서 mm/mremap.c의 move_vma() 조건문이 잘못 바뀜
  - 원래는 do_vmi_munmap() 결과가 < 0일 때만 에러 처리로 vm_acct_memory()를 되돌려야 했음

- 문제의 핵심은 < 0이 !로 바뀐 한 글자급 실수였음
  - 올바른 조건은 “unmap 실패 시에만 메모리 회계를 복구”하는 것임
  - 깨진 조건은 “unmap 성공 시에도 메모리 회계를 복구”해버림
  - 결과적으로 mremap이 성공할 때마다 Committed_AS가 단조 증가했고, strict overcommit에서는 시간이 지나 CommitLimit을 넘으며 정상 할당까지 실패하게 됨

> [!IMPORTANT]
> 이 버그는 기본 overcommit 모드에서는 Committed_AS가 주로 참고용이라 잘 안 보였음. strict overcommit처럼 커널이 이 값을 실제 할당 판단에 쓰는 환경에서만 몇 주 뒤 장애로 터지는, 딱 운영자 괴롭히는 타입의 버그임.

- Linus Torvalds가 원인 분석 후 조건문을 다시 < 0으로 고치면서 해결됐음
  - 실제 VM 동작은 거의 안 바뀌고, VM_ACCOUNT가 붙은 VMA의 메모리 회계만 틀어지는 미묘한 버그였다고 설명함
  - 시스템은 계속 “동작하는 것처럼” 보이지만 Committed_AS가 점점 이상해지고, 나중에 VM 휴리스틱이나 strict overcommit에서 문제가 터지는 구조였음

---
## 기술 맥락

- PostgreSQL에서 strict overcommit을 쓰는 이유는 OOM killer에게 결정을 맡기면 데이터베이스 입장에서 너무 위험하기 때문이에요. 백엔드 하나만 죽어도 공유 메모리 일관성을 보장할 수 없어서 전체 연결을 끊고 복구해야 하거든요.

- Committed_AS는 단순 모니터링 숫자처럼 보이지만, strict overcommit에서는 커널이 새 메모리 할당을 허용할지 판단하는 기준이 돼요. 그래서 8GB 서버에 651GB가 커밋됐다고 잘못 찍히면, 실제 메모리가 남아 있어도 할당이 실패할 수 있어요.

- Ubicloud가 huge pages를 먼저 의심한 건 합리적인 접근이에요. PostgreSQL 백엔드마다 2GB shared_buffers 매핑이 VSZ에 반복해서 보였고, hugetlb는 커널 메모리 회계에서 특수한 경로를 타기 때문이에요.

- 결정적인 포인트는 /proc/<pid>/smaps의 VMA 플래그였어요. hugepage 매핑에 ac 플래그가 없다는 건 해당 영역이 committed memory accounting 대상이 아니라는 뜻이라, 원인을 PostgreSQL 설정이 아니라 커널 카운터 누수 쪽으로 좁힐 수 있었어요.

- 이 사례가 무서운 건 실제 메모리 관리 동작은 정상인데 회계 숫자만 틀어진다는 점이에요. 기본 설정에서는 조용히 숨어 있다가, strict overcommit처럼 더 안전한 운영 정책을 쓰는 곳에서만 장애로 드러났거든요.

## 핵심 포인트

- PostgreSQL은 백엔드 프로세스 하나가 OOM으로 죽어도 공유 메모리 손상 가능성 때문에 전체 연결을 끊고 복구에 들어감
- Ubicloud는 엄격한 오버커밋을 쓰다 8GB 서버에서 651GB Committed_AS가 찍히는 이상 현상을 발견함
- 리눅스 6.5의 mremap 관련 조건문이 < 0에서 !로 잘못 바뀌며 성공 경로에서도 메모리 회계가 되돌려지는 버그가 생겼음

## 인사이트

데이터베이스 운영에서 “리눅스 기본값이면 되겠지”가 얼마나 위험할 수 있는지 보여주는 글임. 특히 Postgres, OOM killer, overcommit, huge pages가 한 번에 얽히는 케이스라 운영자 입장에서는 꽤 저장해둘 만함.
