Kubernetes를 위한 Linux Swap 튜닝: 심층 분석

sysctl 관련 문서를 보던 중에 swap의 동작방식을 해설해 놓은 것 같은 글이 보여해서 정리해봤습니다. 영어를 잘 못하지만 구글 번역기+약간 수정한 것입니다.

출처: https://kubernetes.io/blog/2025/08/19/tuning-linux-swap-for-kubernetes-a-deep-dive/

Kubernetes NodeSwap 기능은 곧 출시될 Kubernetes v1.34 릴리스에서 안정화 될 가능성이 높으며 , 스왑 사용을 허용합니다. 이는 성능 예측을 위해 스왑을 비활성화하는 기존 방식과는 크게 다릅니다. 이 글에서는 이 기능을 사용할 수 있는 Linux 노드에서의 스왑 조정에 대해 집중적으로 다룹니다. 노드 스왑 지원은 Linux 노드가 물리적 RAM이 고갈될 때 추가 가상 메모리를 위해 보조 저장소를 사용할 수 있도록 함으로써 리소스 활용도를 높이고 out-of-memory(OOM)으로 인한 킬(kill)을 줄이는 것을 목표로 합니다.

하지만 스왑 활성화는 “완벽한(turn-key)” 해결책이 아닙니다. 메모리 부족 상황에서 노드의 성능과 안정성은 Linux 커널 매개변수 집합에 크게 좌우됩니다. 잘못된 구성은 성능 저하로 이어지고 Kubelet의 제거 로직을 방해할 수 있습니다.

이 블로그 게시물에서는 스왑 동작을 제어하는 ​​중요한 Linux 커널 매개변수를 자세히 살펴보겠습니다. 이러한 매개변수가 쿠버네티스 워크로드 성능, 스왑 사용률, 그리고 중요한 제거 메커니즘에 어떤 영향을 미치는지 살펴보겠습니다. 다양한 구성의 영향을 보여주는 다양한 테스트 결과를 제시하고, 안정적이고 고성능 쿠버네티스 클러스터를 위한 최적의 설정을 달성하는 방법에 대한 저의 연구 결과를 공유하겠습니다.

리눅스 스왑 소개

리눅스 커널은 높은 수준에서 일반적으로 4KB 크기의 페이지로 메모리를 관리합니다. 물리적 메모리가 제한되면 커널의 페이지 교체 알고리즘이 스왑 공간으로 이동할 페이지를 결정합니다. 정확한 로직은 정교한 최적화이지만, 이러한 의사 결정 과정은 다음과 같은 주요 요인의 영향을 받습니다.

  1. 페이지 액세스 패턴(페이지가 최근에 액세스된 방법)
  2. 페이지 더티함(dirtyness)(페이지가 수정되었는지 여부)
  3. 메모리 부족(시스템에 얼마나 긴급하게 여유 메모리가 필요한지)

익명(Anonymous) vs 파일 백업(File-backed) 메모리

모든 메모리 페이지가 동일하지 않다는 점을 이해하는 것이 중요합니다. 커널은 익명 메모리와 파일 기반 메모리를 구분합니다.

익명 메모리 : 프로그램의 힙이나 스택처럼 디스크의 특정 파일에 의해 뒷받침되지 않는 메모리입니다. 애플리케이션 관점에서 이는 개인 메모리이며, 커널이 이러한 페이지를 회수해야 할 때 전용 스왑 장치에 기록해야 합니다.

파일 백업 메모리 : 이 메모리는 파일 시스템의 파일에 의해 백업됩니다. 여기에는 프로그램의 실행 코드, 공유 라이브러리, 파일 시스템 캐시가 포함됩니다. 커널이 이러한 페이지를 회수해야 할 때, 수정되지 않은 페이지(“클린”)는 간단히 삭제할 수 있습니다. 페이지가 수정된 페이지(“더티”)인 경우, 커널은 삭제하기 전에 먼저 파일에 변경 사항을 다시 기록해야 합니다.

