Hlavní navigace

Inicializace aneb Od Initu k Runitu (2)

7. 7. 2004
Doba čtení: 8 minut

Sdílet

Je mnoho lidí, kteří si uvědomují, že na špatných základech nejde vystavět spolehlivý systém. Jde, ale za cenu obrovského úsilí a vloženého času. Jedním z těchto lidí je Daniel J. Bernstein. Nahradil init.d skripty Daemontools, které si dnes ukážeme.

Podle mého názoru je běžný inicializační systém přesložitělý. Člověk se utápí v záplavě skriptů, které v zájmu co největší podobnosti s MS Windows obsahují konstrukce typu „zde zapni splashscreen, když je proměnná a rovna b a zároveň existuje soubor /a/b/c a xz | grep gh | sed klmn…“.

Je mnoho lidí, kteří si uvědomují, že na špatných základech nejde vystavět spolehlivý systém. Jde, ale za cenu obrovského úsilí a vloženého času. Jedním z těchto lidí je Daniel J. Bernstein. Nahradil init.d skripty Daemontools, které si dnes ukážeme.

Démonizace

Stěžejní princip i problém init.d skriptů tkví ve spouštění programů (služeb) na pozadí. Program je spuštěn jako kterýkoli jiný, avšak očekává se od něj, že se sám odebere na pozadí, tzv. se detachuje. Své vstupy a výstupy odpojí od terminálu a stane se nezávislým na ukončení konzole. Proto se takovému programu říká daemon. My tím ale ztrácíme možnost s daemonem jednoduše pracovat. Chceme-li ho ukončit, můžeme tak učinit pouze zasláním signálu jeho procesu, k čemuž potřebujeme znát jeho číslo – process id – PID.

Není nic lepšího než konkrétní příklad – budeme spouštět a ukončovat apache.

# /etc/init.d/apache start
  1. init.d skript spouští /usr/sbin/apache2
  2. apache testuje, zda mu nic nebrání ve fungování. Jelikož ne, odebírá se na pozadí a vrací exitcode 0.
  3. init.d skript na základě exitcode píše „OK“
  4. apache vytváří soubor /var/run/apache2­.pid, do něhož zapisuje číslo svého procesu
# /etc/init.d/apache2 stop
  1. init.d skript načetl ze souboru /var/run/apache2­.pid číslo PID apache
  2. apache dostává signál TERM (kill PID)
  3. kill vrací exitcode 0 a init.d skript píše „OK“

Toto je ideální stav. Jak je vidět, i v něm potřebujeme znát alespoň název pid souboru. Jaké jsou další problémy?

  • Jistě jste se setkali se situací, kdy se sice napsalo OK, ale služba se nenastartovala.
  • init.d skript předpokládá, že daemon pid soubor vytvoří

    Apache jsem vybral právě proto, že pid soubor vytváří i s několikase­kundovým zpožděním a opačně, při ukončení pid soubor zmizí, ale apache ještě několik sekund běží. Budete-li ho restartovat rychle za sebou, dojde ke kolizím, které vyústí v nemožnost ho vypnout jinak než ručním zabitím. Aby ke kolizím nedocházelo, vyřešili to dalším skriptem, apachectl.

  • předpokládáme, že pid soubor neexistuje od minule

    Nastává po pádu počítače nebo apache. Ve většině init.d skriptů je proto před spuštěním služby pid soubor smazán, případně se služba odmítne spustit, dokud ho nesmažeme ručně. Důvod? Představte si, že apache spadl. Chcete ho tedy spustit znovu. Jak má init.d skript zjistit, zda náhodou neběží a nejde o několikanásobné spuštění? Podle pozůstalého pid souboru. Co když byl ale mezitím spuštěn další proces, který obdržel stejné PID?

  • pokud byste chtěli napsat vlastního daemona, musíte v něm implementovat detachování, a to není jednoduché

Shrnutí: init.d skripty nejsou spolehlivé. Aby se spolehlivost zvýšila, bývají plné „hacků“, jejichž výsledek není stoprocentní a které vše jen zesložiťují. Přitom lze celý tento „balast“ nahradit…

DJBware

