콘텐츠로 이동

컨테이너 관리

PureCVisor는 LXC 컨테이너의 ZFS backend와 명시적으로 선택하는 Btrfs backend를 관리합니다.

초기 2.0.0 태그의 LXC 생성은 ZFS 전용입니다.
공개 소스 e028ef2부터 선택형 Btrfs backend가 구현되어 있으며, 기본값과 제품 버전은 zfs·2.0.0으로 유지합니다.
새 태그나 버전 인상을 뜻하지 않으므로 설치한 소스 commit을 확인하세요.
지정 Arch/Btrfs 호스트의 실제 API 통합 검증을 통과했으며 Btrfs API 검증 기록에 결과를 별도로 기록합니다.
설계 계약은 ADR-0058을 따릅니다.

[container] storage_backend=zfs가 기본값입니다.
이 backend에는 사용 가능한 ZFS 풀, ZFS 커널 모듈과 zfs 명령이 필요합니다.
[storage] container_pool은 컨테이너용 부모 파일시스템 dataset이며 기본값은 pcvpool/containers입니다.
부모 dataset이 없으면 생성 과정에서 만들기를 시도합니다.
VM용 블록 볼륨 zvol을 별도로 만들 필요는 없습니다.

Btrfs를 사용할 때는 daemon.conf에 다음 값을 명시합니다.

[container]
storage_backend = btrfs
lxc_path = /var/lib/purecvisor/lxc
rootless = false

lxc_path는 실제로 mount된 Btrfs 위의 절대 경로여야 합니다.
관리 디렉터리와 상위 경로는 root가 관리하고, 관리 디렉터리는 그룹·다른 사용자가 쓸 수 없어야 하며 symlink 경로는 허용하지 않습니다.
LXC 런타임과 Btrfs 커널 지원을 준비하고, btrfs-progs로 파일시스템과 subvolume을 점검합니다.
이미 Btrfs인 경로를 선택한 뒤 다음과 같이 확인할 수 있습니다.
이 명령은 파일시스템을 생성하거나 변환하지 않습니다.

Terminal window
sudo install -d -o root -g root -m 0755 /var/lib/purecvisor/lxc
findmnt -T /var/lib/purecvisor/lxc -o TARGET,FSTYPE,OPTIONS
sudo btrfs filesystem show /var/lib/purecvisor/lxc

Btrfs 생성은 lxc-create -B btrfs를 사용합니다.
rootless=false인 privileged 컨테이너만 지원하며, 요청에서 rootless=true를 지정해도 거부합니다.
잘못된 backend 값, 비Btrfs 경로와 식별자 불일치는 오류로 종료합니다.
일반 디렉터리 backend와 다른 backend로의 자동 폴백은 없습니다.
Btrfs LXC만 사용할 때 ZFS는 필요하지 않습니다.

LXC 7의 cgroup v2에서는 vcpu_count를 상대 CPU 배분 가중치로 기록합니다.
예를 들어 2는 cpu.weight=200이며 CPU 코어 수의 강제 상한을 뜻하지 않습니다.
1의 가중치는 100이고 최대값은 10000입니다.
v1·v2 CPU 설정이 모두 거부되면 생성을 실패로 처리합니다.
실제 CPU 상한과 상대 가중치는 구분해서 확인하세요.

Arch의 파일시스템은 설치자가 선택합니다.
조사한 Omarchy 시험 환경이 Btrfs였다는 사실을 모든 Arch 설치에 적용하지 않습니다.

각 컨테이너의 rootfs 밖 purecvisor.storage에는 backend와 실제 ZFS dataset 또는 Btrfs filesystem UUID·rootfs subvolume UUID/ID를 기록합니다.
후속 작업은 이 기록과 실제 저장소를 대조하며, 현재의 기본 backend나 pool 이름으로 다시 계산하지 않습니다.
기본값 변경은 기존 컨테이너의 변환·이동이 아닙니다.
marker 없는 기존 ZFS는 실제 mountpoint와 dataset이 정확히 일치할 때만 호환하며, marker 없는 Btrfs를 자동 편입하지 않습니다.

생성 실패 후 marker가 없는 디렉터리나 subvolume이 남으면 데이터를 보존하고 작업을 거부합니다.
이름이나 현재 설정만으로 삭제 대상을 추측하지 않습니다.
오류·설정·mount와 subvolume identity를 확보한 뒤 관리자가 생성 결과를 확인해야 하며, marker를 임의로 작성하거나 경로를 재귀 삭제해서 성공 상태로 만들지 마세요.

