libvirt / KVM 관리
libvirt는 KVM, QEMU, LXC, Xen 등 다양한 하이퍼바이저(Hypervisor)를 통합 관리하는 오픈소스 가상화(Virtualization) API 라이브러리입니다. virsh, XML 도메인 설정, UEFI firmware auto-selection, Secure Boot/TPM, 네트워크/스토리지 관리, 스냅샷, 라이브 마이그레이션, Python API, virtiofsd까지 커널 개발자를 위한 종합 가이드입니다.
핵심 요약
- 하이퍼바이저 추상화 — libvirt는 KVM/QEMU, Xen, LXC 등 다양한 하이퍼바이저를 단일 API로 추상화하여 도구(virsh, virt-manager, oVirt)를 하이퍼바이저 독립적으로 만듭니다.
- 드라이버 모델 — 도메인, 네트워크, 스토리지, 노드 디바이스 등 기능별 드라이버가 분리되어 있어 하이퍼바이저 교체 시에도 관리 코드 변경이 최소화됩니다.
- XML 도메인 정의 — VM의 CPU, 메모리, 디스크, 네트워크, UEFI/TPM 설정을 XML 파일 하나로 선언적 관리하며, 버전 관리와 자동화에 적합합니다.
- 라이브 마이그레이션 — 실행 중인 VM을 다른 호스트로 무중단 이동하여 유지보수, 부하 분산, HA(High Availability)를 구현합니다.
- 모듈식 데몬 (6.0+) —
libvirtd단일 프로세스에서virtqemud,virtnetworkd등 기능별 독립 데몬 구조로 전환되어 보안과 안정성이 향상되었습니다.
단계별 이해
- 아키텍처 파악
libvirt의 클라이언트-서버 구조, 드라이버 모델,libvirtd와 모듈식 데몬의 차이를 이해합니다. - virsh 기본 명령 익히기
virsh list,define,start,console,destroy등 VM 생명주기 명령을 실습합니다. - XML 도메인 설정
CPU 토폴로지, NUMA 피닝, virtio 디바이스, UEFI/Secure Boot, passthrough 설정을 XML로 작성합니다. - 네트워크·스토리지·마이그레이션
가상 네트워크(NAT/Bridge/macvtap), 스토리지 풀(LVM/RBD/NFS), 라이브 마이그레이션을 구성하고 트러블슈팅합니다.
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 터널(Tunnel) | 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 대신 기능별로 분리된 모듈식 데몬 아키텍처를 지원합니다. 각 하이퍼바이저/스토리지/네트워크 기능이 독립 데몬으로 분리되어 보안성과 안정성이 향상됩니다.
| 데몬 | 역할 | 소켓(Socket) 경로 |
|---|---|---|
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) 직렬화(Serialization) 기반 RPC 프로토콜을 사용합니다.
| 전송 방식 | URI 예시 | 특징 |
|---|---|---|
| UNIX 도메인 소켓 | qemu:///system | 로컬 전용, 가장 빠름, root/libvirt 그룹 접근 |
| SSH 터널 | qemu+ssh://host/system | 원격 접근, SSH 키 인증, 방화벽(Firewall) 불필요 |
| 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 | 스토리지 컨테이너(Container) |
| 스토리지 볼륨 | virStorageVolPtr | virStorageVol | 개별 디스크 이미지 |
| 인터페이스 | virInterfacePtr | virInterface | 호스트 네트워크 인터페이스 |
| 시크릿 | virSecretPtr | virSecret | 암호화(Encryption) 키/비밀값 |
설치 및 초기 설정
패키지 설치
# 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 프로세스(Process) 실행 컨텍스트를 제어합니다.
# /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/메모리 핫플러그(Hotplug)
# 실행 중 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 토폴로지(Topology) 설정
<!-- 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>
브리지(Bridge) 네트워크 (물리 인터페이스 연결)
# 호스트에서 브리지 생성 (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>
네트워크 대역폭(Bandwidth) 제한 (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 스케줄러(Scheduler) 설정이 핵심입니다.
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를 전용 스레드(Thread)로 분리하여 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 | 캐시(Cache) 미스 감소 | NUMA 토폴로지 고려 |
| Huge Pages | 1GB/2MB | TLB 미스 감소 | 호스트 사전 설정 필요 |
| 메모리 잠금(Lock) | <locked/> | 스왑(Swap) 방지 | Huge Pages 필수 동반 |
| IOThread | vCPU와 별도 cpuset | I/O 지연(Latency) 감소 | 디스크당 1개 권장 |
| 디스크 캐시 | cache='none' | 더블 버퍼(Buffer) 제거 | 배리어/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의 사용자 공간(User Space) 데몬입니다. 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) 모드는 호스트의 페이지 캐시(Page Cache)를 게스트에 직접 매핑(Mapping)합니다. 데이터를 두 번 복사하지 않아 메모리 효율과 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 | 모든 파일 공격적 캐싱 | 약함 | 높음 | 읽기 전용(Read-Only) 데이터셋 |
보안 모델 옵션 (sandbox)
| sandbox 모드 | 설명 | 격리(Isolation) 수준 |
|---|---|---|
none | 격리 없음 (테스트용) | 낮음 |
namespace | Linux 네임스페이스(Namespace) 격리 | 중간 (권장) |
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 처리량(Throughput) 향상 |
--writeback | writeback 캐시 활성화 | 쓰기 성능 향상 |
--allow-direct-io | 게스트 O_DIRECT 허용 | 버퍼 없는 직접 I/O |
--announce-submounts | 서브마운트 알림 | 마운트(Mount) 포인트 정확한 처리 |
--xattr | 확장 속성(Extended Attribute) 지원 | 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 완전 지원 | 부분 | 거의 완전 | 거의 완전 |
| 파일 잠금(File Lock) | 부분 | 지원 | 지원 |
| 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 아키텍처, 디버깅(Debugging) 설정, QMP
- NUMA — NUMA 하드웨어 토폴로지, 메모리 정책(Memory Policy)
- FUSE — virtiofsd 기반 파일시스템 구조
보안 및 접근 제어(Access Control)
libvirt는 다층 보안 모델을 제공하여 VM 간 격리와 호스트 보호를 보장합니다. sVirt(SELinux/AppArmor 기반 MAC), DAC(소유자/그룹), cgroup 자원 제한이 핵심 축입니다.
sVirt — 강제적 접근 제어(MAC)
sVirt는 각 VM 프로세스에 고유 보안 레이블을 자동 할당하여, 한 VM이 다른 VM의 디스크 이미지나 메모리에 접근하지 못하게 합니다.
| 보안 프레임워크 | 레이블 형식 | 설정 위치 | 기본 동작 |
|---|---|---|---|
| SELinux (RHEL/Fedora) | svirt_t:s0:c123,c456 | /etc/libvirt/qemu.conf | 동적 레이블 자동 할당 |
| AppArmor (Ubuntu/SUSE) | /etc/apparmor.d/libvirt/ | security_driver = "apparmor" | 프로파일 자동 생성 |
| 없음 | — | security_driver = "none" | 보안 격리 없음 (테스트용) |
<!-- 도메인 XML에서 보안 레이블 직접 지정 (정적 모드) -->
<domain type='kvm'>
<seclabel type='static' model='selinux' relabel='yes'>
<label>system_u:system_r:svirt_t:s0:c100,c200</label>
<imagelabel>system_u:object_r:svirt_image_t:s0:c100,c200</imagelabel>
</seclabel>
</domain>
<!-- 동적 모드 (기본값) — libvirt가 자동으로 고유 레이블 할당 -->
<seclabel type='dynamic' model='selinux' relabel='yes'/>
# qemu.conf 보안 드라이버 설정
# /etc/libvirt/qemu.conf
security_driver = "selinux" # 또는 "apparmor", "none"
security_default_confined = 1
security_require_confined = 1
DAC — 소유자/그룹 기반 접근 제어
QEMU 프로세스의 실행 사용자/그룹을 제어하여 파일시스템 수준에서 접근을 격리합니다.
# /etc/libvirt/qemu.conf
# QEMU 프로세스 실행 사용자 설정
user = "qemu" # 기본값 (RHEL: qemu, Ubuntu: libvirt-qemu)
group = "qemu"
dynamic_ownership = 1 # 디스크 이미지 소유권 자동 변경
# 도메인별 사용자 오버라이드 (XML)
<!-- 도메인 XML에서 DAC 레이블 지정 -->
<seclabel type='static' model='dac'>
<label>+107:+107</label> <!-- uid:gid -->
</seclabel>
cgroup 자원 격리
libvirt는 cgroup v1/v2를 활용하여 VM별 CPU, 메모리, I/O 자원 사용량을 제한합니다.
# libvirt가 관리하는 cgroup 계층 확인
systemctl status libvirtd
ls /sys/fs/cgroup/machine.slice/
# 도메인별 cgroup 디렉토리 (cgroup v2)
ls /sys/fs/cgroup/machine.slice/machine-qemu\\x2d1\\x2dmyvm.scope/
# CPU 가중치 확인
cat /sys/fs/cgroup/machine.slice/machine-qemu\\x2d1\\x2dmyvm.scope/cpu.weight
# 메모리 제한 확인
cat /sys/fs/cgroup/machine.slice/machine-qemu\\x2d1\\x2dmyvm.scope/memory.max
<!-- 도메인 XML에서 메모리 제한 (memtune) -->
<memtune>
<hard_limit unit='GiB'>8</hard_limit>
<soft_limit unit='GiB'>4</soft_limit>
<swap_hard_limit unit='GiB'>8</swap_hard_limit>
</memtune>
<!-- blkio 튜닝 (디스크 I/O 가중치) -->
<blkiotune>
<weight>500</weight>
<device>
<path>/var/lib/libvirt/images/myvm.qcow2</path>
<weight>200</weight>
<read_bytes_sec>104857600</read_bytes_sec> <!-- 100 MB/s -->
<write_bytes_sec>52428800</write_bytes_sec> <!-- 50 MB/s -->
</device>
</blkiotune>
security_driver = "none"으로 설정하면 sVirt가 비활성화되어 한 VM이 다른 VM의 디스크 이미지에 접근할 수 있습니다. 운영 환경에서는 반드시 SELinux 또는 AppArmor 보안 드라이버를 활성화하십시오.
Polkit 접근 제어
libvirt는 D-Bus 기반 Polkit을 사용하여 비루트 사용자의 API 접근을 세밀하게 제어합니다.
# /etc/polkit-1/rules.d/50-libvirt.rules
# 특정 그룹에게 VM 관리 권한 부여
polkit.addRule(function(action, subject) {
if (action.id == "org.libvirt.unix.manage" &&
subject.isInGroup("libvirt")) {
return polkit.Result.YES;
}
});
# 특정 사용자에게 읽기 전용 권한만 부여
polkit.addRule(function(action, subject) {
if (action.id == "org.libvirt.unix.monitor" &&
subject.user == "monitor-user") {
return polkit.Result.YES;
}
});
| Polkit Action | 권한 | 기본값 |
|---|---|---|
org.libvirt.unix.manage | VM 생성/삭제/시작/중지 | root 또는 libvirt 그룹 |
org.libvirt.unix.monitor | VM 상태 조회 전용 | 모든 로컬 사용자 |
Hook 스크립트
libvirt는 도메인 생명주기 이벤트(시작, 중지, 마이그레이션 등) 발생 시 사용자 정의 스크립트를 자동 실행할 수 있습니다. Hook 스크립트는 네트워크 방화벽 규칙 동적 추가, 외부 모니터링 연동, 백업 트리거 등에 활용됩니다.
Hook 스크립트 위치 및 종류
| Hook 파일 | 트리거 대상 | 호출 시점 |
|---|---|---|
/etc/libvirt/hooks/qemu | QEMU/KVM 도메인 | prepare, start, started, stopped, release, migrate, reconnect |
/etc/libvirt/hooks/lxc | LXC 컨테이너 | prepare, start, started, stopped, release |
/etc/libvirt/hooks/network | 가상 네트워크 | start, started, stopped, plugged, unplugged |
/etc/libvirt/hooks/daemon | libvirtd 데몬 | start, shutdown, reload |
Hook 스크립트는 다음 인자로 호출된다: hook_script guest_name operation sub-operation extra_arg
QEMU Hook 스크립트 예제
#!/bin/bash
# /etc/libvirt/hooks/qemu
# 도메인 시작/중지 시 iptables 포트 포워딩 자동 설정
GUEST_NAME="$1"
OPERATION="$2"
SUB_OPERATION="$3"
# 도메인 XML은 stdin으로 전달됨
if [ "$GUEST_NAME" = "webserver" ]; then
GUEST_IP="192.168.122.100"
HOST_PORT=8080
GUEST_PORT=80
if [ "$OPERATION" = "started" ]; then
# VM 시작 후 포트 포워딩 추가
iptables -t nat -A PREROUTING -p tcp --dport "$HOST_PORT" \
-j DNAT --to-destination "$GUEST_IP:$GUEST_PORT"
iptables -I FORWARD -p tcp -d "$GUEST_IP" --dport "$GUEST_PORT" \
-j ACCEPT
logger "libvirt hook: $GUEST_NAME 포트 포워딩 추가 ($HOST_PORT -> $GUEST_PORT)"
elif [ "$OPERATION" = "stopped" ]; then
# VM 중지 후 포트 포워딩 제거
iptables -t nat -D PREROUTING -p tcp --dport "$HOST_PORT" \
-j DNAT --to-destination "$GUEST_IP:$GUEST_PORT"
iptables -D FORWARD -p tcp -d "$GUEST_IP" --dport "$GUEST_PORT" \
-j ACCEPT
logger "libvirt hook: $GUEST_NAME 포트 포워딩 제거"
fi
fi
네트워크 Hook 스크립트 예제
#!/bin/bash
# /etc/libvirt/hooks/network
# 가상 네트워크 시작/중지 시 커스텀 라우팅 설정
NETWORK_NAME="$1"
OPERATION="$2"
if [ "$NETWORK_NAME" = "isolated-lab" ]; then
case "$OPERATION" in
started)
# 특정 VLAN에 대한 라우팅 추가
ip route add 10.10.0.0/24 dev virbr1
logger "libvirt hook: $NETWORK_NAME 라우팅 추가"
;;
stopped)
ip route del 10.10.0.0/24 dev virbr1 2>/dev/null
logger "libvirt hook: $NETWORK_NAME 라우팅 제거"
;;
esac
fi
chmod +x)이 필요하며, 종료 코드 0이면 성공입니다. 0이 아닌 종료 코드를 반환하면 해당 작업(도메인 시작 등)이 실패 처리됩니다. Hook 스크립트 내부에서 장시간 블로킹하면 libvirt 전체가 멈출 수 있으므로, 긴 작업은 백그라운드로 실행하십시오.
트러블슈팅 및 디버깅
libvirt 환경에서 자주 발생하는 오류와 진단 방법을 정리합니다. 로그 파일 위치, 디버그 레벨 설정, 일반적인 오류 해결 절차를 다룹니다.
로그 설정 및 위치
| 로그 파일 | 내용 | 위치 |
|---|---|---|
| libvirtd 데몬 로그 | 데몬 시작/API 호출 처리 | /var/log/libvirt/libvirtd.log |
| QEMU 도메인 로그 | 개별 VM의 QEMU 프로세스 출력 | /var/log/libvirt/qemu/<em>도메인명</em>.log |
| 네트워크 데몬 로그 | dnsmasq 로그 (DHCP/DNS) | /var/log/libvirt/qemu/dnsmasq-<em>네트워크명</em>.log |
| 감사 로그 | 도메인 시작/중지 이벤트 | /var/log/audit/audit.log (SELinux) |
# /etc/libvirt/libvirtd.conf 디버그 로그 활성화
log_level = 1 # 1=DEBUG, 2=INFO, 3=WARNING, 4=ERROR
log_filters = "1:qemu 1:security 3:event"
log_outputs = "1:file:/var/log/libvirt/libvirtd.log"
# 런타임 디버그 레벨 변경 (데몬 재시작 없이)
virt-admin -c virtqemud:///system daemon-log-filters "1:*"
# 특정 도메인의 QEMU 로그 확인
cat /var/log/libvirt/qemu/myvm.log | tail -50
자주 발생하는 오류 및 해결
| 오류 메시지 | 원인 | 해결 |
|---|---|---|
Cannot access storage file: Permission denied |
QEMU 프로세스가 디스크 이미지에 접근 불가 | chown qemu:qemu 또는 SELinux 레이블 수정: chcon -t svirt_image_t |
Network not found: no network with matching name 'default' |
기본 네트워크 미시작 | virsh net-start default && virsh net-autostart default |
Unable to connect to server: Connection refused |
libvirtd 데몬 미실행 | systemctl start libvirtd 또는 systemctl start virtqemud |
CPU is incompatible with host CPU |
게스트 CPU 모델이 호스트와 불일치 | <cpu mode='host-passthrough'/> 또는 <cpu mode='host-model'/> 사용 |
internal error: qemu unexpectedly closed the monitor |
QEMU 프로세스 크래시 | QEMU 로그 확인: cat /var/log/libvirt/qemu/도메인명.log |
Timed out during operation: cannot acquire state change lock |
이전 작업이 아직 진행 중 | 도메인 상태 확인 후 강제 종료: virsh destroy 도메인명 |
진단 명령어
# libvirt 연결 테스트
virsh -c qemu:///system list --all
# 호스트 가상화 지원 확인
virt-host-validate
# KVM 모듈 로드 확인
lsmod | grep kvm
ls /dev/kvm
# 도메인 능력 조회 (지원 기능 확인)
virsh domcapabilities --virttype kvm
# 특정 도메인의 현재 XML 덤프 (런타임 정보 포함)
virsh dumpxml myvm --update-cpu
# QEMU 프로세스 상태 확인
ps aux | grep qemu-system
virsh qemu-monitor-command myvm --hmp "info status"
# 도메인 이벤트 실시간 관찰
virsh event --domain myvm --event lifecycle --loop
# 네트워크 DHCP 임대 현황
virsh net-dhcp-leases default
# 스토리지 풀/볼륨 상태 점검
virsh pool-list --all --details
virsh vol-list default --details
LIBVIRT_DEBUG=1 virsh list로 클라이언트 측 디버그 출력을 확인할 수 있습니다. 서버 측 상세 로그는 virt-admin daemon-log-filters "1:*"로 런타임에 활성화합니다.
백업 및 복구
libvirt 환경에서 VM을 안전하게 백업하고 복구하는 전략을 다룹니다. 도메인 XML 설정 백업, 디스크 이미지 백업, 전체 복구 절차를 포함합니다.
도메인 XML 설정 백업
# 모든 도메인 XML 일괄 백업
for dom in $(virsh list --all --name); do
virsh dumpxml "$dom" > "/backup/libvirt/xml/${dom}.xml"
done
# 네트워크/스토리지 풀 설정도 백업
for net in $(virsh net-list --all --name); do
virsh net-dumpxml "$net" > "/backup/libvirt/net/${net}.xml"
done
for pool in $(virsh pool-list --all --name); do
virsh pool-dumpxml "$pool" > "/backup/libvirt/pool/${pool}.xml"
done
디스크 이미지 백업
# 오프라인 백업 (VM 중지 상태)
virsh shutdown myvm
cp /var/lib/libvirt/images/myvm.qcow2 /backup/libvirt/images/
# 온라인 백업 — 외부 스냅샷 활용
# 1단계: 외부 스냅샷 생성 (쓰기가 새 파일로 분리됨)
virsh snapshot-create-as myvm snap-backup \
--diskspec vda,snapshot=external,file=/var/lib/libvirt/images/myvm-snap.qcow2 \
--disk-only --atomic
# 2단계: 원본 이미지를 안전하게 복사 (읽기 전용 상태)
cp /var/lib/libvirt/images/myvm.qcow2 /backup/libvirt/images/myvm-backup.qcow2
# 3단계: 스냅샷 블록 커밋 (변경분을 원본에 병합)
virsh blockcommit myvm vda --active --pivot --verbose
# 증분 백업 (libvirt 6.0+ checkpoint 기반)
virsh backup-begin myvm --backupxml backup-spec.xml
복구 절차
# 1. 디스크 이미지 복원
cp /backup/libvirt/images/myvm-backup.qcow2 /var/lib/libvirt/images/myvm.qcow2
# 2. 도메인 XML로 VM 재등록
virsh define /backup/libvirt/xml/myvm.xml
# 3. 파일 소유권/보안 레이블 복원
chown qemu:qemu /var/lib/libvirt/images/myvm.qcow2
restorecon -v /var/lib/libvirt/images/myvm.qcow2
# 4. VM 시작
virsh start myvm
# managed-save 상태 복원 (RAM 포함 스냅샷에서 복구)
virsh restore /backup/libvirt/save/myvm.save
자동 백업 스크립트
#!/bin/bash
# libvirt 전체 백업 스크립트
BACKUP_DIR="/backup/libvirt/$(date +%Y%m%d)"
mkdir -p "$BACKUP_DIR"/{xml,images,net,pool}
# 도메인 XML 백업
for dom in $(virsh list --all --name); do
[ -z "$dom" ] && continue
virsh dumpxml "$dom" > "$BACKUP_DIR/xml/${dom}.xml"
# 디스크 이미지 경로 추출 및 백업
for img in $(virsh domblklist "$dom" --details | awk '/disk/{print $4}'); do
if [ -f "$img" ]; then
# qcow2 압축 복사 (공간 절약)
qemu-img convert -c -O qcow2 "$img" \
"$BACKUP_DIR/images/$(basename $img)"
fi
done
done
# 네트워크/스토리지 풀 설정 백업
for net in $(virsh net-list --all --name); do
[ -z "$net" ] && continue
virsh net-dumpxml "$net" > "$BACKUP_DIR/net/${net}.xml"
done
echo "백업 완료: $BACKUP_DIR"
du -sh "$BACKUP_DIR"
cp로 qcow2 이미지를 직접 복사하면 이미지가 손상될 수 있습니다. 반드시 외부 스냅샷이나 virsh backup-begin을 사용하여 일관성을 보장하십시오.
UEFI Firmware Auto-Selection
libvirt 5.2+에서는 <os firmware='efi'/> 설정만으로 호스트에 설치된 OVMF/AAVMF 펌웨어를 자동 선택합니다. 수동으로 OVMF 파일 경로를 지정할 필요가 없어져 관리가 간편해집니다.
기본 자동 선택
<!-- firmware='efi'만 선언하면 libvirt가 호스트의 OVMF 자동 탐색 -->
<os firmware='efi'>
<type arch='x86_64' machine='q35'>hvm</type>
<boot dev='hd'/>
</os>
Secure Boot 자동 선택
<!-- Secure Boot + enrolled keys 조합 자동 선택 (libvirt 7.2+) -->
<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>
<loader secure='yes'/>
<boot dev='hd'/>
</os>
domcapabilities로 펌웨어 목록 확인
# 호스트에서 사용 가능한 EFI 펌웨어 목록 조회
virsh domcapabilities --virttype kvm | xmllint --xpath '//os/loader' -
# 사용 가능한 펌웨어 기능 전체 확인
virsh domcapabilities --virttype kvm --machine q35 | xmllint --xpath '//firmware' -
<!-- domcapabilities 출력 예시 -->
<os supported='yes'>
<enum name='firmware'>
<value>bios</value>
<value>efi</value>
</enum>
<loader supported='yes'>
<value>/usr/share/OVMF/OVMF_CODE_4M.fd</value>
<value>/usr/share/OVMF/OVMF_CODE_4M.secboot.fd</value>
<enum name='type'>
<value>rom</value>
<value>pflash</value>
</enum>
<enum name='secure'>
<value>no</value>
<value>yes</value>
</enum>
</loader>
</os>
수동 지정 vs 자동 선택 비교
| 방식 | XML 예시 | 장점 | 단점 |
|---|---|---|---|
| 수동 지정 | <loader>/usr/share/OVMF/OVMF_CODE.fd</loader> |
정확한 펌웨어 버전 고정 | 배포판별 경로 상이, 이식성 낮음 |
| 자동 선택 | <os firmware='efi'/> |
배포판 독립적, 간결한 XML | libvirt 5.2+ 필요 |
| 자동 + Secure Boot | <feature name='secure-boot'/> |
enrolled keys 자동 매칭 | libvirt 7.2+ 필요, OVMF 4M 필요 |
/usr/share/qemu/firmware/*.json 디스크립터 파일을 기반으로 동작합니다. 자세한 UEFI 부팅 과정(Boot Process)은 UEFI 페이지를, Secure Boot 인증 체인은 Secure Boot 페이지를 참조하십시오.
초보자 실습 가이드
libvirt와 KVM을 처음 접하는 개발자를 위한 단계별 안내입니다. 가상 머신을 생성하고 관리하는 기본적인 워크플로를 다룹니다.
사전 요구사항 확인
# 1. CPU 가상화 지원 확인 (Intel VT-x 또는 AMD-V)
grep -E '(vmx|svm)' /proc/cpuinfo
# vmx (Intel) 또는 svm (AMD)이 출력되면 지원
# 2. KVM 모듈 로드 확인
lsmod | grep kvm
# kvm_intel 또는 kvm_amd 모듈이 로드되어야 함
# 3. 가상화 환경 전체 점검
sudo virt-host-validate
첫 번째 VM 생성
# 1. 패키지 설치 (Ubuntu/Debian)
sudo apt install -y qemu-kvm libvirt-daemon-system virtinst
# 2. 사용자를 libvirt 그룹에 추가
sudo usermod -aG libvirt $(whoami)
# 로그아웃 후 재로그인 필요
# 3. 기본 네트워크 시작
virsh net-start default
virsh net-autostart default
# 4. ISO에서 VM 생성 (virt-install)
virt-install \
--name my-first-vm \
--ram 2048 \
--vcpus 2 \
--disk size=20 \
--os-variant ubuntu22.04 \
--cdrom /path/to/ubuntu-22.04-live-server-amd64.iso \
--network network=default \
--graphics vnc,listen=0.0.0.0
기본 VM 운영 명령어
| 작업 | 명령어 | 설명 |
|---|---|---|
| VM 목록 | virsh list --all | 모든 VM (실행/중지 포함) 표시 |
| VM 시작 | virsh start my-first-vm | 중지 상태의 VM 시작 |
| VM 종료 | virsh shutdown my-first-vm | ACPI 전원 끄기 (안전한 종료) |
| VM 강제 종료 | virsh destroy my-first-vm | 즉시 전원 차단 (긴급 시) |
| 콘솔 접속 | virsh console my-first-vm | 시리얼 콘솔 (Ctrl+] 로 탈출) |
| VM 삭제 | virsh undefine my-first-vm --remove-all-storage | VM 설정과 디스크 모두 삭제 |
| VM 일시정지 | virsh suspend my-first-vm | VM 프리징 (메모리 유지) |
| VM 재개 | virsh resume my-first-vm | 일시정지된 VM 재시작(Reboot) |
일반적인 작업 흐름
virt-install의 --boot kernel=/path/to/bzImage,initrd=/path/to/initrd.img,kernel_args="console=ttyS0" 옵션을 사용하면 디스크 없이 커널을 직접 부팅할 수 있습니다. 커널 빌드-테스트 사이클을 단축하는 핵심 기법입니다. 자세한 내용은 QEMU 실전 가이드를 참조하십시오.
참고 링크
- libvirt 공식 사이트 — libvirt 프로젝트 메인 페이지로, 최신 릴리스 및 문서 색인을 제공합니다
- libvirt Domain XML format — 가상 머신 도메인 XML 설정의 전체 스키마를 설명합니다
- libvirt Storage management — 스토리지 풀과 볼륨 관리 방법을 다룹니다
- libvirt Network XML format — 가상 네트워크 XML 설정 형식을 정의합니다
- libvirt Node device management — 호스트 노드 디바이스의 열거와 관리 방법을 설명합니다
- virsh 명령어 레퍼런스 — virsh CLI 도구의 전체 명령어와 옵션을 참조할 수 있습니다
- libvirt Migration — 라이브 마이그레이션 프로토콜과 설정 방법을 다룹니다
- libvirt Hooks — 도메인 생명주기 이벤트에 연동하는 훅 스크립트 작성법을 설명합니다
- libvirt API reference — C 언어 기반 libvirt API의 전체 레퍼런스입니다
- libvirt QEMU driver — QEMU/KVM 드라이버 고유 설정과 기능을 설명합니다
- Firmware metadata (BIOS/UEFI 부트로더) — 도메인 XML의 펌웨어 자동 선택 및 UEFI 설정 방법을 다룹니다
- LWN: libvirt — a toolkit for virtualization management — libvirt의 아키텍처와 설계 철학을 소개하는 기사입니다
- LWN: Live migration — KVM 라이브 마이그레이션의 내부 동작과 최적화 기법을 분석합니다
- LWN: NUMA and KVM — NUMA 토폴로지 인지 가상화와 성능 튜닝을 다룹니다
- oVirt — KVM 기반 데이터센터 가상화 관리 플랫폼입니다
- Proxmox VE — KVM과 LXC를 통합 관리하는 오픈소스 가상화 플랫폼입니다
- virt-manager — libvirt 기반 데스크톱 가상 머신 관리 GUI 도구입니다
- Cockpit machines — 웹 기반 서버 관리 도구로, libvirt 가상 머신 관리 기능을 제공합니다
virt/kvm/kvm_main.c— KVM 코어 모듈로, libvirt가 호출하는 ioctl 경로를 포함합니다include/uapi/linux/kvm.h— KVM userspace API 헤더로, libvirt QEMU 드라이버가 사용하는 상수를 정의합니다arch/x86/kvm/x86.c— x86 KVM 공통 코드로, vCPU 및 메모리 관리 로직을 포함합니다
관련 문서
- 가상화 (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 튜닝 전제
- TPM 2.0 — TPM 아키텍처, vTPM, swtpm 연동, PCR 정책
- FUSE — virtiofsd와 사용자 공간 파일시스템 구조