K8s GPU 스케줄링 완전판

하드웨어(PCIe · DMA · NVLink)부터 스케줄러(device plugin · DRA · gang scheduling), 2×3090 실습까지 — 학습 세션 종합 문서 · 2026-06

READ ME · 읽는 법

문서 구성과 표기

대상 독자. Kubernetes·GPU 배경지식이 없어도 따라올 수 있도록 1·2장을 배경지식 장으로 두었다. 두 분야에 익숙하다면 4장부터 시작해도 되고, 모르는 용어는 부록 D 용어집에서 찾는다.

색상 규약(문서 전체 공통). 보라 = 벤더 컴포넌트·GPU 청록 = kubelet · NVLink 파랑 = API server · PCIe 주황 = 스케줄러 호박 = 할당 결과 · 활성 경로

인터랙티브 패널. [이전]·[다음] 버튼으로 단계를 진행하면 도식의 해당 경로가 켜진다. 6장의 시뮬레이터는 모드를 전환해 같은 상황을 두 방식으로 비교한다.

검토 반영. 원 대화 대비 정밀화·정정 5건이 본문에 반영되어 있으며(호박색 노트 박스), 목록은 부록 A에 있다.

TRACK A · KUBERNETES TRACK B · HARDWARE 1장 · K8s 상태 흐름허브-스포크 · 장부 계산 4장 · Device plugin카운터 모델과 한계 5장 · DRA속성 · CEL · 동시 결정 2장 · 컴퓨터 구조CPU 패키지 · PCIe · DMA 3장 · NVLink · island물리 구조 · 대역폭 절벽 6장 · Gang원자적 배치 7장 · 2×3090 실습직접 측정으로 재현
학습 지도 — 쿠버네티스 트랙(1→4→5→6장)과 하드웨어 트랙(2→3장)이 6장에서 만나고, 7장에서 손으로 확인한다
PART I — 기반: 두 세계의 기본기
CH 1 · KUBERNETES BASICS

K8s 상태 흐름의 기본 — 모든 것은 게시판을 경유한다

배경지식 장. GPU 스케줄링의 모든 설계 결정은 이 장의 원칙 위에 서 있다.

Kubernetes(K8s)는 여러 대의 서버(노드)를 하나의 자원 풀로 묶어, 컨테이너(Pod)를 어느 노드에서 실행할지 자동으로 결정·유지하는 시스템이다. 등장인물은 넷이면 충분하다: 상태의 중앙 게시판인 API server, 배치를 결정하는 kube-scheduler, 각 노드에 상주하며 실제로 컨테이너를 띄우는 kubelet, 그리고 게시판의 저장소인 etcd.

1.1 허브-스포크 원칙

K8s의 제1원칙: 컴포넌트들은 서로 직접 통신하지 않는다. 모두가 API server의 오브젝트(Node, Pod 등)를 읽고(get), 쓰고(update), 변경을 구독(watch)하는 방식으로만 협업한다. 스케줄러가 kubelet에게 명령을 "보내는" 일은 없다 — 스케줄러가 오브젝트에 결정을 써두면, kubelet이 그것을 읽어간다. 예외는 노드 내부의 로컬 통신(kubelet ↔ 컨테이너 런타임, kubelet ↔ device plugin)뿐이다.

API server오브젝트 게시판 사용자 (kubectl)Pod 생성 요청 kube-scheduler배치 결정 kubelet ×N노드마다 1개 controller들Deployment 등 관리
모든 상태 변화가 API server를 경유한다. watch = 오브젝트 변경을 폴링 없이 구독하는 메커니즘

1.2 kubelet의 자원 발견 — 실물 담당

kubelet은 시작할 때 내장 cAdvisor로 /proc/cpuinfo, /proc/meminfo를 직접 읽어 머신의 CPU 코어 수와 메모리 총량을 파악하고, 이를 Node 오브젝트의 status에 기록한 뒤 주기적으로 갱신한다. 생존 신호는 별도의 Lease 오브젝트로 보낸다.

필드의미
status.capacity머신의 물리 총량CPU 64, 메모리 512Gi
status.allocatablePod에 배정 가능한 양 = capacity − 시스템·K8s 데몬 예약분CPU 62, 메모리 500Gi

1.3 스케줄러의 장부 계산 — 실측이 아니다

스케줄러는 노드의 실제 사용량을 보지 않는다. 보는 것은 장부다: 노드의 allocatable에서 그 노드에 이미 배정된 Pod들의 requests 합을 뺀 잔여량과, 새 Pod의 requests를 비교한다. 예: allocatable CPU 62, 기존 Pod들의 requests 합 40이면 잔여 22 — 새 Pod이 request 4를 내면 통과. 실제 CPU 사용률이 10%든 90%든 이 계산에는 등장하지 않는다.

결정은 binding으로 기록된다: Pod 오브젝트의 spec.nodeName에 노드 이름을 써넣는 것. 그러면 해당 노드의 kubelet이 watch로 "내게 배정된 Pod"을 발견하고, 최종 확인(admission) 후 컨테이너를 실행한다. 결정조차 오브젝트 쓰기라는 점이 허브-스포크 원칙의 일관성이다.

1.4 GPU는 왜 이 구조에 못 들어가는가

CPU·메모리는 커널이 표준 인터페이스(/proc, cgroup)를 제공하므로 kubelet이 스스로 발견하고 격리·회수까지 관리할 수 있다. GPU는 다르다 — 발견·할당·헬스가 전부 벤더 드라이버 영역이고, 커널 표준 경로가 없다. kubelet에 NVIDIA 전용 코드를 넣을 수는 없으므로, K8s는 "발견과 할당만 벤더 에이전트에 위임하는 확장점"을 만들었다. 그것이 4장의 device plugin이다.

CH 1 핵심 압축
  1. K8s 협업 = API server 오브젝트의 읽기/쓰기/watch. 직접 통신은 노드 로컬(kubelet↔plugin·런타임)뿐.
  2. kubelet은 실물 발견(capacity/allocatable), 스케줄러는 장부 계산(allocatable − Σrequests) — 실측 아님.
  3. GPU는 커널 표준 인터페이스가 없어 kubelet이 스스로 발견 불가 → 벤더 위임 확장점(device plugin)이 필요.
CH 2 · COMPUTER ARCHITECTURE

컴퓨터 구조 기초 — CPU 패키지, PCIe, DMA

배경지식 장. "GPU 2장 달라"가 왜 스케줄링 문제가 되는지는 데이터가 다니는 물리 경로를 알아야 보인다.

2.1 CPU 패키지 ≠ CPU 코어

