A virtualizáció jövője?

Posted 2023-03-25 10:30:00 by sanyi ‐ 5 min read

Manapság a virtualizációról a legtöbb üzemeltetőnek a VMWare, HyperV, KVM/Qemu, Virtualbox, Xen és hasonlók jutnak eszébe. Ezek arra a feladatra születtek hogy egy fizikai hardveren képesek legyünk több virtuális gépet futtatni párhuzamosan, egymástól biztonságosan elszeparálva. Azért hogy a virtuális gépekben futó (vendég) operációs rendszereket a lehető legkisebb mértékben kelljen módosítani, ezek a hypervisor-ok igyekszenek a lehető legjobban emulálni egy valós fizikai hardvert, annak minden problémájával együtt.

Aztán jött az LXC és a Docker, ezek a container technológia segítségével próbálnak egy operációs rendszer példányon belül többé-kevésbé biztonságosan elszeparálni egymástól alkalmazásokat. A biztonsági szintjük jobb mint ha simán az operációs rendszeren futtatnánk az alkalmazásokat egymás mellett, de nincs olyan erős mint ha külön virtuális gépeket használnánk.

Következő lépésként jött a Kubernetes és vele együtt további container runtime-ok. A Kubernetes lehetővé tette hogy container-ek tömegeit menedzseljük automatizáltan.

Mire ide eljutottunk, a technológiai stack elég bonyolulttá vált:

  • van a fizikai hardver
  • ezen fut egy host operációs rendszer és egy hypervisor ami a virtualizációt biztosítja
  • a következő réteg egy virtuális gép
  • amiben fut egy újabb operációs rendszer
  • majd ezen az operációs rendszeren egy container runtime
  • a container-ben egy újabb, container-izált operációs rendszer (kernel nélkül)
  • végül a container-izált operációs rendszerben maga az alkalmazás

Ha jól számolom ez hét réteg, félelmetes komplexitás néha triviálisan egyszerű alkalmazások futtatására. Némelyik réteg feleslegesen bonyolult, bőséges felületet adva a potenciális támadásoknak.

Jött az első megoldási kísérlet, a Kubernetes on bare metal: hagyjuk ki az első virtualizációs réteget, így marad:

  • a fizikai hardver
  • a host operációs rendszer
  • azon egy container runtime
  • a container-ben egy újabb, container-izált operációs rendszer (kernel nélkül)
  • végül a container-izált operációs rendszerben maga az alkalmazás

Ez már csak öt réteg, valamivel jobb a helyzet, de feláldoztunk kicsit a biztonságból: ezen az architektúrán nem tudunk egymástól biztonságosan elszeparálva kiszolgálni több különböző ügyfelet.

Az AWS irányából jött a következő lépés: csináljunk egy teljesen leegyszerűsített virtualizációs technológiát, ami nem akar egy komplex valós hardvert emulálni, csak annyit abból ami egy container runtime futtatásához szükséges. Ez lett a Firecracker, illetve a rá épülő AWS Fargate szolgáltatás. Ez jelentősen leegyszerűsíti a hypervisor funkcionalitását, erőforrás igényét, a potenciális támadási felület méretét miközben továbbra is biztosítja az erős biztonsági elkülönítést a vendégek között.

A rétegezés ebben az esetben:

  • van a fizikai hardver
  • ezen fut egy host operációs rendszer és a Firecracker / KVM hypervisor
  • a Firecracker-re épülő container runtime
  • a Firecracker által biztosított microVM-ben egy újabb, container-izált operációs rendszer
  • végül a container-izált operációs rendszerben maga az alkalmazás

Egy másik hasonló megoldás a kata containers irányából érkezett, ők eredetileg a Qemu-t használták virtualizációs rétegként, de ma már a Firecracker hypervisor-ral is együtt tudnak működni.

