Туториали

Build a Production KVM Virtual Lab with Bridged Networking and Cloud-Init

Изградете продукциска KVM виртуелна лабораторија со мостовно вмрежување и Cloud-Init

Корисната виртуелна лабораторија треба да се однесува како инфраструктура, а не како збирка од заменливи десктоп ВМ. Потребни ѝ се предвидливо адресирање, ограничена потрошувачка на ресурси, повторливо обезбедување, видливи неуспеси при подигнување и патека за опоравување што сè уште функционира откако хост-дискот ќе ве разочара.

Овој туторијал го гради тој систем на Ubuntu Server KVM хост. Резултатот е Ubuntu гостин со две vCPU јадра, именуван lab01, директно поврзан со физичката LAN мрежа преку br0, обезбеден со cloud-init, заштитен со ограничувања на ресурсите и покриен со чисти снимки и независно обновливи резервни копии.

Архитектура, претпоставки и компромиси

Примерната LAN мрежа е 192.168.50.0/24. Нејзиниот портал и DNS-разрешувач се 192.168.50.1; хостот користи 192.168.50.2; а lab01 користи 192.168.50.10. Пред да продолжите, потврдете дека овие адреси се надвор од вашиот DHCP опсег и дека не се користат.

Хостот има еден жичен интерфејс, enp3s0. Тој станува порта на br0, па адресата на хостот се преместува од физичкиот интерфејс на мостот. Направете ја таа промена од локална или вонпојасна конзола: дури и правилната мрежна миграција може да прекине SSH сесија.

Физичкиот мост им дава на гостите првокласно присуство на LAN мрежата и избегнува NAT или пренасочување порти на хостот. Цената е изложеност: горниот прекинувач гледа друга MAC-адреса, а гостинот мора да биде обезбеден како секој физички сервер. Обичните Wi-Fi клиентски интерфејси генерално не можат да обезбедат ваква форма на транспарентно мостување, бидејќи пристапните точки најчесто отфрлаат дополнителни изворни MAC-адреси.

Распоредот на складирањето користи непроменлива Ubuntu cloud слика како qcow2 основна датотека и тенок преклоп за ВМ. Ова заштедува простор, но основната слика станува дел од синџирот на активниот диск и никогаш не смее да се замени на самото место. Затоа, резервните копии ќе го израмнат синџирот во самостојни qcow2 слики.

Предуслови и структура на проектот

Користете Ubuntu Server хост со овозможена хардверска виртуелизација, жична Ethernet-врска и доволно резервиран капацитет за самиот хост. На административната сметка ѝ треба пристап до sudo и постоечки Ed25519 SSH јавен клуч.

Инсталирајте KVM, libvirt, алатки за слики, алатки за cloud-init и инсталерот за ВМ. Додавањето на сметката во libvirt и kvm стапува на сила по одјавување и повторно најавување.

sudo apt update
sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients \
  virtinst qemu-utils cloud-image-utils

sudo systemctl enable --now libvirtd
sudo usermod -aG libvirt,kvm "$USER"

sudo virt-host-validate qemu
sudo virsh --connect qemu:///system list --all

Лабораторијата ги чува трајните артефакти под една изречно наведена патека:

/srv/kvm-lab/
├── cloud-init/
│   ├── lab01-meta-data
│   ├── lab01-network-config
│   └── lab01-user-data
├── images/
│   ├── noble-server-cloudimg-amd64.img
│   └── lab01.qcow2
├── seeds/
│   └── lab01-seed.img
├── backups/
└── scripts/
    └── backup-lab01.sh

Создадете ја со дозволи што му овозможуваат на системскиот libvirt процес да поминува низ складирањето и да го чита:

sudo install -d -o "$USER" -g kvm -m 0770 \
  /srv/kvm-lab/{cloud-init,images,seeds,backups,scripts}

Безбедно изградете го мостот на хостот

Следната Netplan конфигурација го претпоставува renderer-от systemd-networkd на Ubuntu Server. Зачувајте ја како /etc/netplan/60-kvm-bridge.yaml, но прво усогласете ја секоја постоечка Netplan датотека што го конфигурира enp3s0. Интерфејсот не смее да добива спротивставени дефиниции од повеќе датотеки.

network:
  version: 2
  renderer: networkd
  ethernets:
    enp3s0:
      dhcp4: false
      dhcp6: false
  bridges:
    br0:
      interfaces:
        - enp3s0
      addresses:
        - 192.168.50.2/24
      routes:
        - to: default
          via: 192.168.50.1
      nameservers:
        addresses:
          - 192.168.50.1
      parameters:
        stp: false
        forward-delay: 0
      dhcp4: false
      dhcp6: false

