게스트를 클러스터망(192.168.10.0/24) 에 직접 올리려면 물리 NIC 을 브리지로 전환해야 한다. 그런데 그 노드는 단일 NIC 만 살아 있고 Harbor 가 그 NIC 으로 ECS 노드들에게 이미지를 내주고 있었으며 OOB 콘솔도 없었다. 브리지 전환 중 SSH 가 끊기면 복구가 어렵다. 그래서 별도 대역(192.168.50.0/24) 의 routed 네트워크를 만들고 호스트가 라우터 역할을 하도록 했다.
사후 평가는 분명하다. Harbor 를 15~30분 내릴 수 있었다면 브리지가 훨씬 깔끔했다. routed 를 택한 대가로 MASQUERADE 룰 세 개, 피어 노드 네 대에 정적 라우트, DNS · 미러마다 추가 룰이 따라붙었다. 브리지였다면 전부 0 이다.
| 모드 | 게스트 위치 | 외부 → 게스트 | 쓰임 |
|---|---|---|---|
| NAT | 호스트 뒤에 숨는다 | 포트 포워딩 없이는 불가 | 노트북 데스크톱 가상화 |
| Routed | 별도 서브넷, 호스트가 라우터 | 피어에 라우트를 넣으면 가능 | 통제된 네트워크 |
| Bridge | 호스트와 같은 L2 | 자유롭게 가능 | 운영 서버 |
egrep -c '(vmx|svm)' /proc/cpuinfo
virt-host-validate qemu
dnf install -y qemu-kvm libvirt libvirt-client virt-install virt-manager \
bridge-utils libguestfs-tools cockpit-machines
systemctl enable --now libvirtd
mkdir -p /var/lib/libvirt/images
virsh pool-define-as default dir --target /var/lib/libvirt/images
virsh pool-autostart default
virsh pool-start default
<network>
<name>ipanet</name>
<forward mode='route'/>
<bridge name='virbr1' stp='on' delay='0'/>
<ip address='192.168.50.1' netmask='255.255.255.0'>
<dhcp><range start='192.168.50.100' end='192.168.50.200'/></dhcp>
</ip>
</network>
virsh net-define /tmp/ipanet.xml
virsh net-autostart ipanet
virsh net-start ipanet
virsh net-list --all
ip -br a | grep virbr1
virbr1 이 DOWN · NO-CARRIER 로 보이는 것은 정상이다. libvirt 브리지는 연결된 게스트가 0 개일 때 DOWN 으로 표시되고, 게스트가 붙어 vnet0 이 생기는 순간 UP 으로 바뀐다. 케이블이 안 꽂힌 스위치 포트와 같다. ip route 에 linkdown 이 붙어 보이는 것도 같은 이유다.
Docker 가 같이 도는 호스트라면 FORWARD 체인 순서를 확인해 둔다. libvirt 룰이 Docker 룰보다 앞에 오면 게스트 트래픽이 Docker 룰 평가 전에 처리되어 이상적이다.
iptables -L FORWARD -n -v | head -20
docker ps --format 'table {{.Names}}\t{{.Status}}'
routed 모드에서는 게스트가 나가는 길은 호스트가 알지만 상대가 돌아오는 길을 모른다. 두 가지로 푼다.
관리 권한이 있는 클러스터 노드에는 정적 라우트를 넣는다. 이 경로는 SNAT 를 걸지 않아야 게스트의 실제 IP 가 보이고 Kerberos 가 정상 동작한다.
nmcli con mod ens6f0np0 +ipv4.routes '192.168.50.0/24 192.168.10.20'
nmcli con up ens6f0np0
ip route | grep 192.168.50
라우트를 넣을 수 없는 상대(사내 DNS, yum 미러) 에는 그 목적지로 갈 때만 호스트 IP 로 위장한다.
iptables -t nat -A POSTROUTING -s 192.168.50.0/24 -d 192.168.10.30 -j MASQUERADE # 미러
iptables -t nat -A POSTROUTING -s 192.168.50.0/24 -d 192.168.10.18 -j MASQUERADE # DNS
NIC 연결 이름은 노드마다 다를 수 있으므로(eno2 · enp5s0f1 · ens6f0np0) 일괄 스크립트를 돌리기 전에 각 노드에서 확인한다.
virt-install --name freeipa --vcpus 2 --memory 4096 \
--disk size=20,bus=virtio,format=qcow2 \
--network network=ipanet,model=virtio \
--location http://192.168.10.30/rhel8.10/ \
--os-variant rhel8.10 --graphics none \
--extra-args 'console=ttyS0,115200n8 inst.text'
--disk 에 path= 를 생략하면 default 풀 아래 <게스트명>.qcow2 로 만들어진다. size=20 은 최대치이고 qcow2 는 sparse 라 실제로는 쓴 만큼만 차지한다.
--location 은 DNF 저장소가 아니라 설치 트리여야 한다. .treeinfo 와 images/pxeboot/vmlinuz · initrd.img 가 있어야 anaconda 가 부팅한다. 사내 미러는 BaseOS/repodata/ 만 있는 경우가 흔하므로 먼저 확인한다.
curl -sI http://192.168.10.30/rhel8.10/.treeinfo | head -1