virsh + cloud-init VM 생성 가이드
작성일: 2026-03-05 환경: Ubuntu 24.04 호스트, libvirt/KVM, cloud-init
사전 조건
- 호스트에
br-host브릿지가 구성되어 있어야 함 (203.0.113.0/24 대역) - 원본 클라우드 이미지:
/var/lib/libvirt/images/noble-server-cloudimg-amd64.img - cloud-init 설정 디렉토리:
/var/lib/libvirt/cloud-init/
디렉토리 구조
/var/lib/libvirt/
├── images/
│ ├── noble-server-cloudimg-amd64.img ← 원본 (수정 금지)
│ ├── k8s-cp1.qcow2 ← VM별 디스크
│ └── ...
└── cloud-init/
├── k8s-cp1-user-data.yaml
├── k8s-cp1-network-config.yaml
├── k8s-cp1-cidata.iso
└── ...
생성 절차
1. 변수 설정
NAME=k8s-worker1 # VM 이름
IP=203.0.113.71 # 할당할 IP
MEM=2048 # 메모리 (MB)
VCPU=2 # vCPU 수
DISK_SIZE=20G # 디스크 크기
BASE_IMG=/var/lib/libvirt/images/noble-server-cloudimg-amd64.img
IMG=/var/lib/libvirt/images/${NAME}.qcow2
CLOUD_INIT_DIR=/var/lib/libvirt/cloud-init
2. user-data.yaml 작성
sudo tee ${CLOUD_INIT_DIR}/${NAME}-user-data.yaml > /dev/null <<'EOF'
#cloud-config
hostname: k8s-worker1
manage_etc_hosts: true
users:
- name: kube
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
ssh_authorized_keys:
- ssh-ed25519 AAAA...REPLACE-WITH-YOUR-PUBLIC-KEY... user@host
package_update: true
packages:
- curl
- wget
- vim
- net-tools
- jq
- tree
- tcpdump
- socat
- conntrack
- ipset
EOF
주의: 첫 줄의
#cloud-config은 반드시 포함해야 한다. 없으면 cloud-init이 동작하지 않는다.
3. network-config.yaml 작성
sudo tee ${CLOUD_INIT_DIR}/${NAME}-network-config.yaml > /dev/null <<EOF
version: 2
ethernets:
enp1s0:
dhcp4: false
addresses:
- ${IP}/24
gateway4: 203.0.113.1
nameservers:
addresses:
- 1.1.1.1
- 8.8.8.8
EOF
주의: IP 대역은 연결할 브릿지 대역(br-host = 203.0.113.0/24)과 일치해야 한다.
4. cloud-init ISO 생성
sudo cloud-localds "${CLOUD_INIT_DIR}/${NAME}-cidata.iso" \
"${CLOUD_INIT_DIR}/${NAME}-user-data.yaml" \
--network-config "${CLOUD_INIT_DIR}/${NAME}-network-config.yaml"
5. 디스크 이미지 준비
sudo cp "$BASE_IMG" "$IMG"
sudo qemu-img resize "$IMG" "$DISK_SIZE"
주의: 반드시 원본 클라우드 이미지(
noble-server-cloudimg-amd64.img)에서 복사해야 한다. 이미 cloud-init이 실행된 이미지를 복사하면 새 설정이 적용되지 않는다.
6. VM 생성
sudo virt-install \
--name "$NAME" \
--memory "$MEM" \
--vcpus "$VCPU" \
--disk path="$IMG",format=qcow2 \
--disk path="${CLOUD_INIT_DIR}/${NAME}-cidata.iso",device=cdrom \
--os-variant ubuntu24.04 \
--network bridge=br-host,model=virtio \
--graphics none \
--console pty,target_type=serial \
--noautoconsole \
--import
7. 접속 확인
# 30초 정도 대기 후
ping -c 3 ${IP}
ssh kube@${IP}
전체 스크립트 (복사-붙여넣기용)
#!/bin/bash
set -e
NAME=k8s-worker1
IP=203.0.113.71
MEM=2048
VCPU=2
DISK_SIZE=20G
BASE_IMG=/var/lib/libvirt/images/noble-server-cloudimg-amd64.img
IMG=/var/lib/libvirt/images/${NAME}.qcow2
CLOUD_INIT_DIR=/var/lib/libvirt/cloud-init
# user-data
sudo tee ${CLOUD_INIT_DIR}/${NAME}-user-data.yaml > /dev/null <<EOF
#cloud-config
hostname: ${NAME}
manage_etc_hosts: true
users:
- name: kube
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
ssh_authorized_keys:
- ssh-ed25519 AAAA...REPLACE-WITH-YOUR-PUBLIC-KEY... user@host
package_update: true
packages:
- curl
- wget
- vim
- net-tools
- jq
- tree
- tcpdump
- socat
- conntrack
- ipset
EOF
# network-config
sudo tee ${CLOUD_INIT_DIR}/${NAME}-network-config.yaml > /dev/null <<EOF
version: 2
ethernets:
enp1s0:
dhcp4: false
addresses:
- ${IP}/24
gateway4: 203.0.113.1
nameservers:
addresses:
- 1.1.1.1
- 8.8.8.8
EOF
# cloud-init ISO
sudo cloud-localds "${CLOUD_INIT_DIR}/${NAME}-cidata.iso" \
"${CLOUD_INIT_DIR}/${NAME}-user-data.yaml" \
--network-config "${CLOUD_INIT_DIR}/${NAME}-network-config.yaml"
# 디스크
sudo cp "$BASE_IMG" "$IMG"
sudo qemu-img resize "$IMG" "$DISK_SIZE"
# VM 생성
sudo virt-install \
--name "$NAME" \
--memory "$MEM" \
--vcpus "$VCPU" \
--disk path="$IMG",format=qcow2 \
--disk path="${CLOUD_INIT_DIR}/${NAME}-cidata.iso",device=cdrom \
--os-variant ubuntu24.04 \
--network bridge=br-host,model=virtio \
--graphics none \
--console pty,target_type=serial \
--noautoconsole \
--import
echo "VM '${NAME}' 생성 완료. 30초 후 ssh kube@${IP} 로 접속 가능."
VM 삭제
sudo virsh destroy $NAME # 강제 종료
sudo virsh undefine $NAME # 정의 제거
sudo rm /var/lib/libvirt/images/${NAME}.qcow2
sudo rm /var/lib/libvirt/cloud-init/${NAME}-*
현재 IP 할당 현황
| VM 이름 | IP | 용도 |
|---|---|---|
| 호스트 (br-host) | 203.0.113.2 | KVM 호스트 |
| k8s-cp1 | 203.0.113.70 | K8s Control Plane |
주의사항 체크리스트
- [ ] user-data.yaml 첫 줄이
#cloud-config인가? - [ ] network-config의 IP가 br-host 대역(203.0.113.0/24)인가?
- [ ] 디스크를 원본 클라우드 이미지에서 복사했는가?
- [ ]
--network bridge=br-host을 사용했는가? (virbr0 아님) - [ ] IP가 다른 VM과 충돌하지 않는가?