nspawn + btrfs

V tomto článku si ukážeme vytvoření nspawn kontejneru na BTRFS, minimální instalaci OS Debian Jessie a připojení do sítě. Článek předpokládá základní znalost systemd, btrfs a síťování a má sloužit jako odpíchnutí pro další studium.

Kontejner aneb úvod z rychlíku

Kontejner je virtualizační technologie pro běh OS nebo skupiny procesů. Vedle emulace, plné virtualizace a paravirtualizace se jedná o další formu virtualizace.

Každá forma virtualizace má svá pro a proti. Emulace dokáže emulovat široké množství HW platforem, ale je také nejpomalejší. Plná virtualizace má dnes podporu (nejen) v CPU, což ale také znamená, že OS musí být pro tento CPU a HW připraven (nelze provozovat OS pro jinou hw platformu). Kontejner je „jen“ oddělená skupina procesů (od init po celý strom procesů), které jsou sice do určité míry odděleny, ale běží všechny na společném jádře hostitelského systému. Což také znamená, že není možné v kontejneru pouštět jiný OS tak jak třeba na plné virtualizaci, ani není možné testovat jiná jádra.

(Skoro) každý OS nabízí nějakou formu kontejnerů, Linux nám už pár let nabízí LXC, na BSD je k disposici např. Jail. Je jich samozřejmě mnohem víc.

Proč nspawn

Leckoho asi napadne otázka, proč jsem si vybral zrovna nspawn. Virtualizaci (plnou) používám už hodně dlouho KVM. Jenže ne vždy potřebuji virtualizovat jiný OS, vlastně většina virtuálek má stejný OS jako hostitel. Hlavní výhoda plné virtualizace tím tedy padá.

Proč nspawn a proč ne lxc? Odpověď je jednoduchá. Vždy se snažím hledat minimální množinu nástrojů pro daný úkol. Jestliže tedy mám již na OS (Debian Testing) systemd a nspawn je jeho součástí, přijde mi zbytečné instalovat další nástroj. Koncept nspawn dobře zapadá do světa systemd a nastavuje se podobně jako ostatní služby. Takže pokud jste na systemd zvyklí (a já jsem asi ukázka toho, že ho nemusíte mít rádi, stačí ho jen umět používat), tak pro vás nástroj systemd-nspawn nebude nic moc nového.

Na tomto místě nemůžu nezmínit systemd-networkd. Je to zvláštní, ale linux, který na síti vyrostl a je tam jako doma nikdy neměl pořádné nastavení sítě. Různé distribuce to řešily různě, ale upřímně, málokterá z nich je připravená na to, že by server měl mít více síťovek a už vůbec ne na to, že by tam bylo více IP adres.

systemd-networkd je první z „distribučních“ pokusů o nastavení sítě, které se mi fakt líbí. Jednoduchá, jednotná konfigurace v ini souborech. Brigde se nastavuje úplně stejně (až na nutné detaily) jako bond. Více adres není problém. Konfigurace jednoduchá, přehledná.

Nutné balíčky a požadavky

Před chvílí jsem vám sprostě lhal. nspawn byl součástí balíčku systemd, ale od nějaké verze (a pravděpodobně je to jen v Debianu) je oddělen v balíčku systemd-container. Dalším nutným balíčkem je btrfs-tools.

Hostitelský systém (v mém případě Debian Testing, systemd 228) máme nainstalovaný na BTRFS (nebo alespoň /var/lib/container a místo pro snapshoty je btrfs), máme připraven síťový most br0 připojený do sítě, máme systemd a nspawn. Pojďme na to.

Vytvoření kontejneru

Kontejner umístíme do adresáře /var/lib/container. Toto je nutné pro správnou funkci nástrojů jako machinectl a unity systemd-nspawn@.service.

Na počátku si vytvoříme btrfs subvolume:

cd /var/lib/container
btrfs sub create jmeno_kontejneru

Minimální instalace

Stáhneme a nainstalujeme minimální Debian Jessie:

debootstrap --arch=amd64 jessie jmeno_kontejneru/

Uděláme si snapshot:

btrfs sub snap jmeno_kontejneru /backup/jmeno_`date +%F_%T`

Základní nastavení

Vstoupíme do kontejneru a nastavíme základní věci tak, abychom se potom do kontejneru dostali po ssh:

systemd-nspawn -D jmeno_kontejneru

a nastavíme:

# passwd
# ssh keys apod.
apt-get install openssh-server
apt-get install dbus
  • Balíček dbus je důležitý pro správnou fci nástroje machinectl a vůbec komunikaci mezi hostitelským OS a kontejnerem.

Poznámka: Dodatečné balíčky je možné také předat parametrem --include programu debootstrap, např: debootstrap --arch=amd64 --include=dbus,openssh-server jessie jmeno_kontejneru/.

Příprava na síť:

systemctl disable networking
systemctl enable systemd-networkd
systemctl enable systemd-resolved
editor /etc/hostname

