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 등 기능별 독립 데몬 구조로 전환되어 보안과 안정성이 향상되었습니다.

단계별 이해

  1. 아키텍처 파악
    libvirt의 클라이언트-서버 구조, 드라이버 모델, libvirtd와 모듈식 데몬의 차이를 이해합니다.
  2. virsh 기본 명령 익히기
    virsh list, define, start, console, destroy 등 VM 생명주기 명령을 실습합니다.
  3. XML 도메인 설정
    CPU 토폴로지, NUMA 피닝, virtio 디바이스, UEFI/Secure Boot, passthrough 설정을 XML로 작성합니다.
  4. 네트워크·스토리지·마이그레이션
    가상 네트워크(NAT/Bridge/macvtap), 스토리지 풀(LVM/RBD/NFS), 라이브 마이그레이션을 구성하고 트러블슈팅합니다.

libvirt 개요 및 아키텍처

libvirt는 하이퍼바이저 독립적인 가상화 관리 인터페이스를 제공하는 미들웨어 계층입니다. 클라이언트-서버 구조로 동작하며, libvirtd 데몬이 하이퍼바이저와 통신합니다.

드라이버 모델

libvirt는 각 하이퍼바이저마다 드라이버 플러그인을 제공합니다.

드라이버하이퍼바이저연결 URI
QEMU/KVMQEMU with KVMqemu:///system
LXCLinux Containerslxc:///
XenXen 하이퍼바이저xen:///
OpenVZOpenVZopenvz:///system
원격TCP/TLS/SSH 터널(Tunnel)qemu+ssh://host/system

주요 도구 비교

도구인터페이스용도
virshCLI도메인/네트워크/스토리지 관리 (자동화에 적합)
virt-managerGUI데스크탑 GUI 관리
virt-installCLIVM 생성 전문 도구
virt-viewerGUIVM 콘솔 표시 (VNC/Spice)
Python libvirtAPI프로그래밍 자동화
클라이언트 계층 virsh CLI virt-manager GUI Python API libvirt.open() virt-install VM 생성 원격 클라이언트 SSH / TLS libvirtd 데몬 XML 파싱 & 도메인 상태 관리 UNIX 소켓: /var/run/libvirt/libvirt.sock 드라이버 디스패치 & RPC ACL 권한 / 감사 로깅 드라이버 계층 QEMU 드라이버 qemu:///system LXC 드라이버 lxc:/// 네트워크 드라이버 bridge / NAT / SR-IOV 스토리지 드라이버 dir / logical / netfs / rbd 하이퍼바이저 QEMU/KVM 프로세스 Linux Containers (LXC) Xen 도메인 -enable-kvm -machine q35 ... cgroups v2 + 네임스페이스 xl / xm 커맨드

libvirtd vs 모듈식 데몬 (libvirt 6.0+)

libvirt 6.0부터 단일 libvirtd 대신 기능별로 분리된 모듈식 데몬 아키텍처를 지원합니다. 각 하이퍼바이저/스토리지/네트워크 기능이 독립 데몬으로 분리되어 보안성과 안정성이 향상됩니다.

데몬역할소켓(Socket) 경로
virtproxyd클라이언트 프록시 (하위 호환)/run/libvirt/virtproxyd-sock
virtqemudQEMU/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
virsh / 클라이언트 virtproxyd RPC 라우팅 / 하위 호환 프록시 virtqemud QEMU/KVM 도메인 virtstoraged 스토리지 풀/볼륨 virtnetworkd 가상 네트워크 virtsecretd 외 시크릿/필터/인터페이스 QEMU/KVM 프로세스

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 타입설명
연결virConnectPtrvirConnect하이퍼바이저 연결 핸들
도메인virDomainPtrvirDomainVM 인스턴스 (실행/정지 모두)
네트워크virNetworkPtrvirNetwork가상 네트워크
스토리지 풀virStoragePoolPtrvirStoragePool스토리지 컨테이너(Container)
스토리지 볼륨virStorageVolPtrvirStorageVol개별 디스크 이미지
인터페이스virInterfacePtrvirInterface호스트 네트워크 인터페이스
시크릿virSecretPtrvirSecret암호화(Encryption) 키/비밀값
도메인 상태 머신 Undefined Defined Running Paused Shutdown Shutoff Crashed define start suspend resume shutdown 완료 start crash destroy

설치 및 초기 설정

패키지 설치

