A runit működése

A Unix/Linux szerverek üzemeltetése wikiből
(Változatok közti eltérés)
(v0.1)
(v0.2)
56. sor: 56. sor:
 
** Alapból a djb-féle /package, /command könyvtárstruktúrát használja, aminek ugyan van előnye, de azért eléggé eltér az LFS-től
 
** Alapból a djb-féle /package, /command könyvtárstruktúrát használja, aminek ugyan van előnye, de azért eléggé eltér az LFS-től
   
== Rendszerindítás runittal ==
+
== A runit részei ==
   
Ha init helyett a runit indul, akkor a /etc/runit/1 nevű scriptet indítja el először. Ennek a tartalma lehet pl ez (a runit-run csomagban is ilyesmi van):
+
A runit csomag számos programot tartalmaz; mindegyik valamilyen viszonylag kicsi részfeladatot lát el, és emiatt egyszerű a kódja.
  +
  +
Tekintsük át ezeket felülről lefelé haladva a processz-fában, amely az init lecserélése esetén pl. így nézhet ki:
  +
  +
<pre>
  +
runit-+-events/0
  +
[...]
  +
|-runsvdir-+-runsv-+-dnscache
  +
| | `-svlogd
  +
| |-runsv-+-run---sleep
  +
| | `-svlogd
  +
| |-runsv-+-qmail-send-+-qmail-clean
  +
| | | |-qmail-lspawn
  +
| | | `-qmail-rspawn
  +
| | `-svlogd
  +
| |-4*[runsv-+-socklog]
  +
| | `-svlogd]
  +
| |-runsv---dd
  +
| |-runsv---klogd
  +
| |-runsv---cron
  +
| |-runsv-+-munin-node
  +
| | `-svlogd
  +
| |-8*[runsv---getty]
  +
| |-runsv-+-ntpd
  +
| | `-svlogd
  +
| |-runsv---sshd---sshd---zsh---screen
  +
| |-runsv-+-smartd
  +
| | `-svlogd
  +
| |-runsv---mdadm
  +
| |-runsv---pound---pound---2*[{pound}]
  +
| `-runsv-+-svlogd
  +
| `-tcpserver
  +
[...]
  +
</pre>
  +
  +
=== runit-init ===
  +
  +
Ez a rendkívül egyszerű program, amely a <tt>/sbin/init</tt>et váltja ki, mindössze 76 sor hosszú.
  +
  +
Ha 1-es a process ID-ja (vagyis ő az init), akkor elindítja (execve() hívással) a <tt>runit</tt>ot.
  +
  +
Ha más a process ID-ja, akkor úgy viselkedik, mint a <tt>telinit</tt>, tehát mintha runlevelt akarnánk váltani, de csak a 0-ás és a 6-os runlevelt ismeri.
  +
* 0: rendszerleállítás.
  +
*# Létrehozza a /etc/runit/stopit file-t (ez csak egy flag-file, a runit megnézi majd, hogy létezik-e és futtható-e)
  +
*# Futtathatóvá teszi
  +
*# Elveszi a jogokat a /etc/runit/reboot file-ról
  +
*# CONT signalt küld az 1-es processznek (ami remélhetőleg a runit, és ettől majd elkezd leállni)
  +
* 6: újraindítás
  +
*# Létrehozza a /etc/runit/stopit file-t (ez csak egy flag-file, a runit megnézi majd, hogy létezik-e és futtható-e)
  +
*# Futtathatóvá teszi
  +
*# Létrehozza a /etc/runit/reboot file-t (ez csak egy flag-file, a runit megnézi majd, hogy létezik-e és futtható-e)
  +
*# Futtathatóvá teszi
  +
*# CONT signalt küld az 1-es processznek (ami remélhetőleg a runit, és ettől majd elkezd leállni)
  +
  +
=== runit ===
  +
  +
Ez a program való arra, hogy 1-es PID-del fusson és az init feladatait ellássa, vagyis:
  +
* elindítsa a rendszert,
  +
* leállítsa a rendszert és
  +
* a kettő között gondoskodjon a szolgáltatások futtatásáról.
  +
  +
Ha init helyett a runit indul, akkor a /etc/runit/1 nevű scriptet indítja el először.
  +
Ennek a tartalma lehet pl. ez (a runit-run csomagban is ilyesmi van):
   
 
<pre>
 
<pre>
79. sor: 79. sor:
 
chmod 0 /etc/runit/stopit
 
chmod 0 /etc/runit/stopit
 
</pre>
 
</pre>
  +
  +
Az 1-es script tartalmazza a rendszerindításkor kiadandó parancsok listáját, emiatt nem hordozható korlátozás nélkül.
   
 
Ne feledjük, hogy a beállított és exportált környezeti változókat a script gyermekfolyamatai örökölni fogják.
 
Ne feledjük, hogy a beállított és exportált környezeti változókat a script gyermekfolyamatai örökölni fogják.
   
Az 1-es script tartalmazza a rendszerindításkor kiadandó parancsok listáját, emiatt nem hordozható korlátozás nélkül.
 
 
Vegyük észre, hogy a fenti scriptben használhattunk volna rc.d mechanizmust is a szolgáltatások elindítására, ami
 
Vegyük észre, hogy a fenti scriptben használhattunk volna rc.d mechanizmust is a szolgáltatások elindítására, ami
 
* kicsit nehezebben átlátható,
 
* kicsit nehezebben átlátható,
 
* egy picit lassúbb, cserébe
 
* egy picit lassúbb, cserébe
 
* modulárisabb és
 
* modulárisabb és
* könnyebb automatizálni a karbantartását.
+
* könnyebb automatizálni a karbantartását - de erre nem biztos, hogy van igény.
  +
Konkrétan írhattuk volna akár azt is, hogy
  +
<pre>
  +
[...]
  +
/etc/init.d/rcS
  +
/etc/init.d/rc 2
  +
[...]
  +
</pre>
   
A /etc/runit/1 után a /etc/runit/2 jön (micsoda meglepetés):
+
A /etc/runit/1 után - micsoda meglepetés - a /etc/runit/2 jön:
   
 
<pre>
 
<pre>
101. sor: 102. sor:
   
 
Innentől, amig a runsvdir ki nem lép, monitorozza a szolgáltatásainkat és fut a rendszer.
 