Z shellu kontejneru vyskočíme 3x stiskem Ctrl+]

Opět si můžeme udělat snapshot.

Ověření přístupu po síti

Teď už můžeme kontejner nabootovat s podporou sítě:

systemd-nspawn -bD jmeno_kontejneru/ --network-bridge=br0

Kde se předpokládá, že:

  • existuje síťový most br0
  • je připojený do fyzické sítě, kde chceme mít zapojený i kontejner

Zkontrolujeme, zda OS dostal IP adresu z DHCP, případně nastavíme statickou. Dále zkontrolujeme, zda se na kontejner dostaneme po ssh ze sítě odkud potřebujeme.

Šablona (template)

Tímto tedy máme hotovou minimální instalaci kontejneru Debian Jessie, máme zprovozněný přístup po síti. Zde je tedy vhodná chvíle si pořídit snapshot a uložit si jej jako template pro další kontejnery. Příště už se nemusíme zdržovat s debootstrapem a základním nastavením, ale můžeme si nový kontejner vytvořit rovnou jako snapshot této šablony.

btrfs sub snap -r jmeno_kontejneru \
      /backup/template.debian.jessie.ro

Snapshot pro template můžeme a taky nemusíme vytvořit jako readonly. Je to jen konvence, šablona by se měnit neměla.

Rekapitulace

  • Vytvořili jsme btrfs subvolume
  • Pomocí nástroje debootstrap nainstalovali minimální obraz OS
  • Pomocí nástroje systemd-nspawn jsme do tohoto obrazu přistoupili a udělali základní nastavení:
    • Připravili OS na přístup po síti
    • Zkontrolovali jsme přístup po síti
  • Vytvořili šablonu pro další kontejnery vycházející ze stejného OS.

Registrace kontejneru

V adresáři /var/lib/container máme tedy subvolume s připraveným OS. Pokud nám stačí přístup do kontejneru pomocí systemd-nspawn -D tak máme hotovo. V další části se budeme věnovat registraci kontejneru, jeho startu při bootu hostitelského OS apod.

Seznam kontejneru

Pomocí příkazu machinectl získáme seznam:

machinectl list-images
 NAME TYPE RO USAGE CREATED MODIFIED
 jmeno subvolume no n/a Wed 2016-02-17 11:02:34 CET n/a

Zapnutí a vypnutí, nastavení parametrů

Pokud náš kontejner vidíme v seznamu, můžeme jej zkusit nastartovat. Ale pozor, co se vlastně stane?

Příkaz:

machinectl start jmeno

Zavolá unitu: /lib/systemd/system/systemd-nspawn@.service s příslušným parametrem (jméno subvolume kontejneru). Když se do této unity podíváme:

ExecStart=/usr/bin/systemd-nspawn --quiet --keep-unit \
    --boot --link-journal=try-guest --network-veth \
    --settings=override --machine=%I

tak vidíme, že se sice vytvoří virtuální síťovka --network-veth ale nikde není určeno, do jaké sítě (bridge) se má přiřadit. Při ručním startování jsme zadávali parametr --network-bridge=br0.

Musíme tedy někde určit, že daný kontejner má mít síťovku připojenou na daný bridge.

To se nejsnadněji provede přidáním souboru jmeno_kontejneru.nspawn do adresáře /etc/systemd/nspawn:

[Exec]
Boot=on

[Network]
VirtualEthernet=yes
Bridge=br0

Nyní už můžeme kontejner zapnout:

machinectl start jmeno_kontejneru

nebo vypnout:

machinectl poweroff jmeno_kontejneru

Poznámka: Úplně stejného efektu bychom dosáhli i pomocí systemctl start systemd-nspawn@jmeno jen s tím rozdílem, že zatímco systemctl stop ... čeká na dokončení operace (v tomto případě vypnutí os v kontejneru), tak machinectl poweroff ... nikoliv, to jen pošle signál do kontejneru.

Seznam běžících kontejnerů dostaneme pomocí obyčejného zavolání příkazu machinectl bez parametrů.

Registrace pro spuštění při startu hostitelského systému

Pokud chceme kontejner spouštět při startu hostitelského OS, máme opět dvě varianty:

machinectl enable jmeno_kontejneru

nebo

systemctl enable systemd-nspawn@jmeno_kontejneru

Obojí udělá totéž, tedy vytvoří link v /etc/systemd/system/machines.target.wants na /lib/systemd/system/systemd-nspawn@.service.

Osobně bych se držel výhradně používání nástroje machinectl, který je ke správě VM určený.

Souhrn

Možná to někomu bude připadat jako mnoho práce ale není. Vlastně jsme jen udělali btrfs subvolume (a v průběhu prací několik snapshotů, abychom se mohli případně vrátit o krok zpět), do něj jsme nainstalovali základní systém, pomocí „lepšího“ chrootu se do něj dostali a nastavili síť. Tím je příprava OS kontejneru hotová.