Користете netplan try од конзолата на хостот. Таа автоматски се враќа назад, освен ако не ја потврдите работната конфигурација:

sudo chmod 0600 /etc/netplan/60-kvm-bridge.yaml
sudo netplan generate
sudo netplan try

ip -brief address show br0
ip route
bridge link show

Не ја конфигурирајте адресата на хостот и на enp3s0 и на br0. По миграцијата, физичкиот интерфејс не треба да има адреса од трето ниво.

Создадете ја основната слика и преклопот

Преземете ја тековната Ubuntu 24.04 LTS cloud слика и потврдете ја со контролната сума објавена до неа. Контролната сума преземена од истото HTTPS потекло открива оштетување; средините што бараат посилно потекло треба дополнително да ја потврдат потпишаната датотека со контролни суми на Ubuntu според нивната политика на доверба.

cd /srv/kvm-lab/images

curl --fail --location --remote-name \
  https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
curl --fail --location --remote-name \
  https://cloud-images.ubuntu.com/noble/current/SHA256SUMS

sha256sum --check --ignore-missing SHA256SUMS

qemu-img info noble-server-cloudimg-amd64.img
qemu-img create -f qcow2 -F qcow2 \
  -b /srv/kvm-lab/images/noble-server-cloudimg-amd64.img \
  /srv/kvm-lab/images/lab01.qcow2 40G

sudo chgrp kvm noble-server-cloudimg-amd64.img lab01.qcow2
sudo chmod 0660 noble-server-cloudimg-amd64.img lab01.qcow2

Големината од 40 GB е максималната големина видлива за гостинот, а не непосредна алокација. Следете го датотечниот систем на хостот, бидејќи qcow2 не може да заштити ВМ од исцрпен волумен на хостот.

Обезбедете го гостинот со cloud-init

Генерирајте кориснички податоци од постоечкиот Ed25519 јавен клуч на администраторот. Отстранувањето на незадолжителниот коментар на клучот избегнува незгодни YAML знаци, а го зачувува целосниот клуч.

cd /srv/kvm-lab

key_file="${HOME}/.ssh/id_ed25519.pub"
test -s "$key_file" || {
  echo "Missing SSH public key: $key_file" >&2
  exit 1
}
public_key=$(awk 'NR == 1 { print $1 " " $2 }' "$key_file")

cat > cloud-init/lab01-user-data <<EOF
#cloud-config
hostname: lab01
fqdn: lab01.lab.internal
manage_etc_hosts: true
ssh_pwauth: false
disable_root: true
users:
  - name: ops
    groups:
      - sudo
    shell: /bin/bash
    lock_passwd: true
    sudo:
      - ALL=(ALL) NOPASSWD:ALL
    ssh_authorized_keys:
      - ${public_key}
package_update: true
packages:
  - qemu-guest-agent
runcmd:
  - [systemctl, enable, --now, qemu-guest-agent]
EOF

cat > cloud-init/lab01-meta-data <<'EOF'
instance-id: lab01-001
local-hostname: lab01
EOF

cat > cloud-init/lab01-network-config <<'EOF'
version: 2
ethernets:
  ens3:
    match:
      driver: virtio_net
    set-name: ens3
    addresses:
      - 192.168.50.10/24
    routes:
      - to: default
        via: 192.168.50.1
    nameservers:
      addresses:
        - 192.168.50.1
EOF

cloud-localds \
  --network-config=cloud-init/lab01-network-config \
  seeds/lab01-seed.img \
  cloud-init/lab01-user-data \
  cloud-init/lab01-meta-data

sudo chgrp kvm seeds/lab01-seed.img
sudo chmod 0660 seeds/lab01-seed.img

Cloud-init вообичаено применува даден идентификатор на инстанца само еднаш. Ако намерно го изградите преклопот повторно, сменете го instance-id или исчистете ја состојбата на cloud-init во отфрлениот гостин пред да снимите шаблон.

Распоредете ја ВМ со изречни ограничувања

Создадете го доменот со фиксни две vCPU јадра и горна граница од 2 GB, virtio уреди, сериски пристап и канал за гостинскиот агент. CPU passthrough на хостот ја подобрува достапноста на функциите, но ја врзува ВМ поблиску до ова семејство на процесори, правејќи ја миграцијата на различен хардвер помалку сигурна.

