Przejdź do treści
Administracja·~18 min czytania

virsh w praktyce: zarządzanie maszynami wirtualnymi z terminala

Opisujemy listowanie maszyn, odczyt adresów, dostęp przez konsolę i pulpit graficzny, zasilanie, dyski i sieci oraz kilka pułapek, na których łatwo stracić wieczór.

Autor: Zespół LinuxLab

W LinuxLab maszyny wirtualne obsługujemy z terminala, nie z okienka. Powód jest prozaiczny: na serwerze nie ma pulpitu, a przez SSH i tak trzeba umieć sprawdzić, czy maszyna żyje, jaki ma adres i dlaczego nie wstaje. To wszystko robi jedno narzędzie.

W tym artykule przechodzimy przez codzienną pracę z virsh: od wypisania maszyn, przez odczyt ich adresów i stanu, dostęp przez konsolę szeregową oraz pulpit graficzny, po zasilanie, dyski, sieci i migawki. Wszystkie polecenia były uruchomione na działającym hoście, a nie przepisane z dokumentacji. Nazwy maszyn w przykładach są z naszego labu i możesz je podmienić na swoje.

Zakres pomiaru: tabele i lista narzędzi to snapshot poprzedniego hosta labu z 10 września 2026 (Ubuntu 24.04, libvirt 10.0.0), a nie bieżący inwentarz. Przed użyciem sprawdź własny host przez virsh version --daemon, virsh list --all i virsh pool-list --all.

Spis treści

  1. Czym jest virsh i jak współpracuje z libvirt oraz QEMU/KVM
  2. Połączenie qemu:///system i qemu:///session: dwa osobne zestawy maszyn
  3. Wyświetlanie listy maszyn: virsh list i virsh domstate
  4. Parametry maszyny, jej dyski i interfejsy sieciowe
  5. Jak ustalić adres IP maszyny wirtualnej poleceniem virsh domifaddr
  6. Konsola szeregowa maszyny: virsh console i wyjście z konsoli
  7. Pulpit graficzny maszyny przez virt-viewer
  8. Włączanie i wyłączanie maszyny w virsh: shutdown, destroy i pozostałe polecenia
  9. Podgląd i zmiana konfiguracji maszyny: virsh dumpxml i virsh edit
  10. Dyski, pule i wolumeny w libvirt
  11. Sieci wirtualne libvirt i dzierżawy DHCP
  12. Migawki maszyn w libvirt i ich ograniczenia
  13. Pułapki virsh, które kosztują czas
  14. Ściągawka najczęściej używanych poleceń virsh

Czym jest virsh i jak współpracuje z libvirt oraz QEMU/KVM

virsh to narzędzie wiersza poleceń do libvirt - warstwy, która ujednolica zarządzanie maszynami wirtualnymi. Sama libvirt nie uruchamia maszyn; robi to hipernadzorca pod spodem, najczęściej QEMU wraz z KVM, czyli wirtualizacją wbudowaną w jądro Linuksa. Libvirt daje temu wszystkiemu jeden zestaw pojęć i jeden interfejs.

Praktyczna konsekwencja jest taka, że virsh, graficzny virt-manager i narzędzia w rodzaju virt-install to różne twarze tego samego stanu. Maszyna utworzona w okienku będzie widoczna w terminalu i odwrotnie. Nie ma tu dwóch osobnych rejestrów, które trzeba by synchronizować.

Wersje użyte w tym artykule to libvirt 10.0.0 na Ubuntu 24.04. Pełny opis poleceń zawiera podręcznik virsh, a model obiektów - dokumentacja libvirt. Obie strony opisują najnowsze wydanie, 24 września 2026 był to libvirt 12.7.0. Polecenia są stabilne od lat, ale przy starszych wydaniach część flag może nie istnieć.

Do pracy bez uprawnień administratora konto musi należeć do grupy libvirt:

id -nG | tr ' ' '\n' | grep libvirt

Jeżeli nic nie wypisze, dodaj się do grupy przez sudo usermod -aG libvirt "$USER" i zaloguj ponownie. Bez tego każde polecenie będzie kończyć się odmową dostępu albo - co bardziej mylące - pokaże pustą listę maszyn, o czym za chwilę.

Połączenie qemu:///system i qemu:///session: dwa osobne zestawy maszyn