V další části se jednalo jen o nastavení parametrů kontejneru a jeho automatického spuštění.

Pro další kontejnery už první část můžeme rovnou vynechat, stačí nám vytvořit snapshot šablony jako nový kontejner.

Bezpečnost, výhody, nevýhody

nspawn není v žádném případě bezpečností bariéra! nspawn je jen takový lepší chroot, je to skupina procesů (cgroups), které jsou nějakým způsobem odděleny od ostatních procesů, ale toto oddělení není v žádném případě možné považovat za oddělení bezpečnostní. V kontejneru je přístup do /proc (sice někam jen readonly, ale informace z toho v každém případně unikají) apod.

Z toho snad plyne, jak bychom měli ke kontejneru přistupovat. Primárně bychom se v OS kontejneru měli chovat stejně jako v běžném OS na fyzickém železe. Tedy důsledně hlídat, co instalujeme (používat jen oficiální repositáře), důsledně oddělovat uživatelské účty a minimalizovat práva procesů. Viděl jsem několik příkladů na webu, kdy si někdo řekl: „mám kontejner, takže to může běžet jako root“. Toto je velmi špatný přístup a měli bychom se toho vždy vyvarovat.

V kontejneru bychom tedy měli provozovat pouze to, co bychom jinak provozovali v hostitelském os. Kontejner nám poskytne výhodu v oddělení sítě (skupina procesů má přidělenou jinou IP než má hostitelský OS), v oddělení konfigurace (např. v kontejneru můžeme používat jiný webserver nebo aplikační server), v oddělení fs (díky btrfs můžeme dělat snapshoty jednolivých kontejnerů a získat tak snadnou možnost se kdykoliv vrátit zpět).

Osobně kontejner využívám asi nejvíc pro oddělení sítě (sice by šlo jednotlivým procesům nastavit bind na konkrétní IP, ale tím by se komplikovala konfigurace) a právě pro oddělení konfigurace. Kontejner webserveru má nainstalované pouze a jen to, co je nutně potřeba pro jeho běh. Všechny nainstalované komponenty se „věnují“ jen provozu webserver. Stejně tak kontejner pro gitlab obsahuje jen věci pro gitlab a nic jiného.

Dále, díky tomu, že kontejner je vlastně jen adresář na disku (subvolume), je možné to kdykoliv chytnout a dát na jiný fyzický stroj. (Zde lze s výhodou použít btrfs send a receive.) Používám statické IP (v celé síti), nepoužívám DHCP, takže kontejner zapnu na libovolném fyzickém stroji (s dostatečně novým systemd), který má síťový most. (Generování MAC adresy virtuálního eth je nějakým způsobem svázáno s hostitelským strojem, takže se přesunem na jiného hostitele mění. Nezkoumal jsem, zda je nějaká možnost definovat pevnou MAC, osobně používám výhradně statické nastavení sítě.)

Závěr

nspawn je další z řady nástrojů, bez kterých si už život neumím představit. Lehkost s jakým lze kontejner vytvořit a pomocí btrfs manipulovat přímo vybízí k instalaci dalšího. Už nějakou dobu (půl roku) nové věci instaluji jen do kontejneru, místo vytvoření nového uživatele pro běh nějakého projektu už rovnou vytvářím nový kontejner.

Příspěvek byl publikován v rubrice BTRFS, Debian, Linux, nspawn, systemd. Můžete si uložit jeho odkaz mezi své oblíbené záložky.

4 komentáře: nspawn + btrfs

  1. Michal Růžička napsal:

    Super zápisek, díky.

    Ad bezpečnost – velká kritika Dockeru a LXC bývalo, že kontejner musel běžet pod rootem. LXC umožňuje běh neprivilegovaných kontejnerů už delší dobu (https://www.stgraber.org/2014/01/17/lxc-1-0-unprivileged-containers/; https://linuxcontainers.org/lxc/getting-started/#creating-unprivileged-containers-as-a-user), u Dockeru je to horká novinka (http://www.javaworld.com/article/3030558/application-virtualization/docker-goes-rootless-and-thats-a-good-thing.html#tk.rss_all). Jak je to u nspawn? Je možné vytvořit neprivilegovaný kontejner?

  2. lzap napsal:

    Pěkné. Taky musím zkusit, mám to v TODO.

    Ad systemd-container subbalík – oddělilo se to v RHELu/CentOSu zejména kvůli Dockeru, tuším někdy v minulém roce (když vyšel RHEL 7.1). Ovšem zajímavostí je, že ve Fedoře to oddělené není. Taková menší anomálie, která se doufám srovná časem… http://buildlogs.centos.org/centos/7/systemd-container/

    Neprivilegované kontejnery jsou důležité do budoucna, ovšem implementace v Linuxu je peklo a doslova bordel. Zřejmě se na tom musí hodně zamakat. Důležitý je i SELinux, a to jak na hostiteli, tak i v samotném kontejneru.

Komentáře nejsou povoleny.