LabHub

한국어

시작하기
블로그

블로그

GPU 패스스루와 vfio-pci: VM 에 GPU 를 통째로 넘기는 원리

상태: 초안 (2026-10-05), 실측 반영. 「실측 예시」 절의 값은 GPU 서버 한 대에서 읽기 전용 명령으로 직접 확인한 것이며, 장치 ID 같은 식별 정보는 자리표시자로 바꿨습니다. 원리 설명은 커널과 KubeVirt 문서의 내용에 근거하며, 이 글을 쓰는 시점에 문서를 다시 열어 대조하지는 않았습니다(미확인). 메모리 고정과 크래시 루프의 관계처럼 검증하지 못한 추정은 그렇게 표시했습니다. 작성 규칙: 실측 데이터가 기본이고, 못 잰 것은 「미실측」으로 표시합니다.

1. 왜 패스스루를 쓰는가

GPU 를 워크로드에 주는 방법은 크게 네 가지입니다.

방식 단위 격리 이 글과의 관계
컨테이너(device plugin) GPU 한 장 또는 시분할 프로세스 수준, 호스트 커널과 드라이버 공유 3편
MIG GPU 를 하드웨어로 분할(지원 모델만) 하드웨어 수준 3편
vGPU 드라이버가 만든 가상 GPU 하이퍼바이저가 관리, 라이선스 필요 범위 밖
패스스루 물리 GPU 한 장 전체 VM 이 장치를 독점 이 글

패스스루가 필요한 경우는 세 가지로 좁혀집니다. 게스트에서 호스트와 다른 커널이나 드라이버 버전을 써야 할 때, 게스트 OS 가 리눅스가 아닐 때, 그리고 컨테이너로는 만족시킬 수 없는 격리를 요구할 때입니다. 대신 그 GPU 는 호스트와 컨테이너가 동시에 쓸 수 없습니다. 이 글의 뒷부분에서 같은 장치가 한쪽에서는 nvidia-smi 가 실패하는 모습을 실제로 봅니다.

2. 원리: 네 가지 부품

패스스루는 장치의 소유권을 호스트 커널의 일반 드라이버에서 사용자 공간 프로세스(QEMU)로 옮기는 일입니다. 이를 위해 네 부품이 함께 일합니다.

2.1 드라이버 바인딩

PCI 장치는 제조사 ID 와 장치 ID(예시 GPU 는 10de:XXXX)를 가지고, 커널은 그 ID 에 맞는 드라이버 하나를 붙입니다. 평소에는 nvidia 드라이버가 붙고, 그러면 호스트에서 nvidia-smi 가 동작합니다. 패스스루에서는 vfio-pci 를 대신 붙입니다. vfio-pci 는 장치를 쓰는 드라이버가 아니라, 장치를 사용자 공간에 건네는 중개용 드라이버입니다. 이 드라이버가 붙은 장치는 호스트 커널이 사용하지 않습니다.

붙이는 방법은 두 가지입니다. 실행 중에 driver_override 와 bind/unbind 로 바꾸는 방법과, 부팅할 때 vfio-pci 가 해당 ID 를 먼저 선점하게 하는 방법입니다. 예시 서버는 후자입니다(3절).

2.2 IOMMU: 장치의 메모리 접근을 가두는 장치

GPU 는 DMA 로 시스템 메모리에 직접 읽고 씁니다. 게스트에 장치를 주면 게스트가 쓰는 메모리 주소가 호스트의 실제 주소와 다르므로, 장치가 엉뚱한 호스트 메모리를 건드릴 수 있습니다. IOMMU 는 장치가 내는 DMA 주소를 변환하고 허용 범위를 제한해서 이 문제를 막습니다. 게스트 메모리 영역만 장치에 열어 주는 방식입니다.

격리의 최소 단위는 IOMMU 그룹입니다. 같은 그룹에 속한 장치끼리는 서로의 DMA 를 가를 수 없으므로, 그룹 안의 장치는 전부 같은 소유자에게 넘겨야 합니다. 그래서 GPU 의 오디오 기능(예시의 01:00.1)도 함께 vfio-pci 에 묶습니다.

2.3 VFIO 인터페이스

VFIO 는 장치를 사용자 공간에 안전하게 노출하는 커널 기능입니다. 사용자 공간 프로세스는 /dev/vfio/vfio(컨테이너)와 /dev/vfio/<그룹 번호> 를 열어서 다음을 합니다. 장치의 BAR(메모리 영역)을 자기 주소 공간에 매핑하고, 게스트 메모리를 IOMMU 에 등록하고, 장치의 인터럽트를 이벤트 파일 디스크립터로 받아 게스트에 주입합니다. QEMU 가 이 일을 합니다.