스왑이 없는 시스템은 파일 백업 페이지를 삭제하여 메모리 부족 상황에서도 깨끗한 메모리를 확보할 수 있지만, 익명 메모리를 오프로드할 방법은 없습니다. 스왑을 활성화하면 이러한 기능이 제공되어 커널이 자주 액세스하지 않는 메모리 페이지를 디스크로 이동하여 메모리를 절약하고 시스템 OOM(Out Of Memory)으로 인한 시스템 종료를 방지할 수 있습니다.

스왑 튜닝을 위한 주요 커널 매개변수

스왑 동작을 효과적으로 조정하기 위해 Linux는 sysctl을 통해 관리할 수 있는 여러 커널 매개변수를 제공합니다.

  • vm.swappiness: 가장 잘 알려진 매개변수입니다. 0에서 200(이전 커널에서는 100) 사이의 값으로, 커널이 익명 메모리 페이지를 스왑할지, 아니면 파일 백업 메모리 페이지(페이지 캐시)를 회수할지를 결정합니다.
    • 높은 값(예: 90+) : 커널은 파일 캐시 공간을 확보하기 위해 덜 사용되는 익명 메모리를 적극적으로 교체합니다.
    • 낮은 값(예: < 10) : 커널은 익명의 메모리를 스왑하는 것보다 파일 캐시 페이지를 삭제하는 것을 강력히 선호합니다.
  • vm.min_free_kbytes: 이 매개변수는 커널이 버퍼로 사용할 최소한의 메모리를 비워두도록 지시합니다. 비워둔 메모리 양이 이 안전 버퍼보다 ​​낮아지면 커널은 페이지를 더 적극적으로 회수하기 시작합니다(스왑, 그리고 결국 OOM 킬 처리).
    • 기능: 지연될 수 없는 중요한 할당 요청에 대비해 커널에 충분한 메모리가 있는지 확인하는 안전 장치 역할을 합니다.
    • 스왑에 미치는 영향 : min_free_kbytes를 더 높은 값을 설정하면 실제로 여유 메모리의 한계가 높아지고, 메모리 부족으로 인해 커널이 스왑을 더 일찍 시작하게 됩니다.
  • vm.watermark_scale_factor: 이 설정은 min, low, high와 같은 서로 다른 워터마크 간의 간격을 제어합니다. min_free_kbytes를 기반으로 계산됩니다 .
    • 워터마크에 대한 설명 :
      • low: 여유 메모리가 이 기준치보다 낮아지면 kswapd 커널 프로세스가 깨어나 백그라운드에서 페이지를 회수합니다. 이때 스와핑 주기가 시작됩니다.
      • min: 사용 가능한 메모리가 이 최소 수준에 도달하면 공격적인 페이지 회수가 프로세스 할당을 차단합니다. 페이지 회수에 실패하면 OOM(Out Of Memory)이 발생합니다.
      • high: 여유 메모리가 이 수준에 도달하면 메모리 회수가 중지됩니다.
    • 영향 : 더 높은 watermark_scale_factor 값을 사용하면 low와 min워터마크 사이에 더 큰 버퍼가 생성됩니다 . 이를 통해 kswapd가 시스템이 위험 상태에 도달하기 전에 점진적으로 메모리를 확보할 수 있는 시간이 더 늘어납니다.

일반적인 서버 워크로드에서는 메모리가 ‘콜드(cold)’ 상태가 되는 장기 실행 프로세스가 있을 수 있습니다. swappiness 값을 높이면 콜드 메모리를 다른 활성 프로세스로 교체하여 파일 캐시를 유지하는 것이 유리한 RAM을 확보할 수 있습니다.

스와핑 창을 일찍 이동하기 위해 min_free_kbyteswatermark_scale_factor 매개변수를 조정하면 kswapd가 메모리를 디스크로 오프로드할 수 있는 여유가 늘어나고 갑작 스러운 메모리 급증 시 OOM 킬이 방지됩니다.

스왑 테스트 및 결과

이러한 매개변수의 실제 영향을 파악하기 위해 일련의 스트레스 테스트를 설계했습니다.

