전용 서버를 위한 NUMA 인식 및 CPU 고정

16분 소요 - 2026년 6월 16일

hero section cover
목차
  • 전용 서버를 위한 NUMA 인식 및 CPU 고정
  • 다중 소켓 서버에서 NUMA의 작동 방식
  • Linux에서 NUMA 토폴로지 검사하기
  • CPU 고정 및 메모리 정책
  • 워크로드에 따른 전략 선택
  • 확인해야 할 BIOS 및 커널 설정
공유

NUMA 토폴로지를 확인하고 Linux 워크로드를 적절한 코어와 메모리에 할당하는 방법. numactl, taskset, systemd, BIOS 설정 및 워크로드별 전략을 다룹니다.

전용 서버를 위한 NUMA 인식 및 CPU 고정

다중 소켓 서버에서는 프로세스가 실행되는 위치와 메모리가 상주하는 위치가 서로 다른 문제이며, 이 둘의 동기화가 깨지는 것은 성능을 제대로 발휘하지 못하는 가장 흔한 원인 중 하나입니다. NUMA 인식과 CPU 고정 기능은 이 문제를 해결하는 두 가지 핵심 요소입니다. 이 게시물에서는 NUMA의 작동 원리, Linux에서 NUMA를 확인하는 방법, 그리고 데이터베이스, AI 훈련, 지연 시간에 민감한 서비스를 위해 워크로드를 올바르게 고정하는 방법을 다룹니다.

다중 소켓 서버에서 NUMA의 작동 방식

NUMA(Non-Uniform Memory Access) 노드는 전용 메모리 컨트롤러를 통해 로컬 RAM 블록에 바인딩된 CPU 코어 그룹입니다. 2소켓 서버에는 일반적으로 두 개의 노드가 있습니다. 모든 코어는 어떤 주소든 읽을 수 있지만, 로컬 액세스 시간은 대략 80ns인 반면, 인텔의 UPI나 AMD의 인피니티 패브릭을 통한 소켓 간 이동 시간은 약 130~150ns입니다. 소켓 수가 더 많은 대규모 시스템에서는 최악의 경우 노드 간 이동 시간이 250ns를 넘을 수 있습니다.

대역폭도 동일한 패턴을 보입니다. 2소켓 사파이어 래피즈(Sapphire Rapids) 시스템은 코어가 로컬 메모리에 접근할 때 약 600GB/s의 속도를 유지할 수 있지만, 소켓 간 링크 속도는 그 일부에 불과하므로 이 링크를 통과하는 트래픽은 금방 병목 현상이 발생합니다. 다중 코어 프로세서는 이 문제를 더 세분화합니다. 인텔의 Sub-NUMA Clustering(SNC)과 AMD의 Nodes Per Socket(NPS)은 각 소켓을 여러 NUMA 도메인으로 분할하므로, "2소켓" 시스템도 리눅스에는 4개 또는 8개의 노드로 쉽게 인식될 수 있습니다.

NUMA를 인식하지 못하면, 리눅스 스케줄러는 스레드의 작업 세트가 원래 노드에 남아 있는 동안에도 소켓 간에 스레드를 아무렇지 않게 이동시킵니다. 이후의 모든 액세스는 원격 액세스가 됩니다. 눈에 띄는 증상은 높은 CPU 사용률과 낮은 실제 처리량인데, 이는 코어들이 메모리를 기다리는 데 시간을 소비하기 때문입니다. I/O 장치는 이 문제를 더욱 악화시킵니다. GPU나 NIC는 하나의 NUMA 노드에 속하는 특정 PCIe 루트에 연결됩니다. 이를 공급하는 프로세스가 다른 소켓에서 실행되면, 모든 DMA 전송이 인터커넥트를 통과하게 됩니다.

Linux에서 NUMA 토폴로지 검사하기

다음 네 가지 도구만으로도 필요한 거의 모든 기능을 다룰 수 있습니다:

  • lscpu 소켓 및 노드 요약 정보를 빠르게 확인하려면.
  • numactl --hardware 노드 메모리 총량 및 노드 간 거리 행렬을 확인하려면.
  • numastat 프로세스별 히트/미스 카운터를 확인하려면.
  • lstopo (hwloc에서) 캐시 계층 구조 및 PCIe 장치 근접성 확인용.

다음으로 시작하세요 numactl --hardware. 여기에는 각 노드, 해당 노드에 속한 코어 및 메모리, 그리고 거리 행렬이 나열됩니다. 값이 10이면 로컬, 20 이상이면 원격입니다. 멀티 소켓 시스템에서 단일 노드만 표시된다면, BIOS에서 노드 인터리빙(Node Interleaving)이 활성화되어 토폴로지가 숨겨지고 있는 것이므로, 먼저 이를 수정하십시오(아래 참조).

