콘텐츠로 이동

VM 관리

Terminal window
pcvctl vm create \
web-prod \
--vcpu 2 \
--memory_mb 2048 \
--disk_size_gb 20 \
--qos_min_mbps 0 \
--qos_max_mbps 1000

QoS SLA 필수(D09): 커널 netdev(tc 셰이핑 가능) 조합 — network_bridge 미지정/bridge, 또는 tenant-overlay — 은 --qos_min_mbps/--qos_max_mbps가 필수다(nic_typedpdk/sriov인 경우만 면제).
생략하면 서버가 vm.create 자체를 -32602로 거부한다.
아래 스토리지 타입 예시들은 지면상 이 두 플래그를 생략했지만 실제 호출에는 포함해야 한다.

VM 디스크의 스토리지 백엔드를 선택할 수 있습니다.
저장 위치 계약의 설계 기준은 ADR-0022: VM 생성 저장 위치 계약을 따른다.

타입설명적합한 용도
zvolZFS 볼륨 (블록 디바이스)고성능 I/O, 스냅샷/복제
qcow2QEMU Copy-on-Write범용, 씬 프로비저닝
rawRAW 이미지최대 I/O 성능
Terminal window
# ZFS zvol (기본, ZFS 풀이 있을 때)
pcvctl vm create db-prod --vcpu 4 --memory_mb 8192 --disk_size_gb 100 --storage_type zvol
# ZFS zvol 저장 위치 지정
pcvctl vm create db-prod --vcpu 4 --memory_mb 8192 --disk_size_gb 100 --storage_type zvol --storage_pool tank/vms
# qcow2 (ZFS 풀 없는 localhost 또는 파일 디스크가 필요한 경우)
pcvctl vm create dev-vm --vcpu 2 --memory_mb 4096 --disk_size_gb 50 --storage_type qcow2
# qcow2 저장 디렉터리 지정
pcvctl vm create dev-vm --vcpu 2 --memory_mb 4096 --disk_size_gb 50 --storage_type qcow2 --image_dir /var/lib/libvirt/images
# RAW (최대 성능)
pcvctl vm create bench-vm --vcpu 8 --memory_mb 16384 --disk_size_gb 200 --storage_type raw

자동 감지: CLI/REST/RPC에서 storage_type을 지정하지 않으면 dispatcher inline vm.createvm_manager.c로 위임하고, vm_manager.c가 ZFS 풀 존재 여부를 감지합니다.
ZFS 풀이 있으면 zvol, 없으면 daemon.conf [storage] image_dirqcow2로 폴백합니다.
storage_pool 또는 image_dir를 함께 주면 자동 감지 대상 위치도 해당 값으로 바뀝니다.
storage_type=zvol을 명시하면 지정한 ZFS 부모 데이터셋이 없을 때 실패합니다.

Terminal window
pcvctl vm create \
db-prod \
--vcpu 4 \
--memory_mb 8192 \
--disk_size_gb 100 \
--storage_type zvol \
--storage_pool tank/vms \
--network_bridge pcvnat0 \
--iso_path /pcvpool/iso/ubuntu-24.04-live-server-amd64.iso \
--qos_min_mbps 100 \
--qos_max_mbps 500
옵션설명
--network_bridge연결할 브릿지. 미지정 시 관리형 기본 NAT 네트워크([network] default_bridge, 기본 pcvnat0)에 부착. none이면 NIC 미부착
--iso_path설치용 ISO 또는 seed ISO 경로
--storage_type디스크 타입 (zvol, qcow2, raw)
--storage_poolzvol 부모 데이터셋. 예: tank/vms
--image_dirqcow2/raw 파일 디스크 저장 디렉터리. 예: /var/lib/libvirt/images
--templateVM 템플릿 이름
--qos_min_mbpsQoS 보장 대역(Mbit/s). 커널 netdev 셰이핑 대상 VM 은 필수(위 안내 참고)
--qos_max_mbpsQoS 상한 대역(Mbit/s). 0 이면 서버가 거부(> 0 필수)