sudo virt-install \
  --connect qemu:///system \
  --name lab01 \
  --import \
  --os-variant ubuntu24.04 \
  --memory 2048,maxmemory=2048 \
  --vcpus 2,maxvcpus=2 \
  --cpu host-passthrough \
  --disk path=/srv/kvm-lab/images/lab01.qcow2,format=qcow2,bus=virtio,cache=none,discard=unmap \
  --disk path=/srv/kvm-lab/seeds/lab01-seed.img,device=cdrom,readonly=on \
  --network bridge=br0,model=virtio,mac=52:54:00:50:00:10 \
  --channel unix,target_type=virtio,name=org.qemu.guest_agent.0 \
  --graphics none \
  --console pty,target_type=serial \
  --noautoconsole

sudo virsh autostart lab01

sudo virsh schedinfo lab01 \
  --set vcpu_period=100000 \
  --set vcpu_quota=100000 \
  --live --config

sudo virsh blkdeviotune lab01 vda \
  --total-bytes-sec 52428800 \
  --live --config

CPU квотата дозволува приближно едно CPU јадро на хостот со вкупно извршување низ двете виртуелни CPU јадра; работните оптоварувања сè уште можат да користат двонасочна конкурентност во кратки налети. Правилото за диск го ограничува комбинираниот проток на 50 MiB/s. Прилагодете ги двете ограничувања според измерената конкуренција на хостот, наместо да ги третирате овие вредности како универзални.

Тестирајте, зацврстете и набљудувајте

Почекајте cloud-init пред да го оценувате обезбедувањето. Од хостот, потврдете ја поврзаноста, агентот, ограничувањата на уредите и состојбата на автоматското стартување:

ping -c 3 192.168.50.10
ssh [email protected] 'cloud-init status --wait'
ssh [email protected] 'systemctl is-active qemu-guest-agent'

sudo virsh qemu-agent-command lab01 '{"execute":"guest-ping"}'
sudo virsh dominfo lab01
sudo virsh domblklist lab01 --details
sudo virsh schedinfo lab01
sudo virsh blkdeviotune lab01 vda
sudo virsh domstats lab01 --vcpu --balloon --block --interface

Во гостинот, овозможете заштитен ѕид со стандардно одбивање, задржувајќи SSH од административната LAN мрежа:

sudo apt install ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from 192.168.50.0/24 to any port 22 proto tcp
sudo ufw enable
sudo ufw status verbose

Заштитниот ѕид на хостот не го штити автоматски премостениот сообраќај на гостинот; однесувањето зависи од bridge netfilter конфигурацијата на хостот. Применете политика во гостинот и, каде што е соодветно, на физичкиот прекинувач или горниот заштитен ѕид. Задржете го управувањето со libvirt локално, освен ако автентицираната далечинска администрација не е изречно барање.

Користете journalctl -u libvirtd за неуспеси на страната на хостот, virsh console lab01 за рано подигнување и /var/log/cloud-init-output.log во гостинот за грешки при обезбедувањето. Следете ја алокацијата на диск со qemu-img info само додека сликата е неактивна; за активна ВМ користете libvirt block статистика.

Откако обезбедувањето ќе успее, откачете го seed-от од трајната конфигурација за идните резервни копии да не зависат од него:

sudo virsh detach-disk lab01 \
  /srv/kvm-lab/seeds/lab01-seed.img \
  --config

Користете снимки како точки за враќање

Снимките се практични пред ризично одржување, но не се резервни копии: ги делат истиот хост, складирање и домен на откажување. За чиста внатрешна снимка, запрете го гостинот и почекајте libvirt да пријави shut off.

sudo virsh shutdown lab01
until test "$(sudo virsh domstate lab01)" = "shut off"; do
  sleep 2
done

sudo virsh snapshot-create-as lab01 pre-upgrade \
  --description "Clean checkpoint before package upgrade" \
  --atomic
sudo virsh snapshot-list lab01
sudo virsh start lab01

За да се вратите назад, чисто запрете го гостинот, извршете sudo virsh snapshot-revert lab01 pre-upgrade и повторно стартувајте го. Враќањето ги отфрла сите промени направени по контролната точка. Намерно избришете ги застарените снимки, бидејќи насобраните внатрешни снимки ги зголемуваат qcow2 метаподатоците и оперативната сложеност.

Создадете обновлива офлајн резервна копија

