Extrém rendszeradminisztráció: djbware és társai

A Unix/Linux szerverek üzemeltetése wikiből
(Változatok közti eltérés)
(v0.1)
 
a (typo fixing)
86. sor: 86. sor:
 
Vagyis, ha többszintű include-rendszer volt, már itt elvéreztünk.
 
Vagyis, ha többszintű include-rendszer volt, már itt elvéreztünk.
   
<pre>
+
<pre>
 
errors=`$APACHE2 -t 2>&1`
 
errors=`$APACHE2 -t 2>&1`
 
</pre>
 
</pre>
155. sor: 155. sor:
 
* Ki is lépünk, nem csinálunk semmit
 
* Ki is lépünk, nem csinálunk semmit
   
Lássuk, mi a helyzet, ha ha hibás volt a config:
+
Lássuk, mi a helyzet, ha hibás volt a config:
   
 
<pre>
 
<pre>
226. sor: 226. sor:
 
* Itt azért győzött a józanság szava... egyelőre :)
 
* Itt azért győzött a józanság szava... egyelőre :)
 
* A gond a daemonizálódás
 
* A gond a daemonizálódás
* Ha az apache2 szépen az előtérben maradna, a szülőlje
+
* Ha az apache2 szépen az előtérben maradna, a szülője
 
** tudná a PID-ját és
 
** tudná a PID-ját és
 
** tudna neki signalt küldeni.
 
** tudna neki signalt küldeni.

A lap 2006. november 23., 23:17-kori változata

