VM과 LXC를 구분할 기준을 잡았다면, 다음 단계는 "표준 Linux VM"을 정의하는 일입니다. 템플릿이 준비되어 있으면 새로운 워크로드를 만들 때마다 CPU 타입, 디스크 버스, 클라우드-이닛 설정을 다시 고민할 필요가 없습니다. 이번 글은 Ubuntu/Debian 기반 VM을 대상으로, 초보자가 실수하기 쉬운 옵션을 하나씩 짚고 안전한 기본값과 "왜" 그래야 하는지를 설명합니다.
이 글의 흐름
- CPU 타입과 코어 매핑
- 메모리·Ballooning·HugePages 기본값
- 디스크 버스, 캐시, VirtIO 매개변수
- 네트워크 디바이스와 QEMU Guest Agent
- Cloud-Init과 템플릿 제작 절차
- 흔한 실수와 점검 체크리스트
이번 글에서 새로 나오는 용어
- x86-64-v2/v3: 최신 CPU 명령어 지원 수준을 묶은 가상 CPU 프로파일입니다.
- Ballooning: 게스트 메모리를 동적으로 조정하는 기능으로, 오버커밋에 도움을 줍니다.
- VirtIO SCSI Single: 디스크 성능과 기능을 모두 확보하는 기본 디스크 컨트롤러 옵션입니다.
- Discard/Trim: 게스트가 사용하지 않는 블록을 호스트에 반환해 LVM-Thin 공간을 회수하는 기능입니다.
- Cloud-Init: VM 부팅 시 사용자·SSH키·네트워크 설정을 자동 적용하는 초기화 프레임워크입니다.
읽기 카드
- 예상 소요 시간: 24분
- 사전 준비: Ubuntu 또는 Debian ISO, Proxmox 8.x,
local혹은 별도 ISO 스토리지- 읽고 나면: 반복 가능한 표준 Linux VM 템플릿을 만들고 새 워크로드에 적용하는 방법을 설명할 수 있습니다.
CPU 타입과 코어 매핑
| CPU 타입 | 특징 | 권장 환경 |
|---|---|---|
kvm64 |
가장 호환성 높지만 AVX 등 최신 명령어 없음 | 10년 이상 된 OS만 필요할 때 |
x86-64-v2-AES |
Proxmox 8.x 기본, AVX/_AES 지원 | 대부분의 홈랩 기본값 |
x86-64-v3 |
AVX2/VAES 포함, Zen 3·11세대 Core 이상 필요 | 최신 CPU에서만 사용 |
host |
호스트 CPU와 동일, 성능 최대 | 라이브 마이그레이션 계획이 전혀 없을 때 |
- CPU 타입 결정 절차:
lscpu | grep Flags로 호스트가avx2를 지원하는지 확인합니다. Zen 2 이하나 10세대 이전 Intel이라면x86-64-v2-AES로 고정하고, Zen 3 이상(예: Ryzen 5000)에서만x86-64-v3옵션을 고려합니다. - 코어 수: 기본 템플릿은 2 vCPU(1 소켓, 2 코어)로 두고, 클론 이후 워크로드 맞춤으로 늘립니다. SMT를 켤 때는 VM의 코어 비율을 호스트와 맞춰 NUMA 바운딩을 단순화합니다.
numactl -H출력을 확인해 멀티소켓임이 드러나면qm set <ID> --numa 1과--cpuaffinity 0-3같은 옵션으로 특정 소켓에 고정하고, 단일 소켓 홈랩이면 이 단계를 건너뜁니다. - Type 변경 시 주의: 이미 만들어 둔 VM은 CPU 타입을 바꾸면 커널이 새 하드웨어로 인식합니다. 마이그레이션 전 템플릿 단계에서 타입을 확정하고 문서화하십시오.
- 중첩 가상화 주의점: 게스트 안에서 다시 KVM이나 Docker를 돌릴 계획이 있다면
grep vmx /proc/cpuinfo또는grep svm /proc/cpuinfo로 플래그가 실제로 보이는지 확인합니다. 일부 CPU 타입은 이 플래그를 숨기므로, 중첩 가상화가 정말 필요할 때만host같은 설정을 검토하십시오.
메모리와 Ballooning
- 고정 메모리: 템플릿에는 2GB 고정값을 넣어 두고, 클론 직후 서비스 필요량 + 1~2GB 여유를 배정합니다. 예) 웹 프런트엔드(필요 1GB) -> 2GB, PostgreSQL(필요 4GB) -> 6GB.
- Ballooning 정책: Ballooning을 켜면 게스트가 요청하지 않은 메모리를 호스트가 회수해 다른 VM에 배분합니다. 초보자는 메모리 압박을 눈치채기 어렵기 때문에 기본 템플릿에서는 Ballooning 비활성화와
balloon_min = memory구성을 권장합니다.lsmod | grep virtio_balloon으로 게스트 드라이버를 확인하고, 최대 Balloon 용량의 50% 이상을 swap으로 확보한 뒤에만 켜십시오. 알람을 만들어balloon값이 최대치의 70% 아래로 떨어지면 즉시 통보 받도록 합니다. - HugePages: 휘발성 워크로드라면 비활성화로 두고, DB/게임 서버처럼 TLB miss가 빈번한 VM에 한해
qm set <ID> --hugepages 1024로 지정합니다. 호스트에서는default_hugepagesz커널 인자를 주고,transparent hugepage를echo never > /sys/kernel/mm/transparent_hugepage/enabled로 끄는 것이 안전합니다.
디스크 버스, 캐시, VirtIO
| 컨트롤러 | 장점 | 사용 시점 |
|---|---|---|
SATA |
호환성 | 구형 게스트, Windows 복구용 |
VirtIO Block |
구성이 단순 | 단일 디스크 VM |
VirtIO SCSI |
TRIM·멀티큐 지원, 고성능 | 표준 템플릿 기본값 |
- 버스 선택 절차: Proxmox UI -> VM -> Hardware -> SCSI Controller 항목을
VirtIO SCSI로 지정합니다. 대부분은 single로 충분하고, 4 vCPU 이상이거나 고IOPS VM일 때만 멀티큐 +iothread=1을 고려합니다. 이후 모든 디스크의 Bus/Device를 SCSI로 맞춥니다. - 스토리지별 캐시 모드:
- 로컬 SSD 또는 Ceph RBD: UPS나 배터리 백업 RAID 캐시가 있을 때만
Write back을 사용합니다. - 로컬 HDD 또는 NFS:
Write through로 안전성을 확보합니다. - 벤치마크 전용:
Write back (unsafe)나Directsync는 벤치마크용으로만 쓰고, 전원 장애 시 데이터가 유실된다는 점을 명시하십시오.
- 로컬 SSD 또는 Ceph RBD: UPS나 배터리 백업 RAID 캐시가 있을 때만
- Discard를 위한 호스트 조건: 게스트 쪽에서 Discard를 켜도, 호스트의 LVM-Thin 풀이 discard를 실제로 발행하지 않으면 효과가 없습니다. Datacenter -> Storage -> (스토리지) -> Edit -> Advanced에서 Issue discard를 켠 뒤, 게스트에서
를 실행하고 호스트에서fallocate -l 100M /tmp/trim-test && rm /tmp/trim-test sudo fstrim -v /lvs -o lv_name,data_percent <vg>값이 감소하는지 확인해야 합니다. - VirtIO 드라이버: Ubuntu/Debian은 기본 내장입니다. Windows 게스트를 위한
virtio-win.iso는local스토리지에 업로드해 두고, 템플릿 작성 플레이북에 "필수 ISO" 목록으로 남깁니다.
네트워크와 QEMU Guest Agent
- 브릿지 설계: 기본 브릿지는
vmbr0(LAN)입니다. VLAN 격리가 필요하면vmbr0.10같은 태그 인터페이스를 만들어 템플릿 문서에 어떤 브릿지를 쓰는지 명시합니다. - NIC 모델: 모든 템플릿은
VirtIO (paravirtualized)로 설정합니다. Multiqueue는min(vCPU 수, 4)정도로 두고, 실제 게스트 안에서ethtool -l ens18로 확인합니다. - QEMU Guest Agent 활성화:
- 호스트:
qm set <ID> --agent enabled=1 - 게스트 (Ubuntu/Debian):
sudo apt install -y qemu-guest-agent && sudo systemctl enable --now qemu-guest-agent - 동작 확인:
qm agent <ID> ping
- 호스트:
- 방화벽 기본값: 템플릿 구조에 Proxmox VM 방화벽을 기본적으로 켤지 끌지 기록해 새 워크로드에도 일관되게 적용합니다.
Cloud-Init과 템플릿 절차
- 기본 설치: Ubuntu Server ISO 또는 Cloud 이미지로 설치한 후
sudo apt update && sudo apt dist-upgrade를 실행하고 필요한 패키지를 설치합니다. - 고유 정보 정리:
이 과정을 문서화해 템플릿 스크립트로 자동화하면 실수를 줄일 수 있습니다.sudo truncate -s0 /etc/machine-id sudo rm -f /var/lib/dbus/machine-id sudo rm -rf /var/lib/cloud/instances/* /var/lib/cloud/instance sudo rm /etc/ssh/ssh_host_* # 클론 시 새 키 생성 sudo cloud-init clean --seed --logs --all - Cloud-Init 드라이브 추가: Proxmox UI → VM → Hardware →
Add -> Cloud-Init Drive→ 가능하면 공유 스토리지를 선택합니다. 클라우드-이닛 ISO가local-lvm에 있으면qm migrate가 실패하거나 새 노드에서 드라이브를 찾지 못합니다. 단일 노드 홈랩이라도 이 제약을 감수할 때만 로컬 스토리지를 사용하십시오. 게스트에서cloud-init query --format '%(datasource)s'가ConfigDrive인지 확인하고, 실패하면/dev/sr0존재 여부를 먼저 점검합니다. - 템플릿 변환:
qm shutdown <ID> qm template <ID> - 클론과 초기 매개변수 입력:
SSH 키는 CLI 인자 대신 Cloud-Init 탭이나 사용자 데이터 스니펫(qm clone <TEMPLATE-ID> <NEW-ID> --name <hostname> qm set <NEW-ID> --cores 4 --memory 4096 --ipconfig0 "ip=192.168.1.20/24,gw=192.168.1.1"cicustom)으로 넣어 로그에 남지 않도록 합니다. 값을 채운 뒤 부팅하고cloud-init status --wait --timeout 15,hostnamectl,/var/log/cloud-init-output.log로 적용 결과를 검증합니다. 명령이 멈추면cloud-init query --format '%(datasource)s'를 다시 확인하십시오.
흔한 실수와 체크리스트
- CPU 타입을
host로 고정: 하드웨어 교체 시 부팅 실패. → 템플릿 기본값을x86-64-v2-AES로 문서에 고정. - Ballooning 남용: 메모리 부족이 늦게 나타남. → 모니터링 알람과 함께 켜거나 기본값은 비활성화.
- Guest Agent 미설치: IP/상태 조회 불가. → 클라우드-이닛 스크립트로
qemu-guest-agent자동 설치. - Cloud-Init 드라이브 누락: 자동 사용자/네트워크 구성이 동작하지 않음. → 템플릿 변환 전 하드웨어 목록 점검.
- TRIM 미구성: LVM-Thin이 가득 차면 VM 전체가 멈춤. → 주간
fstrim -av크론 작업까지 포함.
체크리스트
-
x86-64-v2-AES또는 CPU 세대에 맞는 타입으로 템플릿이 설정되어 있는가 - 기본 메모리/balloon 정책과 NUMA 옵션이 문서화되어 있는가
- 디스크 컨트롤러, 캐시, Discard 설정이 항목별로 기록되어 있는가
- vmbr/VLAN, NIC 모델, Guest Agent 설치 커맨드가 플레이북에 포함되어 있는가
- Cloud-Init 드라이브 추가,
machine-id초기화,qm template/clone명령이 검증되었는가 - 클론 후
cloud-init status --wait와 SSH 호스트 키 재생성을 확인하는 절차가 있는가
마무리
표준 Linux VM은 워크로드 추가 속도를 높이고, 여러 사람이 같은 방식으로 VM을 운영하도록 만드는 안전장치입니다. CPU·메모리·디스크·네트워크를 한 번 정의해 두면 이후에는 워크로드 특성에 맞춰 조정만 하면 됩니다. 템플릿을 만들어 두면 실수로 잘못된 드라이버를 고르는 일도 줄어듭니다.
다음 글에서는 이 표준 VM과 LXC 구성을 백업·복구 루틴에 연결하는 방법을 다룰 예정입니다.
💬 댓글
이 글에 대한 의견을 남겨주세요