Module 4: Compute Virtualization - KVM & LXC 운영 가이드¶
적용 범위: 이 문서의 명령과 출력은 Proxmox VE 8.x 계열의 예시다. 실행 전
pveversion -v와 각 명령의--help로 현재 버전의 옵션을 확인한다. 안전 경계:stop,destroy, 디스크 확장, 마이그레이션, bind mount는 서비스 중단이나 데이터 손실을 일으킬 수 있다. 대상 VMID/CTID, 백업, 유지보수 창, 복구 담당자를 먼저 확정한다. 표기 규칙:$가 붙은 줄은 셸 예시이고, PID·경로·버전·출력값은 샘플이다. 샘플 출력이 그대로 나타나는 것을 성공 조건으로 사용하지 않는다.
학습 목표¶
이 모듈은 다음 주제를 다룹니다: - QEMU/KVM 가상화의 커널 레벨 동작 원리 - VM이 Linux 프로세스로서 어떻게 실행되는지 - LXC 컨테이너의 namespace와 cgroup 격리 메커니즘 - qm/pct 명령어의 내부 동작 흐름
1. QEMU/KVM 아키텍처 개요¶
1.1 왜 QEMU와 KVM인가?¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 가상화 기술의 진화 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ [Full Emulation] [Paravirtualization] [Hardware-Assisted] │
│ QEMU Xen PV KVM + QEMU │
│ │ │ │ │
│ 모든 하드웨어를 Guest가 Hypervisor를 CPU가 직접 │
│ 소프트웨어로 에뮬레이션 인식하고 협력 가상화 지원 │
│ │ │ │ │
│ [느림] [빠름, 수정 필요] [빠름, 무수정] │
│ │
└─────────────────────────────────────────────────────────────────────────┘
핵심 개념: - QEMU (Quick Emulator): 하드웨어를 에뮬레이션하는 사용자 공간 프로그램 - KVM (Kernel-based Virtual Machine): Linux 커널 모듈로, CPU의 하드웨어 가상화 확장(Intel VT-x, AMD-V)을 활용
┌────────────────────────────────────────────────────────────────────┐
│ Proxmox VE 가상화 스택 │
├────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ VM 100 │ │ VM 101 │ │ VM 102 │ Guest Layer │
│ │ (Windows) │ │ (Ubuntu) │ │ (CentOS) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ ┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐ │
│ │ QEMU Process│ │ QEMU Process│ │ QEMU Process│ User Space │
│ │ (kvm pid) │ │ (kvm pid) │ │ (kvm pid) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ ───────┴────────────────┴────────────────┴─────────────────── │
│ │ │
│ ┌──────▼──────┐ │
│ │ /dev/kvm │ KVM Interface │
│ └──────┬──────┘ │
│ │ │
│ ────────────────────────┴──────────────────────────────────── │
│ │ │
│ ┌──────▼──────┐ │
│ │ KVM Module │ Kernel Space │
│ │ (kvm.ko) │ │
│ └──────┬──────┘ │
│ │ │
│ ────────────────────────┴──────────────────────────────────── │
│ │ │
│ ┌──────▼──────┐ │
│ │ Intel VT-x │ Hardware │
│ │ AMD-V │ │
│ └─────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────┘
1.2 VM은 Linux 프로세스다¶
Proxmox VE에서 각 VM은 일반적으로 **하나의 주 QEMU 프로세스와 그 내부 스레드**로 관찰됩니다. 스레드 수와 보조 프로세스는 장치, 기능, 버전에 따라 달라지므로 다음 출력은 구조를 설명하기 위한 예시입니다.
# VM 100이 실행 중일 때 프로세스 확인
$ ps aux | grep "kvm.*100"
root 12345 45.2 8.3 4521896 1376520 ? Sl 10:30 125:32 /usr/bin/kvm \
-id 100 \
-name webserver,debug-threads=on \
-no-shutdown \
-chardev socket,id=qmp,path=/var/run/qemu-server/100.qmp,server=on,wait=off \
-mon chardev=qmp,mode=control \
-rtc base=utc \
-smp 4,sockets=1,cores=4,maxcpus=4 \
-m 8192 \
...
프로세스 분석:
┌──────────────────────────────────────────────────────────────────────────┐
│ QEMU 프로세스 구조 │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ Main QEMU Process (PID: 12345) │
│ ├── Main Thread (Event Loop, QMP) │
│ ├── vCPU Thread 0 (Guest CPU 0 실행) │
│ ├── vCPU Thread 1 (Guest CPU 1 실행) │
│ ├── vCPU Thread 2 (Guest CPU 2 실행) │
│ ├── vCPU Thread 3 (Guest CPU 3 실행) │
│ ├── IO Thread (VirtIO 디스크 처리) │
│ ├── Worker Thread (비동기 작업) │
│ └── VNC/SPICE Thread (디스플레이) │
│ │
│ Memory: Guest RAM이 QEMU 프로세스의 가상 메모리로 매핑됨 │
│ Files: /var/run/qemu-server/100.qmp (QMP 소켓) │
│ /var/run/qemu-server/100.pid (PID 파일) │
│ │
└──────────────────────────────────────────────────────────────────────────┘
1.3 /proc 파일시스템을 통한 VM 분석¶
# VM 프로세스의 상세 정보 확인
$ VM_PID=$(cat /var/run/qemu-server/100.pid)
$ echo "VM 100 PID: $VM_PID"
# 메모리 사용량 확인
$ cat /proc/$VM_PID/status | grep -E "^(VmRSS|VmSize|Threads)"
VmSize: 4521896 kB # 가상 메모리 크기 (Guest RAM + QEMU 오버헤드)
VmRSS: 1376520 kB # 실제 사용 중인 물리 메모리
Threads: 9 # 총 스레드 수 (vCPU + IO + 기타)
# 열린 파일 디스크립터 확인
$ ls -la /proc/$VM_PID/fd/
lrwx------ 1 root root 64 Jan 10 10:30 0 -> /dev/null
lrwx------ 1 root root 64 Jan 10 10:30 3 -> /dev/kvm # KVM 디바이스
lrwx------ 1 root root 64 Jan 10 10:30 7 -> /dev/vfio/vfio # VFIO (PCIe passthrough)
lrwx------ 1 root root 64 Jan 10 10:30 10 -> /var/lib/vz/images/100/vm-100-disk-0.qcow2
lrwx------ 1 root root 64 Jan 10 10:30 15 -> socket:[12345] # QMP 소켓
# vCPU가 어떤 물리 CPU에서 실행 중인지 확인
$ cat /proc/$VM_PID/task/*/stat | awk '{print "Thread "$1": CPU "$39}'
Thread 12346: CPU 2
Thread 12347: CPU 5
Thread 12348: CPU 3
Thread 12349: CPU 7
2. QEMU/KVM 실행 흐름¶
2.1 qm start 명령의 내부 동작¶
이 단순한 명령 뒤에서 일어나는 일:
┌─────────────────────────────────────────────────────────────────────────────┐
│ qm start 100 실행 흐름 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ [1] CLI Layer │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ /usr/sbin/qm start 100 │ │
│ │ - Perl 스크립트 │ │
│ │ - PVE::QemuServer 모듈 로드 │ │
│ └─────────────────┬───────────────────────┘ │
│ │ │
│ [2] API Layer ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ pvedaemon (REST API) │ │
│ │ - 인증/권한 확인 │ │
│ │ - Task 생성 (UPID 발급) │ │
│ └─────────────────┬───────────────────────┘ │
│ │ │
│ [3] Config Layer ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ /etc/pve/qemu-server/100.conf 읽기 │ │
│ │ - 메모리: 8192MB │ │
│ │ - CPU: 4 cores │ │
│ │ - 디스크: local-lvm:vm-100-disk-0 │ │
│ │ - 네트워크: vmbr0 │ │
│ └─────────────────┬───────────────────────┘ │
│ │ │
│ [4] QEMU Command ▼ │
│ Generation │
│ ┌─────────────────────────────────────────┐ │
│ │ PVE::QemuServer::vm_start() │ │
│ │ - 설정을 QEMU 명령줄 인자로 변환 │ │
│ │ - 스토리지 볼륨 활성화 │ │
│ │ - 네트워크 TAP 디바이스 생성 │ │
│ └─────────────────┬───────────────────────┘ │
│ │ │
│ [5] Process Fork ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ fork() + exec("/usr/bin/kvm", args...) │ │
│ │ - QEMU 프로세스 시작 │ │
│ │ - /dev/kvm 열기 │ │
│ │ - VM 메모리 할당 │ │
│ │ - vCPU 생성 │ │
│ └─────────────────┬───────────────────────┘ │
│ │ │
│ [6] Guest Boot ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ SeaBIOS/OVMF → Bootloader → OS Kernel │ │
│ │ - BIOS POST │ │
│ │ - 부팅 디바이스 탐색 │ │
│ │ - Guest OS 로드 │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
2.2 실제 QEMU 명령줄 분석¶
주요 옵션 설명:
/usr/bin/kvm
-id 100 # VM ID
-name webserver,debug-threads=on # VM 이름
-no-shutdown # shutdown 시 프로세스 종료 방지
# QMP (QEMU Machine Protocol) - 관리 인터페이스
-chardev socket,id=qmp,path=/var/run/qemu-server/100.qmp,server=on,wait=off
-mon chardev=qmp,mode=control
# CPU 설정
-smp 4,sockets=1,cores=4,maxcpus=4 # 4코어, 1소켓
-cpu kvm64,enforce,+kvm_pv_unhalt,... # CPU 타입 및 기능
# 메모리 설정
-m 8192 # 8GB RAM
-object memory-backend-ram,id=ram-node0,size=8192M
-numa node,nodeid=0,cpus=0-3,memdev=ram-node0
# 스토리지
-drive file=/dev/pve/vm-100-disk-0,if=none,id=drive-scsi0,format=raw,cache=none,aio=io_uring
-device scsi-hd,bus=scsihw0.0,drive=drive-scsi0,id=scsi0
# 네트워크
-netdev type=tap,id=net0,ifname=tap100i0,script=/var/lib/qemu-server/pve-bridge
-device virtio-net-pci,netdev=net0,mac=BC:24:11:12:34:56,id=net0
# 디스플레이
-vga qxl
-spice port=5901,addr=127.0.0.1
2.3 VirtIO - 준가상화 디바이스¶
┌─────────────────────────────────────────────────────────────────────────────┐
│ Emulated vs VirtIO 성능 비교 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ [Emulated IDE Controller] [VirtIO Block] │
│ │
│ Guest OS Guest OS │
│ │ │ │
│ ▼ ▼ │
│ IDE Driver (Guest) VirtIO Driver (Guest) │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ IDE 레지스터 │ ◄── 하드웨어 에뮬레이션 │ VirtIO Ring │ ◄── 공유 메모리│
│ │ 에뮬레이션 │ (느린 trap/emulate) │ (vring) │ (빠른 직접통신)│
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ ▼ ▼ │
│ QEMU IDE Emulation QEMU VirtIO Backend │
│ │ │ │
│ ▼ ▼ │
│ Host Storage Host Storage │
│ │
│ 성능: ~50MB/s 성능: ~500MB/s+ │
│ CPU 오버헤드: 높음 CPU 오버헤드: 낮음 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
VirtIO 디바이스 종류: | 디바이스 | 설명 | Guest 드라이버 | |---------|------|---------------| | virtio-blk | 블록 디바이스 | virtio_blk | | virtio-scsi | SCSI 컨트롤러 | virtio_scsi | | virtio-net | 네트워크 카드 | virtio_net | | virtio-balloon | 메모리 밸루닝 | virtio_balloon | | virtio-serial | 시리얼 포트 | virtio_console |
3. qm 명령어 상세 가이드¶
3.1 VM 생성 및 설정¶
# VM 생성 (기본)
$ qm create 100 \
--name webserver \
--memory 4096 \
--cores 2 \
--net0 virtio,bridge=vmbr0 \
--scsi0 local-lvm:32 \
--cdrom local:iso/ubuntu-22.04.iso
# 설정 확인
$ qm config 100
boot: order=scsi0;ide2;net0
cores: 2
ide2: local:iso/ubuntu-22.04.iso,media=cdrom
memory: 4096
name: webserver
net0: virtio=BC:24:11:AA:BB:CC,bridge=vmbr0
scsi0: local-lvm:vm-100-disk-0,size=32G
scsihw: virtio-scsi-single
smbios1: uuid=12345678-1234-1234-1234-123456789abc
vmgenid: 87654321-4321-4321-4321-cba987654321
# 특정 설정 변경
$ qm set 100 --memory 8192 --cores 4
3.2 VM 라이프사이클 관리¶
# VM 시작
$ qm start 100
# 내부 동작: config 읽기 → QEMU 명령 생성 → 프로세스 fork
# VM 상태 확인
$ qm status 100
status: running
# 또는 상세 정보
$ qm status 100 --verbose
status: running
ha: managed
name: webserver
pid: 12345
qmpstatus: running
running-machine: pc-i440fx-8.1+pve0
running-qemu: 8.1.5
uptime: 86400
# VM 종료 (graceful - ACPI 신호)
$ qm shutdown 100
# Guest OS가 ACPI 전원 버튼 이벤트를 받아 정상 종료
# VM 강제 종료 (guest의 정상 종료 절차를 보장하지 않음)
$ qm stop 100
# 데이터 손실 가능성이 있으므로 qm shutdown의 실패 원인을 확인한 뒤 사용
# VM 리부팅
$ qm reboot 100
# VM 일시 정지 (suspend to RAM)
$ qm suspend 100
# vCPU 중지, 메모리 상태 유지
# VM 재개
$ qm resume 100
3.3 QMP (QEMU Machine Protocol) 인터페이스¶
# QMP 직접 접속 (고급 사용자용)
$ qm monitor 100
Entering QEMU Monitor for VM 100 - type 'help' for help
qm> info status
VM status: running
qm> info cpus
* CPU #0: thread_id=12346
* CPU #1: thread_id=12347
* CPU #2: thread_id=12348
* CPU #3: thread_id=12349
qm> info block
drive-scsi0 (#block256): /dev/pve/vm-100-disk-0 (raw)
Attached to: scsi0-0-0-0
Cache mode: writeback, direct
qm> info network
net0: index=0,type=tap,ifname=tap100i0,script=/var/lib/qemu-server/pve-bridge
qm> quit
3.4 QEMU Guest Agent¶
# Guest Agent 상태 확인
$ qm agent 100 ping
{"return": {}} # 성공
# Guest 내부 정보 조회
$ qm agent 100 get-osinfo
{
"id": "ubuntu",
"version": "22.04",
"name": "Ubuntu",
"kernel-release": "5.15.0-91-generic",
"kernel-version": "#101-Ubuntu SMP",
"machine": "x86_64"
}
# Guest 파일시스템 정보
$ qm agent 100 get-fsinfo
[
{
"name": "/dev/sda1",
"mountpoint": "/",
"type": "ext4",
"total-bytes": 33285996544,
"used-bytes": 12345678901
}
]
# Guest에서 명령 실행
$ qm agent 100 exec -- ls -la /root
$ qm agent 100 exec -- systemctl status nginx
# 파일 시스템 freeze (파일 시스템 관점의 쓰기 정지; 애플리케이션 일관성은 별도 보장 필요)
$ qm agent 100 fsfreeze-freeze
$ # 백업 수행...
$ qm agent 100 fsfreeze-thaw
# 실패 경로에서도 thaw가 실행됐는지 확인하고, 장시간 freeze 상태로 두지 않는다.
3.5 VM 마이그레이션¶
# 온라인 마이그레이션 (최종 전환 시 짧은 중단이 발생할 수 있음)
$ qm migrate 100 node2
# 단계:
# 1. 대상 노드에서 QEMU 시작 (incoming mode)
# 2. 메모리 페이지 복사 시작
# 3. Dirty page 추적 및 반복 전송
# 4. 최종 전환 (중단 시간은 메모리 변경률·네트워크·스토리지에 따라 측정)
# 5. 소스 VM 종료
# 오프라인 마이그레이션 (VM 정지 후)
$ qm migrate 100 node2 --offline
# 로컬 스토리지 포함 마이그레이션
$ qm migrate 100 node2 --with-local-disks --targetstorage local-lvm
4. LXC 컨테이너 아키텍처¶
4.1 컨테이너 vs VM - 근본적 차이¶
┌─────────────────────────────────────────────────────────────────────────────┐
│ VM vs Container 구조 비교 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ [Virtual Machine] [Container (LXC)] │
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ App A │ │ App A │ │
│ ├─────────────────────┤ ├─────────────────────┤ │
│ │ Guest OS Kernel │ │ (호스트 커널 공유) │ │
│ ├─────────────────────┤ ├─────────────────────┤ │
│ │ Virtual Hardware │ │ Namespace 격리 │ │
│ ├─────────────────────┤ │ - pid, net, mnt │ │
│ │ QEMU Process │ │ - uts, ipc, user │ │
│ ├─────────────────────┤ ├─────────────────────┤ │
│ │ KVM Module │ │ cgroup 리소스 제한 │ │
│ └─────────┬───────────┘ └─────────┬───────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Host Linux Kernel │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 오버헤드: 높음 (하드웨어 에뮬레이션) 오버헤드: 매우 낮음 │
│ 격리: 강함 (별도 커널) 격리: 커널 기능에 의존 │
│ 호환성: 모든 OS 호환성: Linux만 │
│ 시작 시간: 수십 초 시작 시간: 수 초 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
4.2 Linux Namespace - "아파트와 방" 개념¶
┌─────────────────────────────────────────────────────────────────────────────┐
│ Linux Namespace 격리 개념도 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────── Host System (아파트 건물 전체) ───────────┐ │
│ │ │ │
│ │ ┌──────────── Container 100 (101호) ────────────┐ │ │
│ │ │ PID Namespace: init(1), nginx(50), ... │ │ │
│ │ │ NET Namespace: eth0 (10.0.0.1), lo │ │ │
│ │ │ MNT Namespace: /, /home, /var │ │ │
│ │ │ UTS Namespace: hostname="webserver" │ │ │
│ │ │ IPC Namespace: 자체 공유 메모리, 세마포어 │ │ │
│ │ │ USER Namespace: root(0) → 100000(host) │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────── Container 101 (102호) ────────────┐ │ │
│ │ │ PID Namespace: init(1), mysql(100), ... │ │ │
│ │ │ NET Namespace: eth0 (10.0.0.2), lo │ │ │
│ │ │ MNT Namespace: /, /home, /var/lib/mysql │ │ │
│ │ │ UTS Namespace: hostname="database" │ │ │
│ │ │ IPC Namespace: 자체 공유 메모리, 세마포어 │ │ │
│ │ │ USER Namespace: root(0) → 200000(host) │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Host Processes: systemd(1), pvedaemon, ... │ │
│ │ Host Network: vmbr0, eth0 │ │
│ │ │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
각 Namespace의 역할:
| Namespace | 격리 대상 | 효과 |
|---|---|---|
| PID | 프로세스 ID | 컨테이너는 자신의 PID 1(init)을 가짐, 호스트 프로세스 보이지 않음 |
| NET | 네트워크 스택 | 독립된 IP, 라우팅 테이블, 방화벽 규칙 |
| MNT | 파일시스템 마운트 | 독립된 루트 파일시스템, 마운트 포인트 |
| UTS | 호스트명, 도메인명 | 컨테이너별 독립된 hostname |
| IPC | IPC 리소스 | 독립된 공유 메모리, 메시지 큐, 세마포어 |
| USER | UID/GID 매핑 | 컨테이너 내 root가 호스트에서는 일반 사용자 |
4.3 Namespace 확인 실습¶
# 컨테이너 100의 init 프로세스 찾기
$ CT_PID=$(cat /var/run/lxc/100/init.pid)
$ echo "Container 100 init PID on host: $CT_PID"
# 호스트에서 본 namespace 정보
$ ls -la /proc/$CT_PID/ns/
lrwxrwxrwx 1 100000 100000 0 Jan 10 10:00 cgroup -> 'cgroup:[4026532xxx]'
lrwxrwxrwx 1 100000 100000 0 Jan 10 10:00 ipc -> 'ipc:[4026532xxx]'
lrwxrwxrwx 1 100000 100000 0 Jan 10 10:00 mnt -> 'mnt:[4026532xxx]'
lrwxrwxrwx 1 100000 100000 0 Jan 10 10:00 net -> 'net:[4026532xxx]'
lrwxrwxrwx 1 100000 100000 0 Jan 10 10:00 pid -> 'pid:[4026532xxx]'
lrwxrwxrwx 1 100000 100000 0 Jan 10 10:00 user -> 'user:[4026532xxx]'
lrwxrwxrwx 1 100000 100000 0 Jan 10 10:00 uts -> 'uts:[4026532xxx]'
# 호스트의 namespace와 비교
$ ls -la /proc/1/ns/
# 숫자가 다름 = 다른 namespace에 있음
# 컨테이너 내부의 프로세스 목록 (호스트에서)
$ cat /proc/$CT_PID/cgroup
# 컨테이너의 cgroup 경로 확인
4.4 Control Groups (cgroups) - 리소스 제한¶
┌─────────────────────────────────────────────────────────────────────────────┐
│ cgroup v2 리소스 제어 구조 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ /sys/fs/cgroup/ │
│ └── lxc/ │
│ ├── 100/ # Container 100 │
│ │ ├── cgroup.controllers # 활성화된 컨트롤러 목록 │
│ │ ├── cpu.max # CPU 제한 (quota period) │
│ │ ├── cpu.weight # CPU 가중치 (상대적 우선순위) │
│ │ ├── memory.max # 메모리 하드 제한 │
│ │ ├── memory.high # 메모리 소프트 제한 (throttling) │
│ │ ├── memory.current # 현재 메모리 사용량 │
│ │ ├── io.max # 디스크 I/O 제한 │
│ │ └── pids.max # 최대 프로세스 수 │
│ │ │
│ └── 101/ # Container 101 │
│ └── ... │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
cgroup 실습:
# 컨테이너의 cgroup 경로 확인
$ CT_CGROUP="/sys/fs/cgroup/lxc/100"
# 메모리 제한 및 사용량 확인
$ cat $CT_CGROUP/memory.max
4294967296 # 4GB
$ cat $CT_CGROUP/memory.current
1234567890 # 현재 ~1.2GB 사용
# CPU 가중치 확인
$ cat $CT_CGROUP/cpu.weight
100 # 기본값
# 실시간 모니터링
$ watch -n 1 "cat $CT_CGROUP/memory.current && cat $CT_CGROUP/cpu.stat"
4.5 Unprivileged vs Privileged 컨테이너¶
┌─────────────────────────────────────────────────────────────────────────────┐
│ Unprivileged Container (권장) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Container 내부 UID/GID 매핑 Host에서 실제 │
│ ─────────────── ───────────── ─────────────── │
│ root (UID 0) ────► +100000 ────► UID 100000 │
│ www-data (UID 33) ────► +100000 ────► UID 100033 │
│ mysql (UID 101) ────► +100000 ────► UID 100101 │
│ │
│ 장점: │
│ - 컨테이너 탈출해도 호스트에서 권한 없는 일반 사용자 │
│ - 커널 취약점 영향 최소화 │
│ │
│ 단점: │
│ - 일부 작업 제한 (raw socket, 일부 마운트) │
│ - NFS 마운트 시 UID 매핑 문제 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ Privileged Container (주의 필요) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Container 내부 UID/GID 매핑 Host에서 실제 │
│ ─────────────── ───────────── ─────────────── │
│ root (UID 0) ────► 매핑 없음 ────► root (UID 0) ⚠️ │
│ www-data (UID 33) ────► 매핑 없음 ────► UID 33 │
│ │
│ 장점: │
│ - 모든 기능 사용 가능 │
│ - 호스트와 UID 일치로 파일 공유 용이 │
│ │
│ 단점: │
│ - 보안 위험: 탈출 시 호스트 root 권한 획득 가능 │
│ - 신뢰할 수 있는 환경에서만 사용 권장 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
5. pct 명령어 상세 가이드¶
5.1 컨테이너 생성¶
# 템플릿 다운로드
$ pveam update
$ pveam available --section system
$ pveam download local debian-12-standard_12.2-1_amd64.tar.zst
# 컨테이너 생성
$ pct create 100 local:vztmpl/debian-12-standard_12.2-1_amd64.tar.zst \
--hostname webserver \
--memory 2048 \
--swap 512 \
--cores 2 \
--net0 name=eth0,bridge=vmbr0,ip=dhcp \
--rootfs local-lvm:8 \
--password \
--unprivileged 1
# nesting은 격리 범위를 넓히므로 실제 중첩 워크로드가 있을 때만 별도로 활성화한다.
# 생성된 설정 확인
$ pct config 100
arch: amd64
cores: 2
features: nesting=1
hostname: webserver
memory: 2048
net0: name=eth0,bridge=vmbr0,ip=dhcp
ostype: debian
rootfs: local-lvm:vm-100-disk-0,size=8G
swap: 512
unprivileged: 1
5.2 컨테이너 라이프사이클¶
# 시작
$ pct start 100
# 내부: lxc-start → namespace 생성 → cgroup 설정 → init 실행
# 상태 확인
$ pct status 100
status: running
# 종료
$ pct shutdown 100 # graceful (init에 signal)
$ pct stop 100 # force; 애플리케이션의 정상 종료를 보장하지 않음
# 리부팅
$ pct reboot 100
5.3 컨테이너 접속¶
# 콘솔 접속 (가상 터미널)
$ pct console 100
# Ctrl+A, Q 로 종료
# 컨테이너 내부에서 명령 실행 (namespace 진입)
$ pct enter 100
root@webserver:/# whoami
root
root@webserver:/# exit
# 단일 명령 실행
$ pct exec 100 -- apt update
$ pct exec 100 -- systemctl status nginx
$ pct exec 100 -- cat /etc/os-release
5.4 파일 전송¶
# 호스트 → 컨테이너
$ pct push 100 /root/config.txt /etc/myapp/config.txt
$ pct push 100 /root/script.sh /usr/local/bin/script.sh --perms 755
# 컨테이너 → 호스트
$ pct pull 100 /var/log/myapp.log /root/myapp.log
# 디렉토리 전체 복사 (tar 사용)
$ tar czf - /root/configs | pct exec 100 -- tar xzf - -C /etc/
5.5 리소스 변경¶
# 메모리 변경 (핫플러그)
$ pct set 100 --memory 4096
# CPU 코어 변경
$ pct set 100 --cores 4
# 디스크 크기 확장
$ pct resize 100 rootfs +10G
# 마운트 포인트 추가
$ pct set 100 --mp0 local-lvm:50,mp=/data
# 컨테이너 내부에 /data로 마운트됨
# Bind mount (호스트 디렉토리 공유)
$ pct set 100 --mp1 /mnt/shared,mp=/shared
# 적용 전 host 경로의 소유권 매핑, 백업 포함 여부, 컨테이너의 쓰기 권한을 확인한다.
5.6 컨테이너 마이그레이션¶
# 오프라인 마이그레이션
$ pct migrate 100 node2
# 로컬 스토리지 포함
$ pct migrate 100 node2 --target-storage local-lvm
# 재시작 마이그레이션 (종료 → 이동 → 시작)
$ pct migrate 100 node2 --restart --timeout 30
6. 설정 파일 구조¶
6.1 VM 설정 파일 (/etc/pve/qemu-server/100.conf)¶
# VM 100 Configuration
boot: order=scsi0;ide2;net0
cores: 4
sockets: 1
cpu: x86-64-v2-AES
memory: 8192
name: production-web
# BIOS/UEFI
bios: ovmf
efidisk0: local-lvm:vm-100-disk-1,efitype=4m,pre-enrolled-keys=1,size=4M
# 스토리지
scsi0: local-lvm:vm-100-disk-0,iothread=1,size=100G
scsihw: virtio-scsi-single
ide2: none,media=cdrom
# 네트워크
net0: virtio=BC:24:11:AA:BB:CC,bridge=vmbr0,firewall=1
# 디스플레이
vga: qxl
machine: q35
# Guest Agent
agent: enabled=1,fstrim_cloned_disks=1
# 옵션
onboot: 1
startup: order=1,up=30,down=60
protection: 0
6.2 컨테이너 설정 파일 (/etc/pve/lxc/100.conf)¶
# Container 100 Configuration
arch: amd64
cores: 2
hostname: webserver
memory: 2048
swap: 512
ostype: debian
unprivileged: 1
# 스토리지
rootfs: local-lvm:vm-100-disk-0,size=8G
mp0: local-lvm:vm-100-disk-1,mp=/data,size=50G
# 네트워크
net0: name=eth0,bridge=vmbr0,firewall=1,hwaddr=BC:24:11:CC:DD:EE,ip=dhcp,type=veth
# 기능
features: nesting=1,keyctl=1
# 시작 옵션
onboot: 1
startup: order=2,up=60
# 보안 (LXC 직접 설정)
lxc.apparmor.profile: generated
lxc.cap.drop: sys_admin
# 장치 접근
#lxc.cgroup2.devices.allow: c 10:200 rwm
#lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
7. 트러블슈팅 가이드¶
7.1 VM 문제 진단¶
# VM이 시작되지 않을 때
$ qm start 100 2>&1
# 오류 메시지 확인
# QEMU 로그 확인
$ journalctl -u "pve-qemu-server@100" --no-pager -n 100
# 부팅 문제 - VNC로 콘솔 확인
$ qm terminal 100 # Serial console
$ qm vnc 100 # VNC 직접 연결
# KVM 하드웨어 가상화 확인
$ kvm-ok
INFO: /dev/kvm exists
KVM acceleration can be used
# CPU 플래그 확인
$ grep -E "(vmx|svm)" /proc/cpuinfo
7.2 컨테이너 문제 진단¶
# 컨테이너 시작 실패
$ pct start 100 --debug
# 상세 로그 출력
# LXC 로그 확인
$ journalctl -u "pve-container@100" --no-pager -n 100
$ cat /var/log/lxc/100.log
# AppArmor 문제 확인
$ dmesg | grep -i apparmor
$ aa-status
# cgroup 문제 확인
$ cat /proc/cgroups
$ mount | grep cgroup
7.3 성능 모니터링¶
# VM 리소스 사용량
$ qm monitor 100
qm> info balloon
balloon: actual=8192
qm> info migrate
qm> quit
# 컨테이너 리소스 사용량
$ pct exec 100 -- top -bn1 | head -20
# 호스트에서 전체 현황
$ pvesh get /cluster/resources --type vm
$ qm list
$ pct list
8. 모범 사례¶
8.1 VM 최적화¶
아래 항목은 일괄 적용할 기본값이 아니라 검토 후보입니다. 변경 전후에 같은 워크로드로 지연 시간, 처리량, CPU 사용량과 오류를 측정하고, 라이브 마이그레이션 호환성도 함께 확인합니다.
┌─────────────────────────────────────────────────────────────────────────────┐
│ VM 성능 최적화 체크리스트 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ [디스크] │
│ ✓ VirtIO SCSI Single 컨트롤러 사용 │
│ ✓ IO Thread 활성화 (iothread=1) │
│ ✓ cache=none 또는 writeback │
│ ✓ aio=io_uring (최신 커널) │
│ ✓ SSD에서는 discard=on 설정 │
│ │
│ [네트워크] │
│ ✓ VirtIO 네트워크 카드 사용 │
│ ✓ 멀티큐 활성화 (queues=4) │
│ │
│ [CPU] │
│ ✓ 동일 클러스터면 host CPU 타입 │
│ ✓ 다양한 노드면 x86-64-v2-AES 이상 │
│ ✓ NUMA 고려 (대형 VM) │
│ │
│ [메모리] │
│ ✓ Ballooning 활성화 (동적 조절) │
│ ✓ 대용량 페이지 고려 (hugepages) │
│ │
│ [Guest] │
│ ✓ QEMU Guest Agent 설치 │
│ ✓ VirtIO 드라이버 설치 (Windows) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
8.2 컨테이너 보안¶
┌─────────────────────────────────────────────────────────────────────────────┐
│ 컨테이너 보안 체크리스트 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ [기본 설정] │
│ ✓ Unprivileged 컨테이너 사용 (기본값) │
│ ✓ AppArmor 프로파일 유지 │
│ ✓ 불필요한 capability 제거 │
│ │
│ [네트워크] │
│ ✓ 방화벽 활성화 (firewall=1) │
│ ✓ 필요한 포트만 개방 │
│ │
│ [파일시스템] │
│ ✓ 호스트 바인드 마운트 최소화 │
│ ✓ 읽기 전용 마운트 활용 (ro=1) │
│ │
│ [리소스] │
│ ✓ 메모리 제한 설정 │
│ ✓ CPU 제한 설정 │
│ ✓ 디스크 쿼터 설정 │
│ │
│ [모니터링] │
│ ✓ 로그 모니터링 │
│ ✓ 이상 행동 탐지 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
9. 주요 명령어 요약¶
9.1 qm (VM 관리)¶
| 명령어 | 설명 |
|---|---|
qm create <vmid> |
VM 생성 |
qm destroy <vmid> |
VM과 연결 리소스 삭제 가능; 대상·백업 확인 후 실행 |
qm start <vmid> |
VM 시작 |
qm stop <vmid> |
VM 강제 종료 |
qm shutdown <vmid> |
VM 정상 종료 |
qm reboot <vmid> |
VM 리부팅 |
qm suspend <vmid> |
VM 일시 정지 |
qm resume <vmid> |
VM 재개 |
qm config <vmid> |
설정 보기 |
qm set <vmid> |
설정 변경 |
qm status <vmid> |
상태 확인 |
qm migrate <vmid> <node> |
마이그레이션 |
qm monitor <vmid> |
QMP 콘솔 |
qm agent <vmid> <cmd> |
Guest Agent 명령 |
qm terminal <vmid> |
시리얼 콘솔 |
9.2 pct (컨테이너 관리)¶
| 명령어 | 설명 |
|---|---|
pct create <ctid> |
컨테이너 생성 |
pct destroy <ctid> |
컨테이너와 연결 리소스 삭제 가능; 대상·백업 확인 후 실행 |
pct start <ctid> |
시작 |
pct stop <ctid> |
강제 종료 |
pct shutdown <ctid> |
정상 종료 |
pct config <ctid> |
설정 보기 |
pct set <ctid> |
설정 변경 |
pct status <ctid> |
상태 확인 |
pct enter <ctid> |
쉘 진입 |
pct exec <ctid> -- <cmd> |
명령 실행 |
pct console <ctid> |
콘솔 접속 |
pct push <ctid> <src> <dst> |
파일 업로드 |
pct pull <ctid> <src> <dst> |
파일 다운로드 |
pct migrate <ctid> <node> |
마이그레이션 |
pct resize <ctid> <disk> <size> |
디스크 크기 변경 |
10. 완료 기준¶
pveversion -v, 대상의qm config/status또는pct config/status, 스토리지와 노드 상태를 작업 기록에 남겼다.- 변경 전 예상 영향과 rollback 조건을 적고, 변경 후 guest 상태·네트워크·스토리지 I/O를 실제 명령 출력으로 확인했다.
- 강제 종료나 마이그레이션이 실패했다면 성공으로 간주하지 않고 task 로그와 guest 상태를 보존해 후속 조치로 넘겼다.
11. 다음 단계¶
Module 5에서는 **High Availability & Cluster Orchestration**을 다룹니다: - Corosync 클러스터 통신 메커니즘 - Quorum 알고리즘과 split-brain 방지 - HA 스택 (pve-ha-crm, pve-ha-lrm) 동작 원리 - Fencing과 Watchdog - 장애 시뮬레이션 및 복구 절차
문서 버전: 1.0 최종 업데이트: 2025년 1월 Proxmox VE 8.x 기준