고급 VM 속성(firmware, boot_mode, tpm, cpu_mode, hugepages, vlan_id, base_image, ovn_switch)은 현재 REST/RPC body로 지정한다.
pcvctl vm create CLI는 위 표의 기본 옵션과 저장 위치 옵션을 직접 파싱한다.

Terminal window
echo '{"jsonrpc":"2.0","method":"vm.create","params":{
"name": "web-prod",
"vcpu": 2,
"memory_mb": 2048,
"disk_size_gb": 20,
"os_variant": "ubuntu24.04",
"storage_type": "zvol",
"storage_pool": "tank/vms",
"network_bridge": "pcvnat0",
"iso_path": "/pcvpool/iso/ubuntu-24.04-live-server-amd64.iso",
"qos_min_mbps": 100,
"qos_max_mbps": 500
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
Terminal window
curl -X POST http://127.0.0.1:8080/api/v1/vms \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "web-prod",
"vcpu": 2,
"memory_mb": 2048,
"disk_size_gb": 20,
"storage_type": "zvol",
"network_bridge": "pcvnat0",
"qos_min_mbps": 100,
"qos_max_mbps": 500
}'

응답:

{
"result": {
"accepted": true,
"message": "VM creation started in background"
}
}

fire-and-forget 패턴: VM 생성은 즉시 accepted: true를 반환하고 백그라운드에서 비동기 실행됩니다.
virDomainDefineXML 실패 시 zvol/디스크가 자동 롤백됩니다.

network_bridge에 OVS 브릿지를 지정하면, 데몬이 ovs-vsctl br-exists로 OVS 여부를 자동 확인하고 VM XML에 <virtualport type='openvswitch'/>를 자동 추가합니다.

부팅 디스크 I/O 정책과 메모리 격리 (2.0.0, NXV-F1)

섹션 제목: “부팅 디스크 I/O 정책과 메모리 격리 (2.0.0, NXV-F1)”

2.0.0 현행 정본에서 vm.create가 만드는 도메인 XML에 다음 정책이 항상 적용됩니다.
켜고 끄는 옵션은 없습니다.

항목이유
부팅 디스크 캐시cache=none호스트 페이지캐시 우회 — zvol/데이터셋 위에서 ZFS ARC와의 이중 캐싱 제거
부팅 디스크 AIOio=nativecache=none(O_DIRECT)과 짝. 버퍼 I/O에서 native AIO는 블로킹될 수 있음
부팅 디스크 discarddiscard=unmap게스트 fstrim이 zvol/qcow2 블록을 실제 해제 → 스냅샷 델타·복제 전송량 감소
메모리 병합<nosharepages/>호스트 KSM의 page-dedup 측면 채널 차단

핫플러그로 붙는 보조 디스크는 이전부터 같은 I/O 정책을 썼습니다.
NXV-F1 시정은 부팅 디스크만 QEMU 기본값 (cache=writeback, io=threads, discard 무시)으로 남아 있던 두 경로의 정책 비대칭을 없앤 것입니다.
전제는 백엔드가 O_DIRECT를 지원한다는 점이며, 지원하지 않는 배치는 비지원입니다.

<nosharepages/>(QEMU mem-merge=off)는 호스트 KSM이 서로 다른 테넌트 VM의 동일 페이지를 병합할 때 생기는 측면 채널을 막습니다 — 병합된 페이지에 쓸 때의 copy 지연을 재는 것만으로 “옆 VM이 이 페이지를 갖고 있는가”를 알아낼 수 있기 때문입니다.
대가는 동종 게스트를 대량 배치할 때의 메모리 절감 포기이며, 도메인별 설정이라 호스트 전역 ksm/run=0보다 되돌리기 쉽습니다.

