설치 및 환경 구성
2.1 솔루션 권장사항 — Ubuntu 26.04.1 LTS
섹션 제목: “2.1 솔루션 권장사항 — Ubuntu 26.04.1 LTS”PureCVisor 2.0.0 Single Edge의 공개 설치 기준은 Ubuntu Server 26.04.1 LTS(Resolute Raccoon) amd64입니다.
Ubuntu 26.04 LTS는 2031년 4월까지 표준 보안 업데이트와 중요 수정이 제공되며, 26.04.1 설치 이미지는 26.04 출시 이후의 설치·기본 플랫폼 수정 사항을 포함합니다.
설치 기준
- 새 노드는 Ubuntu 26.04.1 Server 이미지로 설치합니다.
- 설치 전에 Ubuntu 26.04 LTS 릴리스 노트와 26.04.1 변경 사항을 확인합니다.
권장 배포 프로파일
섹션 제목: “권장 배포 프로파일”| 항목 | 기능 확인용 최소 구성 | 운영 권장 구성 | 검증 기준 노드 |
|---|---|---|---|
| CPU | 8코어, VT-x/AMD-V | 16코어 이상, IOMMU | Intel Xeon E5-2697 v4 2소켓, 36코어/72스레드, VT-x·IOMMU |
| RAM | 32GiB | 64GiB 이상, ZFS 사용 시 ECC 권장 | 128GB급, OS 인식 121GiB |
| 시스템 디스크 | 256GB SSD | OS 전용 SSD 또는 mirror | 2TB NVMe의 ZFS root pool |
| VM 데이터 | 512GB SSD | 별도 1TB 이상 NVMe mirror와 외부 백업 | 단일 2TB NVMe 공유, 검증용이며 디스크 장애 이중화 없음 |
| 관리 네트워크 | 1GbE | 고정 주소 1GbE 이상 | 1GbE 관리 NIC, 설치자가 확인한 미사용 IPv4 |
| 게스트·스토리지 네트워크 | 관리 NIC와 공유 | 별도 10GbE 이상 | 10GbE 전용 NIC, 호스트 L3 주소 없음 |
최소 구성은 제품 기능 확인용이며 동시에 실행할 VM의 vCPU·메모리·디스크를 포함하지 않습니다.
운영 노드는 호스트와 purecvisorsd용으로 최소 4 vCPU와 8GiB RAM을 남기고 나머지를 VM 할당 한도로 잡습니다.
VM 데이터 사용률은 80% 아래로 유지하고, snapshot과 같은 pool에만 있는 복사본을 백업으로 간주하지 않습니다.
검증 기준 노드의 단일 NVMe 구성은 재현·검증용이며 운영 장애 허용 구성이 아니므로, 운영 배포에서는 mirror와 별도 백업 대상을 사용합니다.
Ubuntu 26.04 소프트웨어 기준
섹션 제목: “Ubuntu 26.04 소프트웨어 기준”| 구성 요소 | 26.04 기준 | 검증 기준 노드 확인값 |
|---|---|---|
| OS | Ubuntu Server 26.04.1 LTS amd64 | Ubuntu 26.04.1 LTS |
| 커널 | Ubuntu generic 커널, cgroup v2 | 7.0.0-30-generic, cgroup2fs |
| C compiler | GCC 14 이상, C23 -std=gnu23 | GCC 14.3 |
| GLib | 2.88 계열 | 2.88.0 |
| libvirt | 12.0 계열 | 12.0.0 |
| QEMU/KVM | QEMU 10.2 계열, /dev/kvm | 10.2.1, KVM 검증 통과 |
| ZFS | 2.4 계열, 설치 커널과 같은 Ubuntu archive | 2.4.1 |
| OVS/OVN | 기능 사용 시 OVS 3.7·OVN 26.03 계열 | OVS 3.7.1·OVN 26.03.0 |
패키지 적용 원칙
- 표의 patch version은 2026-08-30 기준 검증값이며 고정 핀이 아닙니다.
resolute-updates와resolute-security의 최신 호환 package를 함께 적용합니다.- Ubuntu 26.04의 HWE virtualization stack은 커널, libvirt, QEMU와 ZFS를 함께 업데이트한 뒤 재검증합니다.
- Ubuntu 24.04 guest clone 검증 이력은 guest 호환 범위이며 host OS 기준을 24.04로 낮추지 않습니다.
관리 IPv4 선정 기준
섹션 제목: “관리 IPv4 선정 기준”| 역할 | 값 | 운영 원칙 |
|---|---|---|
| 관리 NIC | <management-interface> | ip -br link에서 확인한 실제 이름, host L3와 default route를 유지하며 bridge/dedicated에 넣지 않음 |
| 관리 주소 | <unused-management-ipv4>/<prefix-length> | 관리망 대역 내 미사용 IPv4를 선택하고 router DHCP reservation 또는 정적 Netplan으로 고정 |
| gateway | <default-gateway-ipv4> | 기존 default route에서 확인하고 관리 주소와 같은 대역인지 검증 |
| DNS | <dns-ipv4> | 현재 resolver 정책을 유지하고 설치 전후 DNS 응답 확인 |
| 제품 HTTPS | https://<management-ipv4>:443 | 기본 purecvisorsd 자체 HTTPS, NGINX 불필요 |
| 로컬 복구 HTTP | http://127.0.0.1:8080 | loopback에서만 접근, 외부 공개 금지 |
| 기본 VM NAT | pcvnat0, 10.78.0.1/24 | 관리망·기존 libvirt/LXC/VPC 대역과 중복 금지 |
| 선택형 guest uplink | <guest-uplink-interface> | 주소·default route·master가 없음을 확인한 뒤에만 bridge/dedicated 후보로 사용 |
주소 선택 원칙
- 문서의 길이 표시는 그대로 복사할 예시 IP가 아닙니다.
- 설치자는 router·DHCP 할당표와 현재 lease를 확인해 동적 pool 외부이거나 reservation으로 보호된 미사용 IPv4를 선택해야 합니다.
권장 방식은 현재 Netplan renderer와 DHCP를 유지하고 router에서 <management-interface>의 lease를 선택한 IPv4로 예약하는 것입니다.
이 방식은 host에서 주소를 중복 선언하지 않으면서 서비스 주소를 고정합니다.
router reservation을 사용할 수 없을 때만 아래 정적 Netplan 절차를 사용합니다.
2.2 솔루션 설치
섹션 제목: “2.2 솔루션 설치”26.04 host 기본 준비
섹션 제목: “26.04 host 기본 준비”인증서 생성 전 확정
- hostname과 시간대를 확정합니다.
- 관리 IPv4 또는 운영 DNS 이름을 확정합니다.
- 이미 인증서를 운영 중인 노드의 hostname이나 관리 IP를 바꾸면 SAN과 접속 URL도 함께 갱신합니다.
sudo hostnamectl set-hostname purecvisor-edge-01sudo timedatectl set-timezone Asia/Seoul
sudo apt updatesudo apt full-upgrade -ysudo apt install -y ca-certificates curl git jq cpu-checker libvirt-clients
test "$(stat -fc %T /sys/fs/cgroup)" = cgroup2fskvm-oksudo virt-host-validate qemutimedatectl status커널, ZFS 또는 QEMU가 갱신됐다면 이 단계에서 한 번 재부팅하고 같은 검사를 다시 실행합니다.
virt-host-validate의 /dev/kvm, /dev/vhost-net, /dev/net/tun, IOMMU 항목은 PASS여야 합니다.
confidential guest 기능을 사용하지 않는 Single Edge 노드의 SEV/TDX 경고는 기능 미사용 상태로 기록할 수 있지만 KVM 또는 IOMMU 실패는 설치 전에 해결합니다.
관리 주소 고정
섹션 제목: “관리 주소 고정”권장 방식은 router에서 DHCP reservation을 설정한 뒤 host 설정을 그대로 유지하는 것입니다.
MAC 주소는 router 관리 화면에서 직접 확인하고 공개 문서나 이력에 기록하지 않습니다.
MGMT_NIC="<management-interface>"NODE_IPV4="<unused-management-ipv4>"PREFIX_LENGTH="<prefix-length>"GATEWAY_IPV4="<default-gateway-ipv4>"DNS_IPV4="<dns-ipv4>"
sudo netplan getip -4 address show dev "${MGMT_NIC}"ip route show defaultresolvectl status "${MGMT_NIC}"
# 재부팅 후에도 아래 세 값이 유지되는지 확인# address: ${NODE_IPV4}/${PREFIX_LENGTH}# default route: ${GATEWAY_IPV4} via ${MGMT_NIC}# DNS server: ${DNS_IPV4}DHCP reservation을 사용할 수 없는 환경에서는 console 또는 BMC 접속을 확보한 뒤 /etc/netplan/60-purecvisor-mgmt.yaml을 다음과 같이 만듭니다.
먼저 모든 <...> 값을 해당 노드의 실제 값으로 바꾸고, 기존 sudo netplan get에서 확인한 renderer 정책을 유지합니다.
다른 Netplan 파일이 <management-interface>를 함께 정의하면 먼저 중복을 제거합니다.
network: version: 2 ethernets: <management-interface>: dhcp4: false dhcp6: false addresses: - <unused-management-ipv4>/<prefix-length> routes: - to: default via: <default-gateway-ipv4> nameservers: addresses: - <dns-ipv4>sudo chown root:root /etc/netplan/60-purecvisor-mgmt.yamlsudo chmod 600 /etc/netplan/60-purecvisor-mgmt.yamlsudo netplan generatesudo netplan try --timeout 120
MGMT_NIC="<management-interface>"GATEWAY_IPV4="<default-gateway-ipv4>"ip -4 address show dev "${MGMT_NIC}"ip route show defaultresolvectl status "${MGMT_NIC}"ping -c 3 "${GATEWAY_IPV4}"SSH 연결만 있는 상태에서 netplan apply를 바로 실행하지 않습니다.
netplan try 확인에 실패하거나 시간이 끝나면 이전 설정으로 돌아가므로 console에서 원인을 수정합니다.
방법 A — .deb 바이너리 패키지 (권장, Ubuntu 26.04)
섹션 제목: “방법 A — .deb 바이너리 패키지 (권장, Ubuntu 26.04)”릴리스 .deb(purecvisor-single_<version>_amd64.deb)로 데몬·CLI·UI·systemd 유닛을 한 번에 설치합니다.
런타임 의존성인 libvirt, QEMU, dnsmasq, nftables와 iproute2는 apt가 자동 해결하며 OVS, OVN, ZFS는 package Recommends로 설치됩니다.
# release 디렉터리에서 서명 또는 전달받은 SHA256SUMS를 먼저 검증sha256sum -c SHA256SUMS --ignore-missing
# 의존성 자동 해결 포함 설치sudo apt install -y ./purecvisor-single_2.0.0_amd64.deb
# 최초 설치는 sample 기반 daemon.conf를 만들고 서비스는 enable만 한다.sudoedit /etc/purecvisor/daemon.confsudo chown root:root /etc/purecvisor/daemon.confsudo chmod 600 /etc/purecvisor/daemon.conf
sudo systemctl enable --now libvirtdsudo systemctl start purecvisorsdsystemctl --no-pager --full status purecvisorsdpcvctl --version- 설치 위치: 바이너리
/usr/local/bin/{purecvisorsd,pcvctl}, UI/usr/local/share/purecvisor/ui/, 유닛/etc/systemd/system/purecvisorsd.service, 설정/etc/purecvisor/. - 기본 모드는 선택한 관리 IPv4의 데몬 자체 HTTPS
:443과 loopback HTTP127.0.0.1:8080입니다. - NGINX는 외부 TLS 종료가 필요한 경우에만 별도 설치하며 기본 의존성이 아닙니다.
- 업그레이드: 새
.deb로 같은 명령 재실행.
기존daemon.conf는 보존된다.
(conffile prompt 시--force-confold로 유지 또는--force-confnew로 새 유닛 채택). - 제거:
sudo apt remove purecvisor-single(설정 보존) /sudo apt purge purecvisor-single(설정 포함 제거).
방법 B — 소스 빌드
섹션 제목: “방법 B — 소스 빌드”빌드 의존성
섹션 제목: “빌드 의존성”sudo apt update && sudo apt install -y \ build-essential gcc-14 make pkg-config ccache \ libglib2.0-dev \ libvirt-dev libvirt-clients libvirt-daemon-system qemu-system-x86 ovmf \ libguestfs-tools \ libsoup-3.0-dev libjson-glib-dev \ libvirt-glib-1.0-dev liblxc-dev \ zfsutils-linux lxc lxc-utils \ libsqlite3-dev libssl-dev \ libcap-dev libseccomp-dev libreadline-dev liburing-dev \ libbpf-dev libxml2-dev \ protobuf-c-compiler libprotobuf-c-dev참고:
libvirt-glib-1.0-dev가libvirt-gobject-1.0.pc도 함께 제공합니다.
별도의libvirt-gobject-1.0-dev패키지는 존재하지 않습니다.
libbpf-dev부재 시 D07 eBPF 경로가 stub 으로 빌드되고(런타임 DEGRADED_NO_BTF 폴백),libxml2-dev는libvirt-gconfig-1.0.pc의 전이 의존이라 없으면 pkg-config 해석 전체가 비어glib.h부터 못 찾는 것처럼 보일 수 있다.
테스트 스위트 실행에는 추가로wireguard-tools·sqlite3·openvswitch-switch·python3-pytest(게이트 자체테스트)가 필요하다.
운영 필수:
libguestfs-tools는 일반 VM 복제의 Guest reset 경로에 필요하다.
이 패키지가 없으면virt-sysprep,virt-customize,virt-filesystems,guestfish를 실행할 수 없어guest_reset=trueclone이 preflight에서 거부된다.
공통 런타임 의존성 (사용 기능별)
섹션 제목: “공통 런타임 의존성 (사용 기능별)”이 절의 런타임 구성은 .deb 패키지와 소스 빌드에 공통으로 적용합니다.
.deb 설치에서는 libvirt, QEMU, dnsmasq-base, nftables와 iproute2를 package Depends로 자동 설치합니다.
OVS, OVN과 ZFS는 package Recommends로 설치되며, --no-install-recommends를 사용했다면 필요한 기능의 패키지를 직접 설치합니다.
일반 VM 복제, LXC와 iSCSI initiator는 해당 기능을 사용할 때 명시적으로 런타임 패키지를 설치합니다.
ZFS는 선택형 런타임입니다 — ZFS backend를 선택한 LXC에는 필수
PureCVisor 데몬과 Web UI, REST API, CLI는
zvol_pool이나 ZFS volume이 없어도 시작하고 동작합니다.
VM 생성 요청에서storage_type을 생략하면 설정된 ZFS dataset을 먼저 확인하고, 사용할 수 없으면[storage] image_dir에 qcow2 파일 디스크를 생성합니다.
ZFS를 사용하지 않는 노드는zfsutils-linux를 생략할 수 있지만image_dir이 존재하고 쓰기 가능해야 하며qemu-img를 사용할 수 있어야 합니다.
storage_type=zvol을 명시한 VM 생성, ZFS snapshot·rollback·send/receive 기반 backup과storage_backend=zfs인 LXC에는 ZFS가 필요합니다.
선택형 Btrfs LXC의 소스 버전·설정·검증 범위는 4.1절을 따릅니다.
# 일반 VM 복제의 Guest resetsudo apt install -y libguestfs-tools
# LXC 컨테이너sudo apt install -y lxc lxc-utils
# 선택: ZFS zvol·snapshot·backup 또는 ZFS backend의 LXCsudo apt install -y zfsutils-linux
# 선택: Btrfs backend의 LXC 점검 도구sudo apt install -y btrfs-progs
# OVS 오버레이 네트워크sudo apt install -y openvswitch-switchOVN Single Edge 구성
섹션 제목: “OVN Single Edge 구성”OVN을 사용하지 않고 OVS bridge만 사용하는 노드는 이 절을 건너뜁니다.
OVN Single Edge 구성은 NB DB, SB DB, ovn-northd와 ovn-controller를 같은 노드에 설치하고 로컬 OVS를 해당 Southbound DB에 연결합니다.
이 명령은 Multi Edge나 클러스터 제어면을 구성하지 않습니다.
purecvisor-ovn-single과 scripts/install-ovn-single.sh는 동일한 스크립트의 설치본과 저장소 원본입니다.
두 명령을 모두 실행하지 말고 PureCVisor를 설치한 방식에 맞는 한 경로만 선택합니다.
| PureCVisor 설치 방식 | 선택할 명령 | 실행 위치 |
|---|---|---|
릴리스 .deb 패키지 | purecvisor-ovn-single | 패키지가 /usr/local/sbin/에 설치하므로 어느 디렉터리에서나 실행 |
| Git 저장소 소스 빌드 | scripts/install-ovn-single.sh | PureCVisor 저장소 최상위 디렉터리에서 실행 |
일반 실행은 openvswitch-switch, ovn-central, ovn-host를 설치하고 관련 서비스를 활성화합니다.
이어서 OVS의 system ID, 로컬 Southbound DB socket, Geneve encap IPv4를 설정하고 NB/SB 동기화와 local chassis 등록이 끝날 때까지 최대 30초 동안 확인합니다.
성공 메시지가 출력되면 별도의 즉시 검증은 필요하지 않습니다.
--encap-ip에는 Geneve endpoint로 사용할, 이미 이 host에 할당된 IPv4를 지정합니다.
옵션을 생략하면 default route 조회 결과의 source IPv4를 자동 선택하므로 관리망과 overlay transport망이 분리된 환경에서는 명시적으로 지정합니다.
.deb로 설치한 노드는 다음 명령을 사용합니다.
sudo purecvisor-ovn-single --encap-ip <ovn-encap-ipv4>소스 빌드 노드는 저장소 최상위 디렉터리에서 다음 명령을 사용합니다.
sudo scripts/install-ovn-single.sh --encap-ip <ovn-encap-ipv4>--verify-only는 패키지 설치나 OVS 설정을 다시 수행하지 않습니다.
재부팅 후 또는 장애 점검 시 처음 선택한 경로에만 붙여 OVS·OVN 서비스, NB/SB 동기화, local chassis 등록 상태를 재검사합니다.
# .deb 설치 노드sudo purecvisor-ovn-single --verify-only
# 소스 빌드 노드sudo scripts/install-ovn-single.sh --verify-onlyiSCSI 런타임
섹션 제목: “iSCSI 런타임”# iSCSI 스토리지 — 이니시에이터(클라이언트)만 패키지가 필요하다.# 타겟(서버) 측은 D4 전환으로 커널 LIO(configfs)를 직접 쓰므로 `tgt` 패키지가 **불요**하다.sudo apt install -y open-iscsi소스 배포의 LIO 모듈 설정
섹션 제목: “소스 배포의 LIO 모듈 설정”.deb 패키지는 /etc/modules-load.d/purecvisor-lio.conf를 conffile로 설치합니다.
소스 빌드 결과를 직접 배포하는 경우에만 다음 파일을 수동으로 배치합니다.
# iSCSI 타겟용 커널 모듈 로드 — deb 를 설치했다면 이미 깔려 있다(conffile).# 아래 블록은 deb 를 쓰지 않는 배포(소스 빌드 + scp)에서만 필요하다.# ⚠ 모듈명 행에 주석을 붙이지 말 것 — modules-load.d(5) 는 행 전체를 모듈명으로# 읽어 4개 전부 'Failed to find module' 이 되는데도 유닛은 성공으로 끝난다(P13).sudo tee /etc/modules-load.d/purecvisor-lio.conf >/dev/null <<'EOF'# Developer note — 신입 개발자:# 이 modules-load.d 정본은 부팅 시 LIO 3종과 bridge conntrack 모듈을 순서대로 요청한다.# LIO가 없으면 iSCSI export가 degraded되고 nf_conntrack_bridge가 없으면 bridge 보안그룹의# stateful nft 규칙이 실패한다. 행 끝 주석은 모듈명의 일부가 되므로 설명은 별도 행에 둔다.## [쉬운 설명] 운영·플랫폼 엔지니어:# iSCSI 디스크 내보내기와 bridge 연결 추적에 필요한 커널 부품을 데몬보다 먼저 준비한다.# PRIVDROP-1 시정 뒤 데몬 자식은 CAP_SYS_MODULE을 받지 않으므로 이 파일이 유일한 적재 정본이다.# PureCVisor 2.0.0 — 커널 LIO + bridge conntrack 모듈 로드.# 이 파일은 deb 패키징 산출물이며 정본은 저장소의# packaging/deb/purecvisor-lio.conf 이고 deb 가 /etc/modules-load.d/ 로 설치한다.# deb 를 쓰지 않는 배포(소스 빌드 + scp)에서는 운영자가 같은 내용을 손으로 배치한다.# ⚠⚠ 행 끝 주석 금지(P13) — modules-load.d(5) 는 행 전체를 모듈명으로 읽는다.# 주석을 모듈명 뒤에 붙이면 4개 전부 'Failed to find module' 이 되고,# 그런데도 systemd-modules-load.service 는 Finished(성공)로 끝난다 → 조용한 degraded.# target_core_mod : LIO 코어 — configfs `target` 디렉터리를 만든다# iscsi_target_mod : iSCSI 패브릭 — mkdir 을 받아야 <root>/iscsi 를 만든다(지연 생성)# target_core_iblock : IBLOCK 백스토어 — zvol 블록 디바이스 export# nf_conntrack_bridge: Linux bridge family의 stateful nft conntrack 지원target_core_modiscsi_target_modtarget_core_iblocknf_conntrack_bridgeEOFsudo systemctl restart systemd-modules-load검증용 도구
섹션 제목: “검증용 도구”# 런타임 전제 무접근 계약 게이트 필수sudo apt install -y strace
# 정적 분석sudo apt install -y cppcheck
# 메모리 검사sudo apt install -y valgrind
# 커버리지 리포트sudo apt install -y lcovmake check-runtime-prereqs는 strace -f -e trace=%file로 BPF
--verify-only 검증이 설치 root, config, PKI, JWT, 기존 BPF 목적지에 접근하지
않음을 확인한다.
이 보안 게이트에서는 strace 미설치를 skip으로 처리하지 않으며,
Ubuntu에서는 sudo apt install strace로 설치한 뒤 다시 실행한다.
2.3 빌드
섹션 제목: “2.3 빌드”git clone https://github.com/HardcoreMonk/purecvisor.git purecvisor-singlecd purecvisor-single빌드 타겟
섹션 제목: “빌드 타겟”| 명령 | 설명 | 출력 |
|---|---|---|
make single | Single Edge 빌드 | bin/purecvisorsd |
make cli | CLI 빌드 | bin/pcvctl |
make all | 전체 빌드 | 데몬 + CLI |
make release | 릴리스 빌드 (-O2, NDEBUG, 하드닝) | 최적화된 바이너리 |
make clean | 빌드 산출물 정리 | - |
# 클린 빌드 (경고 0 확인)make clean && make single
# 빌드 결과 확인ls -lh bin/# bin/purecvisorsd (~1.9MB, Single Edge)# bin/pcvctl (CLI)품질 검증
섹션 제목: “품질 검증”# 전체 unit·integration testmake test
# 공개 경계와 계약 게이트 전체make check-all
# 정적 분석make cppcheck
# 메모리 누수 검사make memcheck
# 코드 커버리지 HTML 리포트make coverage-html# 결과: coverage_report/html/index.html
# CI용 TAP 형식 테스트 출력make test-tap2.4 디렉터리 구조 생성과 배포 런타임 전제
섹션 제목: “2.4 디렉터리 구조 생성과 배포 런타임 전제”# 런타임 디렉터리sudo install -d -m 0700 /etc/purecvisor/pkisudo install -d -m 0755 /etc/purecvisor/plugins.dsudo install -d -m 0755 /etc/purecvisor/seccompsudo install -d -m 0755 /var/lib/purecvisorsudo install -d -m 0700 /var/run/purecvisorsudo install -d -m 0750 /var/log/purecvisor스토리지 경로 확정
섹션 제목: “스토리지 경로 확정”ZFS를 사용하지 않는 구성
섹션 제목: “ZFS를 사용하지 않는 구성”ZFS volume은 서비스 시작의 필수 조건이 아닙니다.
ZFS를 사용하지 않는 노드는 image_dir을 준비하고 VM 생성 시 storage_type=qcow2 또는 raw를 지정하거나 자동 감지를 사용합니다.
sudo install -d -m 0711 /var/lib/libvirt/imagessudo test -d /var/lib/libvirt/imagessudo test -w /var/lib/libvirt/imagescommand -v qemu-img[storage]image_dir = /var/lib/libvirt/imagesiso_dirs = /data/iso,/var/lib/libvirt/images자동 감지에서 zvol_pool을 찾지 못하면 image_dir에 qcow2 디스크를 생성합니다.
storage_type=zvol을 명시하면 폴백하지 않고 지정한 ZFS 부모 dataset이 없다는 오류로 요청을 종료합니다.
ZFS 미사용 노드에서는 VM file disk와 명시적으로 선택한 Btrfs LXC를 사용할 수 있습니다.
ZFS snapshot·rollback·send/receive backup과 기본 ZFS LXC에는 ZFS가 필요합니다.
Btrfs LXC의 준비 조건은 4.1절을 따릅니다.
ZFS 기능을 사용하는 구성
섹션 제목: “ZFS 기능을 사용하는 구성”ZFS 기능을 사용하는 경우에만 zvol_pool에 실제로 존재하는 ZFS dataset을 지정합니다.
설치 전에 ZFS zvol 또는 file image 중 주 저장 방식을 결정하고 설정과 실제 경로를 일치시킵니다.
Ubuntu가 rpool 단일 NVMe에 설치된 검증 노드는 새 설치일 때만 다음 전용 dataset을 만들 수 있습니다.
기존 VM이 있는 노드에서는 이 명령으로 경로를 바꾸지 말고 별도 migration과 rollback 계획을 사용합니다.
sudo zpool statussudo zfs list
# 새 설치 전용 예시sudo zfs create -o mountpoint=none -o canmount=off rpool/data/purecvisorsudo zfs create -o mountpoint=none -o canmount=off -o compression=lz4 \ rpool/data/purecvisor/vmssudo install -d -m 0711 /var/lib/libvirt/imagessudo install -d -m 0755 /data/iso[storage]zvol_pool = rpool/data/purecvisor/vmsimage_dir = /var/lib/libvirt/imagesiso_dirs = /data/iso,/var/lib/libvirt/imagessudo zfs list rpool/data/purecvisor/vmssudo test -d /var/lib/libvirt/imagessudo test -d /data/iso운영 권장 구성은 OS rpool과 분리된 mirror data pool을 만들고 zvol_pool=pcvpool/vms를 사용하는 방식입니다.
단일 디스크 rpool 예시는 검증 환경 재현용이며 디스크 장애에 대한 중복성을 제공하지 않습니다.
배포 런타임 전제와 보존 정책
섹션 제목: “배포 런타임 전제와 보존 정책”기본 scripts/deploy.sh는 make release 뒤 make bpf를 실행한다.
--skip-build
에서도 daemon/CLI, BPF object, manifest 존재 여부와 helper의 --verify-only 검사를
SSH/SCP보다 앞선 로컬 preflight에서 수행하며, 실패하면 원격 상태를 바꾸기 전에
배포를 중단한다.
--verify-only는 BPF staging만 검증하는 전용 경로이며 설치 대상
root, daemon.conf, PKI, JWT, 기존 BPF 목적지에는 접근하지 않는다.
원격 설치 helper의 계약은 다음과 같다.
- 최초 배포는
/etc/purecvisor/pki를 mode0700으로 준비한다.
helper는 TLS 인증서 내용을 만들지 않으며, 기존 cert/key를 덮어쓰지 않고 보존한다.
인증서와 키가 모두 없을 때의 자가서명 생성은 데몬의 TLS 초기화가 담당한다. - 배포 helper가 관리하는 값은
[daemon] jwt_secret이다.
키가 누락됐거나 빈 값이면 Python CSPRNGsecrets.token_hex(32)로 64자리 소문자 hex를 만들어/etc/purecvisor/daemon.conf에 영속화하고 파일 mode를0600으로 고정한다.
충분한 길이인 32바이트 이상의 기존 JWT와 인증서는 내용 그대로 보존한다. - 비어 있지 않은 기존 JWT가 32바이트 미만이거나
placeholder류 또는 반복 문자처럼 명백히 weak하면 자동 회전으로 예고 없이 기존 토큰을 무효화하지 않는다.
배포를 fail-closed로 거부하고 운영자가 값을 명시적으로 교체하도록 요구한다. - BPF 배포는
manifest.json의 schema와 SHA-256을 검증하고 성공한 object와 manifest만/usr/lib/purecvisor/bpf에 설치한다.
디렉터리는0755, 파일은0644이며 manifest를 마지막 commit marker로 교체한다. - 원격 staging은 추측 불가능한 이름과 mode
0700으로 만들고, helper 실패 시 서비스를 시작하지 않는다.
성공과 실패 모두 EXIT 경로에서 helper와 BPF staging을 cleanup하여 정리한다. - 이 배포는 커널
lsm=bpf활성화나 커널 명령행 변경, 재부팅을 자동으로 수행하지 않는다.
BPF LSM이 필요하면 별도 승인된 운영 절차로 변경하고 재부팅해야 한다.
JWT 로드에는 두 경로가 공존한다.
데몬 시작 시
PCV_SECRET_AUTH_JWT_SECRET과 정확한 [auth] jwt_secret을 먼저 읽는다.
둘 다
없으면 PURECVISOR_JWT_SECRET, [daemon] jwt_secret을 호환 fallback으로 찾고,
마지막으로 다른 섹션을 검색한다.
따라서 deploy helper의 [daemon] jwt_secret 영속화가
모든 runtime override의 단일 정본이라는 뜻은 아니다.
[auth] 또는 환경변수 override를
운영자가 사용한다면 32바이트 이상 값을 별도로 보장해야 한다.
TLS 배포 모드 선택: purecvisorsd 자체 HTTPS 또는 선택형 NGINX 외부 TLS 종료
섹션 제목: “TLS 배포 모드 선택: purecvisorsd 자체 HTTPS 또는 선택형 NGINX 외부 TLS 종료”이 절은 제품 노드의 REST·Web UI·WebSocket 진입 경계를 설명한다.
공개 문서 도메인
purecvisor.site는 GitHub Pages가 정적 파일과 HTTPS를 제공하므로 아래 제품 노드 NGINX
구성과 무관하다.
제품 노드는 다음 두 모드 중 하나를 명시적으로 사용한다.
NGINX는 필수
의존성이 아니라 외부 TLS 종료를 선택한 노드에서만 필수인 구성 요소다.
| 항목 | purecvisorsd 자체 HTTPS | 선택형 NGINX 외부 TLS 종료 |
|---|---|---|
| 외부 TLS 소유자 | purecvisorsd | NGINX |
| NGINX 필요 여부 | 불필요 | 필수 |
| 외부 진입 | https://<node>:443 → 데몬 | https://<node>:443 → NGINX → 데몬 |
| 데몬 평문 리스너 | 127.0.0.1:<rest_port> 복구·로컬 점검용 | 127.0.0.1:8080 프록시 upstream 전용 |
| 핵심 설정 | https_enabled=true, https_port=443 | https_enabled=false, bind_plaintext=loopback |
| 권장 용도 | 별도 proxy 운영이 필요 없는 단순한 단일 노드 | 공인 인증서, 중앙 보안 헤더·rate limit, WebSocket proxy, maintenance fallback이 필요한 환경 |
| 정상 TLS health | mode=internal, enabled=true, degraded=false, status=ok | mode=external_termination, enabled=false, degraded=false, status=disabled_by_config |
# 모드 A — purecvisorsd 자체 HTTPS클라이언트 ── HTTPS :443 ──> purecvisorsd └─ HTTP 127.0.0.1:<rest_port> (로컬 복구·점검)
# 모드 B — 선택형 NGINX 외부 TLS 종료클라이언트 ── HTTPS :443 ──> NGINX ── HTTP 127.0.0.1:8080 ──> purecvisorsd모드 A — purecvisorsd 자체 HTTPS
섹션 제목: “모드 A — purecvisorsd 자체 HTTPS”자체 HTTPS는 기본 전송 모드다.
데몬이 인증서를 로드하고 외부 HTTPS listener를 직접
소유한다.
[server] bind_plaintext=loopback을 유지하면 평문 REST는 로컬 복구와 점검에만
사용되고 원격 자격증명·JWT·API key가 HTTP로 노출되지 않는다.
[tls]https_enabled = truehttps_port = 443cert = /etc/purecvisor/pki/node.crtkey = /etc/purecvisor/pki/node.key
[server]bind_plaintext = loopback- cert와 key가 모두 없으면 데몬이 자체서명 쌍을 원자적으로 생성한다.
둘 중 하나만 있으면 사용자 자산을 덮어쓰지 않고 TLS 초기화를 실패 처리한다. - 운영자가 제공한 cert/key 쌍은 자동 생성보다 우선하며 배포 helper가 내용을 교체하지 않는다.
- TLS 초기화나 HTTPS bind가 실패하면 외부 평문으로 확장하지 않는다.
데몬은 루프백 HTTP와 UDS만 남긴degraded상태로 기동해 복구 경로는 보존하고 외부 표면은 닫는다. - 이 모드에서는 NGINX service, NGINX vhost와
PCV_NGINX_BIND_IP가 필요하지 않다.
NODE_IPV4="<configured-management-ipv4>"systemctl is-active purecvisorsdcurl -ksS "https://${NODE_IPV4}/api/v1/health" | jq '{status, tls: .checks.tls}'sudo ss -lntp | grep -E '127\.0\.0\.1:8080|0\.0\.0\.0:443'# 기대: mode=internal, enabled=true, degraded=false, status=ok모드 B — 선택형 NGINX 외부 TLS 종료
섹션 제목: “모드 B — 선택형 NGINX 외부 TLS 종료”외부 종료 모드에서는 NGINX가 인증서와 :443을 소유하고 REST·Web UI·WebSocket 요청을
데몬의 루프백 HTTP로 전달한다.
데몬 자체 HTTPS는 의도적으로 끄되 평문 upstream은 반드시
loopback으로 제한한다.
[daemon]rest_port = 8080
[tls]https_enabled = false
[server]bind_plaintext = loopbackNGINX vhost는 최소한 TLS 인증서, HTTP→HTTPS 정책, proxy_pass http://127.0.0.1:8080,
WebSocket Upgrade, 보안 헤더와 forwarded header 덮어쓰기를 포함해야 한다.
vhost와 인증서를
먼저 프로비저닝한 다음 opt-in 배포를 실행한다.
PCV_NODES=<ip> \PCV_NGINX_BIND_IP=<ip> \scripts/deploy.sh --no-local
sudo nginx -tsystemctl is-active nginx purecvisorsdcurl -ksS https://<ip>/api/v1/health | jq '.checks.tls'# 기대: mode=external_termination, enabled=false, degraded=false,# status=disabled_by_configenabled=false는 외부 통신이 평문이라는 뜻이 아니라 데몬 대신 NGINX가 TLS를 정상 종료한다는
뜻이다.
X-Forwarded-For와 X-Forwarded-Proto는 loopback peer에서 온 요청만 신뢰한다.
선택·전환 시 금지 조합
섹션 제목: “선택·전환 시 금지 조합”https_enabled=false인데 정상 NGINX listener가 없으면 외부 HTTPS 진입점이 사라진다.- 외부 종료 모드에서
bind_plaintext=all을 쓰거나 값을 누락하면 데몬이 시작을 거부한다. - 같은 IP의
:443을 NGINX와purecvisorsd가 동시에 소유하도록 구성하지 않는다. PCV_NGINX_BIND_IP를 다음 배포에서 생략해도 기존 외부 종료 설정이 자체 HTTPS로 자동 복귀하지 않는다.
이 환경변수가 없으면 배포 스크립트는 기존 NGINX·daemon 전송 설정을 보존한다.- 두 모드 전환은
:443소유자가 바뀌는 maintenance 작업이다.
기존 설정·인증서를 백업하고 새 listener와 health를 검증할 rollback 가능한 순서로 수행한다. PCV-NGINX-TRUST-BOUNDARY: host-loopback을 만족하지 못해 신뢰하지 않는 로컬 프로세스가 loopback upstream에 접근할 수 있는 호스트에서는 NGINX 외부 종료 모드를 활성화하지 않는다.
선택형 NGINX 외부 TLS 종료 설치와 복구
PCV_NGINX_BIND_IP는 명시적으로 설정한 경우에만 활성화되는 opt-in이다.
값이
없으면 nginx 설정, systemd drop-in, daemon.conf의 전송 모드를 바꾸지 않는다.
활성화하면 nginx가 지정한 LAN 주소의 외부 TLS를 종료하고, purecvisorsd는
127.0.0.1 루프백 HTTP만 수신한다.
로컬 노드 배포:
NODE_IPV4="<configured-management-ipv4>"PCV_NODES="" \PCV_NGINX_BIND_IP="${NODE_IPV4}" \scripts/deploy.sh --nodes local원격 노드 배포(ssh 경유, <ip> 를 대상 노드로):
PCV_NODES=<ip> \PCV_NGINX_BIND_IP=<ip> \scripts/deploy.sh --no-localnginx vhost 자체는 이 트랜잭션의 관리 대상이 아니다 — 도메인 노드는
ops/nginx/purecvisor.example.com, LAN IP 노드는ops/nginx/purecvisor-lan-ip.conf.template(+purecvisor-common.confhttp 전제)를 먼저 프로비저닝한다.
설치 트랜잭션은 기존 인증서(cert)와 개인 키(key)를 내용 그대로 보존하고, nginx
설정·systemd drop-in·daemon 설정의 이전 상태를 백업한다.
설치 후 LAN health
검증이 실패하면 rollback으로 이 세 설정을 함께 복원하며 보존된 인증서와 키를
삭제하거나 재생성하지 않는다.
rollback 자체가 실패하면 서비스를 추측 상태로
재시작하지 않고 복구 자료를 남긴다.
부팅 시 wait-for-local-ip가 주소 준비를 기다린다.
제한 시간 안에 IP가 없으면
exit 75로 끝나 systemd가 재시도한다.
반면 nginx -t 문법 오류는 exit 1이며
RestartPreventExitStatus=1로 재시작 방지한다.
패키지 업데이트 뒤에는 vendor
유닛의 ExecStartPre가 정확히
/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'인지 확인하고,
drop-in이 IP 대기와 이 검사를 모두 유지하는지 재검증한다.
Vendor 명령이 불일치하면 설치기는 변경 전에 실패한다.
이 실패는 기존 상태를 변경하지 않고 보존한다.
Vendor 계약은 자동 동기화하지 않는다.
재시도 전에 installer 계약과 테스트를 갱신하고 재검토한다.
정상 health의 전체 서비스 status=ok와 TLS check는 정확히
mode=external_termination, enabled=false, degraded=false,
status=disabled_by_config여야 한다.
이 값은 TLS가 꺼져 노출됐다는 뜻이 아니라
외부 TLS 종료가 정상 선택됐다는 뜻이다.
X-Forwarded-For와
X-Forwarded-Proto 같은 프록시 헤더는 연결 peer가 127.0.0.1 또는 ::1인
경우에만 신뢰하며, 비루프백 클라이언트가 보낸 같은 헤더는 신원·scheme 판정에
사용하지 않는다.
PCV-NGINX-TRUST-BOUNDARY: host-loopback은 이 모드가 호스트의 모든 루프백
프로세스를 privileged/trusted host boundary로 신뢰한다는 뜻이다.
PCV-NGINX-COUNTERFACTUAL: untrusted-local-process처럼 신뢰하지 않는 로컬
사용자나 프로세스가 daemon의 루프백 HTTP에 직접 접속할 수 있으면 전달 헤더를
위조할 수 있으므로 이 모드를 활성화하지 않는다.
전용 호스트·최소 사용자,
서비스 sandbox/MAC, 로컬 방화벽 또는 network namespace로 직접 루프백 접근을
제한한다.
원격 클라이언트는 loopback peer가 아니고 nginx가 전달 헤더를
덮어쓰므로 이 신뢰 경계를 직접 위조할 수 없다.
2.5 systemd 서비스 설치
섹션 제목: “2.5 systemd 서비스 설치”sudo cp systemd/purecvisorsd.service /etc/systemd/system/sudo systemctl daemon-reloadsudo systemctl enable --now purecvisorsd서비스 상태 확인:
sudo systemctl status purecvisorsd
# 로그 확인 (실시간)journalctl -u purecvisorsd -f설정한 관리 IPv4에서 기본 모드가 정상 기동하면 journal에 다음 listener가 기록됩니다.
HTTPS listening on https://0.0.0.0:443 (HSTS enabled)HTTP/2 support enabled (TLS via ALPN negotiation)REST API listening on http://127.0.0.1:8080 + https://0.0.0.0:443/api/v1외부 평문 0.0.0.0:8080이 보이면 정상 기준이 아닙니다.
ss -lntp와 [server] bind_plaintext=loopback을 확인한 뒤 서비스를 다시 시작합니다.
2.6 daemon.conf 설정
섹션 제목: “2.6 daemon.conf 설정”설정 파일 경로: /etc/purecvisor/daemon.conf
설정 우선순위: 환경 변수 > daemon.conf > 컴파일 기본값
REST rest_port의 컴파일 기본값은 80입니다.
아래 Single Edge 권장 예시는 8080을 loopback 복구 listener로 두고 purecvisorsd가 설정한 관리 IPv4의 443 HTTPS를 직접 소유합니다.
NGINX 외부 TLS 종료를 선택한 경우에만 같은 127.0.0.1:8080을 proxy upstream으로 사용합니다.
# Single Edge 권장 baseline
# ─────────────────────────────────────────────# [daemon] 데몬 기본 설정# ─────────────────────────────────────────────[daemon]# UDS 소켓 경로socket_path = /var/run/purecvisor/daemon.sock
# REST API 포트 (1-65535, CAP_NET_BIND_SERVICE 필요)rest_port = 8080# 자체 HTTPS에서는 loopback 복구 listener, 선택형 NGINX에서는 proxy upstream
# 첫 로그인 전에 12자 이상의 임시 bootstrap 비밀번호를 설정# 전용 admin 생성 뒤에는 빈 값으로 바꾸고 재기동해 bootstrap 계정을 비활성화할 수 있음admin_user = adminadmin_password = <운영자가 sudoedit로 설정>
# 그레이스풀 드레인 타임아웃 (초, 최소 5)drain_timeout = 30
# 워커 스레드 풀 크기 (1-64)pool_max_conn = 16
# 배포 helper가 누락/빈 값만 64자리 hex로 영속화jwt_secret =
# ─────────────────────────────────────────────# [storage] 스토리지 설정# ─────────────────────────────────────────────[storage]# ZFS zvol 풀 경로zvol_pool = rpool/data/purecvisor/vms
# qcow2/raw 이미지 폴백 디렉터리image_dir = /var/lib/libvirt/images
# ISO 스캔 디렉터리 (콤마 구분, .iso/.img 파일)iso_dirs = /data/iso,/var/lib/libvirt/images
# ─────────────────────────────────────────────# [tls] HTTPS 설정# ─────────────────────────────────────────────[tls]# 기본 true. false는 nginx 외부 TLS 종료 + server.bind_plaintext=loopback에서만 허용https_enabled = true# 인증서와 키가 모두 없으면 이 기본 경로에 자가서명 쌍을 생성cert = /etc/purecvisor/pki/node.crtkey = /etc/purecvisor/pki/node.key
# 선택적 client mTLS 신뢰 CAca = /etc/purecvisor/pki/ca.crt
# https_enabled=false일 때 반드시 loopback을 명시[server]bind_plaintext = loopback
# ─────────────────────────────────────────────# [auth] 인증 설정# ─────────────────────────────────────────────[auth]# 선택적 JWT runtime override. PCV_SECRET_AUTH_JWT_SECRET이 이 값보다 우선한다.# 미설정 시 위 [daemon] jwt_secret 호환 fallback을 사용한다.# jwt_secret = <32바이트 이상의 운영자 관리 값>
# 셀프 회원가입 활성화 여부# true이면 로그인 랜딩의 회원가입으로 VIEWER 계정을 생성할 수 있습니다.allow_self_register = false
# ─────────────────────────────────────────────# [alert] 알림 엔진 설정# ─────────────────────────────────────────────[alert]enabled = true
# CPU 임계값 (%)cpu_warn = 80cpu_crit = 95
# 메모리 임계값 (%)mem_warn = 85mem_crit = 95
# 디스크 임계값 (%)disk_warn = 80disk_crit = 90
# 데이터 풀 임계값 (%)data_pool_warn = 80data_pool_crit = 90
# 지속 평가 기간 (초, 임계값 초과 지속 시간)eval_period = 30
# 동일 경보 중복 억제 기간 (초)dedup_window = 300
# Webhook URLwebhook_url = https://hooks.slack.com/services/T00/B00/xxxwebhook_crit_url = https://events.example.com/purecvisor/critical
# HMAC 서명 비밀키. 평문 대신 ENC: 값 또는 PCV_SECRET_ALERT_WEBHOOK_SECRET 사용을 권장webhook_secret = ENC:...
# Webhook 포맷: slack | telegram | genericwebhook_format = slacktelegram_chat_id =
# ─────────────────────────────────────────────# [network] 관리형 기본 네트워크 (VP-1/VP-6, 2026-07)# ─────────────────────────────────────────────[network]# vm create 시 network_bridge 미지정이면 부착되는 기본 NAT 네트워크.# 데몬이 기동 시 브릿지+NAT(nftables)+DHCP/DNS(dnsmasq)를 멱등 보장한다.# 마이그레이션: 구 [vm] default_bridge 키는 폐기됨(설정돼 있어도 무시) —# 이 섹션으로 이관할 것.default_bridge = pcvnat0
# 기본 네트워크 게이트웨이 CIDR (호스트 주소 포함 표기)default_subnet = 10.78.0.1/24
# 0이면 기동 시 기본 네트워크 보장을 건너뜀default_ensure = 1
# 호스트 방화벽(UFW/iptables FORWARD DROP) 자동 공존. "auto"면 관리형# NAT 설정 시 필요한 allow 룰을 자동 삽입(AUDIT 로그 기록), "off"면# 데몬이 호스트 방화벽을 건드리지 않음 (공존 실패 시 게스트 네트워크# 불통은 운영자 책임).firewall_integration = auto
# ─────────────────────────────────────────────# [security_group] 보안 그룹# ─────────────────────────────────────────────[security_group]# vnet 캐시 주기 재동기화 간격(초). 0 또는 음수면 타이머 비활성.resync_interval_sec = 300
# ─────────────────────────────────────────────# [cpu] CPU 할당 설정# ─────────────────────────────────────────────[cpu]# 오버커밋 허용 여부allow_overcommit = false
# 격리 코어 목록 (예: 4-7,12-15)# isolated_cores = 4-7
# ─────────────────────────────────────────────# [update] 버전 알림 설정# ─────────────────────────────────────────────[update]# 버전 알림 기능 on/off. true면 데몬이 GitHub 공개 repo 최신 릴리스를 조회해# 대시보드 툴바에 상태 배지(최신 / 업데이트 가능 여부, 예: `✓ vX.Y.Z` / `↑ vX.Y.Z`)로# 표시한다 — 읽기 전용 정보 표시만이며 자동 다운로드/설치는 하지 않는다.# air-gapped/외부망 차단 환경은 false로 꺼서 아웃바운드 호출 자체를 막을 것.check_enabled = true
# 조회 대상 GitHub Releases API URL (기본값: purecvisor 공개 repo의 latest release)check_url = https://api.github.com/repos/HardcoreMonk/purecvisor/releases/latest
# 조회 주기(시간 단위). 이 시간 이내에는 재조회하지 않는다(캐시 재사용).# 1 미만 값은 기본값 24로 보정된다.check_interval_hours = 24
[update]설정 변경은 SIGHUP 핫리로드 대상이 아닙니다 — 데몬 재시작 (systemctl restart purecvisorsd) 후에만 반영됩니다.
설정 검증
섹션 제목: “설정 검증”데몬 시작 시 다음 항목이 자동 검증됩니다:
rest_port: 1-65535 범위drain_timeout: 5초 이상pool_max_conn: 1-64 범위admin_password: 미설정 시 bootstrap admin 비활성화, 12자 미만이면 보안 경고zvol_pool: ZFS 기능 사용 시 실제 dataset 존재 확인- 경로 존재 여부 (
socket_path,[tls] cert/key등)
범위를 벗어나면 PCV_LOG_WARN으로 경고 후 기본값을 사용합니다.
런타임 설정 변경 (SIGHUP)
섹션 제목: “런타임 설정 변경 (SIGHUP)”데몬 재시작 없이 설정을 다시 로드할 수 있습니다:
sudo kill -SIGHUP $(pidof purecvisorsd)2.7 CLI 자동완성 설치
섹션 제목: “2.7 CLI 자동완성 설치”# 시스템 전체 설치 (bash + zsh)make install-completion
# 현재 사용자만 설치make install-completion-user
# 적용 확인 (새 셸에서)pcvctl <TAB><TAB>CLI 종료상태 계약
섹션 제목: “CLI 종료상태 계약”pcvctl은 화면 출력 형식과 무관하게 다음 프로세스 종료상태를 사용한다.
JSON-RPC 오류의
정확한 code가 필요하면 --format=json 응답을 함께 파싱한다.
| 종료코드 | 의미 |
|---|---|
0 | 명령 성공 또는 help/version 같은 정상 로컬 동작 |
1 | 실행·UDS 전송·응답 프로토콜/파싱·JSON-RPC 거절 실패 |
2 | 알 수 없는 명령, 필수 인자 누락, 잘못된 사용법 |
if pcvctl vm start web-prod; then echo "started"else rc=$? echo "pcvctl failed: exit=$rc" >&2fi2026-08-09 이전에는 화면에 RPC 오류가 표시돼도 exit 0이 반환될 수 있었다.
현재 계약은
옵트인 없이 기본 동작이므로, 기존 자동화는 set -e, &&, if 분기가 달라질 수 있다.
2.8 로컬 Single Edge 배포
섹션 제목: “2.8 로컬 Single Edge 배포”배포 대상 노드 자체에서 실행할 때는 deploy target을 local로 명시합니다.
빈 PCV_NODES는 저장된 원격 node 목록이 섞이는 것을 막고 --nodes local은 현재 host만 변경합니다.
# 릴리스 빌드 + BPF + 현재 노드 배포PCV_NODES="" scripts/deploy.sh --nodes local
# 디버그 빌드도 현재 노드로 제한PCV_NODES="" scripts/deploy.sh --nodes local --debug
# 이미 검증한 산출물 재배포PCV_NODES="" scripts/deploy.sh --nodes local --skip-buildWeb UI 정적 자산 배포 체크
섹션 제목: “Web UI 정적 자산 배포 체크”Web UI 배포는 HTML/JS/CSS만 복사하면 끝나지 않습니다.
운영 브라우저는 CSP, PWA manifest, Service Worker 캐시를 함께 검증하므로 다음 자산이 같은 릴리스 단위로 배포되어야 합니다.
ui/index.html,ui/docs.html,ui/guide.html,ui/guide-content.mdui/samples/design-system-preview.html,ui/samples/design-borrowing-mockup.html과 관련ui/samples/*.htmlui/app.js,ui/app.bundle.js,ui/i18n.js,ui/sw.jsui/modules/*.jsui/manifest.json,ui/icon-192.png,ui/icon-512.pngui/vendor/chart.umd.min.jsui/vendor/novnc/novnc.esm.jsui/vendor/pretendard/pretendard.css,ui/vendor/pretendard/woff2/*.woff2ui/vendor/coolicons/coolicons.svg
번들 파이프라인의 정본은 Makefile의 ui-bundle 타깃입니다 — ui/modules/*.js를 ui/app.bundle.js로 다시 묶고(UI_MODULES 등록 누락 가드 + esbuild 민파이/소스맵) ui/sw.js의 CACHE_NAME을 UI_CACHE_INPUTS에 등록된 전체 선캐시 자산 해시로 bump 합니다.
따라서 docs.html, style.css, 아이콘처럼 bundle 밖의 정적 자산만 바뀌어도 기존 precache가 무효화됩니다.
scripts/bundle-ui.sh는 그 타깃에 위임하는 하위호환 진입점이며, 추가로 로컬 데몬 서빙 경로(/usr/local/share/purecvisor/ui)에 산출물을 복사합니다(PCV_NO_DEPLOY=1로 생략).
UI 모듈, WebSocket, metrics, 정적 자산 목록을 바꾸면 배포 전에 다음 순서로 확인합니다.
PCV_NO_DEPLOY=1 scripts/bundle-ui.shpython3 scripts/check_ui_bundle_fresh.pyfor f in ui/app.js ui/modules/*.js ui/vendor/chart.umd.min.js ui/vendor/novnc/novnc.esm.js; do node -c "$f"; donegit diff --check -- ui scripts src/api/rest_server.crg -n "iconify|code\.iconify|api\.iconify|api\.unisvg|api\.simplesvg|cdn\.jsdelivr|fonts\.googleapis|fonts\.gstatic|sourceMappingURL" ui/index.html ui/docs.html ui/guide.html ui/app.bundle.js ui/sw.js ui/vendorrg -n "customConfirm\([^\\n]*<[^\\n]*>|<br><b>|idx \|\| selectedVmIndex" ui/modules ui/app.bundle.js두 rg 명령은 결과가 없어야 정상입니다.
운영 CSP를 넓혀 외부 아이콘 API나 CDN sourcemap을 허용하지 말고, 필요한 런타임 자산은 ui/vendor/ 또는 inline SVG처럼 로컬 자산으로 고정합니다.
customConfirm() 호출부는 HTML 조각을 넘기지 않고 plain text와 \n만 사용합니다.
배포 완료 판정은 저장소 산출물, 설치 파일과 설정한 관리 IPv4에서 실제 제공하는 파일을 함께 비교합니다.
초기 자체서명 인증서를 사용하는 동안만 curl -k를 사용하며 운영 CA 인증서를 설치한 뒤에는 -k 없이 검증합니다.
NODE_IPV4="<configured-management-ipv4>"curl -ksS "https://${NODE_IPV4}/api/v1/health" \ | jq '{status,service,version,node_name,tls:.checks.tls}'curl -ksS -o /tmp/pcv-live-app.bundle.js \ "https://${NODE_IPV4}/ui/app.bundle.js?v=2.0.0"
sha256sum ui/app.bundle.js \ /usr/local/share/purecvisor/ui/app.bundle.js \ /tmp/pcv-live-app.bundle.js
rg -n "iconify|code\.iconify|api\.iconify|api\.unisvg|api\.simplesvg|cdn\.jsdelivr|fonts\.googleapis|fonts\.gstatic|sourceMappingURL" \ /tmp/pcv-live-app.bundle.js세 SHA-256 값은 같아야 하며 외부 런타임 참조를 찾는 rg 명령은 결과가 없어야 합니다.
health의 status=ok, version=2.0.0, TLS mode=internal, enabled=true, degraded=false, status=ok를 확인합니다.
2.9 logrotate 설정
섹션 제목: “2.9 logrotate 설정”# 로그 로테이션 설치sudo cp systemd/purecvisor.logrotate /etc/logrotate.d/purecvisor기본 설정:
- 일일 회전
- 30일 보존
- 압축 활성화
/var/log/purecvisor/*.log/var/log/ovn/ovn-controller.log별도 보호- 일일 회전
size 50M,maxsize 200Mcopytruncate사용
Single Edge OVN local controller 준비 경로는 ovn-controller 재기동 직후
파일 로그 레벨을 ERR로 낮춥니다.
OVNSB commit failed 같은 INFO 폭주가
재발하더라도 운영 로그가 무제한으로 불어나는 것을 막기 위한 안전장치입니다.
OVN 구축 성공은 ovn-nbctl 설치 여부가 아니라 Northbound/Southbound DB 조회,
ovn-northd 동기화, ovn-controller active, 로컬 Chassis 등록까지 모두 확인한
상태를 뜻합니다.
단일 노드는 DB를 TCP로 공개하지 않고 로컬 Unix socket을 사용합니다.