게스트 메모리는 DMA 의 대상이므로 호스트가 회수하거나 스왑할 수 없게 고정(pin)되어야 합니다. 12GiB 메모리의 VM 은 시작할 때 호스트 메모리 12GiB 를 고정한다는 뜻입니다(문서 기준, 직접 측정하지 않았습니다).

2.4 리셋

VM 이 끝나면 장치를 깨끗한 상태로 되돌려야 다음 사용자가 안전합니다. 가장 좋은 방식은 장치가 지원하는 FLR(Function Level Reset)이고, 지원하지 않으면 버스 리셋을 씁니다. 리셋이 제대로 안 되는 GPU 는 VM 을 한 번 쓰고 나면 호스트를 재부팅해야 하는 문제를 일으킵니다.

3. 실측 예시: 6GB급 GPU 를 넘긴 서버 한 대

모두 읽기 전용 명령으로 확인한 값이며, 장비마다 다른 예시 값입니다. 호스트의 커널은 7.0.0-34-generic 입니다. 장치 ID 는 XXXX(GPU)와 YYYY(오디오)로 가렸습니다.

항목 값 의미
lspci -nnk -d 10de: 01:00.0 GPU(10de:XXXX)와 01:00.1 오디오(10de:YYYY) 모두 Kernel driver in use: vfio-pci 두 기능 모두 vfio-pci 가 소유
IOMMU 그룹 그룹 16, 구성원은 01:00.0 과 01:00.1 둘뿐 그룹이 깨끗해서 이 GPU 만 따로 넘길 수 있음
커널 명령줄 iommu 관련 인자 없음, 그런데 IOMMU 그룹은 20개 이 커널에서는 IOMMU 가 기본으로 켜져 있음
로드된 모듈 vfio_pci, vfio_pci_core, vfio_iommu_type1, vfio, kvm_intel VFIO 스택이 올라와 있음
/dev/vfio/ 16(그룹 장치)과 vfio(컨테이너) 사용자 공간에 건넬 준비가 된 상태
reset_method flr bus FLR 을 지원하고, 안 되면 버스 리셋
BAR 영역 영역 0 이 16M, 영역 1 이 8G(prefetchable), 영역 3 이 32M GPU 의 메모리 창을 게스트에 매핑하게 됨
PCIe 링크 능력 16GT/s x16, 현재 16GT/s x8 (downgraded) 링크 폭이 절반으로 낮아져 있음

링크 폭이 x8 로 낮아진 원인은 확인하지 못했습니다. 보드 배선 때문일 수도 있고 전원 상태 때문일 수도 있으므로 단정하지 않습니다.

3.1 설정 파일

이 예시에서 패스스루를 만든 설정은 파일 두 개입니다.

# /etc/modprobe.d/vfio-gpu.conf
options vfio-pci ids=10de:XXXX,10de:YYYY
softdep nvidia pre: vfio-pci
softdep nouveau pre: vfio-pci
softdep snd_hda_intel pre: vfio-pci

# /etc/modules-load.d/vfio-pci.conf
vfio-pci

예시 서버의 부팅 이미지(initramfs)에는 vfio 항목이 한 줄도 없었습니다. 그래서 이 설정은 이미지 재생성 없이 파일만으로 동작하고, 되돌릴 때도 같은 이유로 파일을 치우면 됩니다(다만 재부팅 후 실제로 되돌아오는지는 아직 시험하지 않았습니다).

4. 같은 장치에서 nvidia-smi 가 실패하는 이유

이 호스트에서 nvidia-smi 를 실행하면 「NVIDIA driver 와 통신할 수 없다」는 메시지가 나옵니다. 이것은 드라이버가 설치되지 않았다는 뜻이 아닙니다. 호스트에는 nvidia-driver-595-open(595.91.07)이 설치되어 있고, 현재 커널용 모듈 패키지도 있습니다.

커널 로그가 원인을 그대로 보여 줍니다.

NVRM: GPU 0000:01:00.0 is already bound to vfio-pci.

nvidia 모듈이 장치를 가져가려고 시도할 때마다, 이미 vfio-pci 가 소유하고 있다는 메시지가 반복해서 찍혔습니다. 장치에 붙은 드라이버는 하나뿐이므로, 소유권이 VFIO 에 있는 동안 호스트의 nvidia-smi 는 통신할 대상이 없습니다. 고장이 아니라 설정대로의 정상 동작입니다.

