상태: 초안 (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
options vfio-pci ids=…는vfio-pci가 이 두 ID 를 자기 것으로 선점하게 합니다.softdep … pre: vfio-pci는nvidia,nouveau,snd_hda_intel이 로드되기 전에vfio-pci를 먼저 올려, 그 드라이버들이 장치를 먼저 가져가지 못하게 합니다.modules-load.d는 부팅할 때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 에 연결하는 길
- KubeVirt 설정의
permittedHostDevices에pciVendorSelector: 10DE:XXXX와resourceName: nvidia.com/<모델별 자원 이름>이 등록되어 있고, 기능 게이트HostDevices가 켜져 있습니다. - 노드의
virt-handler가 그 ID 의 장치를 찾아 쿠버네티스 확장 자원으로 광고합니다. 그 결과가 위 표의nvidia.com/<모델별 자원 이름>: 1입니다. - VM 정의의
devices.gpus에deviceName: nvidia.com/<모델별 자원 이름>을 적습니다. 스케줄러는 이 자원이 있는 노드에만 VM 을 놓습니다. - 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 |
| 호스트 | 가동 시간 유지, 부하 변화 없음, 다른 서비스 영향 없음 |
함께 봐야 할 점.
- 오디오 기능(
01:00.1)은vfio-pci에 그대로 남아 있습니다. 컨테이너용 GPU 사용에는 영향이 없다고 보지만 확인하지는 않았습니다. - 커널 로그에
NVRM: … NV_ERR_INVALID_DATA(PlatformRequest) 경고가 두 줄 찍혔고,power.draw가 약 752W 로 읽혔습니다. 이 GPU 의 전력 값으로는 불가능한 숫자라 센서나 플랫폼 정보 읽기에 문제가 있는 것으로 보이며, 원인은 확인하지 못했습니다. 전환 직후의 전력 값은 믿지 마십시오. - 재부팅 후에도
nvidia가 자동으로 붙는지는 시험하지 않았습니다. 설정 파일을 치웠으므로 붙을 것으로 보지만 아직 추정입니다. - 쿠버네티스 쪽은 그대로입니다. 노드의
nvidia.com/gpu는 여전히0, KubeVirt 의nvidia.com/<모델별 자원 이름>도1로 남아 있습니다(virt-handler가 다시 읽을 때까지의 낡은 값으로 보입니다). 컨테이너가 GPU 를 쓰려면 노드 라벨nvidia.com/gpu.deploy.operands를true로 바꿔 GPU Operator 의 구성 요소를 켜야 합니다.
되돌리려면 driver_override 를 vfio-pci 로 바꾸고 nvidia 를 풀면 됩니다. 이 전환 뒤에는 GPU VM 이 이 GPU 를 쓸 수 없습니다. 한 장의 GPU 를 컨테이너와 VM 이 번갈아 쓰려면 이 전환을 매번 해야 하므로, 장 수가 적은 환경에서는 용도를 하나로 정해야 합니다.
8. 면접에서 나올 만한 질문
- 패스스루와 vGPU, MIG 는 어떻게 다른가? 격리 단위(장치 전체, 소프트웨어 분할, 하드웨어 분할), 라이선스, 지원 하드웨어로 구분합니다.
- IOMMU 그룹이 왜 중요한가? 같은 그룹의 장치는 DMA 를 서로 격리할 수 없어서 함께 넘겨야 합니다. 예시 서버의 그룹은 GPU 와 오디오 둘뿐이어서 깨끗했습니다.
- VM 이 시작될 때 호스트 메모리가 왜 고정되는가? 장치가 게스트 메모리에 DMA 를 하므로 그 영역이 이동하거나 스왑되면 안 되기 때문입니다.
- 호스트에서
nvidia-smi가 안 되는데 드라이버는 설치되어 있다면? 장치에 붙은 드라이버를 먼저 봅니다(lspci -nnk).vfio-pci가 붙어 있으면 설정대로의 동작입니다. - 이 GPU 를 컨테이너와 VM 이 함께 쓸 수 있는가? 패스스루 방식에서는 안 됩니다. 시분할이나 MIG, vGPU 를 검토해야 합니다.
9. 흔한 오해
- 「드라이버가 깨졌다」: 소유권이 다른 드라이버에 있는 것일 수 있습니다.
- 「패스스루하면 성능 손실이 없다」: 장치 자체의 성능은 거의 그대로이지만, 링크 폭, 리셋, 메모리 고정 같은 운영 비용이 따라옵니다. 예시 서버에서 성능 손실은 측정하지 않았습니다(미실측, 서비스가 도는 서버라 GPU 를 다시 패스스루로 돌려 시험하지 않았습니다). 외부 측정은 아래 「외부 근거」에 정리했습니다.
- 「IOMMU 는 따로 켜야 한다」: 예시 커널에서는 인자 없이도 켜져 있었습니다. 장비와 커널 설정에 따라 다르므로 그룹이 보이는지 먼저 확인합니다.
외부 근거: 패스스루의 성능 오버헤드
패스스루 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 가 쟁점이 아님 |
- 판정: 제한적으로 지지. 「장치 자체의 성능은 거의 그대로」는 외부 측정이 보고하는 0~5% 범위와 일치합니다. 다만 확인된 수치는 초록, 벤더 글, 장치를 포화시키지 않는 영역이어서 우리 GPU 와 같은 조건의 수치는 얻지 못했습니다. 링크 폭, 큰 페이지, IOMMU 설정을 변수로 놓고 잰 1차 자료는 찾지 못했습니다.
- 「IOMMU 는 따로 켜야 한다」는 KubeVirt 문서가 커널 인자(
intel_iommu=on,amd_iommu=on)를 켜라고 안내하는 것과 배포판 기본값이 다르다는 것을 함께 보면, 이 글의 「그룹이 보이는지 먼저 확인」이 안전한 서술입니다.
확인하지 못한 것
- GPU VM 이 시작할 때 실제로 고정한 메모리 크기(6절의 해석은 로그와 일치하는 추정).
- PCIe 링크가 x8 로 낮아진 원인.
- 패스스루 VM 안에서의 GPU 성능(컨테이너와 비교한 처리량 미실측. 운영 서비스가 같은 서버에서 도는 환경이라 GPU 를 VM 에 다시 넘기는 시험을 하지 않았고, 외부 측정으로 대신했습니다).
- 재부팅 후에도
nvidia가 자동으로 붙는지(재부팅 없이 전환했고, 재부팅은 시험하지 않음). - 이 글의 원리 설명이 인용하는 커널 VFIO 문서와 KubeVirt 호스트 장치 문서를 이번에 다시 열어 대조하지 않았습니다.
참고 자료
- 커널 VFIO 문서: https://www.kernel.org/doc/html/latest/driver-api/vfio.html
- KubeVirt 호스트 장치 사용자 가이드: https://kubevirt.io/user-guide/compute/host-devices/
확인한 버전과 날짜
2026-10-05 기준. 호스트 커널 7.0.0-34-generic, NVIDIA 드라이버 패키지 595.91.07(호스트에 설치), KubeVirt HostDevices 기능 게이트 사용.