hugepage 프리플라이트: hugepages=true로 생성하는데 호스트의 2MB 여유 페이지가 부족하면 디스크 프로비저닝 직전에 즉시 실패합니다(필요 개수와 여유 개수를 오류 메시지에 담습니다).
이 검사가 없으면 정의는 성공하고 vm.start가 libvirt 내부 오류로 죽어 “만들 때는 됐는데 켜지지 않는” 불투명한 상태가 됩니다.
판정은 total이 아니라 free 기준이고, sysfs를 읽지 못하면 막지 않습니다 (프리플라이트가 새 실패 지점이 되면 안 되므로).
프리플라이트를 디스크 생성보다 앞에 두는 이유는 뒤에 두면 실패해도 zvol/qcow2가 남기 때문입니다.

vhost-user(DPDK) 공유 메모리: nic_type="dpdk"는 libvirt가 vhost-user 인터페이스에 공유 메모리 backend를 요구하기 때문에 해당 시정 전에는 도메인 정의 자체가 거부되던 죽은 경로였습니다 (unsupported configuration: 'interface' requires shared memory).
NXV-F1은 memoryBacking을 조합으로 조립해 해소했습니다.

조합memoryBacking
dpdk + hugepages<hugepages…/> + <access mode='shared'/>
dpdk<source type='memfd'/> + <access mode='shared'/>
hugepages종전과 동일

OVS-DPDK 소켓이 없으면 정의가 통과해도 vm.start는 여전히 실패합니다.
NXV-F1의 시정 범위는 “정의조차 되지 않던” 결함까지입니다.

Terminal window
# 목록 조회
pcvctl vm list
# 시작
pcvctl vm start web-prod
# 정상 중지 (graceful 30초 → force destroy)
pcvctl vm stop web-prod
# 일시 정지 / 재개
pcvctl vm pause web-prod
pcvctl vm resume web-prod
# 삭제 (비동기, 스토리지 포함)
pcvctl vm delete web-prod
# 삭제 진행 상태 확인
pcvctl vm delete-status web-prod

중지 동작 상세:

  1. virDomainShutdown으로 ACPI 셧다운 신호 전송
  2. 1초 간격으로 최대 30초 동안 정상 종료 폴링
  3. 타임아웃 시 virDomainDestroy로 강제 종료
  4. 반환값 검증 후 결과 보고

삭제 진행 상태:

Terminal window
# RPC로 삭제 상태 확인
echo '{"jsonrpc":"2.0","method":"vm.delete.status","params":{"name":"web-prod"},"id":"1"}' \
| nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool

응답 상태값: pending | deleting | done | failed

UEFI 삭제와 NVRAM 보존:

공개 소스 5e84387부터 vm.delete는 파일형 NVRAM을 보존한 채 libvirt 정의를 해제하고, 주 디스크 삭제가 성공한 뒤 NVRAM을 정리합니다.
디스크 접근·삭제 실패 시 XML을 복원하고 기존 NVRAM을 보존합니다.
마지막 NVRAM 정리만 실패하면 이미 삭제한 디스크를 복구한 것으로 표시하지 않으며, audit fail과 오류에 남은 파일 경로를 보고합니다.
파일 부재와 권한 오류를 구분하고, block/network NVRAM·varstore와 불명확한 XML은 변경 전에 거부합니다.
최초 부팅 전 NVRAM 파일이 아직 없는 경우도 처리합니다.

삭제 결과는 접수 응답·목록만으로 판정하지 않고 최종 상태, vm.delete audit, 실제 domain·디스크·NVRAM을 대조합니다.
펌웨어 loader/template와 설치 ISO는 삭제 대상이 아닙니다.
지정 Ubuntu·Arch 검증과 수동 정리가 필요한 실패 경계는 NVRAM 수정 인계를 따릅니다.

이름 변경:

VM 이름 변경은 정지된 VM에서만 허용된다.
libvirt domain 이름, 표준 ZFS zvol 또는 표준 qcow2/raw/img 파일 디스크 경로, UEFI NVRAM 경로를 함께 변경한다.