흔히 "CPU"라 부르는 칩(패키지) 안에는 세 가지가 함께 산다: 코어(연산 유닛), 메모리 컨트롤러(RAM과의 인터페이스), PCIe root complex(모든 PCIe 링크의 뿌리). 과거에는 뒤의 둘이 메인보드의 별도 칩(노스브리지)이었으나 현대 CPU에 전부 통합되었다. 이 사실이 이 장의 모든 질문 — "데이터가 CPU를 지나가는가?" — 의 답을 결정한다.

2.2 PCIe — 공유 버스가 아니라 점대점 트리

RAM은 메모리 컨트롤러에 전용 버스로 직결되고, 그 외의 모든 고속 장치(GPU, NVMe SSD, NIC)는 PCIe로 CPU에 연결된다. 핵심 성질: PCIe는 옛 PCI처럼 한 가닥에 여러 장치가 매달리는 공유 버스가 아니라, 각 장치가 root complex와 1:1 전용 링크를 갖는 점대점 트리다. 링크의 폭이 lane(차선)이며, 슬롯 규격 x1/x4/x8/x16이 lane 수를 뜻한다.

세대lane 1개 (단방향)x16 슬롯 (단방향)비고
PCIe Gen3~1 GB/s~16 GB/s세대마다 lane당 속도 2배
PCIe Gen4~2 GB/s~32 GB/sRTX 3090의 연결 규격
PCIe Gen5~4 GB/s~64 GB/sH100 세대 서버

lane은 한정 자원이다: 컨슈머 CPU가 ~20–24개, 서버 CPU가 ~128개를 제공한다. GPU 여러 장 + NIC + NVMe를 다는 서버 설계는 결국 이 lane을 어떻게 분배하느냐의 문제이며, 대형 GPU 서버는 CPU 직결 대신 PCIe 스위치 계층을 두어 lane을 확장하기도 한다.

2.3 RAM→VRAM 전송 — "지나간다"와 "일한다"의 분리

모델 weight를 RAM에서 GPU의 VRAM으로 올릴 때, 데이터는 구조상 반드시 CPU 패키지의 실리콘을 통과한다 — RAM의 출구(메모리 컨트롤러)와 GPU로 가는 길의 입구(root complex)가 둘 다 그 안에 있기 때문이다. 그러나 복사 노동은 코어가 하지 않는다. 이것이 DMA(Direct Memory Access)다: GPU 안의 전용 복사 하드웨어인 DMA 엔진(copy engine)이 PCIe read 요청을 직접 발행해 데이터를 끌어가고, 코어는 "어디서 어디로 얼마나"를 적은 지시서(디스크립터) 작성과 완료 통보에만 등장한다. 아래 스테퍼로 5단계를 따라가 보자.

CPU 패키지 코어 (연산 유닛) 메모리 컨트롤러 DDR 인터페이스 Root complex PCIe 트리의 뿌리 RAM DDR DIMM PCIe x16 전용 x4 DMI (x4급) GPU (그래픽카드) VRAM DMA 엔진 copy engine NVMe SSD 자기 전용 lane 칩셋 USB·SATA 허브

2.4 pinned memory — 예외적으로 코어가 일하는 경우

일반(pageable) 메모리는 커널이 페이지를 언제든 옮기거나 스왑할 수 있어 DMA 엔진이 직접 읽기에 안전하지 않다. 그래서 CUDA는 내부의 고정(pinned) 스테이징 버퍼로 코어가 한 번 memcpy를 수행한 뒤 DMA를 시작한다 — 복사가 두 번 일어나는 셈이다. cudaHostAlloc이나 PyTorch DataLoader의 pin_memory=True는 처음부터 고정 메모리를 할당해 이 스테이징 단계를 제거하고 DMA 직행을 만드는 옵션이다.

2.5 DMA가 가능한 근본 이유 — 공유 주소 공간

DMA의 "read 요청"은 마법이 아니다. PCIe 장치들은 CPU와 같은 물리 주소 공간에 매핑되며(MMIO), GPU는 RAM의 주소를, CPU는 VRAM의 주소(그 창의 크기가 Resizable BAR)를 서로 load/store할 수 있다. DMA란 이 공유 주소 공간 위에서 전용 엔진이 수행하는 대량 load/store일 뿐이다. 이 원리를 확장하면 GPU↔NVMe(GPUDirect Storage), GPU↔NIC(GPUDirect RDMA)처럼 RAM 경유 자체를 건너뛰는 장치 간 직접 전송이 된다 — 5장의 "NIC과 GPU를 같은 스위치에 정렬"이라는 요구가 여기서 나온다.

CH 2 핵심 압축
  1. CPU 패키지 = 코어 + 메모리 컨트롤러 + root complex. PCIe는 RC를 뿌리로 한 점대점 트리(공유 버스 아님).
  2. RAM→VRAM은 CPU 실리콘을 지나가지만, 복사는 GPU의 DMA 엔진이 한다 — 코어는 지시서와 완료 통보에만 등장.
  3. pinned memory = 스테이징 복사 제거. DMA의 본질 = MMIO 공유 주소 공간 위의 load/store.
CH 3 · NVLINK

NVLink — 물리 구조와 island

"어차피 전부 전기 신호"라는 직관은 옳다. 차이는 어느 구리선을, 어느 컨트롤러가, 어느 프로토콜로 쓰느냐다.

3.1 GPU 다이의 두 인터페이스

GPU 칩(다이)은 송수신 물리 계층 회로(PHY)를 두 종류 내장한다. PCIe PHY는 카드 아랫면 단자를 통해 슬롯 → 메인보드 → CPU로 이어지는 회선을 담당하고, NVLink PHY는 별도의 핀으로 나와 피어 GPU와의 전용 회선을 담당한다. NVLink는 소프트웨어 기능이 아니라 이 하드웨어이므로, 회로가 없는 카드에서는 어떤 드라이버로도 만들 수 없다.

3.2 형태 ① — 브리지 (컨슈머·워크스테이션)

RTX 3090 같은 카드에서는 NVLink PHY가 카드 윗면의 금색 핑거로 노출된다. 두 카드를 나란히 꽂고, 핑거 위에 브리지라는 작은 PCB를 압입하면 GPU↔GPU 직결 회선이 완성된다 — 메인보드를 전혀 경유하지 않는다. 카드는 여전히 슬롯에 꽂혀 있는데, 그 슬롯(PCIe)은 CPU와의 통신·전원 일부를 계속 담당한다. 즉 두 회선이 동시에 존재한다.