Előadó: Korn András (BME TMIT)

  • Mi a djbware?
    • Daniel J. Bernstein (http://cr.yp.to/)
    • Nem szabad szoftver, hanem "licence-free software" (kivéve, amelyik public domain)
    • Fő tervezési szempontjai:
      • Egyszerűség és modularitás
        • Ezekre szélsőséges mértékben törekszik ("Unix-filozófia")
      • A konfiguráció könnyen feldolgozható legyen
        • Akár azon az áron is, hogy így több ész kell a megírásához
    • Tágabb értelemben djbware az is, amit nem DJB írt
  • Ismertebb djbware-ek:
    • qmail
    • djbdns
    • daemontools
  • De: ez itt egy szabad szoftveres konferencia
  • Úgyhogy a témánk a runit
    • A daemontools szabad reimplementációja

1 A runit motivációja

  • Cseréljük le a System V initet!
  • Hogy mi a baj vele?
    • Maga az init program túl bonyolult, túl sok dolgot csinál
      • Pl. van benne "UPS-kezelés"
      • utmp/wtmp-kezelés
      • A respawnt is csinálhatná külső program
      • Feleslegesen lassú az elindítás/leállás
        • Sok feladat párhuzamosítható lenne
        • Ráadásul néhány disztribúcióban a csicsázás teszi ki a kód 50+%-át
    • Nem indítja újra a szolgáltatásokat, ha kilépnek
      • kivéve, ha az inittabból futtatjuk őket
      • de akkor nem tudjuk őket szelektíven leállítani
    • Az initscript örökli az őt indító shell környezetét
      • Vagyis parancssorból indítva a szolgáltatást nem biztos, hogy ugyanazt csinálja majd, mint bootkor
    • Nem triviális leállítani egy szolgáltatást, vagy signalt küldeni neki

1.1 Szolgáltatások leállítása á la init

  • Szolgáltatás leállítása: általában signalküldés
  • Csakhogy: melyik PID-nak?
  • Hja kérem, nem kellett volna daemonizálódni...
  • "Mi a gond? Ott a pidfile..."
    • Több processzből álló, processz poollal dolgozó szolgáltatás esetén elavulttá válhat
      • Főleg, ha eleve a start-stop-daemon hozta létre
    • Ha a pidfile helye konfigolható, indítás óta megváltozhatott a konfig, és talán már nem aktuális a pidfile elérési útja
    • Összetett configfile esetén az initscript esetleg nem találja meg az érvényes pidfile-beállítást (pl. apache2)
    • Vagyis: ha a pidfile-ból ki is olvasunk egy természetes számot, meg kell(ene) győződni arról, hogy az valóban a leállítandó szolgáltatáshoz tartozó processz PID-ja-e
      • Itt viszont versenyhelyzetbe kerülhetünk, mert a szám kiolvasása, hitelesítése és a signal elküldése között az eredeti processz kiléphet, és másik kaphatja meg a PID-ját
    • Még akkor is fennáll a versenyhelyzet, ha nem hitelesítjük a PID-t sehogy (ez amúgy rossz ötlet), mert a kiolvasás-signalküldés nem atomi művelet
      • a kiolvasás és a signalküldés között eltűnhet az eredeti processz és létrejöhet ugyanolyan PID-val másik
      • vagy csak eltűnhet az, aminek a signalt küldeni akartuk, de az is elég baj

Nézzünk erre egy példát (apache2, Ubuntu)!

ENV="env -i LANG=C PATH=/usr/local/bin:/usr/bin:/bin"
APACHE2="$ENV /usr/sbin/apache2"
APACHE2CTL="$ENV /usr/sbin/apache2ctl"
  • Környezettisztítás, de hasztalan:
  • Pl. mi lesz az ulimitekkel?
  • LD_*?
apache_stop() {
  PID=""
  PIDFILE=""
  AP_CONF=/etc/apache2/apache2.conf

  # apache2 allows more than PidFile entry in the config but only the
  # last found in the config is used; we attempt to follow includes
  # here, but only first-level includes are supported, not nested ones

  for i in $AP_CONF `awk '$1 ~ /^\s*[Ii]nclude$/ && $2 ~ /^\// {print $2}' $AP_CONF`; do
    PIDFILE=`grep -i ^PidFile $i | tail -n 1 | awk '{print $2}'`
    if [ -e "$PIDFILE" ]; then
      PID=`cat $PIDFILE`
    fi
  done

Vagyis, ha többszintű include-rendszer volt, már itt elvéreztünk.

  errors=`$APACHE2 -t 2>&1`

Vajon hibátlan-e a config?

  • Az apache hibás configgal nem indul
  • Vagyis ha a config hibás, de az apache fut, akkor a configot indítás után módosítottuk
  • Vagyis a megtalált PidFile direktíva kerülhetett oda az indítás után
  • Ha indítás óta módosítottuk a configot, de nem hibás, a script sehonnan se tudja meg
  if [ $? = 0 ]; then
    # if the config is ok then we just stop normally

    if [ -n "$PID" ]; then
      $APACHE2CTL stop

Ez vajon mit csinál? Tömören:

open("/var/run/apache2.pid", O_RDONLY)  = 4
read(4, "9155\n", 13)                   = 5
read(4, "", 8)                          = 0
close(4)                                = 0
kill(9155, SIG_0)                       = 0
kill(9155, SIGTERM)                     = 0

Vagyis majdnem vakon bízik a pidfile-ban, és itt is van versenyhelyzet.

Vissza a scriptre:

      CNT=0
      while [ 1 ]; do
        CNT=$(expr $CNT + 1)
        [ ! -d /proc/$PID ] && break
  • Ha kilépett a pidfájlban szereplő PID, mi is kilépünk
  • De: lehet hogy kilépett, és közben már indult azzal a PID-val más
  • Ekkor esetleg sosem lépünk ki, vagy mégis?
        if [ $CNT -gt 60 ]; then
          if [ "$VERBOSE" != "no" ]; then
            echo " ... failed!"
            echo "Apache2 failed to honor the stop command, please investigate the situation by hand."
          fi
          return 1
        fi
        sleep 1
      done
  • Ha elbukjuk a versenyhelyzetet, azért még kiírunk egy félrevezető hibaüzenetet
    else
      if [ "$VERBOSE" != "no" ]; then
        echo -n " ... no pidfile found! not running?"
      fi
    fi
  • Nem volt pidfile, jaj-jaj!
  • Ki is lépünk, nem csinálunk semmit

Lássuk, mi a helyzet, ha hibás volt a config:

  else
    [ "$VERBOSE" != "no" ] && echo "$errors"

    # if we are here something is broken and we need to try
    # to exit as nice and clean as possible

    # if pidof is null for some reasons the script exits automagically
    # classified as good/unknown feature

    PIDS=`pidof apache2` || true

    REALPID=0
    # if there is a pid we need to verify that belongs to apache2
    # for real
    for i in $PIDS; do
      if [ "$i" = "$PID" ]; then
        # in this case the pid stored in the
        # pidfile matches one of the pidof apache
        # so a simple kill will make it
        REALPID=1
      fi
    done

    if [ $REALPID = 1 ]; then
      # in this case everything is nice and dandy
      # and we kill apache2
      kill $PID

Mi történt?

  • Kerestünk apache2 nevű processzeket
    • Amúgy a pidof(8) is írja, hogy teljes path kéne
    • Lehet, hogy több apache2 példányunk is van, ekkor itt az összeset megtaláljuk
  • Ha a megtalált PID-k közül valamelyik szerepelt a pidfile-ban, kilőjük
  • De: hibás volt a config!
    • Hibás configgal az apache el se indul, ugye
    • Tehát a futó apache nem azzal a configgal indult
    • Tehát ki tudja, mit olvastunk ki a pidfile-ból
    • Arról nem is beszélve, hogy mikor; azóta már egy csomó idő eltelt
      • Processzek léphettek ki és születhettek
      • A pidfile tartalma is megváltozhatott
      • Ráadásul a pidof és a kill között is eltelik valamennyi idő
      • A múltban élünk...

Na és mi van, ha a pidof nem talál áldozatjelöltet?

    else
      # this is the worst situation... just kill all of them
      #for i in $PIDS; do
      #       kill $i
      #done
      # Except, we can't do that, because it's very, very bad
      if [ "$PIDS" ] && [ "$VERBOSE" != "no" ]; then
        echo " ... failed!"
        echo "You may still have some apache2 processes running.  There are"
        echo "processes named 'apache2' which do not match your pid file,"
        echo "and in the name of safety, we've left them alone.  Please review"
        echo "the situation by hand."
      fi
      return 1
    fi
  fi
}
  • Itt azért győzött a józanság szava... egyelőre :)
  • A gond a daemonizálódás
  • Ha az apache2 szépen az előtérben maradna, a szülője
    • tudná a PID-ját és
    • tudna neki signalt küldeni.

2 Megmentőnk, a runit

Személyes eszközök