쿠버네티스 쪽 상태도 같은 이야기를 합니다.

위치 값
노드 nvidia.com/gpu 0 (컨테이너에 줄 GPU 없음)
노드 nvidia.com/<모델별 자원 이름> 1 (VM 에 줄 장치 있음)
노드 라벨 nvidia.com/gpu.deploy.operands false (GPU Operator 구성 요소 꺼짐)
GPU Operator driver.enabled false (호스트에 설치된 드라이버를 쓰는 설정)

5. KubeVirt 가 장치를 VM 에 연결하는 길

  1. KubeVirt 설정의 permittedHostDevices 에 pciVendorSelector: 10DE:XXXX 와 resourceName: nvidia.com/<모델별 자원 이름> 이 등록되어 있고, 기능 게이트 HostDevices 가 켜져 있습니다.
  2. 노드의 virt-handler 가 그 ID 의 장치를 찾아 쿠버네티스 확장 자원으로 광고합니다. 그 결과가 위 표의 nvidia.com/<모델별 자원 이름>: 1 입니다.
  3. VM 정의의 devices.gpus 에 deviceName: nvidia.com/<모델별 자원 이름> 을 적습니다. 스케줄러는 이 자원이 있는 노드에만 VM 을 놓습니다.
  4. VM 이 시작되면 QEMU 가 VFIO 로 해당 IOMMU 그룹을 열고 장치를 게스트에 연결합니다.

여기서 중요한 점은 자원 이름에 nvidia.com/gpu 가 아니라 별도의 이름이 쓰인다는 것입니다. 컨테이너용 GPU 와 VM 용 GPU 는 서로 다른 자원이고, 한 장의 GPU 는 둘 중 하나로만 광고됩니다.

6. 흔한 함정: 메모리 고정과 호스트 OOM

GPU 를 넘긴 VM 은 2.3절처럼 게스트 메모리를 호스트에 고정합니다. 고정된 메모리는 스왑으로 흡수할 수 없으므로, VM 이 시작하는 순간 게스트 크기만큼의 호스트 메모리가 한꺼번에 필요합니다. 같은 호스트에서 다른 워크로드와 노드 데몬이 함께 돌고 있으면 합계가 물리 메모리를 넘기 쉽습니다.

호스트 전체의 메모리가 모자라면 커널의 OOM killer 가 동작합니다. 이때 커널 로그에는 컨테이너 한도 초과(Memory cgroup out of memory)가 아니라 호스트 전체 메모리 부족(Out of memory: Killed process)으로 남습니다. 희생자는 VM 프로세스(qemu-kvm)만이 아니라 노드의 시스템 데몬일 수도 있습니다. VM 이 죽었다가 다시 시작하면 같은 일이 반복되는 크래시 루프가 되고, 그 사이에 데몬들도 계속 함께 죽습니다.

필자의 실험 환경에서 관찰한 호스트 OOM 로그는 이 흐름과 일치했습니다. 다만 VM 이 시작될 때 실제로 고정한 메모리 크기를 재지 않았으므로 이 해석은 추정입니다(미실측). 교훈은 두 가지입니다. 호스트 메모리가 넉넉하지 않은 서버에는 GPU VM 을 여러 대 얹지 않는 편이 안전하고, VM 을 올리기 전에 게스트 메모리의 합과 호스트에서 이미 쓰는 메모리를 먼저 더해 봐야 합니다.

7. 컨테이너용 GPU 로 되돌리기 (예시 절차)

패스스루를 풀고 장치를 컨테이너용으로 되돌리는 가장 단순한 방법은 노드를 비우고(cordon, drain) 설정 파일을 치운 뒤 재부팅하는 것입니다. 다만 그 노드에서 다른 서비스가 돌고 있으면 재부팅이 모두를 내리는 큰 작업이 됩니다. 아래는 재부팅 없이 장치만 넘기는 예시입니다. 가능하다고 판단한 근거는 모두 읽기 전용으로 먼저 확인했습니다.

사전 확인 결과
/dev/vfio/<그룹 번호> 를 잡은 프로세스 없음
GPU 를 쓰는 VM 없음
장치 상태 유휴, 리셋 방식 flr bus 지원
nvidia 모듈 595.91.07, 현재 커널용, 서명됨

먼저 설정 파일 두 개를 백업한 뒤 치우고, 아래 두 단계를 실행했습니다.

# 1) 이 장치는 nvidia 드라이버로만 붙도록 지정한다.
echo nvidia | sudo tee /sys/bus/pci/devices/0000:01:00.0/driver_override
# 2) vfio-pci 에서 푼다. 풀리는 즉시 nvidia 가 장치를 가져간다.
echo 0000:01:00.0 | sudo tee /sys/bus/pci/drivers/vfio-pci/unbind