To jest pierwsza rzecz, która potrafi zmylić, i warto ją znać, zanim zacznie się szukać zaginionych maszyn. Libvirt ma dwa niezależne zestawy maszyn, opisane adresem połączenia:

AdresCzyje maszynyTypowe użycie
qemu:///systemmaszyny hosta, prowadzone przez usługę systemowąserwery, laby, wszystko co ma działać niezależnie od zalogowanego użytkownika
qemu:///sessionmaszyny konkretnego użytkownikamaszyny doraźne, bez uprawnień do zasobów hosta

Na naszym hoście widać to wprost - w jednym świecie jest dwanaście maszyn, w drugim ani jednej:

$ virsh -c qemu:///system  list --all --name | grep -c .
12
$ virsh -c qemu:///session list --all --name | grep -c .
0

Stąd bierze się klasyczne „przecież ta maszyna istniała”. Jeżeli virsh list --all zwraca pustkę, a jesteś pewien, że maszyny są, prawie zawsze pytasz nie ten świat. Które połączenie jest domyślne, sprawdzisz jednym poleceniem:

virsh uri

Domyślne można ustawić na stałe zmienną środowiskową LIBVIRT_DEFAULT_URI, a doraźnie przełączyć flagą -c. Ta sama flaga obsługuje host zdalny przez SSH, co bywa wygodniejsze od logowania się na serwer:

virsh -c qemu+ssh://uzytkownik@serwer/system list --all

Wyświetlanie listy maszyn: virsh list i virsh domstate

To najczęściej używane polecenie w całym zestawie. Bez żadnej flagi pokazuje tylko maszyny działające:

$ virsh list
 Id   Name          State
 -----------------------------
 7    vm-code-ai2   running
 9    awx           running
 10   ubu2204       running
 12   deb13         running

To jest źródło drugiego częstego nieporozumienia: maszyna wyłączona po prostu tu nie występuje, co łatwo wziąć za jej zniknięcie. Pełny obraz daje dopiero:

virsh list --all

Kolumna Id ma znaczenie tylko dla maszyn działających i zmienia się przy każdym uruchomieniu - wyłączone mają w tym miejscu myślnik. Do wszystkiego, co ma być trwałe, używaj nazwy albo identyfikatora UUID, nigdy numeru.

Do skryptów przydają się dwie flagi. Filtr stanu zawęża listę, a --name zwraca same nazwy, bez ramek i nagłówków:

# same działające, tylko nazwy - gotowe do pętli
virsh list --state-running --name

# same wyłączone
virsh list --state-shutoff --name

# ile maszyn w ogóle
virsh list --all --name | grep -c .

Stan pojedynczej maszyny można sprawdzić bez przeszukiwania całej listy:

$ virsh domstate deb13
running

Parametry maszyny, jej dyski i interfejsy sieciowe

Podsumowanie parametrów jednej maszyny pokazuje virsh dominfo:

$ virsh dominfo deb13
Id:             12
Name:           deb13
UUID:           16d6bd44-dfc7-4555-9f82-a416e4fd572e
State:          running
CPU(s):         2
Max memory:     2097152 KiB
Used memory:    2097152 KiB
Persistent:     yes
Autostart:      disable
Managed save:   no
Security model: apparmor

Cztery pola z tego wydruku warto umieć czytać:

  • Persistent - czy maszyna ma zapisaną definicję. yes znaczy, że przetrwa restart hosta jako definicja. no oznacza maszynę ulotną, która po zatrzymaniu zniknie bez śladu.
  • Autostart - czy wstaje razem z hostem. Osobna rzecz od Persistent: definicja może istnieć, a maszyna i tak nie ruszy sama.
  • Managed save - czy leży zapisany stan pamięci, do którego maszyna wróci przy starcie. Jeżeli maszyna „startuje w dziwnym stanie”, sprawdź to pole najpierw.
  • Used memory - pamięć przydzielona, w kibibajtach. Tu 2097152 KiB, czyli 2 GiB.

Dyski i interfejsy sieciowe maszyny pokazują osobne polecenia:

$ virsh domblklist deb13
 Target   Source
 ------------------------------------------------
 vda      /var/lib/libvirt/images/deb13.img
 vdb      /var/lib/libvirt/images/deb13-seed.img