Terminal window
# 설정한 backend로 생성 (기본값은 ZFS)
pcvctl container create --name app-ctr --dist ubuntu --release noble
# 생성 요청 후 반환된 job_id의 completed 상태를 확인하고 시작
pcvctl container create --name web-ctr --dist ubuntu --release jammy
# 아래 작업 결과 조회 절차로 완료를 확인한 뒤 실행
pcvctl container start web-ctr

RPC 직접 호출:

Terminal window
echo '{"jsonrpc":"2.0","method":"container.create","params":{
"name": "app-ctr",
"image": "ubuntu:noble"
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool

비동기 작업 결과: 생성·시작·중지·삭제·복제와 snapshot 생성·복원·삭제는 status=acceptedjob_id를 먼저 반환합니다.
접수는 완료가 아닙니다.
작업 worker의 실제 결과를 GET /api/v1/jobs/<job_id>status=completed|failed와 오류 detail, WebSocket job.complete, audit 기록으로 확인합니다.
Web UI도 최종 job 결과를 기다립니다.
목록에 컨테이너가 보이는 것만으로 생성 성공을 판정하지 마세요.

Terminal window
JOB_ID="<accepted-response-job-id>"
curl -s -H "Authorization: Bearer $TOKEN" \
"http://127.0.0.1:8080/api/v1/jobs/${JOB_ID}" | python3 -m json.tool
Terminal window
# 시작 (cgroup v2 리소스 제한 자동 적용)
pcvctl container start app-ctr
# 중지 (오퍼레이션 잠금으로 동시 실행 방지)
pcvctl container stop app-ctr
# 삭제
pcvctl container destroy app-ctr
# 목록 조회
pcvctl container list

REST API:

Terminal window
# 컨테이너 목록
curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8080/api/v1/containers | python3 -m json.tool
# 컨테이너 시작
curl -X POST -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8080/api/v1/containers/app-ctr/start
# 컨테이너 중지
curl -X POST -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8080/api/v1/containers/app-ctr/stop
Terminal window
# 단일 명령
pcvctl container exec app-ctr "hostname -I"
# 패키지 업데이트
pcvctl container exec app-ctr "apt update && apt upgrade -y"
# 서비스 상태 확인
pcvctl container exec app-ctr "systemctl status nginx"
Terminal window
# CPU/메모리 제한 설정
pcvctl container set-limits app-ctr --cpu_quota 50 --memory_mb 1024
# --cpu_quota: CPU quota
# --memory_mb: 메모리 제한 (MB)

RPC 직접 호출:

Terminal window
echo '{"jsonrpc":"2.0","method":"container.set_limits","params":{
"name": "app-ctr",
"cpu_percent": 50,
"memory_mb": 1024
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
Terminal window
# 대역폭 제한 (tc qdisc 기반, KB/s)
pcvctl container set-bandwidth app-ctr --inbound 100000 --outbound 100000
Terminal window
# 개별 컨테이너 메트릭
pcvctl container metrics app-ctr

출력 예시:

Container: app-ctr (RUNNING)
CPU: 8.3%
Memory: 256/1024 MB
PIDs: 42
Net: eth0 RX 15.2MB TX 3.1MB

Btrfs snapshot은 정지된 privileged 컨테이너의 rootfs만 읽기 전용 subvolume으로 저장합니다.
생성·복원·삭제 중에는 컨테이너 작업 lock을 사용합니다.
nested subvolume, 실제로 연결된 외부 mount, 설정된 외부 bind volume 또는 lxc.mount.fstab이 있으면 복제·snapshot·복원을 거부합니다.
해당 데이터가 snapshot에 포함된다고 가정하지 마세요.

복원은 현재 LXC config, owner와 image metadata를 보존하고 rootfs만 원자적으로 교체합니다.
이전·새 rootfs identity를 가진 journal로 중단된 복원을 판별하며, 다음 저장소 변경 또는 시작 전에 정지 상태와 lock 아래에서 복구합니다.
모호한 identity는 데이터를 보존하고 오류로 남깁니다.
삭제는 관리 rootfs와 snapshot을 먼저 검증하고, 부분 실패 시 남은 marker·삭제 기록을 이용해 같은 삭제 요청을 재시도합니다.

