Jak to všechno začalo
V roce 2015 se konal OpenBSD hackaton v australském Brisbane, to bylo v době, kdy už delší dobu existovala poptávka po rychlém a nativním virtuálním stroji, který by nahradil velmi pomalé Qemu, na kterém sice běží BSD, Windows i Linux, ovšem téměř nepoužitelně pomalu. Tehdy kdosi postrčil Mika Larkina, aby se zamyslel nad řešením.
Mike si prohlédl B-Hyve, který vyvíjí FreeBSD, a uvědomil si, že portování B-Hyve na OpenBSD bude stejně náročné, jako napsání vlastního hypervizoru. Navíc, projekt B-Hyve se míjel v některých cílích se zadáním, jaké Mike dostal, FreeBSD projekt nesplňoval některé požadavky, například podporu staršího hardware a dlouhodobou podporu i386. A tak Mike zkusil zapracovat na nativním hypervizoru, přičemž první krok bylo postavit generický stroj, který by rozběhl jádro na základu virtio, kdyby to vyšlo, dopíšou se periferie a síťové prvky později. Jelikož se práce vyvíjela slibně, pokračoval v kódování přes celé léto a podzim s podporou OpenBSD Foundation, která část jeho práce financovala. Na podzim 2015 nahrál do vývojového stromu první komity.
Jak stav vypadal tehdy, si můžete přečíst ve starším článku na Root.cz.
Sám Mike se stavem úplně spokojen nebyl, a tak vstoupil do hry (jak jinak?) Reyk Floeter, který pomohl kód dostat do kvalitního stavu. Velkou práci celý tým odvedl v dubnu 2016 na hackatonu ve francouzském Nantes. Tam došlo ke stabilizaci běhu stroje, rozšíření na další (starší a neintelové) typy procesorů, zapojení sítě, RTC, rozšíření paměti virtuálů z max 2G na max 32G, rozšíření z amd64 také na i386, vylepšená podpora pledge a v neposlední řadě zjednodušení konfigurace. Navíc jde nyní virtuální stroj z příkazové řádky standardně vypnout nebo restartovat a spouštět pod jakýmkoli uživatelem (kterému ovšem k tomu dáme oprávnění).
Následně ve vydání OpenBSD 6.0 už došlo k přesunu kǒdu VMM do větve stable (ovšem pro výchozí kompilaci kernelu vypnutý) a ve vydání 6.1 se dočkal stavu, kdy je VMM ve výchozím stavu v kernelu zapnuto. Tím je vyslán signál, že VMM je tu k použití. Vývoj je dynamický a bouřlivý a na wishlistu dominují požadavky jako různé sdílení konfigurací, displeje a zároveň izolace sítě, na čemž se údajně už pracuje. Z hlediska podpory dalších systémů je důležitý aktuální vývoj BIOSu a obecně vylepšování podpory ne-OpenBSD hostů (myslím tím samozřejmě guests).
Jak to celé funguje
VMM se skládá z několika částí. Backend dělá vmd, což je démon běžící v user modu, který zpracovává požadavky pro VMM tak, aby virtuály běžely. Dále se vmd stará o I/O virtuálních zařízení. Pak je tu VMM, což je část zabudovaná v kernelu, která má na starosti vlastní provádění kódu hostovaných virtuálů a předává správu démonu vmd, kdykoli dojde k I/O operacím, nebo přerušením. A pak tu máme vmctl, což je zase program běžící v user modu, který spouští, zastavuje a vůbec ovládá virtuály.
Podrobné obrázky a graf, který to vysvětluje, je možné si prohlédnout v prezentaci přednesené na bhyveconu v Tokiu v roce 2016.
Nasazení
Cílem našeho článku není tak úplně podrobný rozbor funkcionality, ale spíše přehled a ukázka nastavení a spuštění. Proto pojďme do práce. Tak jak je zvykem v OpenBSD, je nutné nějaké to low-level nastavení, žádné grafické udělátko a la Virtualbox na vás (zatím) nevykoukne. (I když v jedné prezentaci zmiňuje Mike vývojáře, který pracuje na grafickém rozhraní podobném tomu, které má Linux pro KVM).
Předem všechny nadšence upozorňuji, že uvedený kód není připraven pro nějaké copy & paste, ale že je třeba ho užít v kontextu nastavení vlastního prostředí (např. další pravidla pf, další dhcp sítě, nebo název externího síťového zařízení atd.) Kromě toho se trochu moc rychle mění syntax, a návody, které jsou na vidění na internetu, používají přepínače (například -k) nebo pokyny (například vmmctl), které už v tomto vydání (OpenBSD 6.1) nefungují.
Ukážeme si univerzální zapojení do sítě s DHCP, tak jak známe z KVM, Qemu, VirtualBoxu apod., ale pochopitelně to není jediné možné zapojení. Pro zapojení do sítě je třeba udělat několik kroků:
- vytvořit virtuální síťové zařízení, na straně hostitele
- přidat ho do switche/bridge a nakonfigurovat virtuální stroje
- nakonfigurovat dhcpd pro toto síťové zařízení
- zapnout předávání paketů v kernelu
- v packet filteru nastavit překlad
- zapnout správné daemony
Například takto (všechny akce jako root, resp. doas, příp. sudo):
ad) 1 síťové zařízení
# ifconfig vether0 create # ifconfig vether0 192.168.30.1 255.255.255.0 # ifconfig vether0 up
a pokud chcete, aby toto nastavení přežilo restart, tak:
# echo "inet 192.168.30.1 255.255.255.0 NONE">/etc/hostname.vether0
ad 2) přídáme konektivitu pro vm pomocí switche tak, že do /etc/vm.conf
zapíšeme
switch "local" { add vether0 add tap0 }
Pokud chceme autostart a pohodlné ovládání strojů, můžeme ještě přidat do téhož souboru sekce s konfigurací virtuálů, jako např. tyto dvě:
vm "obsd"{ memory 512M kernel "/bsd.rd" disk "/home/honza/virtualy/obsd.img" interface { switch "local" } } vm "linux" { memory 1G disk "/home/honza/virtualy/alpine-virt-3.6.2-x86_64.iso" disk "/home/honza/virtualy/linux.img" interface { switch "local" } }
ad 3) nastavení DHCP, minimalistické v /etc/dhcpd.conf, může vypadat například takto:
shared-network VMM { subnet 192.168.30.0 netmask 255.255.255.0 { range 192.168.30.100 192.168.30.200; option subnet-mask 255.255.255.0; option broadcast-address 192.168.30.255; option routers 192.168.30.1; option domain-name-servers 8.8.8.8; } }
ad 4) nastavení kernelu
# sysctl -w net.inet.ip.forwarding=1
a pro persistentní konfiguraci:
# echo "net.inet.ip.forwarding=1" >>/etc/sysctl.conf
ad 5) nastavení překladu v packet filteru, např. takto:
/etc/pf.conf ------ snip ------ set skip on lo block return # block stateless traffic pass # establish keep-state ext_if="em0" int_if="{ vether0 tap0 }" set block-policy drop set loginterface egress match in all scrub (no-df random-id max-mss 1440) # Tady se deje preklad adres: match out on egress inet from !(egress:network) to any nat-to (egress:0) # Tady poustime provoz a povolujeme ssh: pass out quick inet pass in on $int_if inet pass in on egress inet proto tcp from any to (egress) port 22 --- snip ------
a spustíme packet filter
# pfctl enable # pfctl -f /etc/pf.conf
ad 6) povolení a start služeb, které jsme nakonfigurovali
# rcctl enable dhcpd vmd # rcctl start dhcpd vmd
Instalace OpenBSD jako VM
Pokud nemáme v rootu instalační kernel /bsd.rd
, tak si ho stáhneme z některého instalačního zrcadla, pochopitelně v závislosti na vaší architektuře CPU:
# cd / ; ftp https://ftp2.eu.openbsd.org/pub/OpenBSD/6.1/amd64/bsd.rd # vmctl create /home/honza/virtualy/obsd.img -s 2G
Trošku si ukážeme ovládání, je to samovysvětlovací, takže není třeba komentář
# vmctl start obsd vmctl: started vm 1 successfully, tty /dev/ttyp0 # vmctl status ID PID VCPUS MAXMEM CURMEM TTY OWNER NAME 1 63695 1 512M 91.5M ttyp0 root obsd # vmctl stop obsd vmctl: terminated vm 1 successfully # vmctl status ID PID VCPUS MAXMEM CURMEM TTY OWNER NAME - - 1 512M - - root obsd # vmctl start obsd vmctl: started vm 2 successfully, tty /dev/ttyp0 # vmctl status ID PID VCPUS MAXMEM CURMEM TTY OWNER NAME 2 63695 1 512M 91.6M ttyp0 root obsd VCPU: 0 STATE: RUNNING # vmctl console 2
Poslední příkaz vás připojí do konsole, přičemž číslo za slovem console je ID, které vyčteme z výpisu příkazu vmctl status
.
Pokud nemáme v instalačním souboru sekce s konfigurací virtuálních strojů, potom musíme do příkazové řádky vypsat ručně adekvátní zápis. Direktiva -c nás připojí rovnou do sériové console, takže vidíme start, i ten neúspěšný, což je výhodou pro debugging, protože vidíme běh přímo od startu, zatímco při připojení do konsole externě se nám zobrazí až finální stav, respektive nic, dokud neťukneme aspoň ENTER na klávesnici
# vmctl start obsd -b /bsd.rd -m 512M -d /home/honza/virtualy/obsd.img -c
Výsledek bude vypadat nějak takto, a je vidět i získaná konektivita sítě
sd0 at scsibus0 targ 0 lun 0: <virtio, block="" device,=""> SCSI3 0/direct fixed sd0: 2048MB, 512 bytes/sector, 4194304 sectors virtio1: irq 5 virtio2 at pci0 dev 3 function 0 "Qumranet Virtio Network" rev 0x00 vio0 at virtio2: address fe:e1:bb:d1:b4:c4 virtio2: irq 7 virtio3 at pci0 dev 4 function 0 "OpenBSD VMM Control" rev 0x00 virtio3: no matching child driver; not configured isa0 at mainbus0 com0 at isa0 port 0x3f8/8 irq 4: ns8250, no fifo com0: console softraid0 at root scsibus1 at softraid0: 256 targets root on rd0a swap on rd0b dump on rd0b erase ^?, werase ^W, kill ^U, intr ^C, status ^T Welcome to the OpenBSD/amd64 6.1 installation program. (I)nstall, (U)pgrade, (A)utoinstall or (S)hell? s # ifconfig -a lo0: flags=8008<loopback,multicast> mtu 32768 llprio 3 groups: lo vio0: flags=8802<broadcast,simplex,multicast> mtu 1500 lladdr fe:e1:bb:d1:b4:c4 llprio 3 media: Ethernet autoselect status: unknown # dhclient vio0 DHCPDISCOVER on vio0 - interval 1 DHCPOFFER from 192.168.30.1 (fe:e1:ba:d0:1f:e4) DHCPREQUEST on vio0 to 255.255.255.255 DHCPACK from 192.168.30.1 (fe:e1:ba:d0:1f:e4) bound to 192.168.30.101 -- renewal in 21600 seconds. # ping www.root.cz PING root.cz (91.213.160.118): 56 data bytes 64 bytes from 91.213.160.118: icmp_seq=0 ttl=57 time=3.554 ms ^C --- root.cz ping statistics --- 1 packets transmitted, 1 packets received, 0.0% packet loss round-trip min/avg/max/std-dev = 3.554/3.554/3.554/0.000 ms #
Instalace Linuxu jako VM
# vmctl create /home/honza/virtualy/linux.img -s 4G # vmctl start linux # vmctl status ID PID VCPUS MAXMEM CURMEM TTY OWNER NAME 6 69297 1 1.0G 3.5M ttyp5 root linux 5 95135 1 512M 91.6M ttyp0 root obsd # vmctl console 6
opět opakuji, že číslo za slovem „console“ je číslo vyčtené z výpisu vmctl status
. Po přihlášení do sériové konzole mi ale linux nereagoval, takže jsem to zkusil přes command line:
# vmctl start linux -d /home/honza/virtualy/debian-9.0.0-amd64-netinst.iso -d /home/honza/virtualy/linux.img -n local -m 1024M -c
Výsledek byl ovšem takový všelijaký. Debian ani CentOS se mi nabootovat nepodařilo, skončilo to následovně:
init timer init virtio-blk found virtio-blk at 00:02.0 pci dev 00:02.0 using legacy (0.9.5) virtio mode virtio-blk 00:02.0 blksize=512 sectors=593920 Registering bootable: Virtio disk PCI:00:02.0 (type:2 prio:9999 data:f1f70) found virtio-blk at 00:03.0 pci dev 00:03.0 using legacy (0.9.5) virtio mode virtio-blk 00:03.0 blksize=512 sectors=8388608 Registering bootable: Virtio disk PCI:00:03.0 (type:2 prio:9999 data:f1f10) init serial Found 1 serial ports Searching bootorder for: HALT Mapping hd drive 0x000f1f70 to 0 drive 0x000f1f70: PCHS=0/0/0 translation=lba LCHS=589/16/63 s=593920 Mapping hd drive 0x000f1f10 to 1 drive 0x000f1f10: PCHS=0/0/0 translation=lba LCHS=522/255/63 s=8388608 malloc finalize Space available for UMB: c0000-ef000, f0000-f1f10 Returned 245760 bytes of ZoneHigh e820 map has 6 items: 0: 0000000000000000 - 000000000009fc00 = 1 RAM 1: 000000000009fc00 - 00000000000a0000 = 2 RESERVED 2: 00000000000f0000 - 0000000000100000 = 2 RESERVED 3: 0000000000100000 - 000000003fffc000 = 1 RAM 4: 000000003fffc000 - 0000000040000000 = 2 RESERVED 5: 00000000fffc0000 - 0000000100000000 = 2 RESERVED locking shadow ram Unable to lock ram - bridge not found Jump to int19 enter handle_19: NULL Booting from 0000:7c00 enter handle_12: a=00000000 b=00000000 c=00000000 d=00000000 ds=0000 es=0000 ss=0000 si=00000000 di=00000000 bp=00000000 sp=00007b9e cs=0000 ip=8675 f=0000 enter handle_12: a=00000200 b=00000000 c=00000000 d=00000000 ds=0000 es=0000 ss=0000 si=00000000 di=00000000 bp=00000000 sp=00007b9e cs=0000 ip=8675 f=0000 invalid handle_1ab1:188: a=0000b101 b=00000000 c=00000000 d=00000000 ds=0000 es=0000 ss=0000 si=00000000 di=00000000 bp=00000000 sp=00007b7e cs=0000 ip=8675 f=0001
Ve všech záznamech o úspěšném běhu Linuxu pod VMM byl zmiňován Alpine Linux, který nepoužívá systemd. Rozhodl jsem se ho také vyzkoušet. Po stažení doporučeného Alpine Linuxu, ve verzi Virtual (mají více verzí ke stažení, např. i verzi pro Xen) jsem postoupil výrazně dále, ovšem nakonec při instalaci se stroj zasekl. To se stalo po různé době i při opakovaných pokusech. Osobně to přisuzuji procesoru, na kterém jsem to testoval Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
, protože jiní lidé na internetu evidentně s Alpine Linuxem uspěli.
# vmctl start linux -d /home/honza/virtualy/alpine-virt-3.6.2-x86_64.iso -d /home/honza/virtualy/linux.img -n local -m 1024M -c No apic - only the main cpu is present. init timer init virtio-blk found virtio-blk at 00:02.0 pci dev 00:02.0 using legacy (0.9.5) virtio mode virtio-blk 00:02.0 blksize=512 sectors=69632 Registering bootable: Virtio disk PCI:00:02.0 (type:2 prio:9999 data:f1f70) found virtio-blk at 00:03.0 pci dev 00:03.0 using legacy (0.9.5) virtio mode virtio-blk 00:03.0 blksize=512 sectors=8388608 Registering bootable: Virtio disk PCI:00:03.0 (type:2 prio:9999 data:f1f10) init serial Found 1 serial ports Searching bootorder for: HALT Mapping hd drive 0x000f1f70 to 0 drive 0x000f1f70: PCHS=0/0/0 translation=lba LCHS=69/16/63 s=69632 Mapping hd drive 0x000f1f10 to 1 drive 0x000f1f10: PCHS=0/0/0 translation=lba LCHS=522/255/63 s=8388608 malloc finalize Space available for UMB: c0000-ef000, f0000-f1f10 Returned 245760 bytes of ZoneHigh e820 map has 6 items: 0: 0000000000000000 - 000000000009fc00 = 1 RAM 1: 000000000009fc00 - 00000000000a0000 = 2 RESERVED 2: 00000000000f0000 - 0000000000100000 = 2 RESERVED 3: 0000000000100000 - 000000003fffc000 = 1 RAM 4: 000000003fffc000 - 0000000040000000 = 2 RESERVED 5: 00000000fffc0000 - 0000000100000000 = 2 RESERVED locking shadow ram Unable to lock ram - bridge not found Jump to int19 enter handle_19: NULL Booting from 0000:7c00 enter handle_12: a=00000000 b=00000000 c=00000000 d=00000000 ds=0000 es=0000 ss=0000 si=00000000 di=00000000 bp=00000000 sp=00007b9e cs=0000 ip=8675 f=0000 enter handle_12: a=00000200 b=00000000 c=00000000 d=00000000 ds=0000 es=0000 ss=0000 si=00000000 di=00000000 bp=00000000 sp=00007b9e cs=0000 ip=8675 f=0000 ISOLINUX 6.04 6.04-pre1 Copyright (C) 1994-2015 H. Peter Anvin et al boot:
zde jsem jen stiskl ENTER
enter handle_12: a=00000000 b=00000000 c=00000000 d=00000000 ds=0000 es=0000 ss=0000 si=00000000 di=00000000 bp=00000000 sp=00007b7e cs=0000 ip=8675 f=0000 invalid handle_legacy_disk:729: a=00000000 b=00000000 c=00000000 d=00000000 ds=0000 es=0000 ss=0000 si=00000000 di=00000000 bp=00000000 sp=00007b7e cs=0000 ip=8675 f=0000 unimplemented handle_15XX:330: a=0000ec00 b=00000002 c=00000000 d=00000000 ds=1000 es=1000 ss=1000 si=00000000 di=00000000 bp=00000000 sp=0000ff44 cs=1000 ip=02fc f=0003 unimplemented handle_16XX:224: a=00000305 b=00000000 c=00000000 d=00000000 ds=1000 es=1000 ss=1000 si=00000000 di=00000000 bp=00000000 sp=0000ff44 cs=1000 ip=02fc f=0003 unimplemented handle_15XX:330: a=0000e980 b=00000000 c=00000000 d=47534943 ds=1000 es=1000 ss=1000 si=00000000 di=00000000 bp=00000000 sp=0000ff44 cs=1000 ip=02fc f=0003 [ 0.000000] ACPI BIOS Error (bug): A valid RSDP was not found (20160831/tbxfroot-244) [ 1.080000] ACPI BIOS Error (bug): A valid RSDP was not found (20160831/tbxfroot-244) [ 1.080000] dmi: Firmware registration failed. OpenRC 0.24.1.faeb98e61b is starting up Linux 4.9.32-0-virthardened (x86_64) * /proc is already mounted * Mounting /run ... * /run/openrc: creating directory * /run/lock: creating directory * /run/lock: correcting owner * Caching service dependencies ... [ ok ] * Remounting devtmpfs on /dev ... [ ok ] * Mounting /dev/mqueue ... [ ok ] * Mounting modloop ... [ ok ] * Mounting security filesystem ... [ ok ] * Mounting persistent storage (pstore) filesystem ... [ ok ] * Mounting cgroup filesystem ... [ ok ] * Starting busybox mdev ... [ ok ] * Loading hardware drivers ... [ ok ] * Loading modules ... [ ok ] * Setting system clock using the hardware clock [UTC] ... [ ok ] * Checking local filesystems ... [ ok ] * Remounting filesystems ... [ ok ] * Mounting local filesystems ... [ ok ] * Configuring kernel parameters ... [ ok ] * Migrating /var/lock to /run/lock ... [ ok ] * Migrating /var/run to /run ... [ ok ] * Creating user login records ... [ ok ] * Wiping /tmp directory ... [ ok ] * Setting hostname ... [ ok ] * Starting busybox klogd ... [ ok ] * Starting busybox syslog ... [ ok ] Welcome to Alpine Linux 3.6 Kernel 4.9.32-0-virthardened on an x86_64 (/dev/ttyS0) localhost login:
Zde jsem se přihlásil jako root bez hesla
Welcome to Alpine! The Alpine Wiki contains a large amount of how-to guides and general information about administrating Alpine systems. See . You can setup the system with the command: setup-alpine You may change this message by editing /etc/motd.
A podle pokynu jsem spustil instalaci příkazem setup-alpine
localhost:~# setup-alpine 12% [##### ]
a tady někde to vždy skončilo…
A závěr?
Především je třeba zdůraznit, že se jedná o velmi mladý projekt. Před rokem a půl nefungovala síť a šlo startovat pouze OpenBSD, navíc ještě dost chybově, a dnes vidíme znatelný posun. Popravdě, prozatím se vývoj posouvá velmi divoce, vývojáři vyřazují kusy kódu a po posunu kupředu je zase zařazují, tak to bylo například s podporou pro i386 nebo shadow ram a tak se dokonce stává, že na vašem CPU virtualizace v jedné verzi jde, a v druhé nejde. CPU rozšíření VMX není zárukou, že vmd rozběhnete.
Zároveň však se od začátku programuje stylem OpenBSD – to znamená rychlost, čistota a minimum kódu, minimum chyb. Co se týče zralosti, tak je vidět znatelný rozdíl v běhu virtualizovaného OpenBSD, které se chová stabilně, a plně virtualizovaného NetBSD/FreeBSD/Linuxu startujících přes BIOS, u kterých ještě běh není doladěn. Pokud někdo má rád jasné závěry, řekl bych, že na provoz londýnské burzy to ještě není, ale vzhledem k posledním implementovaným vlastnostem to vypadá, že nyní se vývojáři už soustředí více na stablizaci než na rozšiřování. Takže třeba za rok v produkci? Proč ne?