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.
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?
Díky :-)
Tohle nevím. V rychlosti jsem našel, že LP v roce 2014 (https://plus.google.com/+LennartPoetteringTheOneAndOnly/posts/W2itNERXvMh) unpriviledged odmítal jako blbost a nějaký zápisek (https://www.flockport.com/a-quick-look-at-systemd-nspawn-containers/) že systemd 220 už začíná mít nějakou počáteční podporu.
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.
Díky :-)