특정 프로세스의 경우, numastat -p <PID> 는 메모리가 실제로 할당된 위치를 상세히 보여줍니다. 다음 네 가지 카운터가 중요합니다:

  • numa_hit: 대상 노드에 할당된 메모리. 이 값이 높을수록 좋습니다.
  • numa_miss: 대상 노드가 가득 차서 할당이 다른 곳으로 넘쳐난 경우.
  • numa_foreign: 다른 노드가 로컬 할당을 시도했으나 실패한 경우로, 메모리 부하를 나타냅니다.
  • other_node: 프로세스가 실행 중인 노드 이외의 노드에 할당된 페이지. 이 수치가 높으면 핀닝(pinning)이 제대로 이루어지지 않았다는 전형적인 징후입니다.

GPU 또는 NIC 워크로드의 경우, 다음 명령을 실행하십시오. lstopo-no-graphics 명령어를 실행하여 각 PCIe 장치가 어떤 NUMA 노드에 연결되어 있는지 확인하십시오. 장치를 구동하는 코어가 다른 노드에 있다면, 이것이 가장 먼저 해결해야 할 문제입니다.

CPU 고정 및 메모리 정책

CPU 고정(또는 CPU 어피니티)은 프로세스를 특정 코어에 바인딩하여 스케줄러가 해당 프로세스를 이동할 수 없도록 합니다. 하지만 이것만으로는 충분하지 않습니다. 리눅스는 기본적으로 '첫 터치(first-touch)' 메모리 정책을 사용하기 때문입니다. 즉, 페이지는 해당 노드에 처음으로 쓰기 작업이 수행되는 곳에서 할당됩니다. 스레드가 고정되기 전에 잘못된 노드에서 시작되면, 그 스레드의 메모리는 해당 노드에 남아 있게 됩니다. 배치와 할당을 함께 제어해야 합니다.

일반적인 경우를 다루는 세 가지 도구는 다음과 같습니다:

도구제어 항목사용 목적
tasksetCPU 코어 전용기존 프로세스에 대한 빠른 일회성 바인딩
numactlCPU 코어 및 메모리엄격한 로컬리티를 가진 워크로드 실행
systemdCPU 코어 및 메모리, 지속적재부팅 시에도 고정되어야 하는 서비스

numactl 다음과 같은 네 가지 메모리 정책을 지원합니다:

  • --membind=N: 노드 N에만 할당하고, 가득 차면 실패합니다.
  • --preferred=N: 노드 N을 우선적으로 사용하며, 필요한 경우 다른 노드로 전환합니다.
  • --interleave=all: 대역폭을 균등하게 분배하기 위해 노드 간 라운드 로빈 방식을 사용합니다.
  • --localalloc: 실행 중인 CPU가 있는 노드에 할당합니다.

워크로드를 특정 노드에 고정하기

먼저, 대상 노드에 속한 코어를 확인합니다:

numactl --hardware

그런 다음 해당 노드에 바인딩된 애플리케이션을 코어와 메모리 모두에 대해 실행합니다:

numactl --cpunodebind=0 --membind=0 ./your_application

이미 실행 중인 프로세스의 경우, 다음 명령어로 CPU 어피니티를 조정합니다: taskset:

taskset -cp 0-7 <PID>

재부팅 후에도 유지되도록 하려면 systemd 유닛에 설정합니다:

[Service]
CPUAffinity=0-7
NUMAPolicy=bind
NUMAMask=0

재로드 후 재시작합니다:

sudo systemctl daemon-reload && sudo systemctl restart <service>

수동으로 할당할 때는 커널의 자동 부하 분산 기능을 비활성화하여 할당 설정과 충돌하지 않도록 하십시오:

sysctl -w kernel.numa_balancing=0

다음에 추가하여 /etc/sysctl.conf 에 추가하여 설정을 영구화하십시오. 그런 다음 실제 워크로드를 몇 분간 실행하며 numastat -p <PID> 실제 워크로드를 몇 분간 실행하여 확인하십시오. 만약 other_node 값이 0 근처를 유지한다면, 고정 설정이 제대로 적용되고 있는 것입니다.

워크로드에 따른 전략 선택

적절한 정책은 워크로드가 낮은 지연 시간의 이점을 더 많이 얻는지, 아니면 모든 노드에 걸친 총 대역폭의 이점을 더 많이 얻는지 여부에 따라 달라집니다.