Daniel J. Bernstein je znám jako tvůrce velmi specifického software (Qmail, tinydns). Jeho programy bývají při zachování obdobných funkcí desetkrát menší (a 2× divnější). Místo monolitického programu sestávají z mnoha malých prográmků, přičemž každý umí jen minimum funkcí, zato spolehlivě. Tím je zajištěna nejen spolehlivost, ale i modularita. K vzájemné komunikaci využívá běžné unixové prostředky jako práci se soubory, shellové skripty a roury.

Jinými slovy: síla v jednoduchosti.

Daemontools

DJB přišel s geniálním nápadem: nepoužívat pid soubory vůbec. Místo toho, aby daemona nejdříve poslal na pozadí a pak ho tam složitě hledal, nechá ho na popředí. Ne tak docela. Na pozadí spustí speciální daemon „supervise“, který vůči sobě rozběhne apache na popředí. Vytvoří pojmenovanou rouru, abychom mu mohli posílat příkazy. Supervise pak bude posílat signály svému potomkovi (apache). To je stoprocentně spolehlivé.

supervise

Vyrobit startovací skript pro daemontools není těžké. Vytvoříme adresář „apache“ a v něm skript „run“ s následujícím obsahem:

#!/bin/sh
exec /usr/sbin/apache2 -D SSL -D PHP4 -D NO_DETACH

Toť vše. Parametr NO_DETACH je důležitý, aby apache zůstal na popředí. Nyní supervise, potažmo apache, spustíme. Syntaxe je jednoduchá, supervise cesta_k_adresa­ri_s_run_skrip­tem

.

# supervise apache

Jestliže se podařilo, supervise vytvořil v adresáři apache podadresář „supervise“. Všimněme si v něm pojmenované roury „control“, pomocí níž lze se supervise komunikovat. pstree -a ukáže

supervise apache
  |-apache2 -D SSL -D PHP4 -DNO_DETACH
  |   |-apache2 -D SSL -D PHP4 -DNO_DETACH
  |   |-apache2 -D SSL -D PHP4 -DNO_DETACH
  |   |-apache2 -D SSL -D PHP4 -DNO_DETACH
  |   |-apache2 -D SSL -D PHP4 -DNO_DETACH
  |   |-apache2 -D SSL -D PHP4 -DNO_DETACH
  |   `-apache2 -D SSL -D PHP4 -DNO_DETACH

svc

Ke komunikaci se supervise samozřejmě nebudeme používat rouru, nýbrž program svc. Syntaxe je obdobná, takže

# svc -u apache
# svc -d apache
# svc -h apache
# svc -o apache

u=up, d=down, h=reload(hup), o=once

Vyrobit vlastního „daemona“ tedy není problém. Stačí obyčejný bashový skript o dvou řádcích.

svscan

Svscan každou sekundu zjišťuje změny určitého adresáře. Spouští se obdobně, svscan adresář, nejčastěji svscan /service. Nad každým podadresářem v /service spustí supervise. Stačí tedy rozběhnout svscan a všechny služby se spustí najednou. Neřeší se přitom závislosti. Víc o paralelním spouštění pojednává třetí díl.

(Používáme-li svscan vedle standardních inicializačních skriptů, spouští se, alespoň v Gentoo, jako /etc/init.d/svscan start.)

Předpokládáme tedy, že svscan nad /service běží. Ukončíme předchozí pokus s apache, zabijeme jeho supervise a uděláme symlink našeho adresáře apache do /service. Svscan to zjistí a spustí nad ním supervise. V paměti uvidíme

svscan /service
  |- supervise apache
  |   |-apache2 -D SSL -D PHP4 -DNO_DETACH
  |   |   |-apache2 -D SSL -D PHP4 -DNO_DETACH
  |   |   |-apache2 -D SSL -D PHP4 -DNO_DETACH
  |   |   |-apache2 -D SSL -D PHP4 -DNO_DETACH
  |   |   |-apache2 -D SSL -D PHP4 -DNO_DETACH
  |   |   |-apache2 -D SSL -D PHP4 -DNO_DETACH
  |   |   `-apache2 -D SSL -D PHP4 -DNO_DETACH

K ovládání služeb používáme svc.

Tip: udělal jsem skript sv, kterým můžu ovládat více služeb najednou, například

# sv start apache MySQL /service/qmail-*
# sv restart apache MySQL

svstat

zobrazuje stav služby. Syntaxe svstat adresář, např. svstat /service/apache

Tip: Lze udělat jednoduchý monitor svmon, stačí spustit svstat pro všechny adresáře v /service. Výsledek:

apache: run (pid 6880) 1253212 seconds
atd: run (pid 3164) 1254850 seconds
axfrdns: run (pid 15548) 167907 seconds
dcron: run (pid 3162) 1254850 seconds
dnscache: run (pid 15542) 167908 seconds
MySQL: run (pid 11564) 68195 seconds
qmail-pop3d: run (pid 3166) 1254849 seconds
qmail-send: run (pid 3181) 1254849 seconds
qmail-smtpd: run (pid 3184) 1254849 seconds
...

multilog

je geniální program. Čte ze standardního vstupu data a loguje je do souboru. Umí ještě další kousky: rozdělovat data do více logů podle obsahu, a hlavně – rotovat logy podle velikosti. Velikost určuje za běhu, takže nemůže dojít k přeplnění disku.

Multilog se spouští stejně jako ostatní služby pomocí supervise. Jeho argumentem je adresář, do něhož bude zapisovat soubory a jejich rotace. Run skript:

#!/bin/sh
multilog /var/log/multilog/apache

Zdá se vám to složité? Svscan spouštění multilogu zjednodušuje. Jestliže v našem adresáři apache vyrobíme podadresář log a do něj umístíme run skript multilogu (totéž, ale o úroveň níž), svscan spustí supervise i nad adresářem log. Zároveň prováže standardní výstup run skriptu apache se standardním vstupem run skriptu multilogu. Vše, co apache vyprodukuje, bude logováno a rotováno.

/service/apache/
                ./run
                /log/
                       ./run

v paměti uvidíme

svscan
  |- supervise apache
  |   |-apache2 -D SSL -D PHP4 -DNO_DETACH
  |   |   |-apache2 -D SSL -D PHP4 -DNO_DETACH
  |   |   |-apache2 -D SSL -D PHP4 -DNO_DETACH
  |   |   `-apache2 -D SSL -D PHP4 -DNO_DETACH
  |   `-multilog -tt /var/log/multilog/apache

Respawning

Zajímavou vlastností supervise je respawning. Jestliže apache spadne, bude spuštěn znovu.

Námitka #1: To je blbost.

Dobrá. Skript lze upravit, aby se spustil jen jednou, takto:

#!/bin/sh
svc -o `dirname $PWD`
exec /usr/sbin/apache2 -D SSL -D PHP4 -D NO_DETACH

Osobně jsem už několikrát ocenil, když jsem nemusel fyzicky k serveru. PostgreSQL po některém dotazu zkonzumoval tolik paměti, až spadl. To však neznamenalo, že po restartu fungoval špatně.

Námitka #2: Co když služba padá pořád?

Napsal jsem bashový skript checkrespawn, který start příliš často padající služby na chvíli pozdrží. Dle filozofie DJB toto není vlastnost, která by měla být součástí supervise. Navíc si chování checkresawn můžu sám přizpůsobit. Použití skriptu je snadné – stačí ho přidat do řetězce v run:

#!/bin/sh
exec checkrespawn /usr/sbin/apache2 -D SSL -D PHP4 -D NO_DETACH 

Bohužel

Ne všechny programy jsou přizpůsobitelné daemontools. Daemon se například detachuje a není možné to zvrátit (atd, MySQL). Existuje program fghack, který by měl udržet daemon na popředí. V případě MySQL udrží jeho první proces, jenže ten není tím, který ukončuje MySQL. Ukázalo se nejjednodušší prostě využít pid souboru. MySQL ho naštěstí vytváří bez problémů. Napsal jsem run pro MySQL, jde o bashový skript, který spustí MySQL, a když obdrží od supervise TERM, provede kill podle pid souboru. Nepoddajných služeb je naštěstí málo.

Dan Bernstain je svérázný člověk. Qmail zůstal ve verzi 1.03 a nikdo ho nedonutí napsat pokračování. Podle něj je totiž dokonalý a jeho funkce jsou dostačující. Faktem zůstává, že dodnes nebyla v Qmailu objevena díra. To samé platí o daemontools.

Příště si ukážeme, jak úplně nahradit init runitem od Gerrita Papeho, který daemontools mírně přepracoval a využil.

CS24_early

Viz:
daemontoo/ls
Runit

Autor je lektorem OKsystem.

Byl pro vás článek přínosný?

Autor článku