결과. unbind 직후 장치의 드라이버가 nvidia 로 바뀌었고 nvidia-smi 가 동작했습니다.

항목 값
GPU 6GB급 노트북 GPU, 6,144MiB
드라이버 595.91.07
lspci 01:00.0 의 Kernel driver in use: nvidia
호스트 가동 시간 유지, 부하 변화 없음, 다른 서비스 영향 없음

함께 봐야 할 점.

되돌리려면 driver_override 를 vfio-pci 로 바꾸고 nvidia 를 풀면 됩니다. 이 전환 뒤에는 GPU VM 이 이 GPU 를 쓸 수 없습니다. 한 장의 GPU 를 컨테이너와 VM 이 번갈아 쓰려면 이 전환을 매번 해야 하므로, 장 수가 적은 환경에서는 용도를 하나로 정해야 합니다.

8. 면접에서 나올 만한 질문

9. 흔한 오해

외부 근거: 패스스루의 성능 오버헤드

패스스루 VM 의 성능을 직접 재지 못해서, 같은 질문을 측정한 외부 자료를 확인했습니다. 열 수 있었던 범위는 초록, 벤더 글, 일부 원문입니다.

자료 확인한 내용과 조건 신뢰도와 한계
Walters 외, 「GPU Passthrough Performance: A Comparison of KVM, Xen, VMWare ESXi, and LXC for CUDA and OpenCL Applications」, IEEE CLOUD 2014 초록: KVM 패스스루가 베어메탈의 98~100%, Xen 과 VMware 가 96~99%(두 세대 GPU, 두 프로세서 마이크로아키텍처) 동료 심사 논문이나 본문을 열지 못해 IOMMU, 큰 페이지, NUMA, 링크 폭 조건은 모름. 2014년 GPU
Steininger 외, 「GPU Passthrough Across Virtualization Platforms for LLM Inference」, Computers(MDPI) 15권, 2026 초록: 워크스테이션급 GPU 한 장을 Proxmox VM·LXC, KVM, OpenStack, Podman 으로 패스스루해 vLLM 소형 모델의 처리량, 토큰 간 지연이 모든 플랫폼에서 베어메탈의 2% 이내(통계적으로 동등) 저자가 일반 주장이 아니라고 밝힘. 장치를 포화시키지 않는 영역. 본문 접근 거부로 초록만 확인
OpenNebula 벤더 블로그(2026-03-18) GB200 NVL4 패스스루 cuBLAS GEMM 71,891 대 71,738 GFLOP/s(0.21% 차), vLLM 처리량 ±4% 이내 벤더 글, NUMA 고정·큰 페이지·IOMMU 설정이 명시되지 않음
VMware 성능 연구(2023-10) vGPU(전체 프로파일 H100)에서 GPT-J 6B 가 베어메탈의 97.9~99.8%(MLPerf 추론 v3.1) 패스스루가 아니라 vGPU. MLCommons 검증을 받지 않은 결과
Samani, Amini Salehi, arXiv 2112.09780, 2021 베어메탈, VM(패스스루), 컨테이너 비교. 일관된 단일 오버헤드는 없고 종단 추론 시간이 CPU 전후처리와 저장소 I/O 에 지배되어 GPU 부분의 영향이 작음. 호스트에 유휴 코어가 없으면 VM 오버헤드가 커지고 CPU 고정이 줄임 Tesla K40, 2 소켓 80코어, HDD. 오래된 GPU
NVIDIA CUDA Programming Guide 3.4.2.5, NCCL GPU troubleshooting 베어메탈에서는 IOMMU 를 끄라고 하고 VM 패스스루에서는 IOMMU 를 켜고 VFIO 를 쓰라고 함. ACS 는 P2P 를 CPU 루트 컴플렉스로 우회시켜 대역폭을 크게 줄이는데 VM 은 ACS 를 끌 수 없고 VM 안에서 최대 성능을 내려면 네트워크 어댑터의 ATS 가 필요 공식 문서. 단일 GPU 서버에서는 P2P 가 쟁점이 아님

확인하지 못한 것

참고 자료

확인한 버전과 날짜

2026-10-05 기준. 호스트 커널 7.0.0-34-generic, NVIDIA 드라이버 패키지 595.91.07(호스트에 설치), KubeVirt HostDevices 기능 게이트 사용.

로그인하면 좋아요를 누를 수 있습니다

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다