NVLink 브리지 — 메인보드 미경유 직결 GPU 0 GPU 1 GPU 다이 GPU 다이 NVLink 핑거 (윗면) 다이에 PHY 2종 아랫면 = PCIe 단자 메인보드 (슬롯 = PCIe x16) 카드는 슬롯으로 CPU와 통신, 윗면 핑거끼리는 브리지로 직결
브리지 구성 측면도 — 청록 = NVLink 회선, 파랑 = PCIe 회선

3.3 형태 ② — SXM + NVSwitch (데이터센터)

A100/H100급 서버는 8장을 일일이 브리지로 이을 수 없으므로 카드라는 형태 자체를 버린다. GPU는 SXM 모듈로 전용 베이스보드에 소켓 장착되고, NVLink 배선은 보드에 구리선으로 인쇄되어 NVSwitch라는 교환 칩에 모인다 — 케이블도 브리지도 없이 보드 자체가 배선이다. PCIe는 베이스보드에서 위로, CPU 호스트 보드와의 연결에만 쓰인다.

CPU 호스트 보드CPU ×2 + RAM PCIe GPU 베이스보드 (HGX 류) GPU 0SXM 모듈 GPU 1SXM 모듈 GPU 2SXM 모듈 GPU 3SXM 모듈 NVSwitchall-to-all 교환기 NVSwitchall-to-all 교환기 NVLink 배선은 보드에 인쇄된 구리선 — 케이블·브리지 없음 GPU↔CPU는 PCIe로만, GPU↔GPU는 전부 NVSwitch 경유 직결
SXM/NVSwitch 구조 — 실제 HGX 보드는 GPU 8개 + NVSwitch 4개 이상이며 그림은 축약

3.4 두 길의 폭 — 대역폭 비교

이제 아래 도식으로 두 회선의 폭을 비교한다. 분산 학습이 매 스텝 GPU 간에 gradient 수~수십 GB를 교환한다는 사실(6장)을 염두에 두면 이 숫자 차이가 곧 학습 속도 차이다.

CH 3-C · PCIe vs NVLink

PCIe와 NVLink — 대역폭의 두 세계

GPU 간 데이터가 어느 길로 다니는지가 분산 학습 성능을 결정한다. 파란 길(PCIe)과 초록 길(NVLink)의 폭 차이 ~10배.

RAM DDR 메모리 CPU 메모리 컨트롤러 · PCIe root 메모리 버스 PCIe Gen4 x16 · 단방향 ~32GB/s GPU 0 VRAM + 연산 코어 GPU 1 VRAM + 연산 코어 NVLink 집계 ~600GB/s · PCIe 경로: GPU0 → CPU(root) 경유 → GPU1 — 양 끝 ~32GB/s 한도 + 다른 장치 트래픽과 경합 · NVLink: CPU를 우회한 GPU 직결 — PCIe 대비 ~10배 이상. 이 절벽이 "NVLink island" 경계를 만든다

개인 연결점: 홈랩 RTX 3090은 PCIe Gen4 x16으로 CPU에 연결되어 있다(~32GB/s). 3090 두 장을 NVLink 브리지로 묶으면 ~112.5GB/s — 같은 원리의 컨슈머 버전. A100 8장 서버는 NVSwitch로 all-to-all 직결.

검토 노트 ① — 비교 기준 통일. "~600GB/s"(A100 NVLink)는 전 링크 양방향 합산 집계이고, "~32GB/s"(PCIe Gen4 x16)는 단방향 수치다. 기준을 맞추면 양방향 64 대 600으로 약 9–10배가 정확한 표현이다. 관례적으로 "~10배"라 부르며, 단방향끼리 비교하면 ~19배까지 벌어진다.

3.5 island — 대역폭은 "쌍"의 속성이다

island = NVLink(또는 NVSwitch)로 상호 직결된 GPU 집합. 여기서 흔한 오해 하나를 바로잡는다: 대역폭은 데이터의 속성이 아니라 "통신하는 GPU 쌍 사이에 어떤 회선이 물리적으로 존재하는가"의 속성이다. "경계를 넘는 순간 추락한다"는 표현은 데이터가 이동 중에 떨어진다는 뜻이 아니라, 쌍이 island 경계를 가로지르면 그 둘 사이에는 NVLink 회선 자체가 깔려 있지 않아 느린 길이 유일한 길이라는 뜻이다. 아래에서 쌍을 바꿔가며 확인한다.

노드 1 노드 2 island A island B GPU 0 GPU 1 GPU 2 GPU 3 CPU · root complex NIC NIC (CPU 생략) GPU 4
검토 노트 ② — island와 노드의 관계. NVSwitch 기반의 현대 8-GPU 서버(HGX/DGX)는 노드 전체가 하나의 island다. island가 노드보다 작은 경우는 브리지 페어 구성(예: 3090 ×2 페어 여러 개), NVSwitch 없는 PCIe 서버, 구형 토폴로지(DGX-1의 하이브리드 메시 등)에서 발생한다. 본문 예시의 "노드 안 island 2개"는 후자 부류의 도식화다.

이것이 스케줄링 문제가 되는 마지막 고리: all-reduce 같은 집합 통신은 참여 쌍 중 가장 느린 링크에 전체 속도가 묶인다. 8장 중 7쌍이 NVLink여도 한 쌍이 PCIe면 그 스텝의 통신은 PCIe 속도로 끝난다. 그래서 성능 요구사항은 "GPU 4장"이 아니라 "전부 같은 island인 4장"이 된다 — 5장에서 이것을 선언적으로 표현하는 방법(matchAttribute)을 본다.

3.6 어떤 카드에 NVLink가 있는가

카드NVLink 하드웨어GPU↔GPU 통신 수단
RTX 3090 (2020, Ampere)있음 — 윗면 핑거브리지 장착 시 NVLink 양방향 합산 ~112.5GB/s
RTX 4090 (2022, Ada)없음 — 회로·단자 제거PCIe뿐. GeForce 40 시리즈는 PCIe P2P도 공식 미지원 — RAM 스테이징 경유가 기본
RTX 5090 (2025)없음PCIe뿐
A100 / H100 / B200 (데이터센터)있음, 세대마다 강화600 → 900 → 1,800 GB/s
검토 노트 ③ — "3090이 마지막"의 범위. 정확히는 GeForce(컨슈머) 라인 기준이다. 워크스테이션 라인은 Ampere 세대(RTX A4500/A5000/A6000)까지 NVLink를 유지했고, Ada 세대 워크스테이션부터 제거되었다. 컨슈머 가격대에서 NVLink를 직접 다뤄볼 수 있는 조합이 "중고 3090 ×2 + 브리지"인 이유다 — 7장의 실습 대상.
CH 3 핵심 압축
  1. GPU 다이는 PCIe PHY와 NVLink PHY를 모두 내장 — NVLink는 메인보드 미경유 별도 회선(브리지 = 끼움 PCB, SXM = 보드 인쇄 배선 + NVSwitch).
  2. 대역폭은 쌍의 속성: 같은 island = NVLink 회선 존재, 밖 = PCIe가 유일, 노드 밖 = 네트워크가 유일.
  3. 집합 통신은 가장 느린 쌍에 묶인다 → "같은 island의 N장"이 성능 요구사항이 되는 물리적 근거.
