Konfiguráció menedzsment eszközök

A Unix/Linux szerverek üzemeltetése wikiből
A lap korábbi változatát látod, amilyen ÁronM (vitalap | szerkesztései) 2011. január 15., 20:51-kor történt szerkesztése után volt.

Előre felhívom a figyelmet, hogy ez a szócikk arra nem elegendő, hogy meg lehessen belőle tanulni a konfiguráció menedzsment eszközök használatát. Arra viszont remélem, elegendő lesz, hogy kiinduló pontot nyújtson az ezek használatáról való döntéshez.

Tartalomjegyzék

1 A probléma

Van n db. számítógép, amik nagyon hasonló feladatot látnak el, így nagyon hasonlóan is kellene őket adminisztrálni. Külön adminisztrálni mindegyiket kényelmetlen, és meglehetősen nagy káoszhoz vezetne.

2 A naív hozzáállás

Az ötlet hihetetlenül egyszerű. Összeállítunk egy shellscriptet, ami elvégzi a gépen a szükséges módosításokat, összecsomagoljuk a számára szükséges file-okkal, majd az adminisztrálni kívánt gépekre valahogyan eljuttatjuk a csomagot, és lefuttatjuk rajtuk a shellscriptet. Ezt pl. egy cron job segítségével automatizálni is lehet valamennyire. Az ötlettel azonban van néhány probléma. Mi van pl., ha néhány gépen sikeresen lefut az így összeállított shell script, néhányon pedig nem? A központilag adminisztrálni próbált gépek konfigurációja ez által el fog térni egymástól, és így megnehezítettük a saját dolgunkat. Arról nem is beszélve, hogy az ilyen céllal összedobott shellscriptek könnyen elérhetik az átláthatatlanság, és ez által a karbantarthatatlanság határát.

3 cfengine

3.1 Mi az a cfengine?

A problémára kitalált első "komoly" megoldás. Sok, különböző, Unix-szerű platformon képes működni. Az alapvető elv az, hogy nem ugyanazt az egy rendszert akarjuk sok példányban replikálni, és nem ugyanazokat a parancsokat akarjuk minden gépnek kiadni, csupán azt akarjuk, hogy teljesítsék ugyanazokat az elvárásokat. Szabályokat lehet benne definiálni, mely szabályoknak megfelelő állapotba hozza a menedzselt gépeket. Ezek a szabályok nem azt írják elő, milyen lépéseket kell tennie a cfengine-nek, csupán azt, milyen állapotban kell lenniük a menedzselt hosztoknak. Arra nincs garancia, hogy tényleg az előírt állapotban lesznek a hosztok, azonban biztosan tartani fognak az előírt állapot felé.

3.2 Komponensek

  • cf-promises - Ellenőrzi, hogy a rendszer megfelel -e a szabályoknak.
  • cf-agent - Módosításokat végez a rendszeren.
  • cf-serverd - Kéréseket fogad létező szabályok alkalmazására.
  • cf-execd - Ütemező.
  • cf-runagent - Kéréseket lehet vele küldeni a cf-serverd-nek.
  • cf-report - Jelentést készít.
  • cf-know - Dokumentációt generál a szabályokról.

3.3 Szabályok felépítése

A szabályok ú.n. ígéretekből állnak. Az ígéreteket fel lehet bontani egy objektumra, melynek teljesítenie kell az ígéretet, opcionálisan egy objektumra, melynek érdeke ez az ígéret (dokumentációs szempontból), valamint a testre. A test balérték => jobbérték (ahol balérték egy foglalt szava a nyelvnek) kijelentések sorozata. Ezen kijelentések sablonokra is hivatkozhatnak, melyek az alábbi formájúak:

body bal jobb
{
bal1 => jobb1;
bal2 => jobb2;
}

Egy ígéret pedig az alábbi formájú:

"ígéretet tevő objektum"
  bal => jobb;