지원 조건:

  • VM 상태가 shut off여야 한다.
  • 새 이름은 pcv_validate_vm_name() 규칙을 따른다.
    허용 문자는 영문, 숫자, 하이픈, 언더스코어이며 최대 64자다.
  • 기본 디스크는 /dev/zvol/<parent>/<vm> 또는 <vm>.qcow2, <vm>.raw, <vm>.img 형식이어야 한다.
  • libvirt snapshot metadata가 남아 있으면 이름 변경을 거부한다.
Terminal window
# CLI
pcvctl vm rename web-prod web-prod-01
# RPC
echo '{"jsonrpc":"2.0","method":"vm.rename","params":{
"name":"web-prod",
"new_name":"web-prod-01"
},"id":"1"}' \
| nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
Terminal window
# VM 목록
curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8080/api/v1/vms | python3 -m json.tool
# VM 시작
curl -X POST -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8080/api/v1/vms/web-prod/start
# VM 중지
curl -X POST -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8080/api/v1/vms/web-prod/stop
# VM 일시 정지
curl -X POST -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8080/api/v1/vms/web-prod/suspend
# VM 재개
curl -X POST -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8080/api/v1/vms/web-prod/resume
# VM 삭제
curl -X DELETE -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8080/api/v1/vms/web-prod
# VM 이름 변경
curl -X PUT -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"new_name":"web-prod-01"}' \
http://127.0.0.1:8080/api/v1/vms/web-prod/rename
# 삭제 상태 확인
curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8080/api/v1/vms/web-prod/delete-status

실행 중인 VM에 리소스를 동적으로 추가하거나 변경할 수 있습니다.

Terminal window
# vCPU 조정 (라이브)
pcvctl vm set-vcpu web-prod 4
# 메모리 조정 (balloon, MB 단위)
pcvctl vm set-memory web-prod 4096
# CPU 피닝 (특정 물리 코어에 고정)
pcvctl vm pin-vcpu web-prod --vcpu 0 --cpuset 2,3
pcvctl vm pin-vcpu web-prod --vcpu 1 --cpuset 4,5
# CPU 통계 확인
echo '{"jsonrpc":"2.0","method":"vm.cpu.stats","params":{"name":"web-prod"},"id":"1"}' \
| nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
# 메모리 balloon 통계 확인
echo '{"jsonrpc":"2.0","method":"vm.memory.stats","params":{"name":"web-prod"},"id":"1"}' \
| nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool

메모리 통계 응답 예시:

{
"actual": 4194304,
"rss": 3145728,
"unused": 1048576,
"available": 4194304,
"swap_in": 0,
"swap_out": 0
}
Terminal window
# NIC 목록
pcvctl nic list web-prod
# NIC 추가
pcvctl nic add web-prod --bridge pcvnat0 --model virtio
# NIC 제거 (MAC 주소 지정)
pcvctl nic remove web-prod --mac 52:54:00:ab:cd:ef

model 허용목록: --modelvirtio / e1000 / e1000e / rtl8139만 허용한다.
범위 밖 값은 device.nic.attach에서 -32602 INVALID_PARAMS로 거부된다.

MTU 명시(N8): NIC XML의 <mtu>는 attach 시점 브리지의 실측 MTU(/sys/class/net/<bridge>/mtu)를 1500 포함 항상 명시한다.
자세한 계약은 6.1 절 참조.

Terminal window
# ISO 목록 (설정된 iso_dirs 전체 스캔)
pcvctl iso list
# ISO 마운트
pcvctl iso mount web-prod /pcvpool/iso/ubuntu-24.04-live-server-amd64.iso
# ISO 꺼내기
pcvctl iso eject web-prod
Terminal window
# USB 장치 목록
pcvctl vm usb-list web-prod
# USB 장치 연결
pcvctl vm usb-attach web-prod 0x1234 0x5678
# USB 장치 분리
pcvctl vm usb-detach web-prod 0x1234 0x5678
Terminal window
# 라이브 디스크 확장 (축소 불가, GB 단위)
pcvctl vm disk-resize web-prod vda 50
# REST API
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:8080/api/v1/vms/web-prod/disk-resize \
-H "Content-Type: application/json" \
-d '{"new_size_gb": 50}'