Зачувајте ја следната скрипта како /srv/kvm-lab/scripts/backup-lab01.sh и направете ја извршна. Таа бара чисто исклучување, чека најмногу две минути, го израмнува основниот синџир, го зачувува XML-от на доменот, го пресметува контролниот збир на резултатот и повторно стартува само ВМ што првично работела. Неуспешната конверзија не остава важечки манифест со контролни суми, па опоравувањето ќе ја отфрли нецелосната директорија.

#!/usr/bin/env bash
set -Eeuo pipefail

vm="lab01"
disk="/srv/kvm-lab/images/lab01.qcow2"
stamp=$(date -u +%Y%m%dT%H%M%SZ)
dest="/srv/kvm-lab/backups/${vm}-${stamp}"
restart=0

cleanup() {
  if (( restart )); then
    state=$(sudo virsh domstate "$vm" 2>/dev/null || true)
    if [[ "$state" == "shut off" ]]; then
      sudo virsh start "$vm"
    fi
  fi
}
trap cleanup EXIT

state=$(sudo virsh domstate "$vm")
if [[ "$state" == "running" ]]; then
  restart=1
  sudo virsh shutdown "$vm"

  for ((attempt = 1; attempt <= 60; attempt++)); do
    [[ "$(sudo virsh domstate "$vm")" == "shut off" ]] && break
    sleep 2
  done
fi

[[ "$(sudo virsh domstate "$vm")" == "shut off" ]] || {
  echo "Guest did not shut down; backup aborted" >&2
  exit 1
}

sudo install -d -o root -g root -m 0700 "$dest"
sudo virsh dumpxml "$vm" | sudo tee "$dest/lab01.xml" >/dev/null
sudo qemu-img convert -p -O qcow2 \
  -o compat=1.1,lazy_refcounts=on \
  "$disk" "$dest/lab01.qcow2"
sudo qemu-img check "$dest/lab01.qcow2"

cd "$dest"
sudo sha256sum lab01.xml lab01.qcow2 |
  sudo tee SHA256SUMS >/dev/null

Извршете ја со chmod 0750 /srv/kvm-lab/scripts/backup-lab01.sh, по што следи патеката до скриптата. Копирајте ги завршените директории со резервни копии на одделно складирање со независно задржување. Исклучувањето ја прави состојбата на датотечниот систем чиста; базите на податоци и надворешните услуги сепак може да бараат процедури за резервна копија што се свесни за апликацијата.

Докажете го опоравувањето пред да му верувате

Изберете завршена резервна копија, потврдете го нејзиниот манифест и прво извршете qemu-img check и проверете ги директориумите за складирање, вратете го дискот на истата апсолутна патека, па потоа дефинирајте го зачуваниот XML. Не ја подигнувајте обновената ВМ паралелно со оригиналната, бидејќи делат IP-адреса, MAC-адреса, идентитет на машината и SSH host клучеви.

Конечна листа за проверка

  • Хостот ја поседува 192.168.50.2 на br0, а не на enp3s0.
  • lab01 стигнува до порталот и е достапен само преку наменетите правила на заштитниот ѕид.
  • Cloud-init заврши без грешки, а гостинскиот агент одговара преку libvirt.
  • Ограничувањата за меморија, vCPU, CPU квота и пропусност на блок-уредот преживуваат рестартирање.
  • ВМ се стартува автоматски, додека хостот задржува доволно неангажирана меморија и складирање.
  • Може да се создаде, прикаже, врати и повлече чиста снимка.
  • Најновата резервна копија ги поминува и SHA-256 потврдата и qemu-img check.
  • Опоравувањето е извежбано без оригиналниот и обновениот идентитет да се пуштат на LAN мрежата истовремено.

Лабораторија од продукциска класа не се дефинира со тоа колку брзо се подигнува нејзината прва ВМ. Таа се дефинира со тоа колку предвидливо се однесува десеттата ВМ, колку јасно се прикажуваат неуспесите и колку смирено системот може да се обнови. Мостувањето, cloud-init, квотите, снимките и потврдените резервни копии го претвораат KVM од практичен хипервизор во инфраструктура за која можете да расудувате.

Портрет на автор на блогот

Mihajlo

Јас сум Михајло - развивач поттикнат од љубопитност, дисциплина и постојаната желба да создадам нешто значајно. Споделувам увиди, упатства и бесплатни услуги за да им помогнам на другите да ја поедностават својата работа и да растат во постојано развивачкиот свет на софтверот и вештачката интелигенција.