Mi a különbség konténer és virtuális gép között és mi az az LXD?

Gyakorlati példákon keresztül bemutatom, hogyan lehet konténereket létrehozni és mi a különbség konténer és virtuális gép között. Mivel szeretném, ha mindenki számára nyilvánvaló lenne, hogy nem csak Docker konténerek léteznek, ezért az LXD-t fogom telepíteni, amivel LXC konténerket és virtuális gépeket is lehet kezelni.

De mégis mi a kapcsolat ezek között?

  • Az LXC a Linux Containers rövidítése és a Linux kernel képességeit kihasználva lehet létrehozni vele konténereket.
  • Az LXD a saját dokumentációja állítása szerint az LXC-hez ad egy új felhasználói élményt, köztük felhasználóbarátabb parancssori felületet. A segítségével virtuális gépeket és azok alternatívájaként olyan konténereket lehet indítani, amikben szinte egy teljes operációs rendszer fut annak minden háttérszolgáltatásávál.
  • Kezdetben a Docker is az LXC-t használta a háttérben, de azóta leváltották. Inkább alkalmazás konténereket indítunk benne minden további, felesleges szolgáltatás nélkül. Itt is van lehetőség viszont mindezt egy plusz virtualizált rétegen belül tenni.

Mivel az LXD-vel nagyjából ugyanaz fut egy konténerben, mint egy virtuális gépben, ez a legjobb módja annak, hogy összehasonlítsuk a kettőt.

LXD telepítése

Az LXD-t a snap csomagkezelővel tudom telepíteni, ami Ubuntun előre telepítve van, de más disztribúciókra is telepíthető.

sudo snap install --channel 4.0/stable lxd

A --channel paraméter fontos, mert így lehet megadni, hogy a jelenlegi hosszan támogatott LTS verzió legyen telepítve, azon belül is a stabil verzió, nem pedig egy fejlesztői.

A következő az lxd init parancs futtatása, ami kérdéseket fog feltenni a konfigurációhoz. Ki fogja írni az alapértelmezett értéket, és ha az megfelelő, akkor azt az enterrel el is tudjuk fogadni. A legtöbb esetben egyébként jó az alapértelmezett érték.

lxd init

Alább láthatók a kérdések és válaszok, ahol az alapértelmezett értéket vastag betűvel emeltem ki, ahol pedig eltértem az alapértelmezéstől, az új választ pirossal jelöltem.

  • Would you like to use LXD clustering? (yes/no) [default=no]:
  • Do you want to configure a new storage pool? (yes/no) [default=yes]:
  • Name of the new storage pool [default=default]:
  • Name of the storage backend to use (lvm, zfs, ceph, btrfs, dir) [default=zfs]:
  • Create a new ZFS pool? (yes/no) [default=yes]:
  • Would you like to use an existing empty block device (e.g. a disk or partition)? (yes/no) [default=no]:
  • Size in GB of the new loop device (1GB minimum) [default=18GB]:
  • Would you like to connect to a MAAS server? (yes/no) [default=no]:
  • Would you like to create a new local network bridge? (yes/no) [default=yes]:
  • What should the new bridge be called? [default=lxdbr0]:
  • What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:
  • What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:
  • Would you like the LXD server to be available over the network? (yes/no) [default=no]:
  • Would you like stale cached images to be updated automatically? (yes/no) [default=yes] no
  • Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]: yes

A storage pool adja majd a konténerek alatti fájlrendszert. A ZFS az alapértelmezett, amitől el lehet térni, de minden fájlrendszernek megvannak a maga előnyei és hátrányai, ami viszont ebbe a cikkbe már nem fér bele. Ha viszont nem zavar, hogy nem tudsz limitet adni a felhasználható fájlrendszerhez és ezzel túl sok helyet tudnál elfoglalni a hoszt rendszeren, akkor a "dir" is választható, amivel egyszerűen csak egy mappába kerül minden. A ZFS esetén viszont egy img kiterjesztésű fájl jön létre, ami ZFS pool-ként lesz használva. Ez a ZFS pool viszont lehet, hogy már létre lett hozva, ezért opcionális a telepítés közben, illetve módosítani lehet a nevét is.

Konfigurálni kell a hálózatot is, amihez léterjön egy bridge, így a konténerek és virtuális gépek az ezen elérhető IPv4 és IPv6 tartományból kaphatnak IP címeket.

A távoli szerver elérését a hálózaton akár be is lehet kapcsolni, ha másik gépről szeretnéd kezelni az LXD-t, de ha van SSH hozzáférésed a géphez, akkor ez nem szükséges.

Én az image-ek automatikus frissítését is le szoktam tiltani, bár ez általában nem szabad, hogy problémát okozzon, de szeretem elkerülni a meglepetéseket. Ha pedig mégis tudatosan frissítem az image-eket és az ezután létrehozott gépem nem úgy működik, ahogy vártam, legalább tudom az okát.