Az ágenseknek van egy speciális, control nevű sablonuk, amin át a viselkedésük befolyásolható. Az ígéretek kötegekbe gyűjthetők. A kötegek ahoz az ágenshez tartoznak, mely a benne foglalt ígéretek betartásával foglalkozik. A sablonok képesek paramétereket fogadni, melyeket a sablonon belül aztán változóként lehet használni. A paramétereket a sablon neve után zárójelek között kell megadni. A változónevek formája $(változónév). Valójában a változók is ígéretek: arra vonatkozó ígéretek, hogy az lesz az értékük, ami meg van adva.

3.4 Döntések

Az ágens az indulásakor meghatározza, milyen paraméterekkel rendelkezik a host, amin fut. Ennek alapján osztályokba sorolja a gépet. Emellett saját osztályozási elvek is definiálhatóak kötegek segítségével. Erre egy példa:

bundle agent osztaly
{
 classes:
  "dhcp_szerver" or => {fileexist("/etc/dhcp/dhcpd.conf"), fileexist("/etc/dhcp/dhcpd6.conf")};
 dhcp_szerver::
  #ide jönnek azok az ígéretek, amik teljesülését akkor szeretnénk, ha a gép egy dhcp szerver
}

Ebben a példában annak alapján döntöttük el (nem igazán helyes módon, de most jobb ötletem nem volt), hogy dhcp szerver -e a gép, hogy van -e /etc/dhcp/dhcpd.conf, vagy /etc/dhcp/dhcpd6.conf nevű fájl. Ha igen, akkor a dhcp_szerver osztályba tartozó gépekre vonatkozó szabályok végrehajtódnak. A :: operátor előtt több osztály nevét is fel lehet sorolni köztük logikai operátorokkal, így a gépek egy tetszőlegesen bonyolult módon meghatározott halmazára vonatkozó szabály alkotható.

3.5 Ígéretek típusai

  • vars - változó, mely adott értéket fog felvenni
  • classes - a rendszer jellemzőire és állapotára vonatkozó kijelentés. Ezek alapján lehet döntéseket hozni.
  • reports - Üzenetek jelentésbe írása.
  • commands - utasítás végrehajtása.
  • databases - adatbázisokra vonatkozó ígéretek.
  • files - fájlra vonatkozó ígéretek. Vonatkozhatnak a létezésére, attributumaira, és tartalmára.
  • interfaces - hálózati interfészek konfigurációjára vonatkozó ígéretek.
  • methods - további ígéretek kötegeire vonatkozó ígéret.
  • packages - csomag telepítettségére vonatkozó ígéret.
  • storage - csatolt tárolókra vonatkozó ígéretek.

3.6 Bővebb dokumentáció

4 Puppet

4.1 Felépítés

A puppet név alapvetően a kliens oldali programot takarja. Szerver oldali párja a puppet-server nevű csomagban található a disztribuciók többségében, és puppetmasterd névre hallgat. Mind a kliens, mind a szerver ruby programnyelven íródott, és GPL licensz alatt érhető el. A fejlesztőktől a két programot egyetlen forráscsomagban tölthetjük le. A szerver a 8140-es TCP porton, SSL titkosított kapcsolaton érhető el. A manifest fájlok a szerver /etc/puppet/manifests/ útvonalán, .pp névvégződésű fájlokban találhatóak. Közülük a nodes.pp az, ami a hostok neve szerint szétválasztva dönti el, mely hostra mely konfigurációs elvárások vonatkoznak. include utasításokkal ebbe további konfigurációs fájlok illeszthetők. Mikor a puppetd elindul, csatlakozik a puppet szerver 8140-es portjára, lekérdezi a rá vonatkozó manifest-et, és a host konfigurációját úgy módosítja, hogy az eleget tegyen a manifest-ben írt elvárásoknak. Ha eleve eleget tesz nekik, akkor természetesen semmit nem tesz. A módosításokról aztán jelentést küldhet a kliens a szerver felé, majd ugyanezt alapértelmezés szerint fél óránként újra megteszi.

4.2 Manifest példa

nodes.pp

node bootszerver {
    include tftp
}

tftp.pp