$ virsh domiflist deb13
 Interface   Type      Source   Model    MAC
 ----------------------------------------------------------
 vnet9       network   lab      virtio   52:54:00:3a:a3:8a

Kolumna Source przy dyskach to ścieżka do pliku - stąd wiadomo, co sprawdzić przez qemu-img info. Przy interfejsach Source mówi, do której sieci maszyna jest podpięta, co bywa pierwszym tropem, gdy „nie ma łączności”.

Bieżące zużycie zasobów można odczytać bez logowania się do maszyny:

virsh domstats deb13 --balloon --vcpu

Jak ustalić adres IP maszyny wirtualnej poleceniem virsh domifaddr

Najczęstsze pytanie w codziennej pracy brzmi: pod jaki adres się połączyć. Są dwa sposoby i różnią się wiarygodnością.

# 1. Odczyt z dzierżaw DHCP libvirt - tylko dla sieci, którą libvirt tak obsługuje
virsh domifaddr deb13 --source lease

# 2. Zapytanie systemu w środku maszyny - wiarygodne, wymaga agenta
virsh domifaddr deb13 --source agent

Wariant lease pokazuje, co przydzielił serwer DHCP (od angielskiego Dynamic Host Configuration Protocol, czyli protokół automatycznego przydziału adresów) zarządzany przez libvirt; nie działa dla każdej sieci ani adresu statycznego i jego wynik może być nieaktualny. Wariant agent pyta system operacyjny gościa o raportowane interfejsy, pod warunkiem że działa qemu-guest-agent. Wybierz adres właściwego interfejsu i potwierdź go nowym połączeniem SSH - agent zwraca także pętlę zwrotną i sieci CNI.

Odczyt przez agenta zwraca wszystkie adresy, także te, które Cię nie interesują:

$ virsh domifaddr awx --source agent
 ... 127.0.0.1/8
 ... 192.168.122.103/24
 ... 10.42.0.1/24

Tu widać pętlę zwrotną, adres z sieci hosta oraz adres wewnętrzny klastra kontenerów działającego w tej maszynie. Ten, którego szukasz, to zwykle adres z zakresu sieci libvirt - w domyślnej instalacji 192.168.122.0/24.

Jeżeli agenta nie ma i dzierżawy też nie, zostaje jeszcze jedna droga: adres sprzętowy interfejsu z virsh domiflist i przeszukanie tablicy sąsiedztwa na hoście.

Konsola szeregowa maszyny: virsh console i wyjście z konsoli

Konsola szeregowa jest tym, po co sięga się, gdy maszyna nie odpowiada po sieci - bo pokazuje to, co dzieje się od momentu startu, razem z komunikatami firmware, programu rozruchowego i jądra. Jest więc jedynym miejscem, gdzie zobaczysz, dlaczego maszyna nie wstała.

virsh console deb13

Zapamiętaj, jak wyjść, zanim wejdziesz. Konsola przejmuje terminal i Ctrl+C trafia do maszyny, a nie do virsh. Z konsoli wychodzi się sekwencją Ctrl+], domyślnie tą samą co w telnecie. Można ją zmienić flagą -e, na przykład virsh -e ^X console deb13. Bez tej wiedzy jedynym wyjściem bywa zamknięcie okna terminala.

Warto znać dwie flagi. --force odłącza sesję, która już zajmuje konsolę - przydatne, gdy ktoś zostawił otwarte połączenie albo poprzednia sesja się urwała. --safe łączy się tylko wtedy, gdy hipernadzorca gwarantuje wyłączność.

Jeżeli konsola nie pokazuje nic poza pustym ekranem, przyczyną zwykle nie jest sama libvirt, tylko brak konfiguracji po stronie gościa: system musi kierować komunikaty na port szeregowy. W definicji maszyny odpowiada za to element konsoli, który sprawdzisz przez virsh dumpxml.

Pulpit graficzny maszyny przez virt-viewer

Gdy maszyna ma pulpit, terminal nie wystarczy. Adres wyświetlacza podaje:

$ virsh domdisplay deb13
spice://127.0.0.1:5904

Zwróć uwagę na adres: 127.0.0.1. Wyświetlacz jest domyślnie wystawiony tylko lokalnie na hoście, i tak ma być - protokół pulpitu bez dodatkowych zabezpieczeń nie powinien słuchać na wszystkich adresach.