Innentől, amig a runsvdir ki nem lép, monitorozza a szolgáltatásainkat és fut a rendszer.
  +
  +
A -P kapcsoló hatására saját processzcsoportban indít el minden szolgáltatást.
  +
A "log: sok pont" egy olyan "helyet" hoz létre, ahová a szolgáltatások naplóüzeneteket írhatnak akkor is, ha sem a diszk, sem a hálózat nem elérhető - nevezetesen a runsvdir processz ''nevébe''.
   
 
Láthatjuk, hogy a 2-es script lényegében semmilyen rendszerspecifikus dolgot nem tartalmaz, hordozható.
 
Láthatjuk, hogy a 2-es script lényegében semmilyen rendszerspecifikus dolgot nem tartalmaz, hordozható.
128. sor: 132. sor:
   
 
Elvileg a 3-as script is lehetne rendszerfüggő, mivel a rendszer leállításával kapcsolatos parancsokat kell benne felsorolnunk, azonban gyakran nem az.
 
Elvileg a 3-as script is lehetne rendszerfüggő, mivel a rendszer leállításával kapcsolatos parancsokat kell benne felsorolnunk, azonban gyakran nem az.
  +
  +
Ha
  +
* megnyomjuk a ctrl-alt-delt (és a kernelt úgy állítottuk be, hogy erről értesítse az initet), vagy
  +
* INT signalt küldünk az 1-es processznek, akkor
  +
megnézi, létezik és futtatható-e a /etc/runit/ctrlaltdel script; ha igen
  +
* lefuttatja, majd
  +
* küld magának egy CONT signalt.
   
 
Ha létezik és végrehajtható a /etc/runit/stopit file, akkor a runit a CONT signal hatására leállítja a rendszert.
 
Ha létezik és végrehajtható a /etc/runit/stopit file, akkor a runit a CONT signal hatására leállítja a rendszert.
A ctrl-alt-del-t kezelő scriptből adhatunk futtatási jogot, ha azt akarjuk, hogy a ctrl-alt-del hatása leállítás legyen.
+
Ha a /etc/runit/reboot is létezik és végrehajtható, akkor leállítás helyett újraindítja a gépet.
  +
  +
A /etc/runit/ctrlaltdel script tartalma pl.:
  +
  +
<pre>
  +
#!/bin/sh
  +
  +
PATH=/bin:/usr/bin
  +
MSG="System is going down in 14 seconds..."
  +
  +
# echo 'disabled.' ; exit
  +
touch /etc/runit/stopit
  +
chmod 100 /etc/runit/stopit && echo "$MSG" | wall
  +
/bin/sleep 14
  +
</pre>
  +
  +
=== runvsdir ===
  +
  +
A runsvdir feladata a runsv-folyamatok menedzselése.
  +
  +
Normális körülmények között a /etc/runit/2 indítja:
  +
  +
<pre>
  +
exec env - PATH=$PATH \
  +
runsvdir -P /var/service 'log: ...........................................................................................................................................................................................................................................................................................................................................................................................................'
  +
</pre>
  +
  +
A runsvdir odavált a megadott könyvtárba (itt /var/service, amire mutató symlinket a daemontools-hoz szokott rendszergazda létrehoz a gyökérben), és minden (legfeljebb 1000) itt található alkönyvtárhoz és alkönyvtárra mutató symlinkhez indít egy <tt>runsv</tt>-t.
  +
A ponttal kezdődő nevű könyvtárakat kihagyja.
  +
Ehhez a <tt>runsv</tt>-nek benne kell lennie a PATH-ban.
  +
Ha valamelyik <tt>runsv</tt> kilép, a runsvdir újraindítja.
  +
  +
Ötmásodpercenként megnézi, megváltozott-e a paraméterként kapott könyvtár (mtime, inode, device).
  +
Ha megváltozott, akkor megnézi, van-e benne új alkönyvtár (ha igen, indít hozzá runsv-t), vagy megszűnt-e régi (ekkor leállítja az adott runsv-t és a szolgáltatást, ami hozzá tartozott).
  +
  +
A "log: sokpont" helyre a runsvdir stderr-je irányítódik; amit amúgy a standard errorra írna ki, azt helyette beleírja a saját processznevébe, amiben a pontok egy körpuffert képeznek.
  +
Negyedóránként mindenképpen kiír ide egy pontot, hogy a régi üzenetek elmúlhassanak, ill. megbecsülhető legyen egy üzenet kora.
  +
  +
=== runsv ===
  +
  +
A runsv feladata egy szolgáltatás (és esetleg egy hozzá tartozó naplózószolgáltatás) monitorozása, elindítása, leállítása.
  +
  +
Egyetlen paramétert kap indításkor, egy könyvtár nevét. Tevékenysége így foglalható össze:
  +
# Induláskor odavált, és lefuttatja a ./run programot.
  +
# Ha ez kilép, és létezik a ./finish, lefuttatja azt.
  +
# Ha a ./finish nem létezik, vagy kilép, újra elindítja a ./run-t.
  +
  +
Ha a scriptek "azonnal" kilépnek, a runsv egy másodpercet vár az újraindítási kísérletek között.
  +
  +
Ha létezik a ./down nevű file, a runsv nem indítja el azonnal a ./run-t, csak akkor, ha erre külön megkérjük.
  +
  +
A létezik a ./log könyvtár, akkor a runsv nyit egy pipe-ot, a ./run és a ./finish standard outputját abba irányítja
  +
(ezt megteheti, hiszen az ő gyermekfolyamatai, vagyis tőle öröklik az stdout filedeszkriptort), és a ./log könyvtárban
  +
található run és finish programokkal pontosan úgy jár el, mint a szolgáltatás hasonló nevű file-jaival.
  +
A log/run abból a pipe-ból fog olvasni, amiben a ./run ír, tehát a szolgáltatás standard outputja megjelenik a logoló alszolgáltatás
  +
standard inputján.
  +
  +
A runsv létrehoz egy ./supervise (és esetleg ./log/supervise, ha van ./log) könyvtárat.
  +
A supervise könyvtár tartalma:
  +
  +
* status: bináris file, ami a szolgáltatás állapotával kapcsolatos információkat tartalmazza; kompatibilis a daemontools supervise programjának status file-jával
  +
* stat: szövegfile. Tartalma lehet:
  +