PART II — Kubernetes의 GPU 스케줄링
CH 4 · INTRO

Device plugin — 카운터 모델

K8s 스케줄러가 네이티브로 아는 자원은 CPU와 메모리뿐이다. GPU 같은 하드웨어는 extended resource로 노출된다: Node status에 nvidia.com/gpu: 8처럼 적히는 이름 붙은 정수 카운터. 이 카운터를 채우는 것이 device plugin — 각 노드에 DaemonSet으로 떠서 kubelet에 GPU를 보고하는 벤더 에이전트다. 핵심 인식: 스케줄러에게 GPU는 의미를 모르는 숫자다. 8이 80GB A100인지 24GB 3090인지, NVLink로 묶였는지 전혀 모른다. 이 한 문장이 이후 모든 한계의 뿌리다. 1장의 베이스라인(스텝 0)에서 시작해 흐름을 따라가자.

CH 4 · DEVICE PLUGIN

두 단계로 찢어진 결정

스케줄러는 "어느 노드"를, kubelet+plugin은 "어느 디바이스"를 결정한다. 단계를 넘기며 화살표를 따라가기.

NODE Device plugin 벤더 DaemonSet kubelet 노드 에이전트 Pod GPU 2장 요청 GPU 하드웨어 (8장) 0 1 2 3 4 5 6 7 API server Node·Pod 오브젝트 허브 kube-scheduler 정수 카운터만 봄 사용자 Pod 매니페스트 제출
1 / 7
검토 노트 ④ — Allocate의 선택 주체 정밀화. 위 스테퍼 6단계의 "GPU-3·5 선택"은 엄밀히는 kubelet의 device manager가 수행한다. plugin은 GetPreferredAllocation으로 선호 조합 힌트를 줄 수 있고, Allocate(선택된 ID들)는 그 디바이스를 컨테이너에 준비(디바이스 파일·환경변수 명세 반환)시키는 호출이다. "노드 위에서, Pod 도착 후에 디바이스가 정해진다"는 구조적 결론은 동일하다.

4.1 구조적 한계 세 가지

흐름에서 확인했듯 결정이 둘로 찢어져 있다 — "어느 노드"는 스케줄러가, "어느 디바이스"는 kubelet+plugin이. 전체를 보는 주체가 없고, 스케줄러가 보는 것은 속성 없는 정수다. 여기서 세 가지 한계가 나온다.

#한계원인현장의 땜질
1속성 표현 불가카운터에 메모리·모델·MIG 프로파일이 없음node label + nodeSelector, 또는 nvidia.com/mig-1g.5gb 같은 자원 이름 폭발
2토폴로지 무지디바이스 선택이 kubelet 단계 → 스케줄러가 NVLink/PCIe/NUMA 배치를 고려 불가plugin 쪽 best-effort 정렬뿐, 노드 선택 자체는 못 바꿈
3정수 전부-아니면-전무분수 할당·공유·동적 분할 개념 없음time-slicing, MIG 정적 사전 분할

땜질이 왜 땜질인가. 한계 1의 결과: 클러스터에 A100 80GB/40GB와 MIG 프로파일 여러 종이 섞이면 자원 이름이 십여 개로 폭발하고, 각각이 독립 카운터라 "메모리 ≥ 30GB면 아무거나"를 표현할 방법이 없다. 한계 2의 결과: 같은 노드 8장 중 어느 조합을 받을지 복불복 — 3장에서 본 대역폭 절벽 때문에 같은 매니페스트가 실행마다 다른 성능을 내는데, 원인이 스케줄링 계층에서 보이지 않는다.

CH 4 핵심 압축
  1. Extended resource = 속성 없는 정수 카운터. 스케줄러에게 GPU는 의미를 모르는 숫자.
  2. 결정이 두 단계로 분리: 노드(스케줄러) → 디바이스(kubelet, Pod 도착 후). 전체를 보는 주체 없음.
  3. 한계 셋 — 속성 불가, 토폴로지 무지, 전부-아니면-전무 — 이 5장 DRA의 존재 이유.
CH 5 · INTRO

DRA — 결정권을 스케줄러로 옮기다

DRA(Dynamic Resource Allocation, K8s 1.34에서 GA)의 본질은 한 문장이다: 디바이스의 속성을 API 오브젝트로 노출하고, 디바이스 선택 결정을 kubelet에서 스케줄러로 이동. 오브젝트 모델은 스토리지의 PV/PVC 패턴과 정확히 닮았다.

스토리지DRA역할
StorageClassDeviceClass"이런 부류의 디바이스" 정의 (드라이버가 제공, cluster-scoped)
PVCResourceClaim"이런 조건의 디바이스 N개" 요청
PV 인벤토리ResourceSlice드라이버가 게시하는 실제 디바이스 목록 + 속성
volumeClaimTemplateResourceClaimTemplatePod마다 claim 자동 생성

비유가 깨지는 지점 세 곳. ① PV는 동적 프로비저닝으로 새로 만들어질 수 있지만 GPU는 고정 인벤토리라 게시된 것 중 선택만 가능하다. ② PV:PVC는 1:1이지만 claim 하나가 디바이스 여러 개를 받을 수 있고 여러 Pod이 한 claim을 공유할 수도 있다. ③ 스토리지는 원격 attach가 되지만 GPU는 노드 로컬이라 디바이스 할당이 곧 노드 선택을 구속한다. 흐름을 단계로 따라가자 — 3단계에서 GPU-3(24Gi)이 조건 미달로 흐려지는 것을 관찰한다.

CH 5 · DRA

결정권이 스케줄러로 이동하면

속성이 ResourceSlice로 게시되고, 노드와 디바이스가 한 결정으로 선택된다. GPU-3(24Gi)이 CEL에서 탈락하는 과정을 관찰.