테스트 설정

  • 환경 : Google Cloud의 GKE
  • 쿠버네티스 버전 : 1.33.2
  • 노드 구성 : n2-standard-2(8GiB RAM, pd-balanced 디스크에 50GB 스왑, 암호화 없음), Ubuntu 22.04
  • 워크로드 : 구성 가능한 속도로 메모리를 할당하고, 파일 캐시 압력을 생성하고, 다양한 메모리 액세스 패턴(무작위 대 순차)을 시뮬레이션하도록 설계된 맞춤형 Go 애플리케이션입니다.
  • 모니터링 : 사이드카 컨테이너가 매초 시스템 지표를 캡처합니다.
  • 보호 : 중요 시스템 구성 요소(kubelet, 컨테이너 런타임, sshd)는 각각의 cgroup에 memory.swap.max=0 설정하여 스왑이 방지되었습니다 .

테스트 방법론

서로 다른 swappiness 설정(0, 60, 90)을 가진 노드에서 스트레스 테스트 포드를 실행하고, 무거운 메모리 할당과 I/O 압력 하에서 결과를 관찰하기 위해 min_free_kbyteswatermark_scale_factor 매개변수를 다양 하게 변경했습니다.

스왑의 실제 동작 시각화

아래 그래프는 100MBps 스트레스 테스트에서 스왑이 작동하는 모습을 보여줍니다. 사용 가능한 메모리(“Memory Usage” 그래프)가 감소함에 따라 스왑 사용량( Swap Used (GiB))과 스왑 아웃 활동( Swap Out (MiB/s))이 증가합니다. 중요한 것은 시스템이 스왑에 더 많이 의존할수록 I/O 활동과 그에 따른 대기 시간(“CPU Usage” 그래프에서 IO Wait %)도 증가하여 CPU 스트레스를 나타낸다는 것입니다.

Kubernetes 노드의 CPU, 메모리, 스왑 사용률 및 I/O 활동을 보여주는 그래프

결과

기본 커널 매개변수(swappiness=60min_free_kbytes=68MB, watermark_scale_factor=10) 를 사용한 초기 테스트 결과, 메모리 부족으로 인해 OOM(Over-The-Once)이 발생하고 심지어 노드가 예기치 않게 재시작되는 현상이 빠르게 발생했습니다. 적절한 커널 매개변수를 선택하면 노드 안정성과 성능의 균형을 적절하게 유지할 수 있습니다.

swappiness의 영향

swappiness 매개변수는 커널이 익명 메모리 회수(스와핑)와 페이지 캐시 삭제 중 어떤 것을 선택할지에 직접적인 영향을 미칩니다. 이를 확인하기 위해, 한 포드에서 파일 캐시 압력을 생성하여 유지하고, 두 번째 포드에서 익명 메모리를 100MB/s로 할당하는 테스트를 실행하여 커널이 메모리 회수를 어떻게 선호하는지 살펴보았습니다.

내가 조사한 바에 따르면 명확한 상충관계가 드러났습니다.

  • swappiness=90: 커널은 파일 캐시를 유지하기 위해 비활성 익명 메모리를 사전에 스왑 아웃했습니다. 이로 인해 스왑 사용량이 높고 지속적으로 발생했으며, 의미있는 I/O 활동(“블록 아웃”)이 발생하여 CPU의 I/O 대기 시간이 급증했습니다.
  • swappiness=0: 커널은 파일 캐시 페이지를 삭제하여 스왑 사용량을 지연시키는 방식을 선호했습니다. 하지만 이것이 스왑을 비활성화하는 것은 아니라는 점을 이해하는 것이 중요합니다 . 메모리 부족 현상이 심할 때에도 커널은 익명 메모리를 디스크로 스왑했습니다.

선택은 워크로드에 따라 달라집니다. I/O 지연 시간에 민감한 워크로드의 경우, 낮은 스왑성이 바람직합니다. 크고 자주 액세스되는 파일 캐시에 의존하는 워크로드의 경우, 기본 디스크가 부하를 감당할 만큼 빠르다면 높은 스왑성이 유리할 수 있습니다.