Do połączenia służy virt-viewer. Uruchomiony z nazwą maszyny sam odpyta libvirt o adres:

# na hoście z pulpitem graficznym
virt-viewer deb13

# poczekaj aż maszyna wstanie i połącz się automatycznie
virt-viewer --wait --reconnect deb13

Z innego komputera nie podawaj adresu wyświetlacza wprost, tylko każ narzędziu połączyć się przez SSH - wtedy ruch idzie tunelem, a nie otwartym portem:

virt-viewer -c qemu+ssh://uzytkownik@serwer/system deb13

Maszyna wyłączona nie ma wyświetlacza. Sprawdziliśmy to wprost: dla działających maszyn virsh domdisplay zwraca adres, a dla wyłączonej nie zwraca nic, choć jej definicja zawiera kompletną konfigurację grafiki. Pusty wynik nie znaczy więc „ta maszyna nie ma pulpitu”, tylko „ta maszyna nie działa”. Kolejność jest jedna: najpierw virsh start, potem domdisplay.

Jeżeli wolisz jedno okno na wszystkie maszyny, tym samym stanem zarządza graficzny virt-manager. Nie jest alternatywą dla virsh, tylko inną twarzą tego samego - maszyny widać w obu miejscach.

Włączanie i wyłączanie maszyny w virsh: shutdown, destroy i pozostałe polecenia

Tu jedno nieporozumienie potrafi kosztować dane, więc zacznijmy od tabeli. Nazwy poleceń są mylące i nie odpowiadają temu, co sugerują po polsku:

PolecenieCo naprawdę robiOdpowiednik w świecie fizycznym
virsh starturuchamia maszynę z zapisanej definicjinaciśnięcie przycisku zasilania
virsh shutdownprosi system gościa o zamknięciewybranie „zamknij system” w menu
virsh rebootprosi system gościa o ponowne uruchomieniewybranie „uruchom ponownie”
virsh destroyodcina zasilanie, nie kasuje niczegowyrwanie kabla z gniazdka
virsh resettwardy reset bez pytania gościaprzycisk reset w obudowie
virsh undefinekasuje definicję maszynywyrzucenie komputera

Nazwa destroy jest tu najgorsza z możliwych, bo brzmi jak usunięcie. Opis w samej pomocy narzędzia mówi wprost: „Forcefully stop a given domain, but leave its resources intact” - czyli zatrzymaj siłowo, ale zostaw zasoby nietknięte. Dysk, definicja i konfiguracja zostają. To jest odpowiednik wyrwania kabla, ze wszystkimi konsekwencjami dla systemu plików gościa, ale bez utraty maszyny.

Maszynę usuwa dopiero undefine, i to on ma flagi, przy których trzeba uważać:

# kasuje samą definicję, dyski ZOSTAJĄ na miejscu
virsh undefine deb13

# kasuje definicję RAZEM ze wszystkimi dyskami - nie ma odwrotu
virsh undefine deb13 --remove-all-storage

Praktyczna kolejność przy wyłączaniu wygląda tak: najpierw shutdown, potem obserwacja przez virsh domstate, a dopiero gdy maszyna nie reaguje przez kilka minut - destroy. Sięganie od razu po to drugie to zwykłe wyrywanie kabla.

Czy maszyna ma wstawać razem z hostem, ustawia się osobno:

virsh autostart deb13              # włącz
virsh autostart --disable deb13    # wyłącz

Jest też pośredni tryb, przydatny gdy chcesz zwolnić pamięć, ale wrócić dokładnie do tego, co było. managedsave zapisuje stan pamięci na dysk i zatrzymuje maszynę; najbliższy start odtworzy ją w tym samym miejscu, razem z otwartymi programami:

virsh managedsave deb13
virsh start deb13                  # wraca do zapisanego stanu

To wyjaśnia pole Managed save z dominfo. Jeżeli maszyna uparcie wraca do stanu, którego się nie spodziewasz, prawie na pewno leży taki zapis - usuwa go virsh managedsave-remove.

Do wstrzymania bez zapisu na dysk służą suspend i resume - maszyna zostaje w pamięci, przestaje dostawać czas procesora.