A következő probléma: a container-izált operációs rendszer fölösleges komplexitása. Kezdetben a container image-ek a normális operációs rendszer image-ekből indultak ki: Debian, Ubuntu, stb. Ezeket próbálták a lehető legkisebbre lecsupaszítani, de gyakran még így is több száz MB-os alap image-ek készültek. A dinamikus programozási nyelvek, mint pl. a PHP vagy Python esetén a runtime függőségek telepítése villámgyorsan fel tudja hízlalni az image méretét. Ezzel szemben az olyan statikus programozási nyelvek esetén mint a Go vagy a Rust nagyon kicsi a runtime függőség. A fordítás eredménye egyetlen futtatható bináris állomány, aminek általában csak a C runtime library (libc) és esetleg néhány shared library kell, mint a libssl / libtls. Static target-re fordítással ezek függőségek is bevihetőek a futtatható binárisba.

A container image mérete tovább csökkenthető az Alpine Linux segítségével ami a GNU libc helyett a musl libc-t használja és a sok különálló unix bináris helyett a busybox-ot.

Ha nincs szükség a busybox eszközökre, akkor lehet indulni from scratch, amikor az image lényegében csak a futtatandó állományt és a hozzá szükséges konfigurációkat tartalmazza. Ilyenkor persze a debuggolás nem lesz egyszerű mert a container-ben még egy shell-t sem tudunk futtatni.

Ha tovább megyünk rájövünk hogy van még egy fölösleges réteg: a vendég operációs rendszer kernele. A Linux és más többfelhasználós operációs rendszerek azzal a céllal születtek hogy egy fizikai vagy virtuális gépen egymástól viszonylag biztonságosan elszeparálva több felhasználó tudjon párhuzamosan dolgozni. Emiatt a kernel és a user space egymástól szigorúan elválasztott. Egy container esetén ahol a kernelen egyetlen alkalmazás fog futni, erre nem lenne szükség.

Erre a problémára születtek az unikernel megoldások. Van amelyik arra törekszik hogy a linuxos alkalmazások a lehető legkisebb változtatással futtathatóak legyenek, mint pl. az OSv. Mások egyszerűen csak egy library-t adnak, ami önmagában helyettesíti a kernel-t az alkalmazás számára, ilyen pl. a RustyHermit. Az unikernel alapú alkalmazások közvetlenül a hardveren vagy a virtuális gépen futtathóak, nincs szükségük operációs rendszerre.

Ha egy Hermit alapú alkalmazást futtatunk Firecracker alapon, akkor a stack-ünk így egyszerűsödik:

  • van a fizikai hardver
  • ezen fut egy host operációs rendszer és a Firecracker / KVM hypervisor
  • a Firecracker által biztosított microVM-ben az unikernellel kombinált alkalmazás

Így már az eredeti hét helyett csak három rétegünk maradt, a teljes értékű virtualizációs réteget egy karcsúsított Firecracker-re cseréltük, három operációs rendszer környezet helyett csak egyre van szükségünk.

Az unikernel megoldások egyelőre még kísérleti stádiumban vannak. Nem igazán lehet még őket Kubernetes segítségével menedzselni végképp nem lehet magát a Kubernetes menedzsment réteget unikernel alapon futtatni.

Mindenesetre ebben az irányban is vannak próbálkozások. Az Unikraft például azzal a megoldással kísérletezik hogy az elkészült unikernel alapú alkalmazást docker kompatibilis OCI image-ekbe csomagolja, a szokásos runc runtime-ot pedig egy saját runu runtime-ra cseréli, ami valójában nem container-eket hanem virtuális gépeket hoz létre, bennük az unikernel alapú alkalmazással.

Egyelőre még kérdéses hogy valóban az unikernel jelenti-e a jövőt, sokan úgy gondolják túl nagy váltás lenne túl kevés előnyért, de az Unikraft próbálkozásai is mutatják hogy lehet benne fantázia, különösen ott ahol iszonyatos mennyiségű container-t kell futtatni és már a kicsivel jobb teljesítmény is dollár ezrekben vagy milliókban mérhető megtakarítást jelenthet.

Címkék:
virtualizáció virtualization firecracker rust unikernel kubernetes unikraft hermit