추방 및 OOM 종료를 방지하기 위한 워터마크 조정

제가 직면한 가장 심각한 문제는 빠른 메모리 할당과 Kubelet의 제거 메커니즘 간의 상호 작용이었습니다. 의도적으로 메모리를 과도하게 할당하도록 구성된 테스트 포드가 메모리를 높은 속도(예: 300~500MBps)로 할당하자 시스템의 여유 메모리가 빠르게 고갈되었습니다.

기본 워터마크를 사용하면 회수 버퍼가 너무 작았습니다. kswapd가 스와핑을 통해 충분한 메모리를 확보하기 전에 노드가 위험 상태에 도달하여 두 가지 잠재적인 결과를 초래했습니다.

  1. Kubelet 추방 Kubelet의 추방 관리자(eviction manager)가 memory.available 임계값보다 낮은 값을 감지하면 Pod를 추방합니다.
  2. OOM 킬러 일부 고위험 시나리오에서는 추방이 완료되기 전에 OOM 킬러가 활성화되어, 압력의 근원이 아닌 우선순위가 더 높은 포드를 파괴하는 경우가 있었습니다.

이를 완화하기 위해 워터마크를 조정했습니다.

  1. min_free_kbytes를 512MiB로 증가 : 이를 통해 커널이 훨씬 일찍 메모리를 회수하기 시작하여 더 큰 안전 버퍼를 제공합니다.
  2. watermark_scale_factor를 2000으로 증가 : 이로 인해 와 워터마크 사이의 간격이 넓어졌고 (테스트 노드의 lowhigh/proc/zoneinfo에서 약 337MB에서 약 591MB로 ), 스와핑 창이 효과적으로 늘어났습니다.

이 조합은 kswapd에 더 넓은 운영 영역과 메모리 급증 시 페이지를 디스크로 교체할 수 있는 시간을 늘려주어, 테스트 실행에서 조기 추방과 OOM 종료를 모두 성공적으로 방지했습니다.

표는 /proc/zoneinfo(비NUMA 노드) 의 워터마크 수준을 비교합니다 .

min_free_kbytes=67584KiB and watermark_scale_factor=10min_free_kbytes=524288KiB and watermark_scale_factor=2000
Node 0, zone Normal
  pages free 583273
  boost 0
  min 10504
  low 13130
  high 15756
  spanned 1310720
  present 1310720
  managed 1265603
Node 0, zone Normal
  pages free 470539
  min 82109
  low 337017
  high 591925
  spanned 1310720
  present 1310720
  managed 1274542

아래 그래프는 커널 버퍼 크기와 스케일링 인자가 시스템의 메모리 부하 대응 방식을 결정하는 데 중요한 역할을 한다는 것을 보여줍니다. 이러한 매개변수를 적절히 조합하면 시스템은 스왑 공간을 효과적으로 활용하여 메모리 축출을 방지하고 안정성을 유지할 수 있습니다.

스왑, 메모리 사용량 및 제거 영향의 차이점을 보여주는 다양한 min_free_kbytes 설정의 나란히 비교

위험성 및 권장 사항

쿠버네티스에서 스왑을 활성화하는 것은 강력한 도구이지만, 신중한 조정을 통해 관리해야 하는 위험이 따릅니다.

  • 성능 저하 위험: 스와핑은 RAM 접근보다 훨씬 느립니다. 애플리케이션의 활성 작업 세트가 스왑 아웃되면 높은 I/O 대기 시간(스래싱)으로 인해 성능이 크게 저하됩니다. 성능 향상을 위해 SSD 지원 스토리지를 스왑에 추가하는 것이 좋습니다.
  • 메모리 누수 마스킹 위험: 스왑은 애플리케이션의 메모리 누수를 은폐할 수 있으며, 그렇지 않으면 OOM(Out Of Memory)으로 인해 빠르게 종료될 수 있습니다. 스왑을 사용하면 누수가 발생한 애플리케이션이 시간이 지남에 따라 노드 성능을 서서히 저하시켜 근본 원인을 진단하기 어렵게 만들 수 있습니다.
  • Kubelet은 노드의 메모리 부족을 사전에 모니터링하고 파드를 종료하여 리소스를 회수합니다. 부적절한 튜닝은 Kubelet이 파드를 정상적으로 제거하기 전에 OOM(Out Of Memory)으로 인해 종료될 수 있습니다. Kubelet의 제거 메커니즘이 효과적으로 작동하려면 적절한 min_free_kbytes 구성이 필수적입니다.