# 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"
현대 libvirt 흐름: 최근 libvirt는 firmware descriptor를 읽어 UEFI 펌웨어(Firmware)를 자동 선택할 수 있으므로, 가능하면 개별 VM XML에서 OVMF 경로를 하드코딩하기보다 <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
자동 선택의 함정: 기존 VM은 처음 정의될 때 해석된 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>
네트워크 모드 비교 NAT 모드 VM VM virbr0 (NAT) 외부 인터넷 VM→외부 가능 외부→VM 불가 Bridge 모드 VM VM br0 (L2 브리지) 물리 네트워크 VM이 실제 IP 할당 양방향 통신 가능 Isolated 모드 VM VM virbr1 (격리) 외부 차단 VM 간 통신만 가능 보안 격리 환경 SR-IOV 모드 VM VM VF1 VF2 PF (물리 NIC) 최고 성능 하드웨어 직접 접근 모드별 특성 비교 NAT: VM이 private IP 사용, 호스트가 SNAT으로 외부 연결. 간단하고 방화벽 불필요. 기본 모드. Bridge: VM이 LAN과 동일 세그먼트의 IP 획득. 외부에서 직접 접근 가능. 서버용으로 적합. Isolated: VM 간 통신만 허용, 외부 완전 차단. 보안 테스트 환경, 내부 클러스터에 적합. SR-IOV: PCIe VF를 VM에 직접 할당. 호스트 오버헤드 없음. 네트워크 집약적 워크로드에 최적. virtio-net multiqueue: 단일 인터페이스에서 다중 RX/TX 큐로 멀티코어 병렬 처리 성능 향상. QoS 대역폭 제한: average(평균), peak(최대), burst(버스트 크기, KB) 단위로 per-VM 제어 가능.

스토리지 풀 관리

libvirt는 스토리지 풀을 통해 VM 이미지와 볼륨을 체계적으로 관리합니다.

풀 타입

풀 타입설명용도
dir로컬 디렉토리기본 이미지 저장소
logicalLVM 볼륨 그룹고성능 블록 스토리지
netfsNFS/CIFS 원격 FS공유 스토리지
rbdCeph RBD분산 스토리지
iscsiiSCSI 타겟SAN 스토리지
disk전체 디스크 파티션로컬 디스크 직접 사용
glusterGlusterFS 볼륨스케일-아웃 분산 스토리지

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 스냅샷 체인 구조 base.qcow2 베이스 이미지 OS 설치 완료 상태 snap1.qcow2 스냅샷 1 base 기반 델타 snap2.qcow2 스냅샷 2 snap1 기반 델타 current.qcow2 현재 활성 실시간 기록 중 backing backing backing 읽기: base <- snap1 <- snap2 <- current (역방향 체인 탐색) 쓰기: 항상 current에만 기록, 아래 레이어는 불변 현재 VM 사용 중

스냅샷 & 마이그레이션

