Hlavní navigace

Virtualizace v OpenBSD prakticky: VMM a Linux

Petr Topiarz

Kdo nevirtualizuje, neexistuje – tak by se dal shrnout současný trend v IT. Všichni znají mainstreamové virtualizační stroje typu VMWare nebo VirtualBox, ale Virtual Machine Manager od OpenBSD je zatím země málo prozkoumaná.

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ů:

  1. vytvořit virtuální síťové zařízení, na straně hostitele
  2. přidat ho do switche/bridge a nakonfigurovat virtuální stroje
  3. nakonfigurovat dhcpd pro toto síťové zařízení
  4. zapnout předávání paketů v kernelu
  5. v packet filteru nastavit překlad
  6. 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?

Zajímavé odkazy

Našli jste v článku chybu?