Swap 사용이 위험한 이유

1,423 단어·3 분·원문(.md)

Swap이란 물리적 메모리 RAM이 부족할 때, 하드 디스크나 SSD 일부 공간을 메모리 처럼 사용하는 기술이다.

리눅스 커널은 당장 사용되지 않는 메모리 페이지를 디스크로 몰아내고(Swap-out) 필요할때 다시 불러온다.

물론 스왑이 발생하면 더 많은 공간을 쓸 수 있다는 점은 있지만, out된 메모리에 접근할때 성능 저하가 발생한다. 아무래도 2차 메모리다보니까

  • 성능의 비결정성(Non-deterministic): 특정 시점에는 api응답이 빠르다가 갑자기 수 초씩 걸리는 현상 발생
  • Dis Thrashing: CPU는 놀고있는데 Disk IO가 100%를 치며 시스템이 사실상 멈추는 현상이 나타남
  • 거짓 양성(False Positive) 상태: 프로세스가 살아는 있으나 응답을 못하는 좀비 상태가 되어 로드 밸런서의 헬스 체크를 통과해버리는 문제가 생김.

Latency Gap #

RAM의 접근 속도는 나노초 ns단위인 반면 SSD, HDD는 ms 단위다.

단순 계산으로도 10만배 차이가 나고 swap이 발생하는 순간 애플리케이션의 성능은 느려지는 수준이 아니라 멈추는 수준이 된다

OOM Killer 지연 #

리눅스에는 메모리가 부족할 대 프로세스를 강제로 종료해 시스템을 보호하는 OOM Killer가 있는데

Swap이 켜져있으면 커널은 프로세스를 죽이는 대신 어떻게든 Swap으로 버티려고한다. 결과적으로 시스템이 즉시 실패하지 않고 매우 고통스럽게 죽으며 서비스 전체에 영향을 준다.

GC와의 상성 #

java jvm, go의 런타임은 메모리 전체를 훑으며 gc를 수행한다.

이때 메모리 일부가 swap에 가있다면 gc를 할때마다 disk io가 발생하고 평소 10ms면 끝날 gc가 10초이상 걸리며 stw가 오래 잡아먹게 된다.

그래서 swap을 방지하게 된다면 예측 가능한 성능으로 메모리 부족시 swap으로 버티지 않고 oom을 발생시켜 장애 원인을 오히려 명확히 파악할 수 있다. 즉시 대응할수 있으므로

그리고 특정 프로세스 메모리 누수가 전체 시스템의 io를 점유해 다른 멀정한 서비스까지 마비시키는 인접 소음 noisy neighbor 문제를 방지한다.

swap 상태 확인 및 제어 #

가장 먼저 시스템 전체의 메모리 상태를 확인한다

free -h
total        used        free      shared  buff/cache   available
Mem:           15Gi       12Gi       500Mi       1.0Gi       2.5Gi       2.0Gi
Swap:         2.0Gi       1.5Gi       500Mi  # <--- 주의! Swap이 1.5Gi나 사용 중입니다.

어떤 프로세스가 쓰는지도 추적한다

# 1초 간격으로 시스템 리포트 출력
vmstat 1

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  1 1572864 512000  10240 524288  150  200    10    50  500  800 10  5 80  5  0
  • si(swap in): 디스크에서 메모리로 읽어오는 양
  • so (swap out): 메모리에서 디스크로 밀어내는 양
  • 해석 si, so수치가 0보다 크고 지속적으로 발생하면 현재 disk thrashing이 일어나고 있다는 증거다.

swappiness 설정 확인 및 변경 #

커널이 얼마나 공격적으로 swap을 사용할지 결정하는 파라미터고 0~100 사이 낮을수록 swap을 억제한다.

cat /proc/sys/vm/swappiness
# 결과: 60 (리눅스 기본값, 서버 환경에서는 너무 높음)

# 임시로 10으로 변경 (최대한 RAM을 쓰도록 유도)
sudo sysctl vm.swappiness=10

비활성화 해버려도됨. k8s환경이나 고성능 db서버에서는 swap을 아예 끄는것도 방법이다.

# 모든 Swap 장치 비활성화
sudo swapoff -a

# /etc/fstab 파일에서 swap 관련 라인을 주석 처리하여 재부팅 후에도 적용
sudo vi /etc/fstab
# /swapfile none swap sw 0 0  <-- 이 줄 앞에 # 을 붙여 주석 처리

swap은 보험이 아니다. swap은 메모리 부족할때를 대비한 안전장치라고 생각하지만 실무에서는 천천히 고통스럽게 죽느냐 빨리 죽고 다시 살아나느냐의 문제라서

고통스럽게 천천히는 swap, 빨리 죽기는 oom이다. 가용성 측면에서는 후자가 더 유리하다.

swap-out 발생 시점을 임계치로 잡아서 알람을 거는것보다 Memory Usage > 85 일때 미리 알람을 받고 대응하는것이 오히려 낫다.

k8s의 경우 노드는 공식저긍로 swap을 비활성화하는것을 권장한다.

스케쥴러가 노드의 가용자원을 계산할때 swap을 고려하지 않기 때문이다.

SRE/question/q_23.md