NODE kubelet 노드 에이전트 DRA driver GPU 발견·준비 Pod claimTemplate 참조 GPU + 메모리 속성 GPU-080Gi GPU-180Gi GPU-240Gi GPU-324Gi ResourceSlice GPU-0 · 80Gi · nvlink 0 GPU-1 · 80Gi · nvlink 0 GPU-2 · 40Gi · nvlink 1 GPU-3 · 24Gi · nvlink 1 driver가 게시한 디바이스 인벤토리 + 속성 ResourceClaim selector: memory ≥ 40Gi · count: 2 status: GPU-0, GPU-1 할당 ✓ kube-scheduler + DRA plugin 노드 + 디바이스 동시 결정
1 / 5

5.1 CEL — 속성 정의가 아니라 필터

스테퍼의 "요청"에 등장한 CEL(Common Expression Language)은 구글이 만든 임베디드 식 언어로, K8s가 정책·검증 전반(ValidatingAdmissionPolicy 등)에 채택한 표준이다. 역할을 정확히 하자: CEL은 속성을 정의하지 않는다. 속성은 driver가 ResourceSlice에 게시하고, CEL selector는 그 위에서 디바이스마다 평가되어 boolean을 반환하는 필터다. SQL에 비유하면 —

ResourceSlice  =  테이블 (디바이스 = 행, 속성 = 열)
CEL selector   =  WHERE 절
스케줄러       =  쿼리 실행기 — 행마다 식을 평가해 true인 것만 후보로
디바이스memorymemory.compareTo(quantity("40Gi")) >= 0후보
GPU-080Gi80 ≥ 40 → true
GPU-180Gi80 ≥ 40 → true
GPU-240Gi40 ≥ 40 → true (경계 포함)
GPU-324Gi24 ≥ 40 → false 탈락

후보 3개에서 count: 2를 고르는 것은 CEL의 일이 아니라 스케줄러의 일이다 — CEL은 자격 심사, 선택은 스케줄러. quantity()compareTo()가 붙는 이유: "40Gi"는 단순 숫자가 아닌 K8s Quantity 타입(Gi/Mi/m 단위 체계)이라 기본 비교 연산자를 쓸 수 없어 타입 전용 함수를 거친다.

비유가 깨지는 지점: SQL WHERE는 JOIN으로 행 간 관계를 걸 수 있지만, CEL selector는 디바이스 1개씩 독립 평가되는 술어(predicate)다. "선택된 디바이스들이 서로 같은 NVLink 도메인일 것" 같은 디바이스 관계는 selector로 표현 불가 — 그래서 별도 메커니즘인 constraints.matchAttribute가 있다(선택된 디바이스 전원이 지정 속성에서 같은 값을 가져야 함). 3장의 island 요구가 여기서 선언적 표현을 얻는다.

5.2 전체 예제 — 적용 가능한 매니페스트

DRA driver(예: NVIDIA k8s-dra-driver)가 설치되어 DeviceClass gpu.nvidia.com과 ResourceSlice가 게시된 클러스터 기준. 속성 이름은 드라이버 버전에 따라 다르므로 kubectl get resourceslices -o yaml로 실제 게시 속성을 확인 후 맞춘다.

apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
  name: training-gpus
spec:
  spec:
    devices:
      requests:
      - name: gpus
        exactly:
          deviceClassName: gpu.nvidia.com
          count: 2
          selectors:
          - cel:
              expression: >-
                device.capacity["gpu.nvidia.com"].memory
                  .compareTo(quantity("40Gi")) >= 0
      constraints:
      - requests: ["gpus"]
        matchAttribute: "gpu.nvidia.com/nvlinkDomain"   # 2장이 같은 도메인
---
apiVersion: v1
kind: Pod
metadata:
  name: trainer
spec:
  restartPolicy: Never
  containers:
  - name: train
    image: nvcr.io/nvidia/pytorch:24.10-py3
    command: ["python", "-c", "import torch; print(torch.cuda.device_count())"]
    resources:
      claims:
      - name: gpus
  resourceClaims:
  - name: gpus
    resourceClaimTemplateName: training-gpus

5.3 한계 → 해법 대응

4장의 한계DRA의 해법
속성 표현 불가ResourceSlice가 속성 게시 + CEL 조건 요청 → 자원 이름 폭발 소멸
토폴로지 무지노드와 디바이스를 스케줄러가 한 결정으로 선택 + matchAttribute → island 정렬 표현 가능
정수 전부-아니면-전무partitionable device(MIG 동적 분할), claim 공유

DRA가 풀지 않는 문제도 있다: 노드 정렬. "GPU와 같은 PCIe 스위치의 RDMA NIC을 함께 달라"(2.5절의 GPUDirect RDMA) 같은 멀티-드라이버 교차 제약은 현재진행형 영역이다.

CH 5 핵심 압축
  1. DRA = PV/PVC 패턴의 디바이스 적용: ResourceSlice(인벤토리+속성) / ResourceClaim(조건 요청) / 선택 결정의 스케줄러 이동.
  2. CEL은 속성 정의가 아니라 디바이스별 boolean 필터(WHERE 절). 디바이스 간 관계는 CEL 밖 — matchAttribute의 영역.
  3. 노드+디바이스 동시 결정이 토폴로지 인지 스케줄링의 구조적 전제. 남은 숙제 = 노드 간 정렬.
CH 6 · INTRO

Gang scheduling — Pod N개의 원자성

DRA가 "Pod 1개 ↔ 디바이스" 문제를 풀었다면, 분산 학습은 "Pod N개가 원자적으로" 문제를 추가한다. 용어부터: 분산 학습에서 Job 1개 = worker Pod N개이고 각 Pod이 GPU를 잡는다. rank는 worker의 번호(0…N−1)다.

왜 N개가 한 몸인가. 데이터 병렬(data parallel) 학습은 모델을 N개로 복제하고 데이터를 나눠 계산한 뒤, 매 스텝 끝마다 all-reduce로 전원의 gradient를 평균낸다. all-reduce는 집합 연산(collective operation) — 참가자 전원이 도착해야 완료되는 barrier다. 회의에 비유하면 8명 전원 참석해야만 시작되는 회의가 매 스텝 열리는 셈인데, 비유의 한계도 명확하다: 회의는 7명으로 시작할 수도 있지만 all-reduce는 수학적으로 전원의 텐서가 필요해 부분 진행 자체가 불가능하다. worker 8개 중 7개만 떠 있으면 7개는 첫 all-reduce에서 GPU를 점유한 채 8번째를 영원히 기다린다 — 점유는 100%, 진행은 0.