Podgląd i zmiana konfiguracji maszyny: virsh dumpxml i virsh edit

Cała konfiguracja maszyny to dokument XML. Podgląd:

virsh dumpxml deb13

# tylko to, co Cię interesuje
virsh dumpxml deb13 | grep -E '<memory|<vcpu|<disk|<interface'

Do edycji nie otwieraj pliku w katalogu libvirt bezpośrednio - zmiany wprowadzone poza narzędziem bywają nadpisywane, a błąd składni potrafi uszkodzić definicję. Właściwa droga sprawdza XML przed zapisem:

virsh edit deb13

Otwiera edytor ze zmiennej $EDITOR, a po zamknięciu waliduje dokument i odrzuca zmianę, jeżeli jest niepoprawna. Większość zmian - pamięć, liczba rdzeni, dyski - zadziała dopiero po pełnym zatrzymaniu i uruchomieniu maszyny. Samo reboot z wnętrza gościa nie wystarczy, bo proces maszyny nadal działa ze starą konfiguracją.

Do zmian skryptowych, bez otwierania edytora, służy virt-xml:

virt-xml deb13 --edit --memory 4096
virt-xml deb13 --edit --vcpus 4

Dyski, pule i wolumeny w libvirt

Libvirt grupuje pliki dysków w pule, czyli katalogi lub inne miejsca składowania. Przegląd:

$ virsh pool-list --all
 Name     State    Autostart
 ------------------------------
 golden   active   yes
 images   active   yes

$ virsh vol-list images
$ virsh vol-info --pool images deb13.img

Powiększenie dysku działającej maszyny robi się w dwóch krokach, o czym łatwo zapomnieć. Najpierw rośnie plik po stronie hosta:

virsh vol-resize --pool images deb13.img 30G

Ale system w środku nadal widzi starą wielkość, bo tablica partycji i system plików nic o zmianie nie wiedzą. Drugi krok wykonuje się w gościu, narzędziami w rodzaju growpart i resize2fs. Sam vol-resize niczego w maszynie nie powiększy.

Obowiązuje przy tym zasada „tylko w górę”. Zmniejszenie dysku, na którym jest system plików z danymi, to prosta droga do jego uszkodzenia - libvirt pozwoli, system plików nie wybaczy.

Sieci wirtualne libvirt i dzierżawy DHCP

$ virsh net-list --all
 Name      State    Autostart   Persistent
 --------------------------------------------
 default   active   yes         yes
 lab       active   yes         yes

$ virsh net-dhcp-leases default
$ virsh net-dumpxml lab | grep -E '<forward|<ip |<range'

Wydruk definicji sieci odpowiada na dwa pytania naraz: w jakim trybie sieć pracuje i jaki ma zakres adresów. Tryb translacji adresów oznacza, że maszyny widzą świat, ale świat ich nie - żeby dostać się do usługi z innego komputera, potrzebny jest tunel albo przekierowanie portu.

Sieć da się zatrzymać i uruchomić niezależnie od maszyn, co bywa potrzebne po zmianie jej definicji:

virsh net-destroy lab      # zatrzymuje sieć, nie kasuje jej
virsh net-start lab

Nazewnictwo jest tu tak samo mylące jak przy maszynach: net-destroy zatrzymuje, a kasuje dopiero net-undefine.

Własna sieć wirtualna: definicja XML i adresacja

Sieć opisuje się tak samo jak maszynę - dokumentem XML. Poniżej pokazujemy najprostszą sensowną definicję: własną adresację, bramę na hoście i serwer DHCP rozdający adresy gościom.

<network>
  <name>projekt-a</name>
  <forward mode='nat'/>
  <bridge name='virbr-proja' stp='on' delay='0'/>
  <ip address='192.168.130.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.130.10' end='192.168.130.100'/>
    </dhcp>
  </ip>
</network>

Trzy rzeczy warto tu rozumieć. address to adres, który weźmie host - staje się bramą dla maszyn w tej sieci. Zakres DHCP celowo zaczyna się od .10, żeby zostawić kilka adresów na maszyny konfigurowane ręcznie. Nazwa mostka może być dowolna, ale musi być unikalna w systemie i mieścić się w limicie długości nazw interfejsów.

virsh net-define projekt-a.xml     # zapisuje definicję
virsh net-start projekt-a          # uruchamia
virsh net-autostart projekt-a      # ma wstawac razem z hostem

