VM 관리
3.1 VM 생성
섹션 제목: “3.1 VM 생성”기본 생성
섹션 제목: “기본 생성”pcvctl vm create \ web-prod \ --vcpu 2 \ --memory_mb 2048 \ --disk_size_gb 20 \ --qos_min_mbps 0 \ --qos_max_mbps 1000QoS SLA 필수(D09): 커널 netdev(tc 셰이핑 가능) 조합 —
network_bridge미지정/bridge, 또는tenant-overlay— 은--qos_min_mbps/--qos_max_mbps가 필수다(nic_type이dpdk/sriov인 경우만 면제).
생략하면 서버가vm.create자체를-32602로 거부한다.
아래 스토리지 타입 예시들은 지면상 이 두 플래그를 생략했지만 실제 호출에는 포함해야 한다.
스토리지 타입 선택
섹션 제목: “스토리지 타입 선택”VM 디스크의 스토리지 백엔드를 선택할 수 있습니다.
저장 위치 계약의 설계 기준은 ADR-0022: VM 생성 저장 위치 계약을 따른다.
| 타입 | 설명 | 적합한 용도 |
|---|---|---|
zvol | ZFS 볼륨 (블록 디바이스) | 고성능 I/O, 스냅샷/복제 |
qcow2 | QEMU Copy-on-Write | 범용, 씬 프로비저닝 |
raw | RAW 이미지 | 최대 I/O 성능 |
# 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 inlinevm.create가vm_manager.c로 위임하고,vm_manager.c가 ZFS 풀 존재 여부를 감지합니다.
ZFS 풀이 있으면zvol, 없으면daemon.conf [storage] image_dir에qcow2로 폴백합니다.
storage_pool또는image_dir를 함께 주면 자동 감지 대상 위치도 해당 값으로 바뀝니다.
storage_type=zvol을 명시하면 지정한 ZFS 부모 데이터셋이 없을 때 실패합니다.
고급 옵션
섹션 제목: “고급 옵션”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_pool | zvol 부모 데이터셋. 예: tank/vms |
--image_dir | qcow2/raw 파일 디스크 저장 디렉터리. 예: /var/lib/libvirt/images |
--template | VM 템플릿 이름 |
--qos_min_mbps | QoS 보장 대역(Mbit/s). 커널 netdev 셰이핑 대상 VM 은 필수(위 안내 참고) |
--qos_max_mbps | QoS 상한 대역(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는 위 표의 기본 옵션과 저장 위치 옵션을 직접 파싱한다.
RPC 직접 호출
섹션 제목: “RPC 직접 호출”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.toolREST API
섹션 제목: “REST API”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/디스크가 자동 롤백됩니다.
OVS 자동 감지
섹션 제목: “OVS 자동 감지”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와의 이중 캐싱 제거 |
| 부팅 디스크 AIO | io=native | cache=none(O_DIRECT)과 짝. 버퍼 I/O에서 native AIO는 블로킹될 수 있음 |
| 부팅 디스크 discard | discard=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의 시정 범위는 “정의조차 되지 않던” 결함까지입니다.
3.2 VM 라이프사이클
섹션 제목: “3.2 VM 라이프사이클”# 목록 조회pcvctl vm list
# 시작pcvctl vm start web-prod
# 정상 중지 (graceful 30초 → force destroy)pcvctl vm stop web-prod
# 일시 정지 / 재개pcvctl vm pause web-prodpcvctl vm resume web-prod
# 삭제 (비동기, 스토리지 포함)pcvctl vm delete web-prod
# 삭제 진행 상태 확인pcvctl vm delete-status web-prod중지 동작 상세:
virDomainShutdown으로 ACPI 셧다운 신호 전송- 1초 간격으로 최대 30초 동안 정상 종료 폴링
- 타임아웃 시
virDomainDestroy로 강제 종료 - 반환값 검증 후 결과 보고
삭제 진행 상태:
# 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가 남아 있으면 이름 변경을 거부한다.
# CLIpcvctl vm rename web-prod web-prod-01
# RPCecho '{"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.toolREST API 라이프사이클
섹션 제목: “REST API 라이프사이클”# 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-status3.3 핫플러그
섹션 제목: “3.3 핫플러그”실행 중인 VM에 리소스를 동적으로 추가하거나 변경할 수 있습니다.
vCPU / 메모리
섹션 제목: “vCPU / 메모리”# 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,3pcvctl 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}NIC 관리
섹션 제목: “NIC 관리”# 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:efmodel 허용목록:
--model은virtio/e1000/e1000e/rtl8139만 허용한다.
범위 밖 값은device.nic.attach에서-32602 INVALID_PARAMS로 거부된다.
MTU 명시(N8): NIC XML의
<mtu>는 attach 시점 브리지의 실측 MTU(/sys/class/net/<bridge>/mtu)를 1500 포함 항상 명시한다.
자세한 계약은 6.1 절 참조.
ISO 관리
섹션 제목: “ISO 관리”# 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-prodUSB 핫플러그
섹션 제목: “USB 핫플러그”# 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디스크 리사이즈
섹션 제목: “디스크 리사이즈”# 라이브 디스크 확장 (축소 불가, GB 단위)pcvctl vm disk-resize web-prod vda 50
# REST APIcurl -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 volsize와virDomainBlockResize가 동시에 수행됩니다.
게스트 OS 내에서 파티션 확장이 별도로 필요할 수 있습니다.
Block I/O 스로틀
섹션 제목: “Block I/O 스로틀”# I/O 제한 설정 (IOPS, bytes/sec)pcvctl vm disk-throttle web-prod --read-iops 1000 --write-iops 500pcvctl vm blkio-set web-prod --read_iops 1000 --write_iops 500
# 현재 I/O 제한 조회pcvctl vm blkio-get web-prodRPC 직접 호출:
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네트워크 QoS (대역폭 제한)
섹션 제목: “네트워크 QoS (대역폭 제한)”# VM 네트워크 대역폭 제한 (KB/s)pcvctl vm bandwidth web-prod --inbound-kbps 100000 --outbound-kbps 1000003.4 스냅샷
섹션 제목: “3.4 스냅샷”생성 및 관리
섹션 제목: “생성 및 관리”# 스냅샷 생성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개 보존 옵션을 지원합니다.
# "auto-" 프리픽스 스냅샷 중 최근 5개만 보존, 나머지 삭제pcvctl vm snapshot-delete-all web-prod --prefix auto- --keep 5
# 모든 스냅샷 삭제 (보존 없음)pcvctl vm snapshot-delete-all web-prodRPC 직접 호출:
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스냅샷 스케줄
섹션 제목: “스냅샷 스케줄”# 스케줄 상태 조회pcvctl snapshot schedule-status3.5 VM 복제
섹션 제목: “3.5 VM 복제”# 준비된 템플릿: 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-resetRPC 직접 호출:
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-tools의virt-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 full은pcv_spawn_pipe_sync()로zfs send와zfs 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=true는virt-sysprep으로 machine-id, DHCP/SSH/cloud-init 상태, hostname, LVM PV/VG UUID를 분리하고,guestfish로 ext filesystem UUID를 보정하며,virt-customize로/etc/fstabUUID 참조와 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.log의vm.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-sysprep의user-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를 수행한다.
실환경 확인 예시:
sudo pcvctl vm clone example-vm-source example-vm-clone --mode cow --template-preparedvirsh -c qemu:///system list --all | grep example-vm-clonesudo zfs list -H -o name,origin rpool/example-vm-clone예상 결과:
example-vm-clone shut offrpool/example-vm-clone rpool/example-vm-source@clone-example-vm-clonefull clone 확인 예시:
sudo pcvctl --format=json vm clone example-vm-source example-vm-full-clone --mode full --template-preparedvirsh -c qemu:///system dominfo example-vm-full-clonesudo zfs list -H -o name,origin rpool/example-vm-full-clonesudo 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: yesrpool/example-vm-full-clone -not found2026-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까지 성공했다.
클론 후 독립 데이터셋으로 전환하려면:
# ZFS 프로모트 (원본 스냅샷 의존 해소)pcvctl storage promote rpool/web-staging3.6 OVA 내보내기 / 가져오기
섹션 제목: “3.6 OVA 내보내기 / 가져오기”# 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-imported3.7 Guest Agent 연동
섹션 제목: “3.7 Guest Agent 연동”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 명령을 실행하지 않는다.
# 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.toolREST API:
# 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 pingcurl -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-usageVM 내부 설치 명령:
# Debian / Ubuntusudo apt update && sudo apt install -y qemu-guest-agentsudo systemctl enable --now qemu-guest-agent
# RHEL / Rocky / Fedorasudo dnf install -y qemu-guest-agentsudo systemctl enable --now qemu-guest-agent상태 해석:
| 상태 | 의미 | 다음 조치 |
|---|---|---|
ok | channel이 있고 agent ping 성공 | 정상 |
channel_missing | VM XML에 guest agent channel이 없음 | guest-agent-channel 보정 |
reboot_required | 영구 XML에는 channel이 있으나 실행 중 VM에는 아직 없음 | VM 재시작 또는 live attach 재시도 |
agent_unavailable | channel은 있으나 VM 내부 agent가 응답하지 않음 | VM 내부 qemu-guest-agent 설치/시작 |
vm_stopped | channel은 있으나 VM이 꺼져 있어 ping 불가 | VM 시작 후 재확인 |
3.8 VM 템플릿
섹션 제목: “3.8 VM 템플릿”템플릿을 사용하면 사전 정의된 설정으로 빠르게 VM을 생성할 수 있습니다.
# 프리셋 템플릿 목록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자).
3.9 VM 메트릭
섹션 제목: “3.9 VM 메트릭”# 개별 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# 전체 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:
# 개별 VM 메트릭curl -s -H "Authorization: Bearer $TOKEN" \ http://127.0.0.1:8080/api/v1/vms/web-prod/metrics | python3 -m json.tool