Disassembler

Artificial intelligence is no match for natural stupidity.
06březen2016

LXC, LXD a WordPress v kontejneru


Hraju si tu tak s vývojovou verzí Ubuntu 16.04 a připravuju pro ni svoje balíčky a skripty, a koukám, že už v základu umí pracovat s LXC virtuálními kontejnery. Taky koukám, že ač na svém blogu provozuji tag virtualizace, obsahuje ostudně malé množství článků. Rozhodl jsem se tedy, že se zas jednou naučím něco nového a vyzkouším si jen tak z plezíru vyrobit virtuální kontejner s WordPressem.

Univerzální ptakopysk


Virtuální kontejnery jsou koncept, který tu s námi je už více než 10 let, ale rozmach zažívají až v posledních několika letech a jejich popularita stále vzrůstá. Není divu, protože donedávna jste si buď mohli vybrat jen plnotučný hypervizor s celým virtualizovaným operačním systémem, což byl v některých případech poněkud overkill, anebo pouze nějaký chroot či virtuální prostředí, což zase mohlo být pro potřeby některých aplikací málo. Platformy typu Docker, OpenVZ či LXC jsou přesně takovým chybějícím mezičlánkem. Na jedné straně pracují s kernelem a všemi dalšími jadernými vymoženostmi hostitele, takže mají v tomto směru prakticky zanedbatelnou režii, ale na straně druhé kompletně izolují veškeré procesní, síťové a user-space záležitosti tak, jak by to udělala klasická virtuálka. Řekl bych tedy, že se jedná o to nejcloudovitější možné řešení jaké dnes cloudové technologie nabízejí.

Na Ubuntu 16.04 zajišťuje běh LXC kontejnerů daemon LXD, což je Ubuntí derivát, který ale funguje prakticky stejně jako původní čisté LXC. Ovládá pomocí komandlajnového nástroje lxc. Vše se tedy odehrává v podobném duchu jako u libvirt (který mimochodem LXC a jiné kontejnery taky podporuje) a virsh. LXC pracuje s obrazy, ze kterých se následně vytvářejí jednotlivé virtuální kontejnery.

Můj milý deníčku...


„...dneska jsem to poprvé zkusila a ani to tolik nebolelo. Nikdy bych nevěřila, že se mi tam vleze celý. I s databází!“ Mluvím samozřejmě o instalaci předesílaného virtuálního kontejneru pro Apache, PHP, MariaDB a WordPress, ve kterém si pak můžete vést vlastní deníček. Jak jsem naznačil v předchozím odstavci, kontejnery mají vlastní síťová rozhraní, takže pokud chcete, aby kontejner komunikoval s okolním světem, LXC hostitel (v případě plné virtualizace bychom jej mohli označit jako hypervizor) se musí postarat o bridge a NAT. Ubuntu 16.04 přichází s většinou věcí nastavených už out-of-the-box, takže jediná záležitost, o kterou se musí postarat administrátor, je zapnout forwarding.

echo 1 >/proc/sys/net/ipv4/ip_forward
sysctl -w net.ipv4.ip_forward=1

Ubuntu nabízí několik zdrojů (remotes), odkud můžete hotové obrazy sosat. Jejich seznam můžete vypsat pomocí

lxc remote list

a jejich obsah pak následovně (dvojtečka na konci je důležitá)

lxc image list <remote>:

Jelikož raději pracuji s LTS verzemi a 16.04 ještě nevyšla, vyžádám si obraz 14.04 a rovnou v příkazu zadám i název svého kontejneru.

lxc launch ubuntu:14.04 wordpress

Obraz se stáhne, rozbalí a spustí. Chci-li přejít do bashe samotného kontejneru, spustím

lxc exec wordpress -- /bin/bash

A můžu začít vesele řádit. Schválně si zkuste vypsat nainstalované balíčky a verzi kernelu. Zjistíte, že žádné balíčky s kernelem nainstalované nejsou a že kontejner používá jádro hostitele.

Instalace WordPressu


Následující příkazy se až do odvolání odehrávají uvnitř kontejneru. Teď teprve nainstaluju a nastavím svou aplikaci dle své chuti a libosti. Začnu aktualizací a instalací potřebných balíčků.

apt-get update
apt-get upgrade
apt-get install apache2 libapache2-mod-php5 php5-gd php5-json php5-mysql mariadb-server unzip

Tady by se asi hodilo říci, že kontejnery využívají optimalizační strategii copy-on-write a že doporučované souborové systémy pro hostování kontejnerů jsou btrfs a ZFS, jelikož podporují data deduplication. To umožňuje mít hromadu kontejnerů a hromadu snapshotů, kde fyzicky budou zabírat místo jen soubory, které se od sebe vzájemně liší. Identické kopie jednoho kontejneru tedy na disku zaberou pořád zhruba stejné množství místa, ať je jich kolik chce. Doporučuju tedy postavit nějaký kontejner se základní funcionalitou, ten pak publikovat jako nový obraz a až ten pak dále upravovat a klonovat ke konečnému užití. V tomto případě by tedy mohlo dávat smysl vytvořit obraz s předinstalovaným Apache, PHP a MariaDB. Pokud je obsazení diskového prostoru primárním problémem, pak doporučuji použít LXC obrazy založené na Alpine linuxu.