Te trzy kroki są rozdzielone celowo: definicja może istnieć bez działającej sieci, a sieć uruchomiona bez autostartu zniknie po restarcie hosta.

Trzy sieci na jednym hoście: dwie połączone i jedna odcięta

To jest częsty scenariusz - kilka środowisk na jednym hoście, z których jedno ma być odcięte. Zanim jednak zaczniesz planować reguły, sprawdź, co dzieje się domyślnie. My sprawdziliśmy, budując trzy sieci i mierząc łączność między nimi.

Wynik jest jednoznaczny i dla wielu osób zaskakujący:

SprawdzenieWynik
Maszyna w sieci A do maszyny w sieci B (obie z translacją adresów)nie widzi
Kierunek odwrotny, B do Anie widzi
Maszyna w sieci A na zewnątrzwychodzi
Host do maszyn w obu sieciachwidzi obie
Maszyna w sieci izolowanej do swojej bramywidzi
Maszyna w sieci izolowanej do innej sieci i na zewnątrznie widzi

W naszej konkretnej konfiguracji dwie osobne sieci NAT nie widziały się wzajemnie, mimo że host ma interfejs w każdej z nich, a przekazywanie pakietów w jądrze jest włączone. Nie jest to uniwersalna granica bezpieczeństwa: wynik zależy od reguł filtrowania, routingu i konfiguracji sieci.

Praktyczny wniosek: przed uznaniem środowisk za odcięte sprawdź ruleset oraz testuj ruch A→B i B→A. Dla granicy bezpieczeństwa deklaruj i monitoruj jawne reguły, zamiast polegać wyłącznie na podziale na sieci.

Sieć naprawdę odcięta

Jeżeli maszyny mają nie mieć wyjścia w ogóle, wystarczy jedna różnica w definicji - pomijasz element forward:

<network>
  <name>projekt-c</name>
  <!-- brak elementu forward: siec izolowana -->
  <bridge name='virbr-projc' stp='on' delay='0'/>
  <ip address='192.168.132.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.132.10' end='192.168.132.100'/>
    </dhcp>
  </ip>
</network>

Sprawdziliśmy, jak taka sieć się zachowuje. Maszyna dostaje adres z DHCP, rozmawia z innymi maszynami w tej samej sieci i widzi bramę na hoście - ale nie wychodzi nigdzie dalej. Zapytanie do adresu w Internecie po prostu nie wraca.

To dobre miejsce na maszyny, które mają być odizolowane od świata: analiza podejrzanego oprogramowania, środowisko z danymi, których nie wolno wypuścić, albo test aktualizacji, który nie ma prawa dotknąć niczego produkcyjnego. Odcięcie wynika z definicji sieci, a nie z konfiguracji w gościu, więc nie da się go wyłączyć przez pomyłkę wewnątrz maszyny.

Różnicę widać jednym poleceniem:

$ virsh net-dumpxml projekt-a | grep forward
  <forward mode='nat'>

$ virsh net-dumpxml projekt-c | grep forward
  (nic - i o to chodzi)

Gdy dwie sieci mają się jednak widzieć

Jeżeli własny test potwierdził odcięcie obu sieci w danej konfiguracji, ich połączenie wymaga świadomej decyzji. Są dwie drogi i różnią się miejscem, w którym powstaje przejście.

Pierwsza to maszyna z nogą w obu sieciach. Podpinasz jej drugi interfejs i ta jedna maszyna widzi oba środowiska:

virsh attach-interface --domain MASZYNA \
  --type network --source projekt-b --model virtio --live

Flaga --live dokłada interfejs działającej maszynie; bez --config zmiana zniknie po jej wyłączeniu, co przy testach bywa zaletą. W gościu trzeba potem podnieść nowy interfejs i pobrać adres. Żeby maszyny z jednej sieci sięgały przez taką maszynę do drugiej, musi ona jeszcze przekazywać pakiety i trzeba dodać trasy - to już budowanie routera i warto wiedzieć, że się na to piszemy.

Druga droga to reguły filtrowania na hoście, przepuszczające ruch między mostkami obu sieci. Daje przejście dla wszystkich maszyn naraz, ale trzeba je odtwarzać po restarcie sieci, bo libvirt zakłada swoje reguły od nowa przy każdym net-start.