워크로드정책이유
데이터베이스(PostgreSQL, MySQL, SQL Server)--cpunodebind + --membind대용량 공유 버퍼, 지연 시간에 민감한 쿼리 경로
인메모리 캐시 (Redis, Memcached)단일 노드 바인딩모든 작업이 RAM 액세스이므로 원격 지연 시간이 즉시 나타남
AI/ML 훈련 및 추론GPU의 NUMA 노드에 바인딩PCIe 루트를 가로지르는 텐서 전송 방지
분석 (Spark, Elasticsearch)--interleave=all대규모 작업 세트는 모든 노드에 걸친 대역폭이 필요합니다
지연 시간에 민감한 API, 트레이딩엄격한 핀 + IRQ 어피니티피크 처리량보다 예측 가능성이 더 중요
네트워크 집약적 (RoCEv2, InfiniBand)NIC의 NUMA 노드에 핀 고정, IRQ 전용 코어 할당인터럽트 처리를 로컬에서 수행하고 애플리케이션 스레드의 방해받지 않도록 함

특히 GPU 워크로드의 경우, lstopo 명령어를 실행하여 GPU가 위치한 NUMA 노드를 확인한 후, numactl --cpunodebind=N --membind=N 를 사용하여 해당 N에 대해 실행합니다. 기본 스케줄러 배치는 종종 잘못되므로, 이는 멀티 소켓 GPU 서버에서 가장 쉽게 얻을 수 있는 이점 중 하나입니다.

두 소켓에 걸쳐 있는 HPC 및 MPI 워크로드의 경우, 모든 것을 인터리빙하는 대신 localalloc 를 사용하여 각 랭크를 단일 노드에 고정하십시오. 모든 것을 인터리빙하지 마십시오. 각 랭크는 로컬 메모리를 할당받으며, 병렬 처리는 랭크 수준에서 이루어집니다.

실용적인 팁 하나: 단일 노드에 고정할 경우, 해당 노드에 2~4GB의 여유 공간을 남겨두십시오. 용량이 거의 가득 찬 노드는 메모리 회수(reclaim)를 유발하며, 이는 절약하려던 지연 시간을 오히려 증가시킵니다.

확인해야 할 BIOS 및 커널 설정

도구의 출력 정확도는 펌웨어가 노출하는 토폴로지의 정확도에 달려 있습니다. 확인해야 할 몇 가지 설정은 다음과 같습니다:

  • 노드 인터리빙(Node Interleaving): 비활성화하십시오. 이 기능이 활성화되면 BIOS는 모든 메모리를 단일 플랫 풀로 표시하며 OS에서 NUMA를 완전히 숨깁니다. numactl --hardware 이 경우 멀티 소켓 시스템에서 노드가 하나로 표시됩니다.
  • Sub-NUMA 클러스터링(Intel) 또는 소켓당 노드 수(AMD): 더 세밀한 로컬리티를 원할 경우 고코어 프로세서에서 활성화하십시오. 재부팅 후 lscpu 재부팅 후 확인됩니다.
  • vm.zone_reclaim_mode: 대부분의 프로덕션 서버에서는 0으로 설정하십시오. 0이 아닌 값은 원격 할당 대신 로컬 메모리를 적극적으로 회수하므로, 유용한 페이지 캐시를 제거할 수 있습니다.
  • kernel.numa_balancing: 범용 워크로드의 경우 켜두십시오. 수동으로 고정(pinning)할 때는 끄십시오. 자동 밸런서는 정책과 충돌하는 방식으로 페이지와 스레드를 마이그레이션할 수 있습니다.

BIOS, 커널 매개변수 및 IRQ 어피니티를 제어할 수 있는 베어 메탈 환경에서 NUMA 튜닝을 수행하는 경우, 하이퍼바이저 추상화를 우회할 필요 없이 위의 모든 설정을 적용할 수 있습니다. 이것이 바로 이러한 작업이 클라우드 VM보다 전용 하드웨어에서 더 쉬운 주된 이유입니다.

전체 루트 액세스 권한이 있는 멀티 소켓 전용 서버의 경우, FDC의 전용 서버를 참조하십시오.

블로그

이번 주 추천

더 많은 기사
Linux 서버 워크로드 최적화를 위한 튜닝된 프로필

Linux 서버 워크로드 최적화를 위한 튜닝된 프로필

GPU, 데이터베이스 및 고대역폭 Linux 서버를 위한 튜닝된 프로필을 선택, 적용 및 사용자 지정하는 방법과 예제 및 Ansible 배포 팁을 제공합니다.

16분 소요 - 2026년 6월 9일

VPS를 위한 Linux OOM 킬러 튜닝: 실용적인 가이드

12분 소요 - 2026년 6월 8일

더 많은 기사
background image

질문이 있거나 맞춤형 솔루션이 필요하신가요?

icon

유연한 옵션

icon

글로벌 도달 범위

icon

즉시 배포

icon

유연한 옵션

icon

글로벌 도달 범위

icon

즉시 배포