Végül pedig ki lehet írni a generált konfigurációt yaml formátumban. Ezt el is lehet menteni és egy későbbi újratelepítéskor például az alábbi módon vissza lehet telepíteni az LXD-t:

cat lxd.yml |lxd init --preseed

A fenti kérdésekre válaszolva én például az alábbi konfigurációt generáltam:

config:
  images.auto_update_interval
: "0"
networks
:
- config
:
    ipv4.address
: auto
    ipv6.address
: auto
  description
: ""
  name
: lxdbr0
  type
: ""
storage_pools
:
- config
:
    size
: 18GB
  description
: ""
  name
: default
  driver
: zfs
profiles
:
- config
: {}
  description
: ""
  devices
:
    eth0
:
      name
: eth0
      network
: lxdbr0
      type
: nic
    root
:
      path
: /
      pool
: default
      type
: disk
  name
: default
cluster
: null

Itt egy "profiles" blokk is látható, ahol egy "default" nevű profilt is létrehoztunk, ami tartalmazza azokat az infókat, amiket minden indítandó konténerre alkalmazni kell. Ez most a hálózati beállításokat és a konténerek fájlrendszerét érinti, ami a "default" nevű storage pool-ból lesz a konténerekhez rendelve.

Távoli tárolók

A konténerekhez szükség van template-re, ami tartalmazza a konténer fájlrendszerét. Ezt hívjuk image-nek. Ezeket távoli tárolókból lehet letölteni. A tárolókat pedig az alábbi paranccsal lehet listázni:

lxc remote list

Az "image" nevű tárolóban a "linuxcontainers.org" domain alatt kisebb image-ek vannak, mivel nem tartalmaznak bizonyos elemeket, amik konténerben általában feleslegesek. Mint például a "snap". Az "ubuntu" nevű tárolóban viszont a hagyományos, nem konténerben futó Ubuntu rendszerekhez sokkal hasonlóbbakat lehet találni.

A "local" nevű tároló egy speciális eset, mivel gyakorlatilag nem távoli, hanem azon a gépen található, ahol az LXD szerver, tehát a már letöltött image-eket tartalmazza, viszont ez az alapértelmezett, aminek hamarosan jelentösége lesz.

Keresés az image-ek között

Kereshetünk az image-ek között is az alábbi paranccsal

lxc image list ubuntu

Ahol az "ubuntu" a keresőkifejezés, de mivel az alapértelmezett tároló a local, ami üres, nem találunk semmit. A tároló megadásához a tároló neve után kettőspontot kell írni. Véletlenül az "ubuntu" egy tároló neve is, ezért a következő parancs már működik:

lxc image list ubuntu:

Az "ubuntu:" után már tényleg a kereső kulcsszavak jönnek, így rákereshetek még az amd64-es 20.04 verziójú Ubuntu image-ekre:

lxc image list -c Lfatsd ubuntu:20.04 amd64

A "-c" paraméterrel felsoroltam az oszlopokat is abban a sorrendben, ahogy a listában látni szeretném.

  • L: Aliasok egymás alatt
  • f: Fingerprint, azaz "ujjlenyomat" rövid verziója, ami az egyedi azonosító"
  • a: Architektúra
  • t: Típus, ami vagy CONTAINER vagy VIRTUAL-MACHINE
  • s: Méret
  • d: Leírás

Image információk lekérdezése

Akár egy aliast használva, akár egy fingerprintet megadva egy konkrét image adatai is lekérhetók. Alább például a 20.04 aliast birtokló image infóit kérdezem le:

lxc image info ubuntu:20.04

Ha pedig ugyanezt a --vm paraméterrel együtt használom, akkor a konténer verzió helyett a virtuális gép adatait kapom meg. Itt viszont már fontos, hogy a kettőspont után nem akármilyen keresőkifejezés jön, hanem tényleg csak alias vagy fingerprint!

Konténer indítása

Konténer indításához a "launch" parancsot kell használni, átadva az image nevét (tároló és alias vagy tároló és fingerprint), illetve opcionálisan egy nevet a konténernek. Ha nevet nem adok meg, akkor egy automatikus fantázianevet fog kapni.

lxc launch ubuntu:20.04 ubuntu-container

A konténereket az alábbi paranccsal lehet kilistázni:

lxc list

Illetve az "exec" használatával parancsokat lehet futtatni a konténerben. Ha viszont a "bash" parancsot futtatom, akkor gyakorlatilag belépek a konténerbe.

lxc exec ubuntu-container bash

Kilépni pedig egyszerűen az "exit" beírásával lehet.

Virtuális gép indítása