"원자적"의 의미는 DB 트랜잭션의 원자성과 같다: N개 Pod 배치가 전부 성공하거나 전부 안 하거나. 중간 상태(절반만)는 자원만 태우는 무의미한 상태이므로 존재 자체를 금지해야 한다. 그런데 기본 스케줄러의 결정 단위는 Pod이다 — Job 경계를 모른 채 큐에서 하나씩 꺼내 배치(greedy)하므로, 두 Job이 동시에 오면 인터리브되어 반반 점유에 도달한다. 이 상태는 OS 데드락과 조건까지 동형이다.

Coffman 데드락 조건GPU 스케줄링에서의 대응
상호 배제GPU는 배타 점유 자원
점유와 대기 (hold & wait)각 Job이 절반을 잡은 채 나머지를 대기
비선점실행 중 Pod의 GPU를 강제 회수하지 않음
순환 대기A는 B의 해제를, B는 A의 해제를 기다림

아래 시뮬레이터에서 같은 상황(8-GPU 클러스터, 8-GPU Job 둘 동시 제출)을 두 모드로 끝까지 실행해 비교하라 — 기본 모드의 결말과 Gang 모드의 결말, 그리고 마지막 스텝의 배너 문구.

CH 6 · GANG SCHEDULING

부분 배치가 만드는 교착

8 GPU 클러스터에 8-GPU Job A·B가 동시 제출되는 같은 상황. 두 모드를 번갈아 끝까지 실행해 비교.

Job A 
Job B 

해법은 데드락 조건 중 "점유와 대기"를 원천 제거하는 것: all-or-nothing 입장(admission) — Job 전체 요구량이 한 번에 수용될 때만 Pod들을 풀어주고, 아니면 1개도 주지 않는다. 이것이 gang scheduling이며, K8s 생태계의 표준 구현이 Kueue다. Kueue는 Job을 suspend 상태로 잡아뒀다가 quota가 확보되면 resume하는 방식으로 동작한다 — kube-scheduler를 대체하지 않고 그 앞단의 게이트로 협업한다. gang이 막는 것은 교착뿐이며, "어느 Job부터"(우선순위·팀 간 quota·선점·기아 방지)는 Kueue의 ClusterQueue/cohort/preemption 영역이다 — 부록 E의 다음 학습 주제.

CH 6 핵심 압축
  1. all-reduce는 전원 barrier — 부분 배치는 "점유 100% · 진행 0"의 무의미 상태라 N개 Pod 배치에 트랜잭션급 원자성이 필요.
  2. 기본 스케줄러는 Pod 단위 greedy(Job 경계 무지) → 인터리브 → Coffman 4조건 충족 = 데드락 동형.
  3. gang = all-or-nothing 입장. Kueue는 suspend/resume 게이트로 kube-scheduler와 협업 — 교착만 막고, 순서 결정은 다음 층.
PART III — 실습: 손으로 확인하기
CH 7 · HANDS-ON

2×RTX 3090 NVLink 구성 — 전체 방법론

학습 가치의 핵심은 "NVLink가 있어서 빠르다"가 아니라, 한 시스템 안에 빠른 길과 느린 길이 공존해 끄고 켜며 대조 실험을 할 수 있다는 점이다. 3장의 island 절벽을 책상 위에서 직접 측정한다.

7.0 구매 전 호환성 체크 — 여기서 실패하면 전부 무효

구매 순서: 보드 슬롯 간격 확인 → 브리지 pitch 결정 → 카드 구매.

항목체크 내용함정
메인보드 슬롯x16 물리 슬롯 2개가 CPU 직결이고 x8/x8 분기 지원두 번째 슬롯이 칩셋 경유면 탈락(DMI 병목). 보드 매뉴얼의 블록 다이어그램으로 확인
슬롯 간격(pitch)두 슬롯 간 거리 = 브리지 pitch와 정확히 일치GeForce 정품 브리지는 4-슬롯 간격 단일 SKU. 2/3-슬롯 보드면 RTX A6000용 브리지(동일 NVLink 3세대) 사용 — 호환 보고 다수이나 정품 보증 밖
두 번째 3090브랜드 혼용 무방(NVLink는 칩 기능)카드 두께가 다르면 핑거 높이가 어긋날 수 있음 — 비슷한 두께 권장. 중고는 채굴 이력 리스크
전원1000W 이상, 권장 1200W3090은 순간 스파이크가 큼. 운영 시 nvidia-smi -pl 280 수준 제한이 현실적
발열·공간3-슬롯급 2장 — 케이스 폭, 카드 간 흡기 간격위 카드가 아래 카드 배기를 흡기 → power limit + 팬커브 필수

KVM/libvirt 환경이라면 검증은 베어메탈(호스트)에서 먼저 한다. NVLink 쌍의 VM passthrough는 두 GPU+브리지를 같은 VM에 넣어야 하고 IOMMU 그룹 이슈가 겹친다 — 변수를 분리할 것.

7.1 조립 후 인식 확인

양쪽 카드의 핑거 커버를 제거하고 브리지를 위에서 압입. 부팅 후:

nvidia-smi topo -m
        GPU0    GPU1
GPU0     X      NV4      <- NVLink 4링크. PHB/PIX/SYS면 미인식(브리지 접촉 불량 의심)
GPU1    NV4      X
nvidia-smi nvlink --status
# 기대: GPU당 Link 0~3 각각 14.062 GB/s (4링크 × 14.06 = 단방향 56.25GB/s)

7.2 CUDA P2P 검증

git clone https://github.com/NVIDIA/cuda-samples.git
cd cuda-samples && mkdir build && cd build && cmake ..
make -j"$(nproc)" p2pBandwidthLatencyTest
./Samples/5_Domain_Specific/p2pBandwidthLatencyTest/p2pBandwidthLatencyTest

검증 포인트 셋: ① Peer access from GPU0 → GPU1: Yes ② 단방향 대역폭 P2P enabled ~50GB/s(이론 56.25의 약 90%) vs disabled(RAM 경유) 수 배 낮음 ③ 레이턴시 enabled ~1–2µs vs disabled ~10µs+. 2장의 그림과 연결하면: disabled는 GPU0→RAM 스테이징→GPU1의 두 번 복사, enabled는 GPU0의 DMA 엔진이 GPU1 VRAM 주소로 직접 쓰기다.

7.3 NCCL 대조 실험 — 이 구성의 학습 본체