** "run": a szolgáltatás fut
  +
** "down": a szolgáltatás nem fut
  +
** "finish": a szolgáltatáshoz tartozó finish program fut
  +
** a fentiek még kiegészülhetnek a következőkkel:
  +
*** ", paused": a szolgáltatást felfüggesztettük
  +
*** ", got TERM": a szolgáltatásnak küldtünk TERM signalt, de még nem lépett ki
  +
*** ", want down": a szolgáltatást le szeretnénk állítani, de még nem állt le
  +
*** ", want exit": a runsv-t is le szeretnénk állítani, de ehhez előbb a szolgáltatást kell, az pedig még nem állt le
  +
* pid: a szolgáltatáshoz tartozó éppen futó program (run vagy finish) process ID-ja
  +
* control: egy fifo, amibe parancsokat írhatunk (ezt csinálja az sv(8) program, l. később):
  +
** u: "up". Ha a szolgáltatás nem fut, elindítja; amikor kilép, újraindítja.
  +
** d: "down". Ha a szolgáltatás fut, kap egy TERM, majd egy CONT signalt (hogy a TERMre akkor is tudjon reagálni, ha fel volt függesztve). Ha a ./run kilép, lefut a ./finish; amikor az is kilép, a runsv nem indítja újra a szolgáltatást.
  +
** o: "once". Ha a szolgáltatás nem fut, elindítja. Amikor kilép, nem indítja újra (de a finish lefut).
  +
** p: "pause". Ha a szolgáltatás fut, felfüggeszti (STOP signalt küld neki).
  +
** c: "continue". Ha a szolgáltatás fut, folytatja (CONT signalt küld).
  +
** h: "hangup". Ha a szolgáltatás fut, HUP signalt küld neki.
  +
** a: "alarm". ALARM signal.
  +
** i: "interrupt". INT signal.
  +
** q: "quit". QUIT signal.
  +
** 1: USR1 signal.
  +
** 2: USR2 signal.
  +
** t: "terminate". TERM signal. Ha a szolgáltatás "up" állapotban volt, és a TERM hatására kilép, akkor ez lényegében egy újraindítással egyenértékű, mert a runsv újra el fogja indítani.
  +
** k: "kill". KILL signal.
  +
** x: "exit":
  +
*** Ha a szolgáltatás fut, kap egy TERM, majd egy CONT signalt.
  +
*** Amikor kilép, lefut a finish.
  +
*** Ha nincs log alszolgáltatás, a runsv kilép.
  +
*** Ha van log, a runsv lezárja a log standard inputját, és megvárja, amíg a log/run is kilép.
  +
*** Lefut a log/finish.
  +
*** A runsv kilép.
  +
*** A log szolgáltatásnak nem lehet exit parancsot küldeni (ill. lehet, de ignorálja).
  +
  +
Fontos, hogy a parancsok pontosan egy karakterből állnak, sorvége-jel sem kell utánuk;
  +
tehát ha nem az sv programmal, hanem kézzel akarjuk kiadni őket, és precízek akarunk lenni, akkor <tt>echo -n</tt>-t használjunk.
  +
Ha nem akarunk precízek lenni, az sem baj, mert a runsv nem törődik az ismeretlen parancskarakterekkel.
  +
  +
Gondoljunk arra, hogy ha nem fut a runsv (és így nincs, aki elvegye a fifoba írt karaktert), akkor az echo blokkolódni fog.
  +
  +
Ha a runsv TERM signalt kap, úgy tesz, mintha "x" parancsot kapott volna.
  +
  +
==== A parancsok hatásának testreszabása ====
  +
  +
Megtehetjük, hogy a fenti parancsok hatását megváltoztatjuk.
  +
  +
Ehhez létre kell hoznunk a szolgáltatás könyvtárában egy control nevű alkönyvtárat, amiben a parancsokról elnevezett programokat helyezhetünk el.
  +
Ha olyan parancsot adunk ki, amelyhez tartozó control program létezik és futtatható, akkor a runsv
  +
* lefuttatja a control/parancs programot, és megvárja, amíg kilép;
  +
* ha a visszatérési érték sikeres, a runsv nem kézbesíti a kért signalt a szolgáltatásnak;
  +
* ha sikertelen, akkor kézbesíti.
  +
  +
A logoló alszolgáltatás vezérlése nem testreszabható.
  +
  +
=== sv ===
  +
  +
Az sv feladata az, hogy információt szolgáltasson a runsv által menedzselt szolgáltatások állapotáról ill. hogy parancsokat adjon a runsv-nek.
  +
Képes továbbá korlátozottan emulálni egy SystemV initscript működését.
  +
  +
Indítása:
  +
  +
<pre>
  +
# sv [-v] [-w sec] command services
  +
  +
# /etc/init.d/service [-w sec] command
  +
</pre>
  +
  +
A <tt>services</tt> helyén felsorolhatunk egy vagy több szolgáltatást, amikre hatni szeretnénk; a hozzájuk tartozó runsv-s directory nevét kell megadni.
  +
  +
A <tt>command</tt> lehet a runsv által ismert egykarakteres parancsok valamelyike (de kiírhatjuk a teljes parancsot is, mert csak az első karaktert nézi), valamint a következők, amelyek a SystemV-initscript-emulációt segítik:
  +
  +
* start: mint az "up", de megvárja, hogy a szolgáltatás el is induljon (alapértelmezés szerint 7 másodpercet). A szolgáltatáshoz tartozó check scripttel ellenőrzi, hogy elindult-e, vagy, ha ilyen nincs, akkor azt várja meg, hogy epszilonnál hosszabb ideje fusson. Ha 7 (vagy <tt>sec</tt>) mp után nincs eredmény, kilép, a szolgáltatás pedig továbbra is próbál elindulni.
  +
* stop: mint a "down", de megvárja, hogy a szolgáltatás le is állon (mint fent).
  +
* restart: mint egy "down", majd egy "up" egymás után.
  +
* shutdown: mint az "exit", de megvárja (timeout mint fent), hogy a runsv is kilépjen.
  +
* force-stop: mint a "stop", de időtúllépés esetén KILL signalt küld a szolgáltatásnak.
  +
* force-reload: mint egy "term" és egy "continue", de ha nem áll le időben, kap egy KILL signalt is.
  +