Dwie pułapki, na które trafiliśmy przy tym teście. Pierwsza: maszyna z dwoma interfejsami dostaje dwie trasy domyślne o tym samym koszcie. Pakiet wysłany z adresem źródłowym jednej sieci może wtedy wyjść drugim interfejsem i nie wrócić. Objawia się to jako „sieć nie działa”, choć działa - to kwestia trasowania w gościu, nie definicji sieci. Sprawdzając łączność wskazuj interfejs wprost, na przykład ping -I enp8s0.

Druga: liczba interfejsów, które można dołożyć działającej maszynie, jest ograniczona. Przy próbie podpięcia trzeciego dostaliśmy No more available PCI slots. Kolejny interfejs wymaga wtedy zatrzymania maszyny i zmiany jej definicji, a nie dokładania na gorąco.

Na koniec sprzątamy po eksperymencie w tej samej kolejności co przy maszynach: najpierw odłączamy interfejs od maszyny, potem zatrzymujemy sieć i usuwamy jej definicję:

virsh detach-interface --domain MASZYNA --type network --mac 52:54:00:aa:bb:cc --live
virsh net-destroy projekt-a
virsh net-undefine projekt-a

Migawki maszyn w libvirt i ich ograniczenia

Migawka pozwala wrócić do stanu sprzed zmiany i jest naturalnym odruchem przed ryzykowną operacją. Warto jednak wiedzieć, że są dwa różne rodzaje migawek i że format dysku decyduje o tym, który z nich zadziała.

RodzajGdzie trzyma daneWymaga
Wewnętrznaw samym pliku dyskuformatu qcow2
Zewnętrznaw nowym pliku nakładki nad dotychczasowymdziała także na dysku surowym

Sprawdziliśmy to na plikach roboczych: qemu-img snapshot -c tworzy migawkę w pliku qcow2, a na pliku surowym nie ma jej gdzie zapisać. Jeżeli więc dyski Twoich maszyn są surowe - a bywa tak w konfiguracjach nastawionych na wydajność albo wymuszonych przez narzędzie tworzące maszyny - migawki wewnętrzne odpadają.

Zostaje wariant zewnętrzny. Tworzy on nowy plik, do którego trafiają wszystkie kolejne zapisy, a dotychczasowy dysk staje się niezmienną podstawą:

virsh snapshot-list deb13
virsh snapshot-create-as deb13 przed-zmiana --disk-only --atomic
virsh snapshot-revert deb13 przed-zmiana

Po takiej migawce virsh domblklist pokaże już nie ten plik, co wcześniej, tylko nakładkę. To normalne, ale bywa zaskoczeniem, gdy ktoś w międzyczasie szuka „swojego” dysku.

Powrót do stanu bez migawki polega na scaleniu nakładki z podstawą. Robi to operacja blokowa, wykonywana na działającej maszynie:

virsh blockcommit deb13 vda --active --pivot --wait

Zapisy z nakładki trafiają wtedy do pliku podstawowego, a maszyna zostaje przełączona z powrotem na niego. Trzeba to powtórzyć dla każdego dysku maszyny osobno - jeżeli ma ich dwa, jedno wywołanie zostawi drugi nadal na nakładce.

Uwaga na flagę, która wygląda jak tryb próbny, ale nim nie jest. Pisząc ten artykuł sprawdzaliśmy zachowanie migawek i użyliśmy --validate w przekonaniu, że to bezpieczne sprawdzenie. Nie jest. Opis w pomocy narzędzia brzmi „validate the XML against the schema” - flaga sprawdza poprawność dokumentu i mimo to tworzy migawkę. Trybem próbnym jest --print-xml, opisane jako „print XML document rather than create”. Skutek był łatwy do naprawienia scaleniem nakładek, ale kosztował kwadrans, którego można było uniknąć, czytając opis flagi zamiast zgadywać z nazwy.

Pułapki virsh, które kosztują czas

Zebrane w jednym miejscu, bo każda z nich potrafi wysłać w złą stronę na dłużej niż powinna.

