VM 관리
2.0.0 시작 계약: 정지 VM의 NUMA·2MiB hugepage 요구를 실제 XML로 확인하고 DPDK 준비
뒤 다시 검사합니다.
용량 snapshot은 자원 예약 보장이 아닙니다.
PMSUSPENDED VM은
깨우기 요청 뒤 RUNNING을 확인하며 기존 active CPU 예약을 다시 할당하지 않습니다.
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 라이프사이클”Web UI에서 VM 삭제
섹션 제목: “Web UI에서 VM 삭제”가상 머신 화면에서 VM을 선택한 뒤 상단 VM 삭제를 누릅니다.
여러 VM은 목록 또는
카드의 체크박스를 선택하고 아래 일괄 삭제 (N) 또는 일괄 작업 → 선택한 VM 삭제를
사용합니다.
전화 폭(600px 이하)은 하단 전원 탭의 VM 카드와 일괄 삭제를 사용합니다.
ADMIN/OPERATOR에게 표시되며 실제 요청은 서버의 소유권·권한 검사를 받습니다.
확인창은 대상 이름 전체와 영구 디스크 삭제 경고를 표시합니다.
단일 삭제는 VM 이름,
일괄 삭제는 표시된 수량 문구(예: 삭제 2)를 정확히 입력해야 진행할 수 있습니다.
실행 중인 VM도 강제 종료됩니다.
취소하면 삭제 요청을 보내지 않습니다.
요청은 하나씩 전송하고 대상별 결과를 남깁니다.
요청 접수 · 완료 미확인은 삭제 완료가
아닙니다.
현재 VM 삭제 서버는 접수만 반환하므로 최종 워커 결과는 감사 로그의 vm.delete
기록을 확인합니다.
목록에서 VM이 사라져도 디스크 정리가 끝났다고 단정하지 않습니다.
결과 창을 닫으면 아직 보내지 않은 요청만 중단하며, 접수된 삭제는 계속됩니다.
통신 오류 시 자동 재전송하지 않습니다.
대상이 없어지거나 동명 VM의 UUID가 바뀌면
삭제 요청을 보내지 않으므로 목록을 새로고침하고 다시 선택합니다.
# 목록 조회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
이름 변경:
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