Btrfs의 rootless 컨테이너, backend 간 migration, 다른 filesystem으로의 CoW 복제, 컨테이너별 디스크 quota와 Btrfs send/receive 기반 제품 백업은 지원하지 않습니다.

Terminal window
# 스냅샷 생성
pcvctl container snap create app-ctr --name v1
# 스냅샷 목록
pcvctl container snap list app-ctr
# 롤백
pcvctl container snap rollback app-ctr v1
# 삭제
pcvctl container snap delete app-ctr v1

복제는 원본에 기록된 backend를 사용합니다.
Btrfs는 정지된 원본에서 lxc-copy -B btrfs -s로 CoW 복제하며, 대상은 새 저장소 identity와 원본의 image metadata를 갖습니다.
owner는 원본의 값을 복사하지 않고 복제 요청자로 기록합니다.

Terminal window
# 원본 backend로 복제 (Btrfs는 정지 상태의 CoW clone)
pcvctl container clone app-ctr --name app-ctr-clone
Terminal window
# 호스트 디렉터리를 컨테이너에 바인드 마운트
echo '{"jsonrpc":"2.0","method":"container.volume.attach","params":{
"name": "app-ctr",
"host_path": "/data/shared",
"container_path": "/mnt/shared",
"readonly": false
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
# 볼륨 목록
echo '{"jsonrpc":"2.0","method":"container.volume.list","params":{
"name": "app-ctr"
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
# 볼륨 분리
echo '{"jsonrpc":"2.0","method":"container.volume.detach","params":{
"name": "app-ctr",
"container_path": "/mnt/shared"
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool

경로 순회 방어: realpath 검증으로 심볼릭 링크를 통한 경로 순회 공격을 차단합니다.

Terminal window
# 환경변수 설정 (멱등 upsert)
echo '{"jsonrpc":"2.0","method":"container.env.set","params":{
"name": "app-ctr",
"key": "DATABASE_URL",
"value": "postgresql://localhost:5432/app"
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
# 환경변수 목록
echo '{"jsonrpc":"2.0","method":"container.env.list","params":{
"name": "app-ctr"
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
# 환경변수 삭제
echo '{"jsonrpc":"2.0","method":"container.env.delete","params":{
"name": "app-ctr",
"key": "DATABASE_URL"
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool

컨테이너에 주기적 헬스체크를 등록할 수 있습니다.

Terminal window
# 헬스체크 등록
echo '{"jsonrpc":"2.0","method":"container.health.set","params":{
"name": "app-ctr",
"cmd": "curl -sf http://localhost:8080/health || exit 1",
"interval": 30,
"timeout": 5,
"retries": 3
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
# 헬스 상태 확인
echo '{"jsonrpc":"2.0","method":"container.health.get","params":{
"name": "app-ctr"
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool

상태값: healthy | unhealthy | starting

Terminal window
# 헬스체크 해제
echo '{"jsonrpc":"2.0","method":"container.health.delete","params":{
"name": "app-ctr"
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
Terminal window
# 최근 100줄 로그 조회
echo '{"jsonrpc":"2.0","method":"container.logs","params":{
"name": "app-ctr",
"lines": 100
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool

실행 중인 컨테이너의 상태를 저장하고 나중에 복원할 수 있습니다.

Terminal window
# 체크포인트 (실행 상태 저장)
pcvctl container checkpoint app-ctr --dir /tmp/criu-app
# 복원 (체크포인트에서 재개)
pcvctl container restore app-ctr --dir /tmp/criu-app
/etc/purecvisor/seccomp/default.seccomp
# 프로파일 설정
pcvctl container seccomp-set app-ctr --profile default
# 현재 프로파일 조회
pcvctl container seccomp-get app-ctr

컨테이너도 VM과 동일한 NIC 관리 기능을 제공합니다.

Terminal window
# NIC 목록
echo '{"jsonrpc":"2.0","method":"container.nic.list","params":{
"name": "app-ctr"
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
# NIC 추가
echo '{"jsonrpc":"2.0","method":"container.nic.attach","params":{
"name": "app-ctr",
"bridge": "pcvbr0"
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool
# NIC 분리
echo '{"jsonrpc":"2.0","method":"container.nic.detach","params":{
"name": "app-ctr",
"interface": "eth1"
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock | python3 -m json.tool