주의: ZFS zvol의 경우 zfs set volsizevirDomainBlockResize가 동시에 수행됩니다.
게스트 OS 내에서 파티션 확장이 별도로 필요할 수 있습니다.

Terminal window
# I/O 제한 설정 (IOPS, bytes/sec)
pcvctl vm disk-throttle web-prod --read-iops 1000 --write-iops 500
pcvctl vm blkio-set web-prod --read_iops 1000 --write_iops 500
# 현재 I/O 제한 조회
pcvctl vm blkio-get web-prod

RPC 직접 호출:

Terminal window
echo '{"jsonrpc":"2.0","method":"vm.blkio.set","params":{
"name": "web-prod",
"device": "vda",
"read_bytes_sec": 104857600,
"write_bytes_sec": 52428800,
"read_iops_sec": 1000,
"write_iops_sec": 500
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
Terminal window
# VM 네트워크 대역폭 제한 (KB/s)
pcvctl vm bandwidth web-prod --inbound-kbps 100000 --outbound-kbps 100000
Terminal window
# 스냅샷 생성
pcvctl snapshot create web-prod before-upgrade
# 스냅샷 목록
pcvctl snapshot list web-prod
# 스냅샷 롤백 (fire-and-forget: VM 중지 -> 롤백 -> 재시작)
pcvctl snapshot rollback web-prod before-upgrade
# 스냅샷 삭제
pcvctl snapshot delete web-prod before-upgrade

prefix 패턴 필터와 최근 N개 보존 옵션을 지원합니다.

Terminal window
# "auto-" 프리픽스 스냅샷 중 최근 5개만 보존, 나머지 삭제
pcvctl vm snapshot-delete-all web-prod --prefix auto- --keep 5
# 모든 스냅샷 삭제 (보존 없음)
pcvctl vm snapshot-delete-all web-prod

RPC 직접 호출:

Terminal window
echo '{"jsonrpc":"2.0","method":"vm.snapshot.delete_all","params":{
"name": "web-prod",
"prefix": "auto-",
"keep_recent": 5
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
Terminal window
# 스케줄 상태 조회
pcvctl snapshot schedule-status
Terminal window
# 준비된 템플릿: guest reset 생략
pcvctl vm clone web-template web-staging --mode cow --template-prepared
# 일반 VM: target disk 생성 후 libguestfs guest reset 수행
pcvctl vm clone web-prod web-prod-copy --mode full --guest-reset

RPC 직접 호출:

Terminal window
echo '{"jsonrpc":"2.0","method":"vm.clone","params":{
"source": "web-template",
"clone_name": "web-staging",
"mode": "cow",
"template_prepared": true
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool

안전 제한: vm.clone은 source VM이 shut off 상태일 때만 허용하며, 단일 data disk VM만 지원한다.
template_prepared=true가 없으면 guest_reset=true 경로로 libguestfs-toolsvirt-sysprep, virt-filesystems, guestfish, virt-customize를 실행해야 한다.
qcow2/raw는 추가로 mode=full만 허용한다.
자세한 기준은 ADR-0023을 따른다.

fire-and-forget: 클론은 즉시 accepted: true를 반환하고 백그라운드에서 disk clone + guest reset + XML 패치를 수행합니다.

accepted 응답에는 실제 XML에서 확인한 source/target disk가 포함된다.

{
"status": "accepted",
"source": "web-template",
"clone_name": "web-staging",
"mode": "cow",
"job_id": "vm.clone:web-template:web-staging",
"guest_reset": false,
"storage_type": "zvol",
"source_disk": "/dev/zvol/rpool/web-template",
"target_disk": "/dev/zvol/rpool/web-staging"
}