git clone https://github.com/NVIDIA/nccl-tests && cd nccl-tests
make CUDA_HOME=/usr/local/cuda -j"$(nproc)"
# 실험군: NVLink 경로
./build/all_reduce_perf -b 8M -e 1G -f 2 -g 2
# 대조군: P2P 강제 차단 -> 호스트 경유로 추락
NCCL_P2P_DISABLE=1 ./build/all_reduce_perf -b 8M -e 1G -f 2 -g 2
# transport 선택 로그 확인
NCCL_DEBUG=INFO ./build/all_reduce_perf -b 64M -e 64M -g 2 2>&1 | grep -i via
# 실험군 로그 기대:  ... via NVL   (NVLink transport)
# 대조군 로그 기대:  ... via SHM   (호스트 공유메모리 경유)
# busbw @ 1GiB:      실험군 ~40-50GB/s  vs  대조군 한 자릿수 GB/s  (환경 의존, 대략값)

관찰 포인트: 큰 메시지(≥256M)의 busbw 비율이 직접 측정한 island 경계 절벽이다. 작은 메시지 구간은 대역폭이 아니라 레이턴시가 지배하므로 busbw 곡선이 메시지 크기에 따라 꺾인다 — 이 곡선 모양 자체가 집합 통신의 성격이다.

7.4 실전 워크로드 — DDP 벤치마크 (전체 스크립트)

# ddp_bench.py — 2-GPU DDP 스텝 시간 벤치마크 (약 400M 파라미터, fp32)
import time, torch, torch.nn as nn
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP

def main():
    dist.init_process_group("nccl")
    rank = dist.get_rank()
    torch.cuda.set_device(rank)
    model = nn.Sequential(*[nn.Linear(4096, 4096) for _ in range(24)]).cuda()
    ddp = DDP(model, device_ids=[rank])
    opt = torch.optim.SGD(ddp.parameters(), lr=1e-3)
    x = torch.randn(32, 4096, device="cuda")
    y = torch.randn(32, 4096, device="cuda")
    loss_fn = nn.MSELoss()
    for i in range(60):
        if i == 10:
            torch.cuda.synchronize(); dist.barrier()
            t0 = time.time()
        opt.zero_grad(set_to_none=True)
        loss = loss_fn(ddp(x), y)
        loss.backward()          # 여기서 gradient all-reduce 발생
        opt.step()
    torch.cuda.synchronize()
    if rank == 0:
        print(f"avg step: {(time.time()-t0)/50*1000:.2f} ms")
    dist.destroy_process_group()

if __name__ == "__main__":
    main()
# 1 GPU 기준선 / NVLink / P2P 차단 — 세 값을 비교
torchrun --standalone --nproc_per_node=1 ddp_bench.py
torchrun --standalone --nproc_per_node=2 ddp_bench.py
NCCL_P2P_DISABLE=1 torchrun --standalone --nproc_per_node=2 ddp_bench.py

해석: (2-GPU 스텝 시간 − 1-GPU 스텝 시간)이 통신+동기화 비용의 근사치이고, P2P 차단 값과의 차이가 NVLink가 실제로 사주는 것이다. 추론 쪽은 tensor parallel이 매 레이어마다 all-reduce를 발생시켜 효과가 더 즉각적이다:

vllm serve Qwen/Qwen2.5-32B-Instruct-AWQ --tensor-parallel-size 2 --max-model-len 8192
# 24GB ×2로 단일 카드에 못 올리던 30B급 서빙 — 4bit 양자화 기준

7.5 (심화) K8s 연결 — 1·4·5장을 실물로

7.6 문제 해결 표

증상유력 원인조치
topo -m에 NV4가 아닌 PHB/SYS브리지 접촉 불량, pitch 불일치브리지 재장착·좌우 균등 압입, 카드 높이 확인
Peer access: NoPCIe ACS가 P2P 라우팅 차단, IOMMU 간섭BIOS에서 ACS 비활성, 또는 커널 파라미터 조정 후 재시도
NCCL이 항상 via SHMP2P 미가용 상태에서 NCCL이 자동 폴백7.2를 먼저 통과시킬 것 — NCCL 문제가 아님
성능이 기대의 절반 이하power limit 미설정으로 스로틀, 배기 재흡입nvidia-smi -pl 설정, 팬커브, 카드 간격 확보
간헐 Xid 에러·행업전원 순간 스파이크, 라이저 사용PSU 상향/케이블 분리 배선, 라이저 제거 후 재현 확인

7.7 실험 기록 템플릿

측정 항목명령NVLinkP2P 차단비율
P2P 단방향 대역폭p2pBandwidthLatencyTest   
P2P 레이턴시p2pBandwidthLatencyTest   
all_reduce busbw @1GiBnccl-tests   
DDP avg step (vs 1-GPU: ___ms)ddp_bench.py   
vLLM TP=2 tokens/svllm bench   
검토 노트 ⑤ — 수치의 지위. 본 장의 대역폭·지연 수치는 전형값이며 보드·드라이버·냉각 상태에 따라 달라진다. cuda-samples는 최신 버전 기준 cmake 빌드다(구버전은 각 샘플 디렉터리에서 make). 절대값보다 NVLink/차단 비율이 학습의 목표 측정치다.
CH 7 핵심 압축
  1. 구매 게이트는 슬롯 간격: CPU 직결 x8/x8 두 슬롯 + 브리지 pitch 일치. 보드 확인이 카드 구매보다 먼저.
  2. 검증 사다리: topo -m의 NV4 → p2pBandwidthLatencyTest ~50GB/s → nccl-tests "via NVL".
  3. 진짜 실험은 NCCL_P2P_DISABLE=1 대조 — island 절벽을 자기 하드웨어에서 수치로 재현하는 것. 브리지가 사주는 것은 속도가 아니라 대조군이다.
APPENDIX

부록

A. 검토 노트 모음 — 원 대화 대비 정밀화·정정 5건

#내용위치
대역폭 비교 기준 통일: NVLink 수치는 양방향 합산, PCIe는 단방향 — 같은 기준으로 A100은 PCIe Gen4 x16의 약 9–10배3.4
NVSwitch 기반 현대 8-GPU 서버는 노드 전체가 단일 island. "노드 안 복수 island"는 브리지 페어·구형 토폴로지의 경우3.5
"3090이 마지막 NVLink"는 GeForce 라인 기준 — 워크스테이션은 Ampere 세대까지 유지3.6
device plugin의 디바이스 선택 주체는 kubelet device manager(plugin은 GetPreferredAllocation 힌트). Allocate()는 준비 호출4장
실습 수치는 전형값(환경 의존). 목표 측정치는 절대값이 아니라 NVLink/차단 비율. cuda-samples는 최신 기준 cmake 빌드7.7

B. 전체 흐름 — 한 문단 요약