A virtuális gép indítása nagyon hasonló, de a --vm paramétert is meg kell adni. A --console paraméter opcionális, de ezzel követhető a boot folyamat is.

lxc launch ubuntu:20.04 ubuntu-vm  --vm --console

A virtuális gépbe is be lehet lépni az "exec" használatával:

lxc exec ubuntu-vm bash

Ehhez vszont a gépben futnia kell az "lxd-agent" szolgáltatásnak. Ha egy hiba miatt nem fut, de van a géphez beállítva már jelszó, akkor utólag is lehet konzolt kérni:

lxc console ubuntu-vm

Kilépni ilyenkor az "exit"-tel csak a bejelentkező promptig lehet, de a CTRL+a, majd a "q" billentyű lenyomásával a konzolról is le lehet csatlakozni:

Konténer és virtuális gép összehasonlítása

Kernel

Először is a konténerben nincs saját kernel, a hoszt rendszer kernelét fogja használni, mivel épp a kernel, ami a Linux névterek segítségével elszeparálja a konténerben levő processzeket a gazda rendszertől. A gazda gépen kiírva a kernel verzióját

uname -r

majd a konténerben ezt megismételve

lxc exec ubuntu-container -- uname -r

Ugyanazt a verziót kell kapjuk, de nem csak a verzió egyezik. A virtuális gép kernelének verziójából viszont az is egyértelműen látszik, hogy nem használhatja a hoszt kernelét, mivel teljesen más a verzió is.

lxc exec ubuntu-vm -- uname -r

A hosztról láthatók a konténerben futó processzek

A konténerben és a virtuális gépben is egy webszerver elindításával tesztelhető, hogy valóban beláthatunk a konténerbe, azaz láthatjuk a gazda rendszerről a konténerben futó processzeket is, de ugyanezt nem tehetjük meg a virtuális gépben levő processzekkel

Webszerver indítása a konténerben

lxc exec ubuntu-container -- mkdir webroot-container
lxc exec ubuntu-container -- bash -c 'echo "container" > webroot-container/index.html'
lxc exec ubuntu-container -- python3 -m http.server --directory webroot-container 80

Webszerver indítása a virtuális gépben

lxc exec ubuntu-vm -- mkdir webroot-vm
lxc exec ubuntu-vm -- bash -c 'echo "vm" > webroot-vm/index.html'
lxc exec ubuntu-vm -- python3 -m http.server --directory webroot-vm 80

A konténerben futó processzek lekérdezése a hoszt rendszerről

ps        # Processzek listája
ps a      # Minden felhasználó procsszének listája
ps au     # Felhasználónév megjelenítése
ps auf    # Fa struktúrában megjelenítés

Szűrés a konténerben és VM-ben levő webszerver processzre

ps au | grep webroot-container
ps au | grep webroot-vm

A fenti kódból az első sort lefuttatva nem csak az lxc exec parancsot találjuk meg, de a konténerben futtatott, "python3"-mal kezdődő sort is. A virtuális gép esetén viszont ezt már nem lehet látni.

Mit látnak a gépek a hoszt rendszerből?

A konténeren belül a hoszt operációs rendszer hardvereit látjuk, míg a virtuális gépben virtualizált hardvereket. Az LXD alapértelmezetten egy vCPU-t ad a virtuális gépnek, a többit nem is láthatom. A konténer viszont még akkor is látná a teljes processzort, ha korlátoznánk, mennyit használhat belőle. Az alábbi paranccsal összehasonlítható, mit lát a konténer és mit látunk a hosztról. Ha nem ad semmilyen eredményt, az azt jelenti, hogy nincs különbség. Mivel a CPU frekvencia folyamatosan változó érték és minden futattásnál mást adna, így ezt szűrtem a kimenetből.

diff --suppress-common-lines --side-by-side --color <(cat /proc/cpuinfo | grep -v '^cpu MHz') <(lxc exec ubuntu-container cat /proc/cpuinfo | grep -v '^cpu MHz')

Ugyanezt a virtuális gépre futtatva már látszik, hogy van különbség

diff --suppress-common-lines --side-by-side --color <(cat /proc/cpuinfo | grep -v '^cpu MHz') <(lxc exec ubuntu-vm cat /proc/cpuinfo | grep -v '^cpu MHz')

De nyugodtan lehet ellenőrizni külön is szabad szemmel.

lxc exec ubuntu-vm cat /proc/cpuinfo

User névtér

A konténerekben futó processzek izolációja a linux a névterekkel történik. Ebből egyik a "user" névtér. Ennek köszönhető, hogy amikor a konténerben futtattam a webszervert, a hoszt rendszerről az azonosítója "1000000" volt. Biztonsági okokból az LXD-vel indított konténerekben a hosztról nézve minden felhasználónak ennyivel nagyobb azonosítója van. Ha létrehoztam volna egy új felhasználót, ami az 1000-t kapja azonosítóként, a hosztról 1001000-ként láthatnánk. Így a konténerben levő felhasználóknak akkor sem lesz véletlenül joga például rendszerfájlokhoz, ha amúgy a konténeren belül nézve az azonosítókat lenne. Ezt a működést meg lehet változtatni a user névtér kikapcsolásával:

lxc launch ubuntu:20.04 ubuntu-nstest --config "raw.lxc=lxc.namespace.keep=user"

Ha most itt is indítok egy webszervert

lxc exec ubuntu-nstest -- python3 -m http.server --directory webroot-nstest 80

ezt a processzt már úgy látom a hosztról is, hogy a "root" felhasználó futtatja.

ps au | grep webroot-nstest

Net névtér

Alapértelmezetten a konténereknek saját , egyedi IP címeik lesznek, és csak a saját interfészüket látják a "loopback" interfészen kívül. Ha viszont szeretném, hogy ne kelljen portokat átirányítani a konténerre, és a konténer IP címét se kelljen tudnom, akkor a hoszt IP címét ismerve vagy a "localhost" címet használva is elérhetem akár a webszervert, ha a konténert az alábbi módon indítom, miután töröltem az előző példányt az lxc delete --force ubuntu-nstest:

lxc launch ubuntu:20.04 ubuntu-nstest --config "raw.lxc=lxc.namespace.keep=user net"

Most az "ubuntu-container" konténerben futtatva az "ip addr" parancsot csak a saját interfészét látjuk, de az "ubuntu-nstest" konténerben már a host összes interfészét.

Ezek után érdemes még megnézni a konténerlistát is, ahol most csak a neveket és az IPv4 címeket jelenítem meg:

lxc list -c n4

Ha indítok ismét egy szervert ebben a konténerben

lxc exec ubuntu-nstest -- python3 -m http.server 80

Az "ubuntu-nstest" konténer mellett látott bármelyik IP címet választhatom, el fogom érni a webszervert. Én most például a Dockernek létrehozott interfész IP címét adtam meg:

curl http://172.17.0.1

Takarítás

Ha már nem kell az LXD, törölhető, de néhány dologra figyelni kell. Mivel ZFS-sel hoztam létre a storage poolt, amikor törlöm az LXD-t, a ZFS pool továbbra is megmarad. Ha legközelebb újratelepíteném az LXD-t, már nem választhatnám a "default" nevet a poolnak. Bár utólag is megoldható, érdemes előre törölni. Ehhez viszont előbb a konténereket mind le kell állítani, különben nem lehet kivenni alóla a poolt sem.

lxc delete --force ubuntu-container ubuntu-vm ubuntu-nstest

Mielőtt a poolt törlöm, még szerkeszteni kell a default LXD profilt is az alábbi paranccsal:

lxc profile edit default

Ez hivatkozik a default storage poolra, így addig nem lehet törölni. A "profiles" alatt a "root" blokkot kell megkeresni és a teljes blokkot törölni. Ezután már futtatható a pool törlés is:

lxc storage delete default

Végül pedig az LXD törlése

sudo snap remove lxd --purge

Itt az --purge paraméter miatt a törléskor nem fog snapshotként adatokat elmenteni a snap, ezért gyorsabban is végez és nem is foglal feleslegesen helyet. Ha viszont mégis sikerült a --purge nélkül futtatni a törlést, akkor lekérdezhetők a snapshotok az alábbi paranccsal:

snap saved

Majd a megjelenő listában az első oszlopban levő azonosítót használva el lehet feledtetni a snapshotot:

snap forget $id

Ahol az $id helyére a választott snapshot számát kell írni. Ha pedig még a poolt is elfelejtetted törölni, akkor utólag ubuntun a "zfsutils-linux" csomag telepítése után az alábbi módon lehet törölni a ZFS poolt:

zpool destroy default

Befejezés

Ennyi volt tehát a konténerek és virtuális gépek összehasonlítása. Természetesen ennél sokkal többet is lehetett volna mutatni, de arra lesz még lehetőség a jövőben. Az már ebből is kiderülhetett, hogy az alapvető küönbség a processzek izolációjának módjában rejlik és ebből adódnak további lehetőségek. A konténereket viszont nem csak önmagukban lehet használni, hanem ha a szükség úgy hozza, extra biztonságot adhat, ha azokat virtuális gépben futtatjuk. Ez minidg az adott programtól és a környezettől függ.

Mint mindig, kommenteket, like-okat, kritikákat várom. Az oldal Youtube csatornája és a Facebook oldala is mindenkit tárt karokkkal vár, ahol szintén lehet reagálni. Mi tetszett, mi nem tetszettt, mit olvasnál, látnál szívesebben legközelebb?

Kategóriák: 
Megosztás/Mentés