동작 기준:

  • clone plan은 daemon.conf [storage] zvol_pool이나 image_dir을 추측하지 않고 libvirt XML의 실제 disk source를 기준으로 계산한다.
  • source VM이 power on 상태이면 storage type과 무관하게 preflight에서 거부한다.
  • --mode cow는 source snapshot을 origin으로 유지하는 ZFS CoW clone이다.
  • --mode fullpcv_spawn_pipe_sync()zfs sendzfs recv를 직접 연결해 독립 zvol을 만든다.
    셸 파이프, 리다이렉션, /tmp 대용량 임시 파일은 사용하지 않으며, 성공 후 source 임시 snapshot은 정리한다.
  • snapshot 생성 이후 worker가 실패하면 target dataset을 먼저 best-effort 정리하고, source 임시 snapshot이 origin으로 필요하지 않으면 함께 best-effort 정리한다.
  • qcow2/raw 파일 디스크는 mode=full에서 qemu-img convert로 별도 target 파일을 만든다.
  • guest_reset=truevirt-sysprep으로 machine-id, DHCP/SSH/cloud-init 상태, hostname, LVM PV/VG UUID를 분리하고, guestfish로 ext filesystem UUID를 보정하며, virt-customize/etc/fstab UUID 참조와 Ubuntu/Rocky 계열 boot artifact 재생성 command를 실행한다.
  • 현재 실환경 완료 범위는 준비된 zvol clone, Ubuntu 24.04 non-LVM qcow2/raw full clone + guest reset, Ubuntu 24.04 LVM qcow2/raw/ZFS zvol full clone + guest reset이다.
    Rocky/RHEL LVM, SELinux enforcing boot smoke는 문서상 후속 검증 항목으로만 유지한다.
  • 이름, UUID, MAC, disk source를 각각 패치한다.
    MAC 재생성은 원본 XML을 한 번만 스캔해 새 MAC이 다시 매칭되는 무한 루프를 방지한다.
  • clone 생성 결과는 자동 기동하지 않는다.
    운영자가 검증 후 수동으로 시작한다.
  • worker 결과는 accepted 응답의 job_id, /var/log/purecvisor/audit.logvm.clone 결과, WebSocket job completion으로 확인한다.

Guest reset과 Prepared template 선택 기준:

  • Guest reset은 일반 VM 복제 기본값이다.
    운영 서버에 libguestfs-tools가 설치되어 있어야 하며, target disk 생성 후 machine-id, hostname, SSH/DHCP/cloud-init 상태, filesystem/LVM UUID, /etc/fstab 참조를 새 VM 기준으로 분리한다.
  • Prepared template은 운영자가 이미 guest identity를 정리한 템플릿 VM에만 사용한다.
    이 선택은 guest reset을 건너뛰므로 source VM에 중복 식별자가 남아 있지 않다는 책임은 운영자에게 있다.
  • 두 선택을 동시에 사용할 필요는 없다.
    준비된 템플릿이면 --template-prepared, 일반 VM이면 --guest-reset 또는 기본 Guest reset 경로를 사용한다.

Guest reset 후 로그인 정보:

  • Guest reset은 guest OS의 기존 사용자 계정과 /etc/shadow 비밀번호 해시를 새 값으로 바꾸지 않는다.
    현재 구현은 virt-sysprepuser-account나 비밀번호 재설정 operation을 사용하지 않는다.
  • 콘솔 로그인은 원본 VM의 기존 계정/비밀번호가 유지되는 것이 기본 동작이다.
  • virt-sysprep defaults는 SSH host key와 사용자 홈의 .ssh 디렉터리를 정리할 수 있다.
    따라서 SSH 키 로그인은 실패할 수 있고, SSH 비밀번호 로그인 가능 여부는 guest OS의 sshd 설정에 따른다.
  • cloud-init으로 생성한 webadmin 계정도 평문 비밀번호를 복구할 수 있다고 가정하면 안 된다.
    Ubuntu autoinstall의 identity.password는 보통 $6$... 형태의 SHA-512 crypt 해시로 들어가며, 운영 문서에는 해시 원문도 저장하지 않는다.
  • autoinstall seed에 chage -d 0 webadmin이 포함된 VM은 첫 로그인 시 비밀번호 변경을 요구할 수 있다.
  • webadmin 접속 복구가 필요하면 source/target VM을 종료한 뒤 sudo virt-customize -d <vm_name> --password webadmin:password:'새비밀번호'로 새 비밀번호를 주입하고 수동 boot smoke를 수행한다.