class tftp {
    package { "tftpd":
        ensure => installed,
        ensure => latest
    }
    package { "xinetd":
        ensure => installed,
        ensure => latest
    }
    yumrepo { "sajat":
        baseurl => "http://repo.sajatdomain/yumrepo",
        descr => "Sajat yum repository-m, melyben a tftpboot-konyvtar csomag talalhato.",
        enabled => 1,
        gpgcheck => 0
    }
    package { "tftpboot-konyvtar":
        ensure => installed,
        ensure => latest
    }
    file { "/etc/tftpd.map":
        source => "puppet://tftp_konf/tftpd.map"
    }
    service { "xinetd":
        ensure => running
    }
}

Amit ebből a (gyakorlati felhasználásra túl rövid, wiki szócikkbe túl hosszú) példából rögtön láthatunk, az az, hogy a manifest-ek resource-okból állnak, amiknek típusa (pl. package, service, file, exec), neve (a ':' karakter előtt), valamint attributumai vannak. Az attributum nevéhez annak értékét a '=>' operátor segítségével lehet hozzárendelni. Az attributumoknak lehetnek alapértelmezett értékei. Ezen alapértelmezett értékeket típusonként be lehet állítani. Pl. az exec típus path attributumához az alábbi módon rendelhetünk alapértelmezett értéket:

Exec { path => '/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin' }

Ami itt rögtön feltűnik, hogy az exec típus nevét nagy kezdőbetűvel írtuk. Ennek az az oka, hogy a kezdőbetű alapján tudja a puppet, hogy deklarációról van -e szó. A kis kezdőbetű deklarációt jelent. Márpedig most nem deklarálni akarunk egy resource-t, hanem alapértelmezett értéket akarunk megadni egy típusra. Azt, hogy milyen paraméterek vannak, a resource típusa határozza meg, azonban léteznek metaparaméterek, amik minden resource esetében megtalálhatóak. Ilyen a subscribe és a require. Mindkettejük egy resource-ra való referenciát, vagy resource referenciák listáját veheti fel értékéül. Referenciát úgy adhatunk meg, hogy nagy kezdőbetűvel megadjuk a resource típusát, majd '[]' között a nevét. A puppet képes változókat is kezelni. A változók neve '$' karakterrel kezdődik. Példa változó értéke alapján való döntésre:

path => $operatingsystem ? {
    solaris => "/usr/local/etc/sudoers",
    default => "/etc/sudoers"
}

A puppet által ismert típusokról, metaparaméterekről, beépített függvényekről, és minden egyébről korrekt dokumentáció érhető el a hivatalos weblapján: http://projects.puppetlabs.com/projects/puppet/wiki/Reference_Index Érdemes továbbá megemlíteni, hogy a puppet saját típusokat és függvényeket megvalósító, ruby nyelven írt modulokkal is bővíthető, így a fentebb említett dokumentációban adott típusok viszonylag kis száma nem korlátozza az eszköz használhatóságát. Ezen modulok megalkotásának módjáról is olvasható dokumentáció a fentebbi címen.

5 ISconf

Az ISconf mögötti elv meglehetősen egyszerű: Ha két host-on pontosan ugyanazokat a változtatásokat pontosan ugyanabban a sorrendben végzik el, pontosan ugyanúgy fognak viselkedni. Az, hogy a két rendszer garantáltan, pontosan ugyanúgy viselkedjen, a legalacsonyabb költséggel az előző mondatban ismertetett módszerrel oldható meg. A cfengine által alkalmazott elképzelésekhez képest tehát az a fő különbség, hogy a változtatások sorrendje is szigorúan kötött. Így az ISconf nem azt garantálja, hogy a vele adminisztrált rendszerek tartani fognak az előírt állapothoz, hanem azt, hogy egyik, pontosan az előírtnak megfelelő állapotból a másik, pontosan a később előírtnak megfelelő állapotba kerül anélkül, hogy bármikor is nem az előírtnak megfelelő állapotba kerülne. Amennyiben az előírttól eltérő állapotot tapasztalunk, az meghibásodás, vagy a rendszer ellen intézett támadás eredménye, így ebben az esetben ki kell deríteni, mi okozta a problémát. Amennyiben biztonsági hiba az ok, javítani kell a biztonsági hibát, majd az érintett rendszereket újratelepíteni, és hagyni, hogy az ISconf a legutolsó előírt állapotba hozza a rendszert. A helyreállításhoz szükséges idő rövidíthető, ha van egy olyan állapotnak megfelelő image, melyből már viszonylag kevés (néhány óra alatt elvégezhető mennyiségű) műveletet kell végrehajtania az ISconf-nak. Rollback-et tehát soha nem végzünk. Ha hiba miatt tértünk el a helyes állapottól, újratelepítjük a rendszert. Persze, ez azt is jelenti, hogy az adminisztrációra kizárólag az ISconf használandó, hiszen minden, nem az ISconf által elkövetett változtatás az ISconf által előírttól eltérő állapotot eredményezne.