* force-restart: mint a "restart", de ha nem áll le időben, kap egy KILL signalt is.
  +
* force-shutdown: mint a "shutdown", de ... you know the rest. :)
  +
  +
Emellett van még egy <tt>check</tt> parancs, ami azt vizsgálja meg, hogy a szolgáltatás az általunk utoljára kért állapotban van-e, ill. ha nincs, akkor a megadott ideig vár arra, hogy oda eljusson.
  +
  +
Az initscript-emuláció értelme az, hogy ha pl. van egy apache szolgáltatásunk, akkor megcsinálhatjuk ezt (debianos példa):
  +
  +
<pre>
  +
# dpkg-divert --local --rename /etc/init.d/apache
  +
# ln -s /usr/bin/sv /etc/init.d/apache
  +
</pre>
  +
  +
Így a /etc/init.d/apache segítségével majdnem a szokásos módon kezelhetjük az apache-t: működik a stop, a restart, a start és társai, mert az sv megnézi, milyen néven hívtuk meg, és az olyan nevű szolgáltatást próbálja menedzselni.
  +
Sajnos pl. "reload" akciót nem tudunk csinálni, ezt az sv egyelőre nem támogatja - pedig a check mintájára nem lenne nehéz.
  +
Ez nyilván nem jelenti azt, hogy nem tudjuk újraolvastatni az apache-val a konfigurációját, ha az apache amúgy képes erre (mellesleg képes), csak azt, hogy a runit ehhez nem nyújt nekünk segítséget.
  +
  +
=== Runlevel-emuláció: runsvchdir ===
  +
  +
Ha nagyon a szívünkhöz nőttek a runlevelek, runit mellett is használhatjuk őket; ráadásul tetszőlegesen sok lehet belőlük, és szabadon adhatunk nekik neveket.
  +
  +
Az elgondolás lényege az, hogy több /var/service-szerű könyvtárat csinálunk (minden runlevelhez egyet), más-más szolgáltatáskönyvtárak symlinkjeivel, és a runsvdirt rábeszéljük, hogy váltson át egy másikba, és értelemszerűen állítsa le azokat a szolgáltatásokat, amelyeknek az új könyvtárban nincs symlinkje, azokat pedig, amelyeknek az előzőben nem volt, de itt van, indítsa el.
  +
  +
Ehhez
  +
  +
# létre kell hoznunk valahol (alapértelmezés szerint a /etc/runit/runsvdir/ könyvtárban) egy-egy ilyen runlevel-könyvtárat
  +
# ebben létrehozni egy "current" nevű symlinket, ami az aktuális runlevelre mutat
  +
# a /var/service-t (vagy ami a /etc/runit/2-ben a runsvdir paramétere) le kell cserélnünk egy symlinkre, ami erre a current nevű symlinkre mutat.
  +
  +
Ezután a current symlink átállítása runlevel-váltást eredményez.
  +
Ezt csinálja meg a runsvchdir parancs, és ráadásul egy az éppen elhagyott runlevelre mutató symlinket is létrehoz "previous" néven.
  +
Ez azért jó, mert attól, hogy a runsv processzek TERM signalt küldtek a lelövendő szolgáltatásoknak, még nem biztos, hogy mindegyik le is állt; a "previous" symlinken keresztül követhetjük nyomon a legegyszerűbben, hogy az előző runlevelben futó szolgáltatások státusza hogyan alakul.
  +
  +
=== chpst ===
  +
  +
A chpst feladata az, hogy valamilyen környezetet beállítson, és abban elindítson egy gyermekfolyamatot.
  +
Ennek megfelelően a unixos folyamatok állapotterének jelentős részét tetszésünk szerint beállíthatjuk:
  +
  +
* UID
  +
* GID
  +
* supplementary groups ("kiegészítő csoporttagságok")
  +
* környezeti változók (a daemontools envdir-jéhez hasonló módon; l. lejjebb)
  +
* chroot
  +
* nice
  +
* kölcsönös kizárás (a megadott program csak egy példányban futhasson)
  +
* erőforráskorlátok:
  +
** memória (vagy az összes, vagy csak az adatszegmens)
  +
** megnyitható file-ok száma
  +
** processzek száma
  +
** létrehozható file-ok maximális mérete
  +
** core file max. mérete
  +
* új process group létrehozása a program elindítása előtt
  +
* standard filedeszkriptorok bezárása (input, output, error szelektíven) a program elindítása előtt
  +
  +
Ami hiányzik, pedig nem lenne rossz:
  +
  +
* scheduler beállítása (lehetne pl. realtime, bár ez nem hordozható)
  +
* capability-k beállítása (szintén nem hordozható)
  +
* umask
  +
* munkakönyvtár
  +
* max. CPU-idő
  +
  +
A környezeti változók beállítása úgy történik, hogy létrehozunk egy könyvtárat, amiben minden file egy beállítandó környezeti változóról van elnevezve.
  +
A változó értéke az adott file tartalmának első sora lesz. A nullás karaktereket újsorrá alakítja, a sorvégi szóközöket és tabulátorokat pedig lenyeli.
  +
Ha a file nulla byte hosszú, akkor a róla elnevezett változót a chpst törli a környezetből.
  +
  +
A kölcsönös kizárás megvalósításához a chpst megpróbál lockolni egy file-t; ha sikerül, akkor elindítja a megadott programot úgy, hogy az megkapja a lockolt file-t is.
  +
Ha az elindított program bezárja az öröklött file-deszkriptort, akkor a kölcsönös kizárás nem biztosított; szerencsére kevés program csinálja ezt.
   
 
== Ajánlott irodalom ==
 
== Ajánlott irodalom ==
   