실환경 확인 예시:

Terminal window
sudo pcvctl vm clone example-vm-source example-vm-clone --mode cow --template-prepared
virsh -c qemu:///system list --all | grep example-vm-clone
sudo zfs list -H -o name,origin rpool/example-vm-clone

예상 결과:

example-vm-clone shut off
rpool/example-vm-clone rpool/example-vm-source@clone-example-vm-clone

full clone 확인 예시:

Terminal window
sudo pcvctl --format=json vm clone example-vm-source example-vm-full-clone --mode full --template-prepared
virsh -c qemu:///system dominfo example-vm-full-clone
sudo zfs list -H -o name,origin rpool/example-vm-full-clone
sudo zfs list -t snapshot -H -o name rpool/example-vm-source@clone-example-vm-full-clone || echo "not found"

예상 결과:

example-vm-full-clone shut off / Persistent: yes
rpool/example-vm-full-clone -
not found

2026-04-29 KST 운영 서버 검증에서는 위 full clone 경로가 accepted, target zvol origin=-, source 임시 snapshot 없음, audit result=ok로 확인됐다.
이어 Ubuntu 24.04 non-LVM qcow2/raw와 Ubuntu 24.04 LVM qcow2/raw/ZFS zvol clone은 guest reset, hostname/machine-id 재생성, filesystem UUID 분리, LVM PV/VG UUID 분리, /etc/fstab UUID 참조 갱신, 수동 boot smoke까지 성공했다.

클론 후 독립 데이터셋으로 전환하려면:

Terminal window
# ZFS 프로모트 (원본 스냅샷 의존 해소)
pcvctl storage promote rpool/web-staging
Terminal window
# OVA 내보내기 (qemu-img VMDK 변환 + OVF + tar)
pcvctl vm export-ova web-prod --output-dir /tmp
# OVA 가져오기
pcvctl vm import-ova /tmp/web-prod.ova web-imported

QEMU Guest Agent는 VM 내부 패키지와 libvirt channel이 함께 있어야 사용할 수 있다.
v1.1부터 vm create가 생성하는 VM에는 guest-agent channel이 기본 포함되므로 신규 VM은 별도 보정이 필요 없다(패키지만 VM 내부에서 설치).
guest-agent-ensure-channel은 채널이 없는 기존/레거시 VM 보정용이다.
PureCVisor는 상태 진단과 channel 보정을 함께 제공한다.

Web UI에서는 대시보드 > 요약에서 VM을 선택한 뒤 Storage 카드의 디스크 사용량 버튼으로 게스트 파일시스템 사용량을 확인한다.
이 조회는 qemu-guest-agent의 고정 guest-get-fsinfo 명령만 사용하며, 임의 guest-exec 명령을 실행하지 않는다.

Terminal window
# Guest Agent 상태 진단
pcvctl vm guest-agent-status web-prod
echo '{"jsonrpc":"2.0","method":"vm.guest.agent.status","params":{"name":"web-prod"},"id":"1"}' \
| nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
# 기존 VM XML에 Guest Agent channel 보정
pcvctl vm guest-agent-ensure-channel web-prod
echo '{"jsonrpc":"2.0","method":"vm.guest.agent.ensure_channel","params":{"name":"web-prod"},"id":"1"}' \
| nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
# Guest Agent 연결 확인 (5초 타임아웃)
pcvctl vm guest-ping web-prod
echo '{"jsonrpc":"2.0","method":"vm.guest.ping","params":{"name":"web-prod"},"id":"1"}' \
| nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
# Guest Agent를 통한 안전한 셧다운 (ACPI 폴백)
pcvctl vm guest-shutdown web-prod
echo '{"jsonrpc":"2.0","method":"vm.guest.shutdown","params":{"name":"web-prod"},"id":"1"}' \
| nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
# Guest Agent를 통한 명령 실행 (base64 stdout/stderr)
pcvctl vm guest-exec web-prod "hostname -I"
echo '{"jsonrpc":"2.0","method":"vm.guest.exec","params":{
"name":"web-prod",
"command":"hostname -I"
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
# 게스트 파일시스템 디스크 사용량 조회
echo '{"jsonrpc":"2.0","method":"vm.guest.fsinfo","params":{"name":"web-prod"},"id":"1"}' \
| nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool

REST API:

Terminal window
# Guest Agent 상태 진단
curl -H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:8080/api/v1/vms/web-prod/guest-agent
# Guest Agent channel 보정
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:8080/api/v1/vms/web-prod/guest-agent-channel
# Guest Agent ping
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:8080/api/v1/vms/web-prod/guest-ping
# Guest Agent 명령 실행
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
http://127.0.0.1:8080/api/v1/vms/web-prod/guest-exec \
-d '{"command": "cat /etc/hostname"}'
# 게스트 파일시스템 디스크 사용량 조회
curl -H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:8080/api/v1/vms/web-prod/disk-usage

VM 내부 설치 명령:

Terminal window
# Debian / Ubuntu
sudo apt update && sudo apt install -y qemu-guest-agent
sudo systemctl enable --now qemu-guest-agent
# RHEL / Rocky / Fedora
sudo dnf install -y qemu-guest-agent
sudo systemctl enable --now qemu-guest-agent

상태 해석:

상태의미다음 조치
okchannel이 있고 agent ping 성공정상
channel_missingVM XML에 guest agent channel이 없음guest-agent-channel 보정
reboot_required영구 XML에는 channel이 있으나 실행 중 VM에는 아직 없음VM 재시작 또는 live attach 재시도
agent_unavailablechannel은 있으나 VM 내부 agent가 응답하지 않음VM 내부 qemu-guest-agent 설치/시작
vm_stoppedchannel은 있으나 VM이 꺼져 있어 ping 불가VM 시작 후 재확인

템플릿을 사용하면 사전 정의된 설정으로 빠르게 VM을 생성할 수 있습니다.

Terminal window
# 프리셋 템플릿 목록
pcvctl template list
# 템플릿 생성
pcvctl template create web \
--vcpu 2 \
--memory_mb 2048 \
--disk_gb 20 \
--os_variant ubuntu24.04
# 템플릿으로 VM 생성
pcvctl template apply web --name web-prod-01
# 템플릿 삭제
pcvctl template delete web

이름 검증: 템플릿 이름은 pcv_validate_vm_name() 규칙을 따릅니다 (영문소문자, 숫자, 하이픈, 최대 64자).

Terminal window
# 개별 VM 메트릭
pcvctl monitor vm web-prod

출력 예시:

VM: web-prod (running)
CPU: 15.2%
Memory: 1024/2048 MB (50.0%)
Disk: 8.5 GB / 20 GB
Net RX: 1.2 MB/s TX: 0.3 MB/s
Terminal window
# 전체 VM 일괄 메트릭 (fleet 뷰)
pcvctl monitor fleet

출력 예시(사이버 테마):

ENTITY_ID │ LIFELINE │ CPU_TIME │ MEM%
──────────────────────┼────────────┼──────────┼────────
example-vm-source │ RUNNING │ 63.0s │ 26.4%
fitlog │ RUNNING │ 50.7s │ 17.2%

PLAIN/CSV(--format=plain/--csv)는 헤더가 NAME,STATE,CPU_TIME,MEM%로 바뀐다.
CPU_TIME은 누적 CPU 시간(초, 예 63.0s, 값 없으면 -)이고 MEM%는 mem_used_mb/mem_max_mb*100이다(정지 VM 등 계산 불가 시 N/A).

REST API:

Terminal window
# 개별 VM 메트릭
curl -s -H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:8080/api/v1/vms/web-prod/metrics | python3 -m json.tool