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.
Spis treści
- Czym jest virsh
- Dwa światy: system i sesja
- Listowanie maszyn
- Co maszyna ma w środku
- Adres maszyny
- Konsola szeregowa
- Pulpit graficzny i virt-viewer
- Zasilanie maszyny
- Podgląd i zmiana konfiguracji
- Dyski, pule i wolumeny
- Sieci i dzierżawy
- Migawki i ich ograniczenia
- Pułapki, które kosztują czas
- Ściągawka
Czym jest virsh
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. 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ę.
Dwa światy: system i sesja
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:
| Adres | Czyje maszyny | Typowe użycie |
|---|---|---|
qemu:///system | maszyny hosta, prowadzone przez usługę systemową | serwery, laby, wszystko co ma działać niezależnie od zalogowanego użytkownika |
qemu:///session | maszyny konkretnego użytkownika | maszyny 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
Listowanie maszyn
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, bez przeszukiwania całej listy:
$ virsh domstate deb13
running
Co maszyna ma w środku
Podsumowanie parametrów jednej maszyny:
$ 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ę.yesznaczy, że przetrwa restart hosta jako definicja.nooznacza maszynę ulotną, która po zatrzymaniu zniknie bez śladu.Autostart- czy wstaje razem z hostem. Osobna rzecz odPersistent: 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:
$ 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, bez logowania się do maszyny:
virsh domstats deb13 --balloon --vcpu
Adres maszyny
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 tablicy dzierżaw DHCP - działa zawsze, bywa nieaktualny
virsh domifaddr deb13
# 2. Zapytanie systemu w środku maszyny - wiarygodne, wymaga agenta
virsh domifaddr deb13 --source agent
Pierwszy wariant pokazuje, co serwer DHCP (od angielskiego Dynamic Host Configuration Protocol, czyli usługa rozdzielająca adresy w sieci) kiedyś przydzielił. Jeżeli maszyna dostała potem inny adres, ma skonfigurowany adres stały albo dzierżawa wygasła, wynik będzie mylący. Drugi pyta system operacyjny gościa o jego rzeczywistą konfigurację, więc mówi prawdę - pod warunkiem, że w maszynie działa qemu-guest-agent. Jeżeli go nie ma, polecenie zwróci pustkę i trzeba wrócić do wariantu pierwszego.
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
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. Wyjście to Ctrl+] - domyślnie sekwencja znana z telnetu. 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.
Dwie flagi warte znajomości. --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 i 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.
Zasilanie maszyny
Tu jedno nieporozumienie potrafi kosztować dane, więc zacznijmy od tabeli. Nazwy poleceń są mylące i nie odpowiadają temu, co sugerują po polsku:
| Polecenie | Co naprawdę robi | Odpowiednik w świecie fizycznym |
|---|---|---|
virsh start | uruchamia maszynę z zapisanej definicji | naciśnięcie przycisku zasilania |
virsh shutdown | prosi system gościa o zamknięcie | wybranie „zamknij system” w menu |
virsh reboot | prosi system gościa o ponowne uruchomienie | wybranie „uruchom ponownie” |
virsh destroy | odcina zasilanie, nie kasuje niczego | wyrwanie kabla z gniazdka |
virsh reset | twardy reset bez pytania gościa | przycisk reset w obudowie |
virsh undefine | kasuje definicję maszyny | wyrzucenie 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
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
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 i dzierżawy
$ 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ć: definicja i adresacja
Sieć opisuje się tak samo jak maszynę - dokumentem XML. Poniżej najprostsza sensowna definicja: własna adresacja, brama 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: dwie połączone, jedna osobna
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:
| Sprawdzenie | Wynik |
|---|---|
| Maszyna w sieci A do maszyny w sieci B (obie z translacją adresów) | nie widzi |
| Kierunek odwrotny, B do A | nie widzi |
| Maszyna w sieci A na zewnątrz | wychodzi |
| Host do maszyn w obu sieciach | widzi obie |
| Maszyna w sieci izolowanej do swojej bramy | widzi |
| Maszyna w sieci izolowanej do innej sieci i na zewnątrz | nie widzi |
Czyli dwie osobne sieci na tym samym hoście są domyślnie wzajemnie odcięte, mimo że host ma nogę w każdej z nich, a przekazywanie pakietów w jądrze jest włączone. Zadbały o to reguły filtrowania, które libvirt zakłada przy uruchamianiu sieci: przepuszczają ruch danej sieci na zewnątrz, ale nie do sąsiedniej.
Praktyczny wniosek jest wygodny: samo umieszczenie maszyn w osobnych sieciach już je od siebie oddziela. Nie trzeba nic dokładać, żeby środowiska się nie widziały.
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ć
Skoro domyślnie są odcięte, połączenie ich 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ątanie po eksperymencie, w tej samej kolejności co przy maszynach - najpierw zatrzymanie, potem usunięcie definicji:
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 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.
| Rodzaj | Gdzie trzyma dane | Wymaga |
|---|---|---|
| Wewnętrzna | w samym pliku dysku | formatu qcow2 |
| Zewnętrzna | w nowym pliku nakładki nad dotychczasowym | dział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, 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.
| Objaw | Co się dzieje naprawdę | Co zrobić |
|---|---|---|
| Lista maszyn jest pusta, choć maszyny istnieją | pytasz nie ten świat: session zamiast system, albo brak Cię w grupie | virsh uri, potem -c qemu:///system |
| Maszyna „zniknęła” | virsh list pokazuje tylko działające | virsh list --all |
| Nie mogę wyjść z konsoli | konsola przejęła terminal, Ctrl+C idzie do gościa | Ctrl+] |
domdisplay nic nie zwraca | maszyna jest wyłączona, wyświetlacz istnieje dopiero przy działającej | najpierw virsh start |
Adres z domifaddr nie odpowiada | odczyt z dzierżaw DHCP bywa nieaktualny | dodać --source agent |
| Zmiana pamięci nie działa | reboot w gościu nie tworzy procesu maszyny od nowa | pełne shutdown i start |
| Dysk powiększony, gość widzi stary rozmiar | vol-resize rusza tylko plik na hoście | powiększyć partycję i system plików w gościu |
| Migawka utworzona mimo „sprawdzenia” | --validate waliduje, ale tworzy | używać --print-xml |
| Maszyna wraca do dziwnego stanu | leży zapisany stan pamięci | dominfo, 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
# --- 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 # adres, wiarygodnie
# --- 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.
Powiązany artykuł
Lab do testów na Terraform i OpenTofu
Skąd wziąć maszyny, którymi potem zarządzasz poleceniami z tego artykułu.
Zobacz też