Module 4: Compute Virtualization - KVM & LXC Deep Dive¶
학습 목표¶
이 모듈을 완료하면 다음을 이해할 수 있습니다: - 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 강제 종료 (immediate)
$ qm stop 100
# QEMU 프로세스에 SIGTERM → 즉시 종료
# 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
3.5 VM 마이그레이션¶
# 온라인 마이그레이션 (실시간)
$ qm migrate 100 node2
# 단계:
# 1. 대상 노드에서 QEMU 시작 (incoming mode)
# 2. 메모리 페이지 복사 시작
# 3. Dirty page 추적 및 반복 전송
# 4. 최종 전환 (downtime 최소화)
# 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 \
--features nesting=1
# 생성된 설정 확인
$ 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
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 최적화¶
┌─────────────────────────────────────────────────────────────────────────────┐
│ 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. 다음 단계¶
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 기준