* [http://smarden.sunsite.dk/runit/ runit - a UNIX init scheme with service supervision] - a runit honlapja
+
* [http://smarden.org/runit/ runit - a UNIX init scheme with service supervision] - a runit honlapja

A lap 2006. október 16., 21:16-kori változata

A daemontools szabad reimplementációja; majdnem minden, ami igaz a runitre, igaz a daemontoolsra is, és fordítva. Az ötlet az, hogy juttassuk érvényre a Unix-filozófiát: egy program egy dolgot csináljon, lehetőleg jól és hatékonyan, de legyen képes más programokkal együttműködni. A runit csomag egy-egy programja tipikusan kevesebb, mint ezer sor C-kódból áll, és sosem nő a memóriafoglalása. Ha a runitot statikusan linkeljük a dietlibc-hez, akkor az 1-es processzként futó program csak kb. 20KB-ot vesz igénybe. A BSD-licenchez hasonló licenc alatt érhető el.

Az initet és az initscripteket váltja ki, valamint segít a naplózás megszervezésében. Be lehet vezetni kicsit, nagyon, és teljesen.

Ha kicsit vezetjük be, akkor megmarad a System V init, de mellette kapunk:

  • Szolgáltatásmenedzsmentet. A szolgáltatások
    • tiszta környezettel indulnak
    • újraindulnak, ha kilépnek
    • opcionálisan megbízható naplózást kapnak
      • naplóüzenet nem vész el naplózás közben
      • méret alapján rotált logok
      • utófeldolgozás lehetősége
      • akkor is működik, ha a szolgáltatás chrootban fut
      • a logolás és a szolgáltatás egymástól függetlenül újraindítható/leállítható
    • kiválasztott felhasználók sudo nélkül is menedzselhetik őket
    • könnyedén lekérdezhető a státuszuk
    • könnyedén küldhető nekik signal
    • könnyedén biztosíthatjuk ezeket a tulajdonságokat a felhasználók saját "szolgáltatásainak" - pl.:
      • screen, benne irssi
      • eggdrop bot (sajnos át kell írni, mert mindenképpen a háttérbe akarja rakni magát)
      • fetchmail
      • signify
      • akár apache vagy egyéb

Ha nagyon bevezetjük, akkor emellett még az initet is kiváltjuk, de a rendszerindulást továbbra is a hagyományos initscriptjeink vezénylik le, csak az inittab válik feleslegessé. Emellett gyorsabbá válik az indítás és a leállítás, mert sokmindent egyszerre fog csinálni a runit (azért nem kell azzal foglalkoznia, hogy minek mi után kell jönnie, mert ezt az intelligenciát nekünk kell megavlósítanunk a run scriptekben - l. később).

Ha teljesen bevezetjük, akkor az rcS után már csak a runit által menedzselt szolgáltatásaink futnak, vagyis az "rc 2" már nem kell, hogy lefusson.

Tartalomjegyzék

1 Architektúra

  • Minden szolgáltatáshoz tartozik egy könyvtár (directory), amiben:
    • Megvan az a program (általában script), ami az adott szolgáltatást elindítja és futtatja (és run névre hallgat); addig nem léphet ki, amíg a szolgáltatás nem lépett ki
    • A runit létrehoz egy supervise könyvtárat - l. később
    • Lehet egy down nevű file, ami azt jelzi, hogy a szolgáltatás normális állapota az, hogy nem fut
    • Lehet egy log nevű könyvtár, ha a szolgáltatáshoz naplózást is szeretnénk - l. később
    • Lehet egy check nevű program, ami 0-ás visszatérési értékkel lép ki, ha a szolgáltatás működik (ami nem azonos azzal, hogy fut), egyébként nullától különbözővel
    • Lehet egy finish nevű program, amit abban az esetben kell lefuttatni, ha a run kilép

Ezeket az ún. service directorykat általában a /service alá symlinkeljük be, ha az adott szolgáltatást futtatni akarjuk.

A /service könyvtárat figyeli a runsvdir, és minden alkönyvtárra indít egy runsv folyamatot. Erről rövidsen lesz szó.

2 Telepítés

  • Ajánlott verzió: legalább 1.6 (bár a korábbiak is használhatók)
  • Debianhoz és Ubuntuhoz van szép runit csomag, ez csak felrakja a runitot, de nem cseréli le az initet - mindenféle körültekintés nélkül telepíthetjük
    • Van runit-run csomag is, ami le is cseréli az initet - csak óvatosan: reboot után, ha nem vigyázunk, csak a konzolról fogunk tudni belépni, mert nem indul el az sshd
  • Nyilván fel lehet rakni forrásból is
    • Alapból a djb-féle /package, /command könyvtárstruktúrát használja, aminek ugyan van előnye, de azért eléggé eltér az LFS-től

3 A runit részei

A runit csomag számos programot tartalmaz; mindegyik valamilyen viszonylag kicsi részfeladatot lát el, és emiatt egyszerű a kódja.

Tekintsük át ezeket felülről lefelé haladva a processz-fában, amely az init lecserélése esetén pl. így nézhet ki:

runit-+-events/0
[...]
      |-runsvdir-+-runsv-+-dnscache
      |          |       `-svlogd
      |          |-runsv-+-run---sleep
      |          |       `-svlogd
      |          |-runsv-+-qmail-send-+-qmail-clean
      |          |       |            |-qmail-lspawn
      |          |       |            `-qmail-rspawn
      |          |       `-svlogd
      |          |-4*[runsv-+-socklog]
      |          |          `-svlogd]
      |          |-runsv---dd
      |          |-runsv---klogd
      |          |-runsv---cron
      |          |-runsv-+-munin-node
      |          |       `-svlogd
      |          |-8*[runsv---getty]
      |          |-runsv-+-ntpd
      |          |       `-svlogd
      |          |-runsv---sshd---sshd---zsh---screen
      |          |-runsv-+-smartd
      |          |       `-svlogd
      |          |-runsv---mdadm
      |          |-runsv---pound---pound---2*[{pound}]
      |          `-runsv-+-svlogd
      |                  `-tcpserver
[...]

3.1 runit-init

Ez a rendkívül egyszerű program, amely a /sbin/initet váltja ki, mindössze 76 sor hosszú.

Ha 1-es a process ID-ja (vagyis ő az init), akkor elindítja (execve() hívással) a runitot.

Ha más a process ID-ja, akkor úgy viselkedik, mint a telinit, tehát mintha runlevelt akarnánk váltani, de csak a 0-ás és a 6-os runlevelt ismeri.

  • 0: rendszerleállítás.
    1. Létrehozza a /etc/runit/stopit file-t (ez csak egy flag-file, a runit megnézi majd, hogy létezik-e és futtható-e)
    2. Futtathatóvá teszi
    3. Elveszi a jogokat a /etc/runit/reboot file-ról
    4. CONT signalt küld az 1-es processznek (ami remélhetőleg a runit, és ettől majd elkezd leállni)
  • 6: újraindítás
    1. Létrehozza a /etc/runit/stopit file-t (ez csak egy flag-file, a runit megnézi majd, hogy létezik-e és futtható-e)
    2. Futtathatóvá teszi
    3. Létrehozza a /etc/runit/reboot file-t (ez csak egy flag-file, a runit megnézi majd, hogy létezik-e és futtható-e)
    4. Futtathatóvá teszi
    5. CONT signalt küld az 1-es processznek (ami remélhetőleg a runit, és ettől majd elkezd leállni)

3.2 runit

Ez a program való arra, hogy 1-es PID-del fusson és az init feladatait ellássa, vagyis:

  • elindítsa a rendszert,
  • leállítsa a rendszert és
  • a kettő között gondoskodjon a szolgáltatások futtatásáról.

Ha init helyett a runit indul, akkor a /etc/runit/1 nevű scriptet indítja el először. Ennek a tartalma lehet pl. ez (a runit-run csomagban is ilyesmi van):

#!/bin/sh
# system one time tasks

PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/usr/bin/X11
export PATH

/etc/init.d/rcS
/etc/init.d/makedev start
/etc/init.d/systune start
/etc/init.d/ud start
/etc/firewall/firewall start
/etc/init.d/rmnologin start
/etc/init.d/xend start
/etc/init.d/xendomains start

touch /etc/runit/stopit
chmod 0 /etc/runit/stopit

Az 1-es script tartalmazza a rendszerindításkor kiadandó parancsok listáját, emiatt nem hordozható korlátozás nélkül.

Ne feledjük, hogy a beállított és exportált környezeti változókat a script gyermekfolyamatai örökölni fogják.

Vegyük észre, hogy a fenti scriptben használhattunk volna rc.d mechanizmust is a szolgáltatások elindítására, ami

  • kicsit nehezebben átlátható,
  • egy picit lassúbb, cserébe
  • modulárisabb és
  • könnyebb automatizálni a karbantartását - de erre nem biztos, hogy van igény.

Konkrétan írhattuk volna akár azt is, hogy

[...]
/etc/init.d/rcS
/etc/init.d/rc 2
[...]

A /etc/runit/1 után - micsoda meglepetés - a /etc/runit/2 jön:

#!/bin/sh

PATH=/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/X11R6/bin

exec env - PATH=$PATH \
runsvdir -P /var/service 'log: ...........................................................................................................................................................................................................................................................................................................................................................................................................'

Innentől, amig a runsvdir ki nem lép, monitorozza a szolgáltatásainkat és fut a rendszer.

A -P kapcsoló hatására saját processzcsoportban indít el minden szolgáltatást. A "log: sok pont" egy olyan "helyet" hoz létre, ahová a szolgáltatások naplóüzeneteket írhatnak akkor is, ha sem a diszk, sem a hálózat nem elérhető - nevezetesen a runsvdir processz nevébe.

Láthatjuk, hogy a 2-es script lényegében semmilyen rendszerspecifikus dolgot nem tartalmaz, hordozható.

Ha kilép a runsvdir, kilép a /etc/runit/2 is; ha a visszatérési értéke 0 volt, elindul a /etc/runit/3, ami a leállításért felelős.

Ha nem 0 volt a 2-es script visszatérési értéke, akkor a runit újra elindítja.

A 3-as script tartalma például a következő lehet:

#!/bin/sh
exec 2>&1

PATH=/sbin:/bin:/usr/sbin:/usr/bin

LAST=0
test -x /etc/runit/reboot && LAST=6

echo 'Waiting for services to stop...'
sv -w196 force-stop /var/service/*
sv exit /var/service/*

echo 'Shutdown...'
/etc/init.d/rc $LAST

Elvileg a 3-as script is lehetne rendszerfüggő, mivel a rendszer leállításával kapcsolatos parancsokat kell benne felsorolnunk, azonban gyakran nem az.

Ha

  • megnyomjuk a ctrl-alt-delt (és a kernelt úgy állítottuk be, hogy erről értesítse az initet), vagy
  • INT signalt küldünk az 1-es processznek, akkor

megnézi, létezik és futtatható-e a /etc/runit/ctrlaltdel script; ha igen

  • lefuttatja, majd
  • küld magának egy CONT signalt.

Ha létezik és végrehajtható a /etc/runit/stopit file, akkor a runit a CONT signal hatására leállítja a rendszert. Ha a /etc/runit/reboot is létezik és végrehajtható, akkor leállítás helyett újraindítja a gépet.

A /etc/runit/ctrlaltdel script tartalma pl.:

#!/bin/sh

PATH=/bin:/usr/bin
MSG="System is going down in 14 seconds..."

# echo 'disabled.' ; exit
touch /etc/runit/stopit
chmod 100 /etc/runit/stopit && echo "$MSG" | wall
/bin/sleep 14

3.3 runvsdir

A runsvdir feladata a runsv-folyamatok menedzselése.

Normális körülmények között a /etc/runit/2 indítja:

exec env - PATH=$PATH \
runsvdir -P /var/service 'log: ...........................................................................................................................................................................................................................................................................................................................................................................................................'

A runsvdir odavált a megadott könyvtárba (itt /var/service, amire mutató symlinket a daemontools-hoz szokott rendszergazda létrehoz a gyökérben), és minden (legfeljebb 1000) itt található alkönyvtárhoz és alkönyvtárra mutató symlinkhez indít egy runsv-t. A ponttal kezdődő nevű könyvtárakat kihagyja. Ehhez a runsv-nek benne kell lennie a PATH-ban. Ha valamelyik runsv kilép, a runsvdir újraindítja.

Ötmásodpercenként megnézi, megváltozott-e a paraméterként kapott könyvtár (mtime, inode, device). Ha megváltozott, akkor megnézi, van-e benne új alkönyvtár (ha igen, indít hozzá runsv-t), vagy megszűnt-e régi (ekkor leállítja az adott runsv-t és a szolgáltatást, ami hozzá tartozott).

A "log: sokpont" helyre a runsvdir stderr-je irányítódik; amit amúgy a standard errorra írna ki, azt helyette beleírja a saját processznevébe, amiben a pontok egy körpuffert képeznek. Negyedóránként mindenképpen kiír ide egy pontot, hogy a régi üzenetek elmúlhassanak, ill. megbecsülhető legyen egy üzenet kora.

3.4 runsv

A runsv feladata egy szolgáltatás (és esetleg egy hozzá tartozó naplózószolgáltatás) monitorozása, elindítása, leállítása.

Egyetlen paramétert kap indításkor, egy könyvtár nevét. Tevékenysége így foglalható össze:

  1. Induláskor odavált, és lefuttatja a ./run programot.
  2. Ha ez kilép, és létezik a ./finish, lefuttatja azt.
  3. Ha a ./finish nem létezik, vagy kilép, újra elindítja a ./run-t.

Ha a scriptek "azonnal" kilépnek, a runsv egy másodpercet vár az újraindítási kísérletek között.

Ha létezik a ./down nevű file, a runsv nem indítja el azonnal a ./run-t, csak akkor, ha erre külön megkérjük.

A létezik a ./log könyvtár, akkor a runsv nyit egy pipe-ot, a ./run és a ./finish standard outputját abba irányítja (ezt megteheti, hiszen az ő gyermekfolyamatai, vagyis tőle öröklik az stdout filedeszkriptort), és a ./log könyvtárban található run és finish programokkal pontosan úgy jár el, mint a szolgáltatás hasonló nevű file-jaival. A log/run abból a pipe-ból fog olvasni, amiben a ./run ír, tehát a szolgáltatás standard outputja megjelenik a logoló alszolgáltatás standard inputján.

A runsv létrehoz egy ./supervise (és esetleg ./log/supervise, ha van ./log) könyvtárat. A supervise könyvtár tartalma:

  • status: bináris file, ami a szolgáltatás állapotával kapcsolatos információkat tartalmazza; kompatibilis a daemontools supervise programjának status file-jával
  • stat: szövegfile. Tartalma lehet:
    • "run": a szolgáltatás fut
    • "down": a szolgáltatás nem fut
    • "finish": a szolgáltatáshoz tartozó finish program fut
    • a fentiek még kiegészülhetnek a következőkkel:
      • ", paused": a szolgáltatást felfüggesztettük
      • ", got TERM": a szolgáltatásnak küldtünk TERM signalt, de még nem lépett ki
      • ", want down": a szolgáltatást le szeretnénk állítani, de még nem állt le
      • ", want exit": a runsv-t is le szeretnénk állítani, de ehhez előbb a szolgáltatást kell, az pedig még nem állt le
  • pid: a szolgáltatáshoz tartozó éppen futó program (run vagy finish) process ID-ja
  • control: egy fifo, amibe parancsokat írhatunk (ezt csinálja az sv(8) program, l. később):
    • u: "up". Ha a szolgáltatás nem fut, elindítja; amikor kilép, újraindítja.
    • d: "down". Ha a szolgáltatás fut, kap egy TERM, majd egy CONT signalt (hogy a TERMre akkor is tudjon reagálni, ha fel volt függesztve). Ha a ./run kilép, lefut a ./finish; amikor az is kilép, a runsv nem indítja újra a szolgáltatást.
    • o: "once". Ha a szolgáltatás nem fut, elindítja. Amikor kilép, nem indítja újra (de a finish lefut).
    • p: "pause". Ha a szolgáltatás fut, felfüggeszti (STOP signalt küld neki).
    • c: "continue". Ha a szolgáltatás fut, folytatja (CONT signalt küld).
    • h: "hangup". Ha a szolgáltatás fut, HUP signalt küld neki.
    • a: "alarm". ALARM signal.
    • i: "interrupt". INT signal.
    • q: "quit". QUIT signal.
    • 1: USR1 signal.
    • 2: USR2 signal.
    • t: "terminate". TERM signal. Ha a szolgáltatás "up" állapotban volt, és a TERM hatására kilép, akkor ez lényegében egy újraindítással egyenértékű, mert a runsv újra el fogja indítani.
    • k: "kill". KILL signal.
    • x: "exit":
      • Ha a szolgáltatás fut, kap egy TERM, majd egy CONT signalt.
      • Amikor kilép, lefut a finish.
      • Ha nincs log alszolgáltatás, a runsv kilép.
      • Ha van log, a runsv lezárja a log standard inputját, és megvárja, amíg a log/run is kilép.
      • Lefut a log/finish.
      • A runsv kilép.
      • A log szolgáltatásnak nem lehet exit parancsot küldeni (ill. lehet, de ignorálja).

Fontos, hogy a parancsok pontosan egy karakterből állnak, sorvége-jel sem kell utánuk; tehát ha nem az sv programmal, hanem kézzel akarjuk kiadni őket, és precízek akarunk lenni, akkor echo -n-t használjunk. Ha nem akarunk precízek lenni, az sem baj, mert a runsv nem törődik az ismeretlen parancskarakterekkel.

Gondoljunk arra, hogy ha nem fut a runsv (és így nincs, aki elvegye a fifoba írt karaktert), akkor az echo blokkolódni fog.

Ha a runsv TERM signalt kap, úgy tesz, mintha "x" parancsot kapott volna.

3.4.1 A parancsok hatásának testreszabása

Megtehetjük, hogy a fenti parancsok hatását megváltoztatjuk.

Ehhez létre kell hoznunk a szolgáltatás könyvtárában egy control nevű alkönyvtárat, amiben a parancsokról elnevezett programokat helyezhetünk el. Ha olyan parancsot adunk ki, amelyhez tartozó control program létezik és futtatható, akkor a runsv

  • lefuttatja a control/parancs programot, és megvárja, amíg kilép;
  • ha a visszatérési érték sikeres, a runsv nem kézbesíti a kért signalt a szolgáltatásnak;
  • ha sikertelen, akkor kézbesíti.

A logoló alszolgáltatás vezérlése nem testreszabható.

3.5 sv

Az sv feladata az, hogy információt szolgáltasson a runsv által menedzselt szolgáltatások állapotáról ill. hogy parancsokat adjon a runsv-nek. Képes továbbá korlátozottan emulálni egy SystemV initscript működését.

Indítása:

# sv [-v] [-w sec] command services

# /etc/init.d/service [-w sec] command

A services helyén felsorolhatunk egy vagy több szolgáltatást, amikre hatni szeretnénk; a hozzájuk tartozó runsv-s directory nevét kell megadni.

A command lehet a runsv által ismert egykarakteres parancsok valamelyike (de kiírhatjuk a teljes parancsot is, mert csak az első karaktert nézi), valamint a következők, amelyek a SystemV-initscript-emulációt segítik:

  • start: mint az "up", de megvárja, hogy a szolgáltatás el is induljon (alapértelmezés szerint 7 másodpercet). A szolgáltatáshoz tartozó check scripttel ellenőrzi, hogy elindult-e, vagy, ha ilyen nincs, akkor azt várja meg, hogy epszilonnál hosszabb ideje fusson. Ha 7 (vagy sec) mp után nincs eredmény, kilép, a szolgáltatás pedig továbbra is próbál elindulni.
  • stop: mint a "down", de megvárja, hogy a szolgáltatás le is állon (mint fent).
  • restart: mint egy "down", majd egy "up" egymás után.
  • shutdown: mint az "exit", de megvárja (timeout mint fent), hogy a runsv is kilépjen.
  • force-stop: mint a "stop", de időtúllépés esetén KILL signalt küld a szolgáltatásnak.
  • force-reload: mint egy "term" és egy "continue", de ha nem áll le időben, kap egy KILL signalt is.
  • force-restart: mint a "restart", de ha nem áll le időben, kap egy KILL signalt is.
  • force-shutdown: mint a "shutdown", de ... you know the rest. :)

Emellett van még egy check parancs, ami azt vizsgálja meg, hogy a szolgáltatás az általunk utoljára kért állapotban van-e, ill. ha nincs, akkor a megadott ideig vár arra, hogy oda eljusson.

Az initscript-emuláció értelme az, hogy ha pl. van egy apache szolgáltatásunk, akkor megcsinálhatjuk ezt (debianos példa):

# dpkg-divert --local --rename /etc/init.d/apache
# ln -s /usr/bin/sv /etc/init.d/apache

Így a /etc/init.d/apache segítségével majdnem a szokásos módon kezelhetjük az apache-t: működik a stop, a restart, a start és társai, mert az sv megnézi, milyen néven hívtuk meg, és az olyan nevű szolgáltatást próbálja menedzselni. Sajnos pl. "reload" akciót nem tudunk csinálni, ezt az sv egyelőre nem támogatja - pedig a check mintájára nem lenne nehéz. Ez nyilván nem jelenti azt, hogy nem tudjuk újraolvastatni az apache-val a konfigurációját, ha az apache amúgy képes erre (mellesleg képes), csak azt, hogy a runit ehhez nem nyújt nekünk segítséget.

3.6 Runlevel-emuláció: runsvchdir

Ha nagyon a szívünkhöz nőttek a runlevelek, runit mellett is használhatjuk őket; ráadásul tetszőlegesen sok lehet belőlük, és szabadon adhatunk nekik neveket.

Az elgondolás lényege az, hogy több /var/service-szerű könyvtárat csinálunk (minden runlevelhez egyet), más-más szolgáltatáskönyvtárak symlinkjeivel, és a runsvdirt rábeszéljük, hogy váltson át egy másikba, és értelemszerűen állítsa le azokat a szolgáltatásokat, amelyeknek az új könyvtárban nincs symlinkje, azokat pedig, amelyeknek az előzőben nem volt, de itt van, indítsa el.

Ehhez

  1. létre kell hoznunk valahol (alapértelmezés szerint a /etc/runit/runsvdir/ könyvtárban) egy-egy ilyen runlevel-könyvtárat
  2. ebben létrehozni egy "current" nevű symlinket, ami az aktuális runlevelre mutat
  3. a /var/service-t (vagy ami a /etc/runit/2-ben a runsvdir paramétere) le kell cserélnünk egy symlinkre, ami erre a current nevű symlinkre mutat.

Ezután a current symlink átállítása runlevel-váltást eredményez. Ezt csinálja meg a runsvchdir parancs, és ráadásul egy az éppen elhagyott runlevelre mutató symlinket is létrehoz "previous" néven. Ez azért jó, mert attól, hogy a runsv processzek TERM signalt küldtek a lelövendő szolgáltatásoknak, még nem biztos, hogy mindegyik le is állt; a "previous" symlinken keresztül követhetjük nyomon a legegyszerűbben, hogy az előző runlevelben futó szolgáltatások státusza hogyan alakul.

3.7 chpst

A chpst feladata az, hogy valamilyen környezetet beállítson, és abban elindítson egy gyermekfolyamatot. Ennek megfelelően a unixos folyamatok állapotterének jelentős részét tetszésünk szerint beállíthatjuk:

  • UID
  • GID
  • supplementary groups ("kiegészítő csoporttagságok")
  • környezeti változók (a daemontools envdir-jéhez hasonló módon; l. lejjebb)
  • chroot
  • nice
  • kölcsönös kizárás (a megadott program csak egy példányban futhasson)
  • erőforráskorlátok:
    • memória (vagy az összes, vagy csak az adatszegmens)
    • megnyitható file-ok száma
    • processzek száma
    • létrehozható file-ok maximális mérete
    • core file max. mérete
  • új process group létrehozása a program elindítása előtt
  • standard filedeszkriptorok bezárása (input, output, error szelektíven) a program elindítása előtt

Ami hiányzik, pedig nem lenne rossz:

  • scheduler beállítása (lehetne pl. realtime, bár ez nem hordozható)
  • capability-k beállítása (szintén nem hordozható)
  • umask
  • munkakönyvtár
  • max. CPU-idő

A környezeti változók beállítása úgy történik, hogy létrehozunk egy könyvtárat, amiben minden file egy beállítandó környezeti változóról van elnevezve. A változó értéke az adott file tartalmának első sora lesz. A nullás karaktereket újsorrá alakítja, a sorvégi szóközöket és tabulátorokat pedig lenyeli. Ha a file nulla byte hosszú, akkor a róla elnevezett változót a chpst törli a környezetből.

A kölcsönös kizárás megvalósításához a chpst megpróbál lockolni egy file-t; ha sikerül, akkor elindítja a megadott programot úgy, hogy az megkapja a lockolt file-t is. Ha az elindított program bezárja az öröklött file-deszkriptort, akkor a kölcsönös kizárás nem biztosított; szerencsére kevés program csinálja ezt.

4 Ajánlott irodalom

Személyes eszközök