5.1 Használat

Az ISconf használata jelentősen eltér a cfengine és a Puppet használatától. Inkább egy verziókezelő használatára emlékeztet: a változtatások előtt lock-olni kell, utánuk pedig commit-olni. A cfengine-től és a Puppet-től eltérően az ISconf esetén nem egy konfigurációs fájl közvetlen szerkesztése által hajthatóak végre a változtatások, hanem az isconf parancs által.

isconf [-Dhrq] [-c config] [-m message] verb [verb_args]

5.2 Parancsok

  • ci: commit. Hozzáadja az aktuális tranzakciót a journal-hoz.
  • exec: Lokálisan végrehajtja rendszeradminisztrátori jogosultsággal az utána megadott parancsot, és hozzáadja az aktuális tranzakcióhoz is ugyazt a parancsot.
  • fork: Lemásolja az aktuális journal-t egy másik branch-be, és a lokális gépet átállítja erre az újonnan létrehozott branch-re.
  • lock: A tranzakció kezdetét jelöli.
  • migrate: Átállítja egy másik branch-re a lokális gép konfigurációját. Ez csak abban az esetben sikeres, ha az aktuális branch-ben még nem keletkeztek olyan változások, amik abban a branch-ben, ahová migrálunk, nem szerepelnek.
  • reboot: újraindítást tesz az aktuális tranzakció parancsai közé.
  • restart: újraindítja az ISconf-ot.
  • snap: készít egy snapshot-ot a paramétereként megadott fájlról, ami aztán a többi gépre is átkerül.
  • start: elindítja az ISconf daemon-t.
  • stop: leállítja az ISconf daemon-t.
  • unlock: felszabadítja a zárat a lokális branch-re. A tranzakció így nem kerül a journal-ba, a lokális gépen viszont megmaradnak a változtatások, így a lokális gép újratelepítendővé válik, hogy ismét egyezzen a konfigurációja a többi gépével.
  • up: frissíti a helyi gép konfigurációját a journal-nak megfelelően. Ezt a parancsot be szokás tenni a crontab-ba. Ha nincs hozzá megadva a -r kapcsoló, akkor az újraindítást igénylő tranzakciónál megáll.

5.3 Környezeti változók

Az ISconf környezeti változók által konfigurálható. Ezek:

  • IS_DOMAIN - ISconf domain neve. Az összes adminisztrálni kívánt gépen egyeznie kell.
  • IS_HOME - Az ISconf által adattárolásra használt útvonal.
  • IS_HMAC_KEYS - A HMAC kulcs fájl neve.
  • IS_HTTP_PORT - Fájlátvitelre használt HTTP szerver portszáma.
  • IS_NETS - A nets fájl neve
  • IS_NOBROADCAST - Ha be van állítva, nem használ UDP broadcast-eket, csak a nets fájlban megadott hostoknak küld UDP csomagokat.
  • IS_PORT - Az ISconf daemon által kommunikációra használt port száma.
  • IS_REBOOT_CMD - A parancs, amit az ISconf végre kell hajtson az "isconf reboot" parancs hatására.

5.4 Konfiguráció

A konfigurációra használt környezeti változók a /etc/is/main.cf fájlban beállíthatók. A fájl formája:

cél: opcionális include-ok
    változó1 = érték
    változó2 = érték

A cél string a hostnévre illeszkedik. Kis- és nagy betűk között nem tesz különbséget. Ha .-ot is tartalmaz, a teljes domain névre illeszkedik. Csak az első illeszkedő cél kerül kiértékelésre, de a DEFAULT cél mindig kiértékelésre kerül. A commentek "#" karakterrel kezdődnek.

Személyes eszközök