libvirt / KVM 관리
libvirt는 KVM, QEMU, LXC, Xen 등 다양한 하이퍼바이저를 통합 관리하는 오픈소스 가상화 API 라이브러리다. virsh, XML 도메인 설정, UEFI firmware auto-selection, Secure Boot/TPM, 네트워크/스토리지 관리, 스냅샷, 라이브 마이그레이션, Python API, virtiofsd까지 커널 개발자를 위한 종합 가이드.
libvirt 개요 및 아키텍처
libvirt는 하이퍼바이저 독립적인 가상화 관리 인터페이스를 제공하는 미들웨어 계층이다. 클라이언트-서버 구조로 동작하며, libvirtd 데몬이 하이퍼바이저와 통신한다.
드라이버 모델
libvirt는 각 하이퍼바이저마다 드라이버 플러그인을 제공한다.
| 드라이버 | 하이퍼바이저 | 연결 URI |
|---|---|---|
| QEMU/KVM | QEMU with KVM | qemu:///system |
| LXC | Linux Containers | lxc:/// |
| Xen | Xen 하이퍼바이저 | xen:/// |
| OpenVZ | OpenVZ | openvz:///system |
| 원격 | TCP/TLS/SSH 터널 | qemu+ssh://host/system |
주요 도구 비교
| 도구 | 인터페이스 | 용도 |
|---|---|---|
virsh | CLI | 도메인/네트워크/스토리지 관리 (자동화에 적합) |
virt-manager | GUI | 데스크탑 GUI 관리 |
virt-install | CLI | VM 생성 전문 도구 |
virt-viewer | GUI | VM 콘솔 표시 (VNC/Spice) |
| Python libvirt | API | 프로그래밍 자동화 |
libvirtd vs 모듈식 데몬 (libvirt 6.0+)
libvirt 6.0부터 단일 libvirtd 대신 기능별로 분리된 모듈식 데몬 아키텍처를 지원한다. 각 하이퍼바이저/스토리지/네트워크 기능이 독립 데몬으로 분리되어 보안성과 안정성이 향상된다.
| 데몬 | 역할 | 소켓 경로 |
|---|---|---|
virtproxyd | 클라이언트 프록시 (하위 호환) | /run/libvirt/virtproxyd-sock |
virtqemud | QEMU/KVM 도메인 관리 | /run/libvirt/virtqemud-sock |
virtstoraged | 스토리지 풀/볼륨 관리 | /run/libvirt/virtstoraged-sock |
virtnetworkd | 가상 네트워크 관리 | /run/libvirt/virtnetworkd-sock |
virtnodedevd | 호스트 노드 디바이스 | /run/libvirt/virtnodedevd-sock |
virtsecretd | 시크릿(암호) 관리 | /run/libvirt/virtsecretd-sock |
virtnwfilterd | 네트워크 필터 관리 | /run/libvirt/virtnwfilterd-sock |
virtinterfaced | 호스트 네트워크 인터페이스 | /run/libvirt/virtinterfaced-sock |
RPC 통신 메커니즘
libvirt 클라이언트와 데몬 간 통신은 XDR(eXternal Data Representation) 직렬화 기반 RPC 프로토콜을 사용한다.
| 전송 방식 | URI 예시 | 특징 |
|---|---|---|
| UNIX 도메인 소켓 | qemu:///system | 로컬 전용, 가장 빠름, root/libvirt 그룹 접근 |
| SSH 터널 | qemu+ssh://host/system | 원격 접근, SSH 키 인증, 방화벽 불필요 |
| TLS (x509) | qemu+tls://host/system | 원격 접근, 인증서 기반, 포트 16514 |
| TCP (비보안) | qemu+tcp://host/system | 테스트용, 포트 16509, 운영 환경 비권장 |
libvirt 핵심 객체 모델
| 객체 타입 | C 타입 | Python 타입 | 설명 |
|---|---|---|---|
| 연결 | virConnectPtr | virConnect | 하이퍼바이저 연결 핸들 |
| 도메인 | virDomainPtr | virDomain | VM 인스턴스 (실행/정지 모두) |
| 네트워크 | virNetworkPtr | virNetwork | 가상 네트워크 |
| 스토리지 풀 | virStoragePoolPtr | virStoragePool | 스토리지 컨테이너 |
| 스토리지 볼륨 | virStorageVolPtr | virStorageVol | 개별 디스크 이미지 |
| 인터페이스 | virInterfacePtr | virInterface | 호스트 네트워크 인터페이스 |
| 시크릿 | virSecretPtr | virSecret | 암호화 키/비밀값 |
설치 및 초기 설정
패키지 설치
# Ubuntu / Debian
sudo apt install -y libvirt-daemon-system libvirt-clients \
virtinst virt-manager qemu-kvm
# Fedora / RHEL / CentOS
sudo dnf install -y libvirt libvirt-client virt-install \
virt-manager qemu-kvm
# Arch Linux
sudo pacman -S libvirt virt-install virt-manager qemu
권한 설정
# 현재 사용자를 libvirt, kvm 그룹에 추가
sudo usermod -aG libvirt,kvm $USER
# 로그아웃 후 재로그인 또는 즉시 적용
newgrp libvirt
# libvirtd 서비스 시작 및 자동 시작 설정
sudo systemctl enable --now libvirtd
# 연결 테스트
virsh -c qemu:///system list --all
네트워크 초기화 (virbr0)
# 기본 NAT 네트워크 (default) 시작
virsh net-start default
virsh net-autostart default
# 네트워크 목록 확인
virsh net-list --all
# virbr0 브리지 확인
ip link show virbr0
ip addr show virbr0
libvirtd.conf 주요 설정 옵션
/etc/libvirt/libvirtd.conf 파일에서 데몬 동작을 제어한다.
# /etc/libvirt/libvirtd.conf 주요 항목
# TCP 리스닝 활성화 (TLS 사용 권장)
listen_tls = 1
listen_tcp = 0
# 리스닝 주소 (0.0.0.0 = 모든 인터페이스)
listen_addr = "0.0.0.0"
# TCP 인증 방식 (none / sasl / tls)
auth_tcp = "sasl"
auth_tls = "none"
# 최대 클라이언트 연결 수
max_clients = 20
max_queued_clients = 20
# 유닉스 소켓 권한
unix_sock_group = "libvirt"
unix_sock_ro_perms = "0777"
unix_sock_rw_perms = "0770"
# 감사 로깅
audit_level = 1
audit_logging = 1
# 설정 변경 후 재시작
sudo systemctl restart libvirtd
qemu.conf 주요 설정
/etc/libvirt/qemu.conf는 QEMU 프로세스 실행 컨텍스트를 제어한다.
# /etc/libvirt/qemu.conf 주요 항목
# QEMU 프로세스 실행 사용자/그룹
user = "qemu"
group = "qemu"
# 보안 드라이버 (selinux / apparmor / none)
security_driver = "selinux"
# HugePage 디렉토리
hugetlbfs_mount = "/dev/hugepages"
# UEFI 펌웨어 경로 (NVRAM 템플릿)
nvram = [
"/usr/share/OVMF/OVMF_CODE.fd:/usr/share/OVMF/OVMF_VARS.fd",
"/usr/share/AAVMF/AAVMF_CODE.fd:/usr/share/AAVMF/AAVMF_VARS.fd"
]
# 메모리 잠금 허용 (Huge Pages / vfio에 필요)
lock_manager = "lockd"
# vnc 리스닝 주소
vnc_listen = "0.0.0.0"
# QEMU 프로세스 stdio 핸들러
stdio_handler = "logd"
<os firmware='efi'>와 firmware feature를 통해 의도를 표현하는 편이 좋다. 위 nvram 배열은 구버전 호환과 수동 템플릿 이해에는 여전히 중요하지만, 실제 자동 선택 가능 여부는 virsh domcapabilities로 확인하는 편이 정확하다.
모듈식 데몬 활성화 (virtqemud)
# libvirt 6.0+ 모듈식 데몬 활성화
# virtqemud 소켓 및 서비스 활성화
sudo systemctl enable --now virtqemud.socket
sudo systemctl enable --now virtqemud.service
# 스토리지 데몬
sudo systemctl enable --now virtstoraged.socket
# 네트워크 데몬
sudo systemctl enable --now virtnetworkd.socket
# 모듈식 데몬 사용 시 URI
virsh -c qemu+unix:///system list --all
# 기존 libvirtd와 충돌 방지 - libvirtd 비활성화
sudo systemctl disable --now libvirtd.socket libvirtd.service
TLS 인증 설정 (원격 보안 연결)
# CA 인증서 생성 (certtool 사용)
certtool --generate-privkey > cakey.pem
certtool --generate-self-signed --load-privkey cakey.pem \
--template ca.info > cacert.pem
# 서버 키/인증서 생성
certtool --generate-privkey > serverkey.pem
certtool --generate-certificate --load-privkey serverkey.pem \
--load-ca-certificate cacert.pem \
--load-ca-privkey cakey.pem \
--template server.info > servercert.pem
# 인증서 설치
sudo mkdir -p /etc/pki/CA /etc/pki/libvirt/private
sudo cp cacert.pem /etc/pki/CA/
sudo cp servercert.pem /etc/pki/libvirt/
sudo cp serverkey.pem /etc/pki/libvirt/private/
# TLS 원격 연결 테스트
virsh -c qemu+tls://remote-host/system list --all
virsh 원격 SSH 연결 설정
# ~/.ssh/config 설정
Host virt-host
HostName 192.168.1.100
User admin
IdentityFile ~/.ssh/virt_rsa
ServerAliveInterval 60
# SSH 키 기반 원격 virsh 연결
virsh -c qemu+ssh://virt-host/system list --all
# 원격 VM 마이그레이션을 위한 호스트 간 SSH 키 교환
ssh-copy-id -i ~/.ssh/id_rsa.pub root@virt-host2
방화벽 설정
# libvirt TLS 포트 허용 (firewalld)
sudo firewall-cmd --permanent --add-port=16514/tcp # TLS
sudo firewall-cmd --permanent --add-port=16509/tcp # TCP (비권장)
sudo firewall-cmd --permanent --add-port=49152-49215/tcp # 마이그레이션 포트
sudo firewall-cmd --reload
# iptables 직접 설정
iptables -I INPUT -p tcp --dport 16514 -j ACCEPT
iptables -I INPUT -p tcp --dport 49152:49215 -j ACCEPT
virsh 명령어 체계
virsh는 libvirt의 기본 CLI 도구다. 도메인(VM), 네트워크, 스토리지를 관리한다.
도메인 관리
# 도메인 목록 (실행 중)
virsh list
# 모든 도메인 목록 (정지 포함)
virsh list --all
# 도메인 시작
virsh start myvm
# 도메인 안전 종료 (ACPI 신호)
virsh shutdown myvm
# 도메인 강제 종료
virsh destroy myvm
# 도메인 자동 시작 설정
virsh autostart myvm
# 도메인 일시 정지 / 재개
virsh suspend myvm
virsh resume myvm
# 도메인 삭제 (스토리지 포함)
virsh undefine myvm --remove-all-storage
상태 조회
# 도메인 상세 정보
virsh dominfo myvm
# 도메인 통계 (CPU, 메모리)
virsh domstats myvm
# 블록 I/O 통계
virsh domblkstat myvm vda
# 네트워크 통계
virsh domifstat myvm vnet0
# vCPU 정보
virsh vcpuinfo myvm
콘솔 연결
# 시리얼 콘솔 연결
virsh console myvm
# VNC 디스플레이 포트 확인
virsh vncdisplay myvm
# SSH로 연결 (게스트 IP 확인 후)
virsh domifaddr myvm
설정 편집
# XML 설정 실시간 편집
virsh edit myvm
# XML 덤프
virsh dumpxml myvm > myvm.xml
# XML에서 도메인 정의
virsh define myvm.xml
# XML에서 도메인 생성 및 시작 (임시)
virsh create myvm.xml
네트워크 관련 virsh 명령어
# 네트워크 목록
virsh net-list --all
# 네트워크 XML 확인
virsh net-dumpxml default
# 네트워크 XML 편집
virsh net-edit default
# DHCP 임대 목록 확인
virsh net-dhcp-leases default
# 네트워크 시작/중지/자동시작
virsh net-start mynet
virsh net-destroy mynet
virsh net-autostart mynet
# 네트워크 정의/삭제
virsh net-define mynet.xml
virsh net-undefine mynet
스토리지 관련 virsh 명령어
# 풀 목록
virsh pool-list --all
# 풀 정보
virsh pool-info mypool
# 풀 XML 확인
virsh pool-dumpxml mypool
# 풀 새로고침 (볼륨 목록 갱신)
virsh pool-refresh mypool
# 볼륨 목록
virsh vol-list mypool
# 볼륨 상세 정보
virsh vol-info --pool mypool myvm.qcow2
# 볼륨 경로 확인
virsh vol-path --pool mypool myvm.qcow2
# 볼륨 삭제
virsh vol-delete --pool mypool myvm.qcow2
# 볼륨 크기 조정
virsh vol-resize --pool mypool myvm.qcow2 50G
비밀 관리 (secret)
# 시크릿 XML 정의 (예: Ceph 인증 키)
cat << 'EOF' > ceph-secret.xml
<secret ephemeral='no' private='no'>
<description>Ceph RBD 인증 키</description>
<usage type='ceph'>
<name>client.libvirt secret</name>
</usage>
</secret>
EOF
virsh secret-define ceph-secret.xml
# 시크릿 값 설정 (base64 인코딩된 Ceph 키)
virsh secret-set-value \
--secret $(virsh secret-list | grep Ceph | awk '{print $1}') \
--base64 "AQDrBuBfYbBVBhAA..."
# 시크릿 목록 확인
virsh secret-list
# 시크릿 XML 확인
virsh secret-dumpxml <UUID>
CPU/메모리 핫플러그
# 실행 중 vCPU 수 변경 (최대 vCPU 내에서)
virsh setvcpus myvm 8 --live
# 영구 설정 변경
virsh setvcpus myvm 8 --config
# 실행 중 메모리 변경 (balloon 필요)
virsh setmem myvm 8G --live
# 최대 메모리 설정 (재시작 필요)
virsh setmaxmem myvm 16G --config
# 디바이스 핫플러그 (XML 파일로)
virsh attach-device myvm disk-hotplug.xml --live --config
블록 디바이스 관련 명령어
# 도메인 블록 디바이스 목록
virsh domblklist myvm
# 블록 디바이스 상세 정보
virsh domblkinfo myvm vda
# 블록 풀 (백킹 파일 통합)
virsh blockpull myvm vda --wait --verbose
# 라이브 블록 복사 (디스크 교체)
virsh blockcopy myvm vda /new/path/myvm-new.qcow2 \
--format qcow2 --wait --pivot
# 블록 커밋 (스냅샷 병합)
virsh blockcommit myvm vda --active --pivot --wait
# 블록 I/O 통계
virsh domblkstat myvm vda --human
인터페이스 관련 명령어
# 도메인 네트워크 인터페이스 목록
virsh domiflist myvm
# 인터페이스 핫플러그 추가
virsh attach-interface myvm network default \
--model virtio --live --config
# 인터페이스 제거
virsh detach-interface myvm bridge br0 \
--mac 52:54:00:xx:xx:xx --live --config
# 인터페이스 통계
virsh domifstat myvm vnet0
virsh 배치 처리 스크립트
#!/bin/bash
# 여러 도메인 일괄 스냅샷 생성
DOMAINS=$(virsh list --name)
SNAP_NAME="backup-$(date +%Y%m%d-%H%M%S)"
for dom in $DOMAINS; do
echo "스냅샷 생성: $dom / $SNAP_NAME"
virsh snapshot-create-as "$dom" "$SNAP_NAME" \
"자동 백업 $SNAP_NAME" --atomic
done
# 일괄 종료 후 재시작
for dom in $DOMAINS; do
virsh shutdown "$dom"
done
sleep 30
for dom in $DOMAINS; do
virsh start "$dom"
done
# 도메인별 IP 주소 일괄 출력
virsh list --name | while read dom; do
[ -z "$dom" ] && continue
echo -n "$dom: "
virsh domifaddr "$dom" 2>/dev/null | grep -oP '\d+\.\d+\.\d+\.\d+'
done
XML 도메인 설정 심화
libvirt는 XML로 도메인 전체 설정을 기술한다. 커널 개발에서 자주 사용하는 설정 요소를 정리한다.
기본 도메인 XML 구조
<domain type='kvm'>
<name>myvm</name>
<memory unit='GiB'>4</memory>
<currentMemory unit='GiB'>4</currentMemory>
<vcpu placement='static'>4</vcpu>
<os>
<type arch='x86_64' machine='q35'>hvm</type>
<boot dev='hd'/>
</os>
<features>
<acpi/><apic/>
</features>
</domain>
CPU 토폴로지 설정
<!-- CPU 토폴로지: 2소켓 x 2코어 x 2스레드 = 8 vCPU -->
<cpu mode='host-passthrough' check='none'>
<topology sockets='2' cores='2' threads='2'/>
<numa>
<cell id='0' cpus='0-3' memory='2' unit='GiB'/>
<cell id='1' cpus='4-7' memory='2' unit='GiB'/>
</numa>
</cpu>
vCPU 핀닝 설정
<!-- vCPU 핀닝: 게스트 vCPU를 호스트 CPU에 고정 -->
<cputune>
<vcpupin vcpu='0' cpuset='2'/>
<vcpupin vcpu='1' cpuset='3'/>
<vcpupin vcpu='2' cpuset='6'/>
<vcpupin vcpu='3' cpuset='7'/>
<emulatorpin cpuset='0-1'/>
</cputune>
Huge Pages 메모리 설정
<!-- 1GB HugePage 사용 -->
<memoryBacking>
<hugepages>
<page size='1' unit='GiB'/>
</hugepages>
<locked/>
</memoryBacking>
<!-- 호스트에서 HugePage 설정 (사전 필요) -->
# echo 4 > /sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages
디스크 디바이스 설정
<devices>
<!-- virtio-blk 디스크 -->
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2' cache='none' io='native'/>
<source file='/var/lib/libvirt/images/myvm.qcow2'/>
<target dev='vda' bus='virtio'/>
<address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
</disk>
<!-- virtio-net 네트워크 인터페이스 -->
<interface type='network'>
<source network='default'/>
<model type='virtio'/>
</interface>
<!-- 시리얼 콘솔 -->
<serial type='pty'>
<target port='0'/>
</serial>
<console type='pty'>
<target type='serial' port='0'/>
</console>
</devices>
UEFI Secure Boot + TPM 2.0 XML
libvirt는 도메인 XML에서 "어떤 펌웨어 파일을 직접 쓸지"보다 "어떤 펌웨어 기능이 필요한지"를 먼저 기술하는 방식이 점점 중요해지고 있다. Secure Boot 실습 VM이라면 보통 EFI 펌웨어, pre-enrolled keys, TPM 2.0 에뮬레이터를 함께 선언한다.
<domain type='kvm'>
<name>sb-lab</name>
<os firmware='efi'>
<type arch='x86_64' machine='q35'>hvm</type>
<firmware>
<feature enabled='yes' name='secure-boot'/>
<feature enabled='yes' name='enrolled-keys'/>
</firmware>
</os>
<features>
<acpi/><apic/>
<smm state='on'/>
</features>
<devices>
<tpm model='tpm-tis'>
<backend type='emulator' version='2.0'/>
</tpm>
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='/var/lib/libvirt/images/sb-lab.qcow2'/>
<target dev='vda' bus='virtio'/>
</disk>
<interface type='network'>
<source network='default'/>
<model type='virtio'/>
</interface>
</devices>
</domain>
# 호스트가 어떤 EFI firmware feature를 지원하는지 확인
virsh domcapabilities --machine q35 --arch x86_64 | less
# TPM 2.0 에뮬레이터 패키지 확인 (libvirt가 내부적으로 swtpm 사용)
which swtpm
# UEFI VM을 먼저 생성한 뒤 XML 수정 워크플로도 흔하다
virt-install \
--name sb-lab \
--memory 4096 \
--vcpus 2 \
--disk size=20,format=qcow2,bus=virtio \
--cdrom ./debian-13-netinst.iso \
--network network=default,model=virtio \
--boot uefi \
--tpm backend.type=emulator,backend.version=2.0
loader/nvram 값이 XML에 남아 있을 수 있다. 나중에 Secure Boot 정책이나 enrolled keys 여부를 바꾸려면, 생성된 XML이 여전히 예전 OVMF 조합을 가리키는지 먼저 확인해야 한다.
완전한 고성능 도메인 XML 예시
<domain type='kvm'>
<name>highperf-vm</name>
<uuid>550e8400-e29b-41d4-a716-446655440000</uuid>
<memory unit='GiB'>16</memory>
<vcpu placement='static'>8</vcpu>
<!-- OS 및 펌웨어 -->
<os firmware='efi'>
<type arch='x86_64' machine='q35'>hvm</type>
<boot dev='hd'/>
</os>
<!-- HyperV 계몽 + KVM 은닉 -->
<features>
<acpi/><apic/>
<hyperv mode='custom'>
<relaxed state='on'/>
<vapic state='on'/>
<spinlocks state='on' retries='8191'/>
<vpindex state='on'/>
<runtime state='on'/>
<synic state='on'/>
<stimer state='on'/>
<frequencies state='on'/>
</hyperv>
<kvm>
<hidden state='on'/>
</kvm>
<ioapic driver='kvm'/>
</features>
<!-- CPU 호스트 패스스루 -->
<cpu mode='host-passthrough' check='none' migratable='on'>
<topology sockets='1' dies='1' cores='4' threads='2'/>
</cpu>
<!-- vCPU 핀닝 + IOThread -->
<cputune>
<vcpupin vcpu='0' cpuset='4'/>
<vcpupin vcpu='1' cpuset='5'/>
<vcpupin vcpu='2' cpuset='6'/>
<vcpupin vcpu='3' cpuset='7'/>
<vcpupin vcpu='4' cpuset='12'/>
<vcpupin vcpu='5' cpuset='13'/>
<vcpupin vcpu='6' cpuset='14'/>
<vcpupin vcpu='7' cpuset='15'/>
<emulatorpin cpuset='0-1'/>
<iothreadpin iothread='1' cpuset='2-3'/>
</cputune>
<!-- 1GB HugePage 메모리 -->
<memoryBacking>
<hugepages>
<page size='1' unit='GiB'/>
</hugepages>
<locked/><discard/>
</memoryBacking>
<iothreads>1</iothreads>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<!-- 고성능 NVMe 스타일 디스크 -->
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2' cache='none' io='native'
iothread='1' discard='unmap'/>
<source file='/var/lib/libvirt/images/highperf.qcow2'/>
<target dev='vda' bus='virtio'/>
</disk>
<!-- virtio-net multiqueue -->
<interface type='network'>
<source network='default'/>
<model type='virtio'/>
<driver name='vhost' queues='8'/>
</interface>
<!-- virtio-rng (엔트로피 소스) -->
<rng model='virtio'>
<backend model='random'>/dev/urandom</backend>
</rng>
<!-- watchdog -->
<watchdog model='itco' action='reset'/>
<serial type='pty'><target port='0'/></serial>
<console type='pty'><target type='serial' port='0'/></console>
</devices>
</domain>
IOMMU/VFIO PCI 패스스루 설정
# 1. 호스트 IOMMU 활성화 (커널 파라미터)
# GRUB_CMDLINE_LINUX에 추가: intel_iommu=on iommu=pt
sudo vim /etc/default/grub
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
# 2. PCI 디바이스 IOMMU 그룹 확인
for d in /sys/kernel/iommu_groups/*/devices/*; do
n=${d#*/iommu_groups/*}; n=${n%%/*}
printf 'IOMMU Group %s ' "$n"
lspci -nns "${d##*/}"
done
# 3. vfio-pci 드라이버 바인딩
echo "10de 1eb8" > /sys/bus/pci/drivers/vfio-pci/new_id
<!-- PCI 패스스루 디바이스 (VFIO) -->
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</source>
<address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
</hostdev>
<!-- IOMMU 디바이스 (vIOMMU) 활성화 -->
<iommu model='intel'>
<driver intremap='on' caching_mode='on' eim='on' iotlb='on'/>
</iommu>
USB 패스스루 설정
<!-- USB 호스트 디바이스 패스스루 -->
<hostdev mode='subsystem' type='usb' managed='yes'>
<source>
<!-- vendorid/productid로 지정 -->
<vendor id='0x046d'/>
<product id='0xc52b'/>
</source>
</hostdev>
<!-- USB 버스/디바이스 번호로 지정 -->
<hostdev mode='subsystem' type='usb' managed='no'>
<source>
<address bus='2' device='5'/>
</source>
</hostdev>
SPICE/VNC 그래픽 설정
<!-- VNC 그래픽 -->
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1'>
<listen type='address' address='127.0.0.1'/>
</graphics>
<!-- SPICE 그래픽 (고성능, USB 리다이렉션 지원) -->
<graphics type='spice' autoport='yes'>
<listen type='address'/>
<image compression='auto_glz'/>
<streaming mode='filter'/>
<gl enable='no'/>
</graphics>
<!-- 비디오 디바이스 -->
<video>
<model type='virtio' heads='1' primary='yes'>
<acceleration accel3d='no'/>
</model>
</video>
features 설정 심화 (hyperv enlightenments)
<features>
<acpi/><apic/>
<!-- Windows 게스트 최적화 -->
<hyperv mode='custom'>
<relaxed state='on'/> <!-- 타이머 완화 -->
<vapic state='on'/> <!-- 가상 APIC -->
<spinlocks state='on' retries='8191'/>
<vpindex state='on'/> <!-- 가상 프로세서 인덱스 -->
<runtime state='on'/> <!-- 런타임 계몽 -->
<synic state='on'/> <!-- 합성 인터럽트 컨트롤러 -->
<stimer state='on'>
<direct state='on'/>
</stimer>
<frequencies state='on'/> <!-- 레퍼런스 TSC 주파수 -->
<reenlightenment state='on'/>
<tlbflush state='on'/>
</hyperv>
<!-- KVM 은닉 (게스트가 VM임을 숨김) -->
<kvm>
<hidden state='on'/>
</kvm>
<!-- IOAPIC KVM 처리 -->
<ioapic driver='kvm'/>
</features>
네트워크 관리
NAT 네트워크 (기본 virbr0)
# 기본 NAT 네트워크 XML 확인
virsh net-dumpxml default
<network>
<name>default</name>
<forward mode='nat'>
<nat><port start='1024' end='65535'/></nat>
</forward>
<bridge name='virbr0' stp='on' delay='0'/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254'/>
</dhcp>
</ip>
</network>
브리지 네트워크 (물리 인터페이스 연결)
# 호스트에서 브리지 생성 (NetworkManager 사용)
nmcli con add type bridge ifname br0 con-name br0
nmcli con add type bridge-slave ifname eth0 master br0
nmcli con up br0
<network>
<name>bridged</name>
<forward mode='bridge'/>
<bridge name='br0'/>
</network>
SR-IOV VF 직접 할당
<!-- SR-IOV VF를 게스트에 직접 할당 -->
<interface type='hostdev' managed='yes'>
<source>
<address type='pci' domain='0x0000' bus='0x01' slot='0x10' function='0x0'/>
</source>
<model type='virtio'/>
</interface>
<!-- macvtap 방식 -->
<interface type='direct'>
<source dev='eth0' mode='bridge'/>
<model type='virtio'/>
</interface>
Isolated 네트워크 (내부 통신 전용)
<!-- 외부 연결 없는 VM 간 내부 전용 네트워크 -->
<network>
<name>isolated</name>
<!-- forward 없음 = 완전 격리 -->
<bridge name='virbr1' stp='on' delay='0'/>
<ip address='10.10.10.1' netmask='255.255.255.0'>
<dhcp>
<range start='10.10.10.2' end='10.10.10.254'/>
</dhcp>
</ip>
</network>
네트워크 필터 (nwfilter)
네트워크 필터는 VM별로 방화벽 규칙을 적용한다. iptables/nftables 규칙을 libvirt가 자동 관리한다.
<!-- nwfilter XML 정의 (예: ARP 스푸핑 방지) -->
<filter name='no-arp-spoofing' chain='arp'>
<uuid>f88f1932-debf-4aa1-9fbe-f10d3aa4bc95</uuid>
<rule action='drop' direction='out' priority='300'>
<arp arpsrcmacaddr='$MAC' arpsrcipaddr='$IP' match='no'/>
</rule>
<rule action='accept' direction='out' priority='500'>
<arp arpsrcmacaddr='$MAC' arpsrcipaddr='$IP'/>
</rule>
</filter>
<!-- 도메인 인터페이스에 nwfilter 적용 -->
<interface type='network'>
<source network='default'/>
<model type='virtio'/>
<filterref filter='no-arp-spoofing'>
<parameter name='MAC' value='52:54:00:12:34:56'/>
<parameter name='IP' value='192.168.122.100'/>
</filterref>
</interface>
VLAN 태깅 설정
<!-- VLAN 태깅이 있는 네트워크 (브리지 + VLAN) -->
<network>
<name>vlan100</name>
<forward mode='bridge'/>
<bridge name='br0'/>
<vlan trunk='no'>
<tag id='100'/>
</vlan>
</network>
<!-- 도메인 인터페이스에서 VLAN 직접 설정 -->
<interface type='bridge'>
<source bridge='br0'/>
<vlan>
<tag id='200'/>
</vlan>
<model type='virtio'/>
</interface>
네트워크 대역폭 제한 (QoS)
<!-- 인터페이스별 대역폭 제한 -->
<interface type='network'>
<source network='default'/>
<model type='virtio'/>
<bandwidth>
<!-- inbound: VM이 받는 트래픽 제한 -->
<inbound average='100000' peak='200000' burst='256'/>
<!-- outbound: VM이 보내는 트래픽 제한 (KB/s) -->
<outbound average='50000' peak='100000'/>
</bandwidth>
</interface>
스토리지 풀 관리
libvirt는 스토리지 풀을 통해 VM 이미지와 볼륨을 체계적으로 관리한다.
풀 타입
| 풀 타입 | 설명 | 용도 |
|---|---|---|
dir | 로컬 디렉토리 | 기본 이미지 저장소 |
logical | LVM 볼륨 그룹 | 고성능 블록 스토리지 |
netfs | NFS/CIFS 원격 FS | 공유 스토리지 |
rbd | Ceph RBD | 분산 스토리지 |
iscsi | iSCSI 타겟 | SAN 스토리지 |
disk | 전체 디스크 파티션 | 로컬 디스크 직접 사용 |
gluster | GlusterFS 볼륨 | 스케일-아웃 분산 스토리지 |
dir 풀 설정 및 볼륨 관리
# dir 풀 생성
virsh pool-define-as mypool dir --target /var/lib/libvirt/myimages
virsh pool-build mypool
virsh pool-start mypool
virsh pool-autostart mypool
# 풀 목록 확인
virsh pool-list --all
# 볼륨 생성 (qcow2, 20GB)
virsh vol-create-as mypool myvm.qcow2 20G --format qcow2
# 볼륨 목록
virsh vol-list mypool
# 볼륨 정보
virsh vol-info myvm.qcow2 --pool mypool
LVM 풀 설정 및 볼륨 관리
LVM 풀은 기존 볼륨 그룹을 libvirt에서 관리한다. 씬 프로비저닝과 빠른 스냅샷을 지원한다.
# 호스트에서 LVM 볼륨 그룹 생성
sudo pvcreate /dev/sdb
sudo vgcreate vg_vms /dev/sdb
# libvirt LVM 풀 정의
virsh pool-define-as lvm-pool logical \
--source-name vg_vms --target /dev/vg_vms
virsh pool-start lvm-pool
virsh pool-autostart lvm-pool
# LVM 볼륨 생성 (raw 형식)
virsh vol-create-as lvm-pool myvm-lv 20G --format raw
# LVM 볼륨 경로 확인
virsh vol-path --pool lvm-pool myvm-lv
# 출력: /dev/vg_vms/myvm-lv
<!-- LVM 풀 XML 예시 -->
<pool type='logical'>
<name>lvm-pool</name>
<source>
<device path='/dev/sdb'/>
<name>vg_vms</name>
<format type='lvm2'/>
</source>
<target>
<path>/dev/vg_vms</path>
</target>
</pool>
NFS 풀 설정 (netfs 타입)
<!-- NFS 풀 XML -->
<pool type='netfs'>
<name>nfs-pool</name>
<source>
<host name='nfs-server.local'/>
<dir path='/exports/libvirt'/>
<format type='nfs'/>
</source>
<target>
<path>/var/lib/libvirt/nfs-images</path>
<permissions>
<mode>0755</mode>
</permissions>
</target>
</pool>
# NFS 풀 생성 및 시작
virsh pool-define nfs-pool.xml
virsh pool-start nfs-pool
virsh pool-autostart nfs-pool
iSCSI 풀 설정
<!-- iSCSI 풀 XML -->
<pool type='iscsi'>
<name>iscsi-pool</name>
<source>
<host name='iscsi-target.local'/>
<device path='iqn.2023-01.com.example:storage1'/>
<!-- CHAP 인증 (시크릿 UUID 참조) -->
<auth type='chap' username='libvirt'>
<secret usage='iscsi-chap'/>
</auth>
</source>
<target>
<path>/dev/disk/by-path</path>
</target>
</pool>
Ceph RBD 풀 설정
<!-- Ceph RBD 풀 XML -->
<pool type='rbd'>
<name>ceph-pool</name>
<source>
<host name='ceph-mon1.local' port='6789'/>
<host name='ceph-mon2.local' port='6789'/>
<name>rbd</name>
<auth type='ceph' username='libvirt'>
<secret uuid='550e8400-e29b-41d4-a716-446655440001'/>
</auth>
</source>
</pool>
# Ceph 시크릿 등록
cat << 'EOF' > ceph-secret.xml
<secret ephemeral='no' private='no'>
<uuid>550e8400-e29b-41d4-a716-446655440001</uuid>
<usage type='ceph'>
<name>client.libvirt secret</name>
</usage>
</secret>
EOF
virsh secret-define ceph-secret.xml
virsh secret-set-value 550e8400-e29b-41d4-a716-446655440001 \
--base64 "$(ceph auth get-key client.libvirt | base64)"
# Ceph RBD 볼륨 생성
virsh vol-create-as ceph-pool myvm-rbd 20G --format raw
볼륨 클론 및 업로드/다운로드
# 볼륨 클론 (같은 풀 내)
virsh vol-clone --pool mypool myvm.qcow2 myvm-clone.qcow2
# 볼륨 업로드 (로컬 파일 → 풀)
virsh vol-upload --pool mypool myvm.qcow2 /tmp/myvm.qcow2 \
--sparse
# 볼륨 다운로드 (풀 → 로컬 파일)
virsh vol-download --pool mypool myvm.qcow2 /backup/myvm-backup.qcow2 \
--sparse
# 볼륨 크기 조정 (확장만 가능)
virsh vol-resize --pool mypool myvm.qcow2 50G
qcow2 스냅샷 체인
# 백킹 파일 기반 qcow2 스냅샷 체인 생성
qemu-img create -f qcow2 base.qcow2 20G
qemu-img create -f qcow2 -b base.qcow2 -F qcow2 snap1.qcow2
qemu-img create -f qcow2 -b snap1.qcow2 -F qcow2 snap2.qcow2
# 스냅샷 체인 확인
qemu-img info --backing-chain snap2.qcow2
스냅샷 & 마이그레이션
내부 스냅샷 (qcow2 내부)
# 내부 스냅샷 생성 (VM 일시 정지 후)
virsh snapshot-create-as myvm snap1 "스냅샷 설명" --atomic
# 스냅샷 목록
virsh snapshot-list myvm
# 스냅샷 정보
virsh snapshot-info myvm snap1
# 스냅샷으로 복원
virsh snapshot-revert myvm snap1 --running
# 스냅샷 삭제
virsh snapshot-delete myvm snap1
외부 스냅샷 (분리된 파일)
# 외부 스냅샷 생성 (실행 중에도 가능)
virsh snapshot-create-as myvm snap-ext \
--disk-only \
--diskspec vda,snapshot=external,file=/tmp/snap-vda.qcow2 \
--atomic
# 블록 커밋으로 스냅샷 병합 (live block commit)
virsh blockcommit myvm vda --active --pivot
체크포인트 (백업용)
체크포인트는 증분 백업을 위해 어느 블록이 변경되었는지 추적한다. libvirt 6.0+, QEMU 4.2+에서 지원한다.
# 체크포인트 생성
virsh checkpoint-create-as myvm chk1 --diskspec vda,checkpoint=bitmap
# 체크포인트 목록
virsh checkpoint-list myvm
# 체크포인트 XML 확인
virsh checkpoint-dumpxml myvm chk1
# 체크포인트 삭제
virsh checkpoint-delete myvm chk1
라이브 마이그레이션
# 오프라인 마이그레이션
virsh migrate myvm qemu+ssh://destination-host/system
# 라이브 마이그레이션 (무중단)
virsh migrate --live myvm qemu+ssh://destination-host/system
# 공유 스토리지 없는 라이브 마이그레이션 (디스크도 전송)
virsh migrate --live --copy-storage-all \
myvm qemu+ssh://destination-host/system
# 마이그레이션 상태 확인
virsh domjobinfo myvm
마이그레이션 압축 및 고급 옵션
# xbzrle 압축 마이그레이션 (반복 패이지 압축)
virsh migrate --live --compressed \
--comp-methods xbzrle \
myvm qemu+ssh://dest-host/system
# multifd 병렬 마이그레이션 (다중 스트림)
virsh migrate --live \
--parallel --parallel-connections 4 \
myvm qemu+ssh://dest-host/system
# 마이그레이션 속도 제한 (대역폭: Mbps)
virsh migrate-setspeed myvm 1000
# 최대 다운타임 설정 (ms)
virsh migrate-setmaxdowntime myvm 200
# post-copy 마이그레이션 (메모리를 점진적으로 전송)
virsh migrate --live --postcopy \
myvm qemu+ssh://dest-host/system
성능 튜닝 통합 정리
KVM/libvirt 환경에서 성능 최적화는 CPU 핀닝, NUMA 정책, Huge Pages, I/O 스케줄러 설정이 핵심이다.
CPU 핀닝 & NUMA 정책 적용 예시
<domain type='kvm'>
<vcpu placement='static'>8</vcpu>
<cpu mode='host-passthrough'>
<numa>
<cell id='0' cpus='0-3' memory='4' unit='GiB' memAccess='shared'/>
<cell id='1' cpus='4-7' memory='4' unit='GiB' memAccess='shared'/>
</numa>
</cpu>
<cputune>
<vcpupin vcpu='0' cpuset='4'/>
<vcpupin vcpu='1' cpuset='5'/>
<vcpupin vcpu='2' cpuset='6'/>
<vcpupin vcpu='3' cpuset='7'/>
<vcpupin vcpu='4' cpuset='12'/>
<vcpupin vcpu='5' cpuset='13'/>
<vcpupin vcpu='6' cpuset='14'/>
<vcpupin vcpu='7' cpuset='15'/>
<emulatorpin cpuset='0-1'/>
</cputune>
<memoryBacking>
<hugepages>
<page size='1' unit='GiB' nodeset='0'/>
<page size='1' unit='GiB' nodeset='1'/>
</hugepages>
<locked/>
</memoryBacking>
</domain>
IOThread 설정 (스토리지 I/O 분리)
IOThread는 스토리지 I/O를 전용 스레드로 분리하여 vCPU 간섭을 줄인다. NVMe급 성능이 요구되는 환경에 필수적이다.
<!-- IOThread 수 설정 -->
<iothreads>2</iothreads>
<!-- IOThread 핀닝 -->
<cputune>
<iothreadpin iothread='1' cpuset='2'/>
<iothreadpin iothread='2' cpuset='3'/>
</cputune>
<!-- 디스크에 IOThread 할당 -->
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2' cache='none' io='native'
iothread='1' discard='unmap'/>
<source file='/var/lib/libvirt/images/myvm.qcow2'/>
<target dev='vda' bus='virtio'/>
</disk>
스케줄러 정책 설정
<!-- vCPU 스케줄러 정책 (실시간 우선순위) -->
<cputune>
<vcpusched vcpus='0-7' scheduler='fifo' priority='1'/>
<iothreadsched iothreads='1' scheduler='fifo' priority='2'/>
</cputune>
ulimit -r 및 cgroup 설정으로 안전 장치를 마련해야 한다.
balloon 메모리 설정 (동적 메모리 조절)
<!-- virtio-balloon 디바이스 (메모리 동적 회수) -->
<memballoon model='virtio'>
<stats period='5'/>
</memballoon>
# balloon으로 메모리 동적 조절
virsh setmem myvm 4G --live
# balloon 통계 조회
virsh dommemstat myvm
게스트 CPU 플래그 최적화
| CPU 모드 | 설명 | 마이그레이션 | 성능 |
|---|---|---|---|
host-passthrough | 호스트 CPU 그대로 노출 | 동일 CPU만 가능 | 최고 |
host-model | 호스트 CPU 모델 추정 | 호환 CPU 간 가능 | 높음 |
custom | 특정 CPU 모델 지정 | 광범위한 호환 | 낮음 |
maximum | QEMU 지원 최대 기능 | TCG용 | 중간 |
virtio-net multiqueue 성능 설정
<!-- virtio-net multiqueue (queues = vCPU 수와 일치 권장) -->
<interface type='network'>
<source network='default'/>
<model type='virtio'/>
<driver name='vhost' queues='8'/>
</interface>
# 게스트에서 multiqueue 활성화
ethtool -L eth0 combined 8
I/O 최적화 설정
# 게스트 디스크 I/O 스케줄러 확인
cat /sys/block/vda/queue/scheduler
# cache='none': O_DIRECT (최고 성능, 데이터 보호 주의)
# cache='writeback': writeback 캐시 (기본, 성능/안전 균형)
# cache='writethrough': writethrough (안전 우선)
# I/O 통계 모니터링
virsh domblkstat myvm vda --human
성능 측정 명령어
# 종합 도메인 통계 (CPU, 메모리, 블록, 네트워크)
virsh domstats myvm --raw
# 블록 통계
virsh domblkstat myvm vda
# 네트워크 통계
virsh domifstat myvm vnet0
# CPU 사용률 확인 (반복)
watch -n 1 'virsh domstats myvm | grep cpu'
# 메모리 통계 (balloon 활성 시)
virsh dommemstat myvm
# 전체 도메인 통계 덤프
virsh domstats --raw --enforce --domain myvm \
--balloon --block --cpu-total --vcpu --net
성능 튜닝 체크리스트
| 항목 | 설정 | 효과 | 비고 |
|---|---|---|---|
| CPU 모드 | host-passthrough | 네이티브 명령어 사용 | 마이그레이션 제한 |
| vCPU 핀닝 | vcpupin | 캐시 미스 감소 | NUMA 토폴로지 고려 |
| Huge Pages | 1GB/2MB | TLB 미스 감소 | 호스트 사전 설정 필요 |
| 메모리 잠금 | <locked/> | 스왑 방지 | Huge Pages 필수 동반 |
| IOThread | vCPU와 별도 cpuset | I/O 지연 감소 | 디스크당 1개 권장 |
| 디스크 캐시 | cache='none' | 더블 버퍼 제거 | 배리어/fsync 주의 |
| 네트워크 큐 | queues=vCPU수 | 멀티코어 처리 | vhost 드라이버 사용 |
| NUMA 정책 | cell 기반 분할 | 원격 메모리 접근 감소 | numactl 병행 사용 |
| 투명 Huge Pages | 호스트 THP 비활성화 | 지터 감소 | never 설정 권장 |
| CPU Governor | performance | 주파수 조절 지연 제거 | 전력 소비 증가 |
libvirt Python API
libvirt Python 바인딩을 사용하면 VM 관리를 프로그래밍 방식으로 자동화할 수 있다. 커널 개발에서 테스트 VM 자동 프로비저닝, 부팅 검증, 로그 수집 등에 활용할 수 있다.
# Python libvirt 바인딩 설치
pip install libvirt-python
import libvirt
import sys
import xml.etree.ElementTree as ET
# libvirtd에 연결
conn = libvirt.open('qemu:///system')
if conn is None:
print('libvirt 연결 실패')
sys.exit(1)
# 실행 중인 도메인 목록
domains = conn.listAllDomains()
for dom in domains:
state, reason = dom.state()
print(f"{dom.name()}: state={state}")
# 특정 도메인 가져오기
dom = conn.lookupByName('myvm')
# 도메인 시작
if dom.state()[0] == libvirt.VIR_DOMAIN_SHUTOFF:
dom.create()
# 도메인 XML 파싱
xml_desc = dom.XMLDesc()
root = ET.fromstring(xml_desc)
vcpu_count = root.find('vcpu').text
print(f"vCPU 수: {vcpu_count}")
# 메모리 통계
mem_stats = dom.memoryStats()
print(f"사용 가능 메모리: {mem_stats.get('usable', 0) // 1024} MB")
conn.close()
커널 개발 자동화 스크립트 예제
import libvirt, time, subprocess
def boot_and_test_kernel(kernel_path, initrd_path):
"""커널 빌드 후 자동 부팅 테스트"""
conn = libvirt.open('qemu:///system')
try:
dom = conn.lookupByName('kernel-test')
if dom.state()[0] != libvirt.VIR_DOMAIN_SHUTOFF:
dom.destroy()
time.sleep(2)
except libvirt.libvirtError:
pass
subprocess.run([
'virt-install', '--name', 'kernel-test',
'--memory', '2048', '--vcpus', '2',
'--boot', f'kernel={kernel_path},initrd={initrd_path},'
'kernel_args="console=ttyS0 nokaslr panic=-1"',
'--graphics', 'none', '--noautoconsole'
])
time.sleep(10)
dom = conn.lookupByName('kernel-test')
state = dom.state()[0]
print("부팅 성공" if state == libvirt.VIR_DOMAIN_RUNNING else "부팅 실패")
conn.close()
이벤트 모니터링
import libvirt
def domain_event_callback(conn, dom, event, detail, opaque):
"""도메인 이벤트 콜백"""
event_str = {
libvirt.VIR_DOMAIN_EVENT_STARTED: "시작됨",
libvirt.VIR_DOMAIN_EVENT_SUSPENDED: "일시 정지됨",
libvirt.VIR_DOMAIN_EVENT_RESUMED: "재개됨",
libvirt.VIR_DOMAIN_EVENT_STOPPED: "중지됨",
libvirt.VIR_DOMAIN_EVENT_SHUTDOWN: "종료 중",
libvirt.VIR_DOMAIN_EVENT_CRASHED: "크래시됨",
}.get(event, f"알 수 없음({event})")
print(f"[이벤트] {dom.name()}: {event_str} (detail={detail})")
conn = libvirt.open('qemu:///system')
# 이벤트 루프 등록
libvirt.virEventRegisterDefaultImpl()
# 도메인 이벤트 구독
conn.domainEventRegisterAny(
None,
libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE,
domain_event_callback,
None
)
# 이벤트 루프 실행 (블로킹)
while True:
libvirt.virEventRunDefaultImpl()
도메인 스냅샷 Python API
import libvirt
conn = libvirt.open('qemu:///system')
dom = conn.lookupByName('myvm')
# 스냅샷 생성 XML
snap_xml = """
<domainsnapshot>
<name>test-snap</name>
<description>Python API 생성 스냅샷</description>
</domainsnapshot>
"""
snap = dom.snapshotCreateXML(snap_xml,
libvirt.VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC)
print(f"스냅샷 생성: {snap.getName()}")
# 스냅샷 목록
for s in dom.listAllSnapshots():
print(f" - {s.getName()}")
# 스냅샷 복원
snap = dom.snapshotLookupByName('test-snap')
dom.revertToSnapshot(snap)
conn.close()
네트워크/스토리지 관리 API
import libvirt
conn = libvirt.open('qemu:///system')
# 네트워크 목록
for net in conn.listAllNetworks():
active = "활성" if net.isActive() else "비활성"
print(f"네트워크: {net.name()} [{active}]")
# 특정 네트워크 조회
net = conn.networkLookupByName('default')
print(net.XMLDesc())
# 스토리지 풀 목록
for pool in conn.listAllStoragePools():
info = pool.info()
print(f"풀: {pool.name()}, 용량: {info[1]//1024//1024//1024}GB")
# 특정 풀의 볼륨 목록
pool = conn.storagePoolLookupByName('default')
for vol in pool.listAllVolumes():
print(f" 볼륨: {vol.name()}")
conn.close()
통계 수집 및 모니터링 스크립트
import libvirt, time, json
def collect_stats(interval=5):
"""도메인 통계를 주기적으로 수집"""
conn = libvirt.open('qemu:///system')
while True:
stats = {}
for dom in conn.listAllDomains(
libvirt.VIR_CONNECT_LIST_DOMAINS_RUNNING):
name = dom.name()
cpu_stats = dom.getCPUStats(True)
mem_stats = dom.memoryStats()
stats[name] = {
'cpu_time': cpu_stats[0].get('cpu_time', 0),
'mem_rss': mem_stats.get('rss', 0) // 1024,
'mem_available': mem_stats.get('usable', 0) // 1024,
}
print(json.dumps(stats, indent=2))
time.sleep(interval)
collect_stats()
virtiofsd (virtio-fs 데몬)
virtiofsd는 호스트 디렉토리를 게스트에 공유하는 virtio-fs의 사용자 공간 데몬이다. QEMU의 9p virtio보다 성능이 우수하며, DAX(Direct Access) 모드를 지원한다.
virtiofsd 시작
# virtiofsd 시작 (C 구현체, QEMU 내장)
sudo /usr/libexec/virtiofsd \
--socket-path=/var/run/virtiofsd.sock \
--shared-dir=/path/to/host/dir \
--cache=auto \
--sandbox=namespace
libvirt XML 설정
<devices>
<!-- virtiofs 파일시스템 공유 -->
<filesystem type='mount' accessmode='passthrough'>
<driver type='virtiofs'/>
<source dir='/path/to/host/dir'/>
<target dir='hostshare'/>
</filesystem>
</devices>
<!-- virtiofs는 shared 메모리 설정 필요 -->
<memoryBacking>
<source type='memfd'/>
<access mode='shared'/>
</memoryBacking>
# 게스트에서 마운트
mount -t virtiofs hostshare /mnt/host
# /etc/fstab에 추가
hostshare /mnt/host virtiofs defaults 0 0
DAX 윈도우(dax-window) 설정
DAX(Direct Access) 모드는 호스트의 페이지 캐시를 게스트에 직접 매핑한다. 데이터를 두 번 복사하지 않아 메모리 효율과 I/O 성능이 크게 향상된다.
<!-- DAX 윈도우 활성화 (shared memory 매핑) -->
<filesystem type='mount' accessmode='passthrough'>
<driver type='virtiofs'/>
<source dir='/path/to/host/dir'/>
<target dir='hostshare'/>
<!-- DAX 윈도우 크기 설정 (게스트 메모리 공간 사용) -->
<driver type='virtiofs' queue='1024'>
<binary path='/usr/libexec/virtiofsd'>
<cache mode='auto'/>
<sandbox mode='namespace'/>
</binary>
</driver>
</filesystem>
# virtiofsd DAX 활성화 옵션
sudo virtiofsd \
--socket-path=/var/run/virtiofsd-dax.sock \
--shared-dir=/path/to/host/dir \
--cache=always \
--announce-submounts \
--allow-direct-io
캐시 모드 비교
| 캐시 모드 | 동작 | 일관성 | 성능 | 권장 상황 |
|---|---|---|---|---|
none | 캐싱 없음, 매번 호스트 접근 | 강함 | 낮음 | 파일 자주 변경 시 |
auto | 열린 파일만 캐시 (close-to-open) | 중간 | 중간 | 일반 목적 (기본값) |
always | 모든 파일 공격적 캐싱 | 약함 | 높음 | 읽기 전용 데이터셋 |
보안 모델 옵션 (sandbox)
| sandbox 모드 | 설명 | 격리 수준 |
|---|---|---|
none | 격리 없음 (테스트용) | 낮음 |
namespace | Linux 네임스페이스 격리 | 중간 (권장) |
chroot | chroot 격리 (레거시) | 낮음 |
seccomp | syscall 화이트리스트 필터 | 높음 |
성능 튜닝 옵션
# 스레드 풀 크기 설정 (기본: 64)
sudo virtiofsd \
--socket-path=/var/run/virtiofsd.sock \
--shared-dir=/data \
--cache=auto \
--thread-pool-size=128 \
--writeback \
--xattr \
--posix-lock \
--flock
| 옵션 | 설명 | 효과 |
|---|---|---|
--thread-pool-size=N | 요청 처리 스레드 수 | 병렬 I/O 처리량 향상 |
--writeback | writeback 캐시 활성화 | 쓰기 성능 향상 |
--allow-direct-io | 게스트 O_DIRECT 허용 | 버퍼 없는 직접 I/O |
--announce-submounts | 서브마운트 알림 | 마운트 포인트 정확한 처리 |
--xattr | 확장 속성 지원 | SELinux/ACL 동작 |
--posix-lock | POSIX 잠금 전달 | 데이터베이스 파일 안전성 |
9p vs virtiofs 성능 비교
| 항목 | 9p virtio | virtiofs (캐시=auto) | virtiofs + DAX |
|---|---|---|---|
| 순차 읽기 처리량 | 기준 (1x) | 3~5x | 5~10x |
| 순차 쓰기 처리량 | 기준 (1x) | 2~4x | 3~6x |
| 메타데이터 레이턴시 | 높음 | 중간 | 중간 |
| 랜덤 읽기 IOPS | 낮음 | 높음 | 매우 높음 |
| 메모리 사용량 | 이중 캐싱 | 이중 캐싱 | 단일 캐시 (절약) |
| POSIX 완전 지원 | 부분 | 거의 완전 | 거의 완전 |
| 파일 잠금 | 부분 | 지원 | 지원 |
| mmap 지원 | 제한적 | 지원 | 네이티브 mmap |
| live 마이그레이션 | 가능 | 가능 | 제한 (DAX 윈도우) |
| 커널 지원 | 오래됨 (v9fs) | 5.4+ (virtiofs) | 5.4+ (virtiofs) |
virtiofsd Rust 구현체
vhost-device 프로젝트(Rust 구현)는 C 구현 대비 메모리 안전성과 성능이 향상된 virtiofsd를 제공한다. Cloud Hypervisor, Firecracker 등에서 사용된다.
# Rust 구현체 설치 (cargo 사용)
cargo install virtiofsd
# 또는 패키지 설치 (Fedora 38+)
sudo dnf install virtiofsd
# Rust virtiofsd 실행 (옵션 구조 동일)
virtiofsd \
--socket-path=/var/run/virtiofsd-rust.sock \
--shared-dir=/path/to/host/dir \
--cache=auto \
--sandbox=namespace \
--log-level=info
# libvirt에서 Rust 구현체 사용 (binary 경로 지정)
<!-- libvirt XML에서 Rust virtiofsd 경로 지정 -->
<filesystem type='mount' accessmode='passthrough'>
<driver type='virtiofs'>
<binary path='/usr/bin/virtiofsd'>
<cache mode='auto'/>
<sandbox mode='namespace'/>
</binary>
</driver>
<source dir='/path/to/host/dir'/>
<target dir='hostshare'/>
</filesystem>
- 가상화 (KVM) 심화 — KVM 커널 내부 구현, VMCS/VMCB, EPT/NPT
- QEMU 실전 가이드 — QEMU 아키텍처, 디버깅 설정, QMP
- NUMA — NUMA 하드웨어 토폴로지, 메모리 정책
- FUSE — virtiofsd 기반 파일시스템 구조 심화
관련 문서
- 가상화 (KVM) 심화 — KVM 내부 구조, 하드웨어 가상화 확장, 실험 기준선
- QEMU 실전 가이드 — QMP, OVMF, swtpm, 디버그용 CLI 실습
- UEFI — 펌웨어, EFI 변수, HTTP Boot, OVMF 디버깅 기초
- Secure Boot — shim, enrolled keys, UKI, 네트워크 부팅 체인 검증
- VFIO & mdev (디바이스 패스스루) — IOMMU 그룹, PCI passthrough, mediated device 운영
- NUMA — vCPU pinning, hugepage, 메모리 locality 튜닝 전제
- FUSE — virtiofsd와 사용자 공간 파일시스템 구조 심화