Pokračujeme tedy v krasojízdě. Vytvořím pro WordPress databázi.

mysql -p
> CREATE DATABASE `wordpress`;
> CREATE USER "wordpress"@"%" IDENTIFIED BY "supertajneheslo";
> GRANT ALL ON `wordpress`.* TO "wordpress"@"%";
> FLUSH PRIVILEGES;
> quit

Poškádlím Vinnetoua

a2enmod rewrite
service apache restart

A stáhnu a rozbalím samotný WordPress (DocumentRoot Apache je na deb-based systémech ve výchozím stavu nasměrován do /var/www/html)

cd /var/www
wget https://wordpress.org/latest.zip
unzip latest.zip
rm -rf latest.zip html
mv wordpress html
chown -R www-data:www-data html

V tuhle chvíli mám nastavení kontejneru hotové a mohl bych začít s instalací WordPressu v prohlížeči.

Series of tubes


To bych se ovšem do kontejneru musel z venku nějak dostat. Musím tedy nastavit NAT. Nejprve si zjistím IP adresu svého kontejneru. Buď klasicky „zevnitř“ přes ifconfig nebo ip addr anebo to můžu udělat i z hostitele.

root@testbuntu:~# lxc list
+-----------+---------+-------------------+------+------------+-----------+
|   NAME    |  STATE  |       IPV4        | IPV6 |    TYPE    | SNAPSHOTS |
+-----------+---------+-------------------+------+------------+-----------+
| wordpress | RUNNING | 10.0.3.175 (eth0) |      | PERSISTENT | 0         |
+-----------+---------+-------------------+------+------------+-----------+

Případně

root@testbuntu:~# lxc info wordpress
Name: wordpress
Architecture: x86_64
Created: 2016/03/06 12:50 UTC
Status: Running
Type: persistent
Profiles: default
Pid: 13683
Processes: 34
Ips:
  eth0: inet    10.0.3.175       vethN40UWQ
  eth0: inet6   fe80::216:3eff:fee9:4d15        vethN40UWQ
  lo:   inet    127.0.0.1
  lo:   inet6   ::1

Jakmile tedy vím, kam mám požadavky přesměrovávat, můžu na hostiteli spustit

iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.3.175:80

A voilà, veškerý HTTP provoz bude přesměrován do kontejneru.

V případě, že bych měl jiný scénář, kde mám několik kontejnerů s webovými aplikacemi, ale jen jednu veřejnou IP adresu, nepoužiju NAT, ale můžu nainstalovat Apache HTTP Server i na hostiteli a skrze mod_proxy_http přehazovat požadavky na základě hostname nebo URL. Například bych mohl vytvořit virtualhosta

<VirtualHost *:80>
    ServerName wordpress.example.com
    ProxyPass / http://10.0.3.175/
    ProxyPassReverse / http://10.0.3.175/
</VirtualHost>

Který by mi do mého kontejneru s deníčkem přesměrovával jen požadavky příchozí na jednu konkrétní doménu. Vhodnější by zřejmě bylo přidat hostname kontejneru do /etc/hosts hostitele a v ProxyPass pravidlech jej použít. Tím si usnadníte život, zejména pokud budete chtít, aby jeden kontejner obsluhoval více domén skrze name-based virtual hosting.

A je to


V tuto chvíli tedy mám svůj pancéřovaný deníček dostupný z venku a můžu jej konečně přes prohlížeč nainstalovat. WordPress je bohužel velmi nešťastně napsaná aplikace a všude cpe absolutní URL, takže v průběhu instalace neuvidíte obrázky a kaskádové styly, protože si myslí, že URL http://10.0.3.175/ je správná a nenechá si to vymluvit. Chudák prohlížeč, který se na takovou URL samozřejmě nedostane, pak musí čekat na timeout. To by se sice dalo řešit za pomoci modulu proxy_html, ale je to zbytečný kanón na vrabce, protože po instalaci se dá WordPress donutit vkládat URL dle vlastního výběru. Také nezapomeňte, že databázový server je "localhost", protože v kontejneru bydlí i dedikovaný MariaDB server.

Jakmile je WordPress nainstalován a URL opravena, což se dá udělat třeba následujícím zásahem do databáze

UPDATE `wp_options` SET `option_value`="http://wordpress.example.com" WHERE `option_name` IN ("siteurl", "home");

Můžu kontejner vypnout a udělat si snapshot čisté instalace

lxc stop wordpress
lxc snapshot wordpress clean

Kontejner můžu taky třeba publikovat a vytvořit z něj další kopie

lxc publish wordpress --alias=my-wordpress
lxc launch my-wordpress wordpress2
lxc launch my-wordpress wordpress3

A až mě přestane bavit si hrát, smažu kontejner i image

lxc delete wordpress
lxc image delete my-wordpress

A půjdu dělat něco pořádného.