ObjawCo się dzieje naprawdęCo zrobić
Lista maszyn jest pusta, choć maszyny istniejąpytasz nie ten świat: session zamiast system, albo brak Cię w grupievirsh uri, potem -c qemu:///system
Maszyna „zniknęła”virsh list pokazuje tylko działającevirsh list --all
Nie mogę wyjść z konsolikonsola przejęła terminal, Ctrl+C idzie do gościaCtrl+]
domdisplay nic nie zwracamaszyna jest wyłączona, wyświetlacz istnieje dopiero przy działającejnajpierw virsh start
Adres z domifaddr nie odpowiadadzierżawa może być nieaktualna, a agent zwraca wiele interfejsówużyć właściwego źródła, wybrać interfejs i potwierdzić nowym SSH
Zmiana pamięci nie działareboot w gościu nie tworzy procesu maszyny od nowapełne shutdown i start
Dysk powiększony, gość widzi stary rozmiarvol-resize rusza tylko plik na hościepowiększyć partycję i system plików w gościu
Migawka utworzona mimo „sprawdzenia”--validate waliduje, ale tworzyużywać --print-xml
Maszyna wraca do dziwnego stanuleży zapisany stan pamięcidominfo, potem managedsave-remove

Jedna wspólna zasada wychodzi z tej listy: nazwy poleceń w libvirt opisują operację na obiekcie, a nie skutek widziany przez człowieka. destroy niszczy proces maszyny, nie maszynę. net-destroy zatrzymuje sieć, nie kasuje jej. Kiedy nazwa brzmi groźnie albo zbyt łagodnie, szybciej jest sprawdzić opis niż zgadywać:

virsh help destroy
virsh help undefine

Ściągawka najczęściej używanych poleceń virsh

# --- rozeznanie -----------------------------------------------------------
virsh uri                            # które połączenie jest domyślne
virsh list                           # tylko działające
virsh list --all                     # wszystkie
virsh list --state-shutoff --name    # same nazwy wyłączonych
virsh domstate MASZYNA               # stan jednej

# --- co maszyna ma w środku -----------------------------------------------
virsh dominfo MASZYNA                # pamięć, rdzenie, autostart, managed save
virsh domblklist MASZYNA             # dyski i ścieżki do plików
virsh domiflist MASZYNA              # interfejsy i sieć
virsh domifaddr MASZYNA --source agent   # raportowane interfejsy; wybierz właściwy adres

# --- dostęp ---------------------------------------------------------------
virsh console MASZYNA                # konsola szeregowa, wyjście Ctrl+]
virsh domdisplay MASZYNA             # adres pulpitu (tylko gdy działa)
virt-viewer MASZYNA                  # pulpit graficzny
virt-viewer -c qemu+ssh://uzytkownik@serwer/system MASZYNA

# --- zasilanie ------------------------------------------------------------
virsh start MASZYNA
virsh shutdown MASZYNA               # grzecznie, przez system gościa
virsh destroy MASZYNA                # siłowo, jak wyrwanie kabla zasilającego
virsh autostart --disable MASZYNA
virsh managedsave MASZYNA            # zapisz stan i zatrzymaj

# --- konfiguracja ---------------------------------------------------------
virsh dumpxml MASZYNA
virsh edit MASZYNA                   # z walidacją przed zapisem
virt-xml MASZYNA --edit --memory 4096

# --- magazyn i sieć -------------------------------------------------------
virsh pool-list --all
virsh vol-list images
virsh vol-resize --pool images DYSK.img 30G    # potem growpart w gościu
virsh net-list --all
virsh net-dhcp-leases default

# --- pomoc ----------------------------------------------------------------
virsh help                           # grupy tematycznie
virsh help snapshot                  # polecenia jednej grupy
virsh help POLECENIE                 # opis i flagi

Ostatnia linia jest tą, która najbardziej się opłaca. Pomoc w virsh jest podzielona tematycznie - domain, monitor, network, pool, volume, snapshot i kilka innych - a opis pojedynczego polecenia zawiera wszystkie flagi razem z tym, co naprawdę robią. Wystarczy przeczytać jedno zdanie, żeby nie potraktować walidacji jako trybu próbnego.

Potrzebujesz opieki nad infrastrukturą wirtualną?

Projektujemy i utrzymujemy środowiska na libvirt i KVM - od pojedynczego hosta z maszynami testowymi po produkcję opisaną kodem, z backupem i sprawdzonym odtworzeniem.