내부 스냅샷 (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
post-copy 마이그레이션: 메모리를 모두 전송하기 전에 VM을 대상 호스트에서 시작하고, 이후 요청에 따라 페이지(Page)를 전송합니다. 전환 시간이 짧지만 네트워크 단절 시 VM이 중단될 위험이 있습니다.
전제 조건: 라이브 마이그레이션은 두 호스트 간에 libvirt가 실행 중이어야 하며, SSH 또는 TLS 인증이 설정되어 있어야 합니다. 공유 스토리지 방식은 NFS 등 공유 파일시스템(Filesystem)이 필요합니다.
라이브 마이그레이션 단계 (pre-copy) ① Setup 연결 수립 대상 도메인 준비 VM: 계속 실행 중 ② Pre-copy 메모리 반복 전송 변경 페이지 재전송 VM: 계속 실행 중 ③ Stop & Copy VM 일시 정지 나머지 상태 전송 VM: 잠시 중단 (다운타임) ④ Resume 대상에서 VM 재개 소스 VM 삭제 VM: 재개됨 소스 호스트 (Source) 대상 호스트 (Destination) 메모리 페이지 스트리밍 (네트워크)

성능 튜닝 통합 정리

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>
주의: FIFO/RR 실시간(Real-time) 스케줄러를 사용하면 CPU 독점 가능성이 있습니다. 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 모델 지정광범위한 호환낮음
maximumQEMU 지원 최대 기능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 Pages1GB/2MBTLB 미스 감소호스트 사전 설정 필요
메모리 잠금(Lock)<locked/>스왑(Swap) 방지Huge Pages 필수 동반
IOThreadvCPU와 별도 cpusetI/O 지연(Latency) 감소디스크당 1개 권장
디스크 캐시cache='none'더블 버퍼(Buffer) 제거배리어/fsync 주의
네트워크 큐queues=vCPU수멀티코어 처리vhost 드라이버 사용
NUMA 정책cell 기반 분할원격 메모리 접근 감소numactl 병행 사용
투명 Huge Pages호스트 THP 비활성화지터 감소never 설정 권장
CPU Governorperformance주파수 조절 지연 제거전력 소비 증가

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격리 없음 (테스트용)낮음
namespaceLinux 네임스페이스(Namespace) 격리중간 (권장)
chrootchroot 격리 (레거시)낮음
seccompsyscall 화이트리스트 필터높음

성능 튜닝 옵션

# 스레드 풀 크기 설정 (기본: 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) 향상
--writebackwriteback 캐시 활성화쓰기 성능 향상
--allow-direct-io게스트 O_DIRECT 허용버퍼 없는 직접 I/O
--announce-submounts서브마운트 알림마운트(Mount) 포인트 정확한 처리
--xattr확장 속성(Extended Attribute) 지원SELinux/ACL 동작
--posix-lockPOSIX 잠금 전달데이터베이스 파일 안전성
virtiofsd 데이터 흐름 게스트 커널 VFS 레이어 파일 시스템 추상화 virtio-fs 드라이버 FUSE 프로토콜 변환 virtio 링 (공유 메모리) 호스트 사용자 공간 virtiofsd 데몬 FUSE 요청 처리 호스트 VFS 실제 파일시스템 vhost-user 소켓 UNIX 소켓 DAX 모드: 공유 메모리 직접 매핑 (제로 카피) 호스트 페이지 캐시 → 게스트 메모리 공간에 직접 mmap → 복사 없이 접근 일반 모드: 게스트 커널 버퍼 → virtio 링 복사 → virtiofsd → 호스트 VFS (2회 복사) DAX 모드: 게스트 메모리 = 호스트 페이지 캐시 (0회 복사, 메모리 절약)

9p vs virtiofs 성능 비교

항목9p virtiovirtiofs (캐시=auto)virtiofs + DAX
순차 읽기 처리량기준 (1x)3~5x5~10x
순차 쓰기 처리량기준 (1x)2~4x3~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>
성능 비교: virtiofsd는 9p virtio 대비 순차 읽기 기준 약 3~10배 높은 성능을 보입니다. DAX 모드를 사용하면 호스트 페이지 캐시를 게스트와 직접 공유하여 메모리 사용량도 절감됩니다.
다음 학습:
  • 가상화 (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.manageVM 생성/삭제/시작/중지root 또는 libvirt 그룹
org.libvirt.unix.monitorVM 상태 조회 전용모든 로컬 사용자

Hook 스크립트

libvirt는 도메인 생명주기 이벤트(시작, 중지, 마이그레이션 등) 발생 시 사용자 정의 스크립트를 자동 실행할 수 있습니다. Hook 스크립트는 네트워크 방화벽 규칙 동적 추가, 외부 모니터링 연동, 백업 트리거 등에 활용됩니다.

Hook 스크립트 위치 및 종류

Hook 파일트리거 대상호출 시점
/etc/libvirt/hooks/qemuQEMU/KVM 도메인prepare, start, started, stopped, release, migrate, reconnect
/etc/libvirt/hooks/lxcLXC 컨테이너prepare, start, started, stopped, release
/etc/libvirt/hooks/network가상 네트워크start, started, stopped, plugged, unplugged
/etc/libvirt/hooks/daemonlibvirtd 데몬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
Hook 스크립트 규칙: 스크립트는 실행 권한(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:*"로 런타임에 활성화합니다.
VM 시작 실패 트러블슈팅 흐름 virsh start 실패 연결 오류? systemctl status libvirtd 권한 오류? SELinux / 파일 소유권 QEMU 크래시? QEMU 로그 확인 데몬 시작 / 소켓 확인 chown / chcon / restorecon /var/log/libvirt/qemu/*.log 문제 해결 후 재시작 virsh start 도메인명

백업 및 복구

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 필요
참고: UEFI 펌웨어 자동 선택은 /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-vmACPI 전원 끄기 (안전한 종료)
VM 강제 종료virsh destroy my-first-vm즉시 전원 차단 (긴급 시)
콘솔 접속virsh console my-first-vm시리얼 콘솔 (Ctrl+] 로 탈출)
VM 삭제virsh undefine my-first-vm --remove-all-storageVM 설정과 디스크 모두 삭제
VM 일시정지virsh suspend my-first-vmVM 프리징 (메모리 유지)
VM 재개virsh resume my-first-vm일시정지된 VM 재시작(Reboot)

일반적인 작업 흐름

VM 관리 기본 워크플로 1. 설치 virt-install ISO + 디스크 생성 2. 시작 virsh start Running 상태 3. 운영 console / SSH 작업 수행 4. 스냅샷 snapshot-create 상태 저장 5. 종료 virsh shutdown 안전한 종료 snapshot-revert (되돌리기) 스냅샷을 활용하면 언제든 이전 상태로 복구할 수 있다
커널 개발자를 위한 팁: virt-install--boot kernel=/path/to/bzImage,initrd=/path/to/initrd.img,kernel_args="console=ttyS0" 옵션을 사용하면 디스크 없이 커널을 직접 부팅할 수 있습니다. 커널 빌드-테스트 사이클을 단축하는 핵심 기법입니다. 자세한 내용은 QEMU 실전 가이드를 참조하십시오.
다음 단계: 기본 운영에 익숙해졌다면, XML 도메인 설정에서 CPU 토폴로지, NUMA 핀닝 등 고급 설정을 학습하고, 네트워크 관리에서 브리지/SR-IOV 네트워크를 구성해 보라.

참고 링크

  • 가상화 (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와 사용자 공간 파일시스템 구조