K8s의 모든 협업은 API server 오브젝트를 경유한다. kubelet이 실물을 보고하고(capacity/allocatable), 스케줄러는 장부(allocatable − Σrequests)로 결정해 binding으로 기록한다. GPU는 커널 표준 인터페이스가 없어 device plugin이 벤더 대리인으로 카운터를 채우는데, 카운터에는 속성이 없고 디바이스 선택이 kubelet 단계로 밀리며 정수라 나눌 수 없다. 한편 하드웨어 쪽에서 GPU 간 통신 속도는 쌍 사이에 어떤 회선이 존재하느냐로 결정된다 — 같은 island면 NVLink, 아니면 PCIe, 노드 밖이면 네트워크. 집합 통신은 가장 느린 쌍에 묶이므로 "같은 island의 N장"이 성능 요구사항이 된다. DRA는 속성을 ResourceSlice로 게시하고 CEL 필터·matchAttribute로 조건을 선언하게 하며, 노드와 디바이스를 스케줄러의 한 결정으로 합친다. 분산 학습은 여기에 Pod N개의 원자성을 추가한다: all-reduce barrier 때문에 부분 배치는 점유 100%·진행 0이고, Pod 단위 greedy 배치는 데드락과 동형인 상태를 만든다. gang scheduling(all-or-nothing admission)이 그 조건을 제거하며, Kueue가 suspend/resume 게이트로 이를 구현해 kube-scheduler와 협업한다. 남는 질문 — 어느 Job부터, 누구의 quota로 — 이 다음 학습 주제다. 그리고 2×3090+브리지는 이 전부를 책상 위에서 대조 실험으로 재현하는 최소 장비다.

C. What you might be missing — 모음

주제요지연결
GPUDirect Storage / RDMARAM 경유를 건너뛰는 장치 간 직접 DMA. NVMe·NIC과 GPU의 배치 정렬이 새로운 스케줄링 요구가 됨2.5 · 5.3
PCIe 스위치 계층대형 GPU 서버는 RC 직결이 아니라 스위치 트리 — 같은 스위치 아래 정렬이 P2P 성능을 좌우2.2
노드 간 정렬DRA는 노드 내 결정. 멀티노드 Job의 네트워크 인지 배치(rail-optimized)는 미해결 영역5.3
gang이 못 정하는 것순서·우선순위·선점·기아 방지 = Kueue admission의 영역6장
컨슈머 P2P 차단GeForce 40 시리즈는 NVLink 제거에 더해 PCIe P2P도 공식 미지원 — 홈랩에서 3090의 특수 지위3.6 · 7장
x8/x8의 영향 범위슬롯 분기는 NVLink 트래픽과 무관 — 호스트↔GPU 구간(모델 적재, pageable 경유)에만 영향7.0

D. 용어집

API server
모든 K8s 오브젝트의 읽기/쓰기/구독이 경유하는 중앙 컴포넌트. 상태의 게시판.
watch
오브젝트 변경을 폴링 없이 스트림으로 구독하는 메커니즘.
kubelet
각 노드에 상주하며 자원 보고와 컨테이너 실행을 담당하는 에이전트.
cAdvisor
kubelet 내장 수집기 — /proc 등을 읽어 머신 자원을 파악.
capacity / allocatable
머신 물리 총량 / 거기서 시스템·K8s 예약분을 뺀 Pod 배정 가능량.
requests
Pod이 선언하는 자원 요구량. 스케줄러 장부 계산의 입력(실측 아님).
binding
스케줄링 결정의 기록 — Pod의 spec.nodeName에 노드 이름을 써넣는 것.
DaemonSet
모든(또는 선택된) 노드에 Pod을 하나씩 배치하는 워크로드 — device plugin의 배포 형태.
extended resource
CPU·메모리 외 자원을 표현하는 이름 붙은 정수 카운터(예: nvidia.com/gpu).
device plugin
노드의 디바이스를 발견해 kubelet에 보고하고 할당을 준비하는 벤더 에이전트.
DRA
Dynamic Resource Allocation — 속성 기반 디바이스 요청과 스케줄러 단계 디바이스 선택(1.34 GA).
ResourceSlice
드라이버가 게시하는 노드별 디바이스 인벤토리 + 속성.
ResourceClaim / Template
조건 기반 디바이스 요청 오브젝트 / Pod마다 자동 생성하는 틀.
DeviceClass
디바이스 부류의 정의(드라이버 제공, cluster-scoped).
CEL
K8s 표준 임베디드 식 언어. DRA에서 디바이스별 boolean 필터로 사용.
CDI
Container Device Interface — 컨테이너에 디바이스를 주입하는 표준 명세.
PCIe / lane
장치↔CPU 점대점 직렬 링크 / 링크의 폭 단위(x1·x4·x8·x16).
root complex
CPU 패키지 안 PCIe 트리의 뿌리 — 모든 PCIe 트래픽의 접점.
DMA
코어를 거치지 않는 장치 주도 메모리 접근. GPU의 copy engine이 수행.
pinned memory
커널이 옮기지 못하게 고정한 메모리 — DMA 직행 가능(pin_memory=True).
MMIO / BAR
장치 자원을 물리 주소 공간에 매핑하는 방식 / 그 창의 정의(Resizable BAR = VRAM 전체 창).
NVLink / NVSwitch
GPU 간 전용 고대역 인터커넥트 / 그것의 교환 칩(all-to-all).
island
NVLink로 상호 직결된 GPU 집합. 경계 밖 쌍에는 NVLink 회선이 없음.
PHY
다이 위 송수신 물리 계층 회로. GPU는 PCIe PHY와 NVLink PHY를 별도 내장.
SXM
카드가 아닌 소켓형 GPU 모듈 규격 — 베이스보드 인쇄 배선으로 NVLink 연결.
all-reduce / collective / rank
전원의 텐서를 합산·평균해 전원에 배포하는 집합 연산 / 그런 연산의 총칭 / 참가자 번호.
barrier
참가자 전원이 도착해야 통과되는 동기화 지점.
gang scheduling
Job의 Pod N개를 전부-또는-전무로 입장시키는 원자적 배치.
Kueue
K8s의 Job 큐잉·admission 컨트롤러 — suspend/resume으로 gang 구현.
DDP / TP
데이터 병렬(모델 복제, gradient all-reduce) / tensor parallel(레이어 분할, 레이어마다 all-reduce).
NCCL
NVIDIA 집합 통신 라이브러리 — NVLink/PCIe/네트워크 중 최적 경로를 자동 선택.
MIG / time-slicing
GPU 하드웨어 분할 / 시간 분할 공유 — 정수 카운터 한계의 땜질들.

E. 다음 학습 — Kueue admission 진입 질문