쿠버네티스 컨텍스트

커널 워터마크와 kubelet 제거 임계값은 노드에 일련의 메모리 압력 영역을 생성합니다. kubernetes 관리 제거가 OOM(Out Of Memory Object)으로 인해 종료되기 전에 실행되도록 eviction-threshold 매개변수를 조정해야 합니다.

효과적인 스왑 활용을 위한 선호 임계값

다이어그램에서 볼 수 있듯이 이상적인 구성은 커널이 사용 가능한 메모리가 Eviction/Direct Reclaim 영역으로 떨어지기 전에 스왑을 통해 메모리 압력을 처리할 수 있도록 충분히 큰 ‘스와핑 영역'( highmin 워터마크 사이)을 만드는 것입니다.

이러한 결과를 바탕으로, 스왑이 활성화된 Linux 노드의 시작점으로 다음을 권장합니다. 이 내용을 실제 워크로드와 비교 분석해 보시기 바랍니다.

  • vm.swappiness=60: Linux 기본값은 범용 워크로드에 적합한 시작점입니다. 하지만 이상적인 값은 워크로드에 따라 달라지며, 스왑에 민감한 애플리케이션은 더욱 세심한 튜닝이 필요할 수 있습니다.
  • vm.min_free_kbytes=500000(500MB): 노드에 적절한 안전 버퍼를 제공하기 위해 이 값을 적당히 높은 값(예: 전체 노드 메모리의 2-3%)으로 설정합니다.
  • vm.watermark_scale_factor=2000: kswapd가 작업할 때 더 큰 창을 만들어 갑작스러운 메모리 할당 급증 시 OOM 종료를 방지합니다.

쿠버네티스 클러스터에서 스왑을 처음 설정할 때 테스트 환경에서 자체 워크로드로 벤치마크 테스트를 실행하는 것이 좋습니다. 스왑 성능은 CPU 부하, 디스크 유형(SSD 대 HDD), I/O 패턴 등 다양한 환경 차이에 따라 민감하게 반응할 수 있습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

You May Also Like
Read More

/sysctl/user.txt

출처: https://www.kernel.org/doc/Documentation/sysctl/user.txt proc/sys/user/* 커널 버전 4.9.0에 대한 문서(c) 2016 Eric Biederman ebiederm@xmission.com 이 파일에는 /proc/sys/user의 sysctl 파일에 대한…
Read More

Zram 성능 분석

Blog를 보다가 관심 가는 글이 보여서 AI번역+약간 교정해 보았습니다.출처: https://notes.xeome.dev/notes/Zram 소개 Zram은 압축된 가상 메모리 블록 장치를 활용하여…
Read More

/sysctl/fs.txt

/proc/sys/fs/*에 대한 문서 커널 버전 2.2.10(c) 1998, 1999, Rik van Riel riel@nl.linux.org(c) 2009, Shen Fengshen@cn.fujitsu.com 일반 정보 및…
Read More

/sysctl/vm.txt

RHCA 달리던 중에 "RH442 Performance Tuning Linux in Physical Virtual and Cloud"라는 과목이 있습니다. 보던 중 sysctl을 이용해서…
Read More

/sysctl/README

RHCA 달리던 중에 "RH442 Performance Tuning Linux in Physical Virtual and Cloud"라는 과목이 있습니다. 보던 중 sysctl을 이용해서…
Read More

Documentation/sysctl/net.txt

출처: https://www.kernel.org/doc/Documentation/sysctl/net.txt"busy poll timeout" 번역 표현이 참 어색합니다. 적당한 한글 표현을 못 찾겠습니다. proc/sys/net/*에 대한 문서(c) 1999 Terrehon…