Bevezetés a bábba

A Puppet egy konfigurációkezelő rendszer. Arra használják, hogy a gazdagépeket a kívánt állapotba hozzák, és ezt az állapotot fenntartsák.

Már több mint öt éve dolgozom a Puppettel. Ez a szöveg lényegében a hivatalos dokumentáció legfontosabb pontjainak lefordított és újrarendezett összeállítása, amely lehetővé teszi a kezdőknek, hogy gyorsan megértsék a Puppet lényegét.

Bevezetés a bábba

Alapinformációk

A Puppet operációs rendszere kliens-szerver, bár korlátozott funkcionalitással támogatja a szerver nélküli működést is.

Lehívási modellt használnak: alapértelmezés szerint félóránként egyszer felveszik a kapcsolatot a szerverrel a konfigurációért, és alkalmazzák azt. Ha az Ansible-vel dolgozott, akkor más push modellt használnak: az adminisztrátor kezdeményezi a konfiguráció alkalmazásának folyamatát, a kliensek maguk nem alkalmaznak semmit.

A hálózati kommunikáció során kétirányú TLS-titkosítást alkalmaznak: a szervernek és a kliensnek saját privát kulcsa és megfelelő tanúsítványa van. Jellemzően a szerver ad ki tanúsítványokat az ügyfelek számára, de elvileg lehetőség van külső CA használatára is.

Bevezetés a kiáltványokba

A Puppet terminológiában a bábszerverre csatlakozni csomópontok (csomópontok). A csomópontok konfigurációja meg van írva kiáltványokban speciális programozási nyelven - Puppet DSL.

A Puppet DSL egy deklaratív nyelv. Leírja a csomópont kívánt állapotát az egyes erőforrások deklarációi formájában, például:

  • A fájl létezik, és meghatározott tartalommal rendelkezik.
  • A csomag telepítve van.
  • A szolgáltatás elindult.

Az erőforrások összekapcsolhatók:

  • Vannak függőségek, ezek befolyásolják az erőforrások felhasználási sorrendjét.
    Például: „először telepítse a csomagot, majd szerkessze a konfigurációs fájlt, majd indítsa el a szolgáltatást”.
  • Vannak értesítések – ha egy erőforrás megváltozott, értesítést küld a rá előfizetett erőforrásoknak.
    Például, ha a konfigurációs fájl megváltozik, automatikusan újraindíthatja a szolgáltatást.

Ezenkívül a Puppet DSL rendelkezik függvényekkel és változókkal, valamint feltételes utasításokkal és kiválasztókkal. Különféle sablonozási mechanizmusok is támogatottak – EPP és ERB.

A Puppet Ruby nyelven íródott, ezért sok konstrukció és kifejezés onnan származik. A Ruby lehetővé teszi a Puppet bővítését - összetett logikát, új típusú erőforrásokat, funkciókat ad hozzá.

Amíg a Puppet fut, a kiszolgáló minden egyes csomópontjához tartozó manifesztek egy könyvtárba fordítódnak. Каталог Az erőforrások és kapcsolataik listája a függvények, változók értékének kiszámítása és a feltételes utasítások kiterjesztése után.

Szintaxis és kódstílus

Íme a hivatalos dokumentáció részei, amelyek segítenek megérteni a szintaxist, ha a megadott példák nem elegendőek:

Íme egy példa a jegyzék kinézetére:

# Комментарии пишутся, как и много где, после решётки.
#
# Описание конфигурации ноды начинается с ключевого слова node,
# за которым следует селектор ноды — хостнейм (с доменом или без)
# или регулярное выражение для хостнеймов, или ключевое слово default.
#
# После этого в фигурных скобках описывается собственно конфигурация ноды.
#
# Одна и та же нода может попасть под несколько селекторов. Про приоритет
# селекторов написано в статье про синтаксис описания нод.
node 'hostname', 'f.q.d.n', /regexp/ {
  # Конфигурация по сути является перечислением ресурсов и их параметров.
  #
  # У каждого ресурса есть тип и название.
  #
  # Внимание: не может быть двух ресурсов одного типа с одинаковыми названиями!
  #
  # Описание ресурса начинается с его типа. Тип пишется в нижнем регистре.
  # Про разные типы ресурсов написано ниже.
  #
  # После типа в фигурных скобках пишется название ресурса, потом двоеточие,
  # дальше идёт опциональное перечисление параметров ресурса и их значений.
  # Значения параметров указываются через т.н. hash rocket (=>).
  resource { 'title':
    param1 => value1,
    param2 => value2,
    param3 => value3,
  }
}

A behúzás és a sortörés nem kötelező része a jegyzéknek, de van egy ajánlott stílus útmutató. Összegzés:

  • Két szóközű behúzás, tabulátor nem használatos.
  • A göndör kapcsos zárójeleket szóköz választja el, a kettőspontokat nem választja el szóköz.
  • Vessző minden paraméter után, beleértve az utolsót is. Minden paraméter külön sorban van. Kivételt képez a paraméter nélküli és egy paraméter nélküli eset: írhat egy sorba és vessző nélkül (pl. resource { 'title': } и resource { 'title': param => value }).
  • A paramétereken lévő nyilaknak azonos szinten kell lenniük.
  • Előtte az erőforrás-kapcsolat nyilak vannak írva.

Fájlok helye a pappetszerveren

További magyarázatként bemutatom a „gyökérkönyvtár” fogalmát. A gyökérkönyvtár az a könyvtár, amely egy adott csomópont Bábkonfigurációját tartalmazza.

A gyökérkönyvtár a Puppet verziójától és a használt környezetektől függően változik. A környezetek független konfigurációs halmazok, amelyek külön könyvtárakban vannak tárolva. Általában a git-tel együtt használják, ebben az esetben a környezetek git-ágakból jönnek létre. Ennek megfelelően minden csomópont egy vagy másik környezetben helyezkedik el. Ez konfigurálható magán a csomóponton, vagy az ENC-ben, amiről a következő cikkben fogok beszélni.

  • A harmadik verzióban ("old Puppet") az alapkönyvtár volt /etc/puppet. A környezetek használata nem kötelező – például a régi Puppetnél nem használjuk őket. Ha környezetet használnak, általában tárolják azokat /etc/puppet/environments, a gyökérkönyvtár a környezeti könyvtár lesz. Ha nem használ környezeteket, akkor a gyökérkönyvtár lesz az alapkönyvtár.
  • A negyedik verziótól („új Puppet”) a környezetek használata kötelezővé vált, az alapkönyvtár átkerült a /etc/puppetlabs/code. Ennek megfelelően a környezetek tárolódnak /etc/puppetlabs/code/environments, a gyökérkönyvtár a környezeti könyvtár.

A gyökérkönyvtárban kell lennie egy alkönyvtárnak manifests, amely egy vagy több, a csomópontokat leíró manifestet tartalmaz. Ezen kívül kell lennie egy alkönyvtárnak modules, amely a modulokat tartalmazza. Kicsit később elmondom, hogy milyen modulok vannak. Ezenkívül a régi Puppet-nek is lehet alkönyvtára files, amely különféle fájlokat tartalmaz, amelyeket a csomópontokba másolunk. Az új Puppetben minden fájl modulokba kerül.

A jegyzékfájlok a kiterjesztéssel rendelkeznek .pp.

Pár harci példa

A csomópont és a rajta lévő erőforrás leírása

A csomóponton server1.testdomain fájlt kell létrehozni /etc/issue tartalommal Debian GNU/Linux n l. A fájlnak egy felhasználónak és egy csoportnak kell lennie root, hozzáférési jogoknak kell lenniük 644.

Kiáltványt írunk:

node 'server1.testdomain' {   # блок конфигурации, относящийся к ноде server1.testdomain
    file { '/etc/issue':   # описываем файл /etc/issue
        ensure  => present,   # этот файл должен существовать
        content => 'Debian GNU/Linux n l',   # у него должно быть такое содержимое
        owner   => root,   # пользователь-владелец
        group   => root,   # группа-владелец
        mode    => '0644',   # права на файл. Они заданы в виде строки (в кавычках), потому что иначе число с 0 в начале будет воспринято как записанное в восьмеричной системе, и всё пойдёт не так, как задумано
    }
}

Egy csomóponton lévő erőforrások közötti kapcsolatok

A csomóponton server2.testdomain Az nginx-nek futnia kell, és egy korábban elkészített konfigurációval kell működnie.

Bontsuk fel a problémát:

  • A csomagot telepíteni kell nginx.
  • Szükséges a konfigurációs fájlok másolása a szerverről.
  • A szolgáltatásnak futnia kell nginx.
  • Ha a konfiguráció frissül, a szolgáltatást újra kell indítani.

Kiáltványt írunk:

node 'server2.testdomain' {   # блок конфигурации, относящийся к ноде server2.testdomain
    package { 'nginx':   # описываем пакет nginx
        ensure => installed,   # он должен быть установлен
    }
  # Прямая стрелка (->) говорит о том, что ресурс ниже должен
  # создаваться после ресурса, описанного выше.
  # Такие зависимости транзитивны.
    -> file { '/etc/nginx':   # описываем файл /etc/nginx
        ensure  => directory,   # это должна быть директория
        source  => 'puppet:///modules/example/nginx-conf',   # её содержимое нужно брать с паппет-сервера по указанному адресу
        recurse => true,   # копировать файлы рекурсивно
        purge   => true,   # нужно удалять лишние файлы (те, которых нет в источнике)
        force   => true,   # удалять лишние директории
    }
  # Волнистая стрелка (~>) говорит о том, что ресурс ниже должен
  # подписаться на изменения ресурса, описанного выше.
  # Волнистая стрелка включает в себя прямую (->).
    ~> service { 'nginx':   # описываем сервис nginx
        ensure => running,   # он должен быть запущен
        enable => true,   # его нужно запускать автоматически при старте системы
    }
  # Когда ресурс типа service получает уведомление,
  # соответствующий сервис перезапускается.
}

Ahhoz, hogy ez működjön, körülbelül a következő fájlhelyre van szüksége a bábszerveren:

/etc/puppetlabs/code/environments/production/ # (это для нового Паппета, для старого корневой директорией будет /etc/puppet)
├── manifests/
│   └── site.pp
└── modules/
    └── example/
        └── files/
            └── nginx-conf/
                ├── nginx.conf
                ├── mime.types
                └── conf.d/
                    └── some.conf

Erőforrás típusok

A támogatott erőforrástípusok teljes listája itt található a dokumentációban, itt leírok öt alaptípust, amelyek az én gyakorlatomban elegendőek a legtöbb probléma megoldásához.

filé

Fájlokat, könyvtárakat, szimbolikus hivatkozásokat, azok tartalmát és hozzáférési jogait kezeli.

lehetőségek:

  • erőforrás neve - a fájl elérési útja (opcionális)
  • ösvény - a fájl elérési útja (ha nincs megadva a névben)
  • biztosítására - fájltípus:
    • absent - fájl törlése
    • present - bármilyen típusú fájlnak kell lennie (ha nincs fájl, akkor egy normál fájl jön létre)
    • file - normál fájl
    • directory - Könyvtár
    • link - szimbolikus link
  • tartalom — fájltartalom (csak normál fájlokhoz alkalmas, nem használható együtt forrás vagy cél)
  • forrás - hivatkozás arra az elérési útra, ahonnan a fájl tartalmát másolni szeretné (nem használható együtt tartalom vagy cél). Megadható URI-ként egy sémával puppet: (akkor a bábszerver fájljai kerülnek felhasználásra), és a sémával http: (Remélem, világos, hogy ebben az esetben mi fog történni), és még a diagrammal is file: vagy abszolút elérési útként séma nélkül (akkor a csomópont helyi FS fájlja kerül felhasználásra)
  • cél — ahová a szimbolikus hivatkozásnak mutatnia kell (nem használható együtt tartalom vagy forrás)
  • tulajdonos — az a felhasználó, akinek a fájl tulajdonosa
  • csoport — a csoport, amelyhez a fájlnak tartoznia kell
  • üzemmód - fájlengedélyek (karakterláncként)
  • visszatérő - lehetővé teszi a rekurzív címtárfeldolgozást
  • tisztítás - lehetővé teszi olyan fájlok törlését, amelyek nincsenek leírva a Puppetnél
  • kényszeríteni - lehetővé teszi olyan könyvtárak törlését, amelyek nincsenek leírva a Puppetben

csomag

Telepíti és eltávolítja a csomagokat. Képes kezelni az értesítéseket - újratelepíti a csomagot, ha a paraméter meg van adva reinstall_on_refresh.

lehetőségek:

  • erőforrás neve – csomag neve (nem kötelező)
  • név - csomag neve (ha a névben nincs megadva)
  • ellátó - csomagkezelő használata
  • biztosítására — a csomag kívánt állapota:
    • present, installed - bármely telepített verzió
    • latest - a legújabb verzió telepítve
    • absent - törölve (apt-get remove)
    • purged – törölve a konfigurációs fájlokkal együtt (apt-get purge)
    • held - a csomag verziója zárolva van (apt-mark hold)
    • любая другая строка — a megadott verzió telepítve van
  • reinstall_on_refresh - Ha egy true, akkor az értesítés kézhezvétele után a csomag újratelepítésre kerül. Hasznos forrás alapú disztribúciókhoz, ahol újraépítésre lehet szükség az összeállítási paraméterek megváltoztatásakor. Alapértelmezett false.

szolgáltatás

Kezeli a szolgáltatásokat. Képes az értesítések feldolgozására - újraindítja a szolgáltatást.

lehetőségek:

  • erőforrás neve – kezelendő szolgáltatás (opcionális)
  • név — a kezelendő szolgáltatás (ha a névben nincs megadva)
  • biztosítására — a szolgáltatás kívánt állapota:
    • running - indított
    • stopped - megállt
  • lehetővé — szabályozza a szolgáltatás indításának lehetőségét:
    • true - az automatikus indítás engedélyezve van (systemctl enable)
    • mask - rejtett (systemctl mask)
    • false - az automatikus indítás le van tiltva (systemctl disable)
  • újraindítás - parancs a szolgáltatás újraindításához
  • állapot — parancs a szolgáltatás állapotának ellenőrzésére
  • újra kell indítani — jelzi, hogy a szolgáltatás initscript támogatja-e az újraindítást. Ha false és a paraméter megadva újraindítás — ennek a paraméternek az értéke kerül felhasználásra. Ha false és paraméter újraindítás nincs megadva - a szolgáltatás leáll és újraindul (de a systemd a parancsot használja systemctl restart).
  • hasstatus — jelzi, hogy a szolgáltatás initscript támogatja-e a parancsot status... Ha false, akkor a paraméter értéke kerül felhasználásra állapot. Alapértelmezett true.

exec

Külső parancsokat futtat. Ha nem ad meg paramétereket teremt, csak ha, hacsak nem vagy üdítően, a parancs minden alkalommal lefut, amikor a Puppet fut. Képes az értesítések feldolgozására – parancsot futtat.

lehetőségek:

  • erőforrás neve - végrehajtandó parancs (opcionális)
  • parancs - a végrehajtandó parancs (ha a névben nincs megadva)
  • ösvény — elérési utak, amelyekben a végrehajtható fájlt meg kell keresni
  • csak ha — ha az ebben a paraméterben megadott parancs nulla visszatérési kóddal fejeződött be, a főparancs végrehajtásra kerül
  • hacsak nem — ha az ebben a paraméterben megadott parancs nullától eltérő visszatérési kóddal fejeződött be, akkor a főparancs végrehajtásra kerül
  • teremt — ha az ebben a paraméterben megadott fájl nem létezik, a főparancs végrehajtásra kerül
  • üdítően - Ha egy true, akkor a parancs csak akkor fut le, ha ez a végrehajtó értesítést kap más erőforrásoktól
  • cwd — könyvtár, ahonnan a parancs fut
  • használó — a felhasználó, akitől a parancsot futtatni kell
  • ellátó - hogyan kell futtatni a parancsot:
    • posix — egy gyermekfolyamat egyszerűen létrejön, feltétlenül adja meg ösvény
    • héj - a parancs elindul a shellben /bin/sh, nem adható meg ösvény, használhat gömbölyűt, csöveket és egyéb héjfunkciókat. Általában automatikusan észleli, ha vannak speciális karakterek (|, ;, &&, || stb).

cron

Vezérli a cronjobokat.

lehetőségek:

  • erőforrás neve - csak valami azonosító
  • biztosítására — crownjob állam:
    • present - létrehozni, ha nem létezik
    • absent - törölni, ha létezik
  • parancs - milyen parancsot kell futtatni
  • környezet - melyik környezetben futtassa a parancsot (környezeti változók listája és értékeik ezen keresztül =)
  • használó — melyik felhasználótól futtassa a parancsot
  • perc, óra, hétköznap, hónap, hónap nap — mikor kell cront futtatni. Ha ezen attribútumok bármelyike ​​nincs megadva, akkor annak értéke a crontabban lesz *.

A Puppet 6.0-ban cron mintha eltávolítva a dobozból puppetserverben, így az általános oldalon nincs dokumentáció. De ő a dobozban van báb-ügynökben, így nem kell külön telepíteni. Megnézheti a dokumentációt a Puppet ötödik verziójának dokumentációjábanVagy a GitHubon.

Az erőforrásokról általában

Az erőforrás egyediségének követelményei

A leggyakoribb hiba, amellyel szembesülünk Ismétlődő nyilatkozat. Ez a hiba akkor fordul elő, ha két vagy több azonos típusú, azonos nevű erőforrás jelenik meg a könyvtárban.

Ezért újra leírom: ugyanazon csomóponthoz tartozó manifesztek nem tartalmazhatnak azonos típusú erőforrásokat azonos címmel!

Néha szükség van azonos nevű, de különböző csomagkezelőkkel rendelkező csomagok telepítésére. Ebben az esetben a paramétert kell használni namea hiba elkerülése érdekében:

package { 'ruby-mysql':
  ensure   => installed,
  name     => 'mysql',
  provider => 'gem',
}
package { 'python-mysql':
  ensure   => installed,
  name     => 'mysql',
  provider => 'pip',
}

Más erőforrástípusok hasonló opciókkal rendelkeznek a duplikáció elkerülése érdekében − name у szolgáltatás, command у exec, stb.

Metaparaméterek

Minden erőforrástípusnak van néhány speciális paramétere, természetétől függetlenül.

A meta paraméterek teljes listája a Puppet dokumentációjában.

Rövid lista:

  • szükség — ez a paraméter azt jelzi, hogy ez az erőforrás mely erőforrásoktól függ.
  • előtt - Ez a paraméter határozza meg, hogy mely erőforrások függenek ettől az erőforrástól.
  • Iratkozz fel — ez a paraméter határozza meg, hogy az erőforrás mely erőforrásoktól kap értesítéseket.
  • értesít — Ez a paraméter határozza meg, hogy mely erőforrások kapjanak értesítést ettől az erőforrástól.

A felsorolt ​​metaparaméterek mindegyike egyetlen erőforráshivatkozást vagy szögletes zárójelben lévő hivatkozások tömbjét fogadja el.

Hivatkozások a forrásokhoz

Az erőforráshivatkozás egyszerűen az erőforrás említése. Főleg a függőségek jelzésére szolgálnak. Nem létező erőforrásra való hivatkozás fordítási hibát okoz.

A hivatkozás szintaxisa a következő: erőforrás típusa nagybetűvel (ha a típusnév kettős kettőspontot tartalmaz, akkor a kettőspontok közötti név minden része nagybetűs), majd az erőforrás neve szögletes zárójelben (a név kis- és nagybetűje nem változik!). Szóköz ne legyen, közvetlenül a típusnév után szögletes zárójelet kell írni.

Példa:

file { '/file1': ensure => present }
file { '/file2':
  ensure => directory,
  before => File['/file1'],
}
file { '/file3': ensure => absent }
File['/file1'] -> File['/file3']

Függőségek és értesítések

Dokumentáció itt.

Mint korábban említettük, az erőforrások közötti egyszerű függőségek tranzitívak. Amúgy legyen óvatos a függőségek hozzáadásakor – ciklikus függőséget is létrehozhat, ami fordítási hibát okoz.

A függőségekkel ellentétben az értesítések nem átmenetiek. Az értesítésekre a következő szabályok vonatkoznak:

  • Ha az erőforrás értesítést kap, akkor frissítésre kerül. A frissítési műveletek az erőforrás típusától függenek − exec futtatja a parancsot, szolgáltatás újraindítja a szolgáltatást, csomag újratelepíti a csomagot. Ha az erőforráshoz nincs megadva frissítési művelet, akkor semmi sem történik.
  • A Puppet egy futtatása során az erőforrás legfeljebb egyszer frissül. Ez azért lehetséges, mert az értesítések függőségeket tartalmaznak, a függőségi gráf pedig nem tartalmaz ciklusokat.
  • Ha a Puppet megváltoztatja egy erőforrás állapotát, az erőforrás értesítést küld az összes előfizetett erőforrásnak.
  • Ha egy erőforrás frissül, értesítést küld az összes előfizetett erőforrásnak.

Meghatározatlan paraméterek kezelése

Általános szabály, hogy ha néhány erőforrás-paraméternek nincs alapértelmezett értéke, és ez a paraméter nincs megadva a jegyzékben, akkor a Puppet nem módosítja ezt a tulajdonságot a csomópont megfelelő erőforrásához. Például, ha egy típusú erőforrás filé paraméter nincs megadva owner, akkor a Puppet nem módosítja a megfelelő fájl tulajdonosát.

Bevezetés az osztályokba, változókba és definíciókba

Tegyük fel, hogy több csomópontunk van, amelyeknek ugyanaz a konfiguráció része, de vannak különbségek is - különben leírhatnánk az egészet egy blokkban node {}. Természetesen egyszerűen másolhatja a konfiguráció azonos részeit, de általában ez egy rossz megoldás - a konfiguráció nő, és ha megváltoztatja a konfiguráció általános részét, akkor sok helyen ugyanazt kell szerkesztenie. Ugyanakkor könnyű hibázni, és általában a SZÁRAZ (ne ismételd magad) elvet okkal találták ki.

A probléma megoldására van egy olyan kialakítás, mint osztály.

osztályok

Osztály a poppet kód elnevezett blokkja. A kód újrafelhasználásához osztályokra van szükség.

Először is le kell írni az osztályt. Maga a leírás nem ad hozzá semmilyen forrást sehol. Az osztály leírása a manifesztekben:

# Описание класса начинается с ключевого слова class и его названия.
# Дальше идёт тело класса в фигурных скобках.
class example_class {
    ...
}

Ezt követően az osztály használható:

# первый вариант использования — в стиле ресурса с типом class
class { 'example_class': }
# второй вариант использования — с помощью функции include
include example_class
# про отличие этих двух вариантов будет рассказано дальше

Példa az előző feladatból - helyezzük át az nginx telepítését és konfigurálását egy osztályba:

class nginx_example {
    package { 'nginx':
        ensure => installed,
    }
    -> file { '/etc/nginx':
        ensure => directory,
        source => 'puppet:///modules/example/nginx-conf',
        recure => true,
        purge  => true,
        force  => true,
    }
    ~> service { 'nginx':
        ensure => running,
        enable => true,
    }
}

node 'server2.testdomain' {
    include nginx_example
}

Változók

Az előző példa osztálya egyáltalán nem rugalmas, mert mindig ugyanazt az nginx konfigurációt hozza. Tegyük meg a konfigurációs változó elérési útját, akkor ez az osztály használható az nginx telepítésére bármilyen konfigurációval.

Meg lehet csinálni változók segítségével.

Figyelem: a Puppet változói megváltoztathatatlanok!

Ráadásul egy változóhoz csak deklarálás után lehet hozzáférni, különben a változó értéke lesz undef.

Példa a változókkal való munkára:

# создание переменных
$variable = 'value'
$var2 = 1
$var3 = true
$var4 = undef
# использование переменных
$var5 = $var6
file { '/tmp/text': content => $variable }
# интерполяция переменных — раскрытие значения переменных в строках. Работает только в двойных кавычках!
$var6 = "Variable with name variable has value ${variable}"

Bábnak van névterek, és a változók ennek megfelelően rendelkeznek láthatósági terület: Az azonos nevű változót különböző névterekben lehet meghatározni. Egy változó értékének feloldásakor a változót az aktuális névtérben keresi, majd a befoglaló névtérben, és így tovább.

Példák névtérre:

  • globális - az osztály- vagy csomópontleíráson kívüli változók oda mennek;
  • csomópont névtér a csomópont leírásában;
  • osztály névterét az osztályleírásban.

A kétértelműség elkerülése érdekében a változó elérésekor megadhatja a névteret a változó nevében:

# переменная без пространства имён
$var
# переменная в глобальном пространстве имён
$::var
# переменная в пространстве имён класса
$classname::var
$::classname::var

Egyezzünk meg abban, hogy az nginx konfiguráció elérési útja a változóban található $nginx_conf_source. Akkor az osztály így fog kinézni:

class nginx_example {
    package { 'nginx':
        ensure => installed,
    }
    -> file { '/etc/nginx':
        ensure => directory,
        source => $nginx_conf_source,   # здесь используем переменную вместо фиксированной строки
        recure => true,
        purge  => true,
        force  => true,
    }
    ~> service { 'nginx':
        ensure => running,
        enable => true,
    }
}

node 'server2.testdomain' {
    $nginx_conf_source = 'puppet:///modules/example/nginx-conf'
    include nginx_example
}

A megadott példa azonban rossz, mert van némi „titkos tudás”, hogy valahol az osztályon belül egy ilyen és ilyen nevű változót használnak. Sokkal helyesebb ezt a tudást általánossá tenni - az osztályoknak lehetnek paraméterei.

Osztály paraméterei változók az osztály névterében, az osztály fejlécében vannak megadva, és az osztály törzsében szokásos változókhoz hasonlóan használhatók. A paraméterértékek az osztály használatakor vannak megadva a jegyzékben.

A paraméter alapértelmezett értékre állítható. Ha egy paraméternek nincs alapértelmezett értéke, és az érték nincs beállítva használat közben, az fordítási hibát okoz.

Paraméterezzük az osztályt a fenti példából, és adjunk hozzá két paramétert: az első kötelező a konfiguráció elérési útja, a második pedig nem kötelező az nginx csomag neve (pl. Debianban vannak csomagok nginx, nginx-light, nginx-full).

# переменные описываются сразу после имени класса в круглых скобках
class nginx_example (
  $conf_source,
  $package_name = 'nginx-light', # параметр со значением по умолчанию
) {
  package { $package_name:
    ensure => installed,
  }
  -> file { '/etc/nginx':
    ensure  => directory,
    source  => $conf_source,
    recurse => true,
    purge   => true,
    force   => true,
  }
  ~> service { 'nginx':
    ensure => running,
    enable => true,
  }
}

node 'server2.testdomain' {
  # если мы хотим задать параметры класса, функция include не подойдёт* — нужно использовать resource-style declaration
  # *на самом деле подойдёт, но про это расскажу в следующей серии. Ключевое слово "Hiera".
  class { 'nginx_example':
    conf_source => 'puppet:///modules/example/nginx-conf',   # задаём параметры класса точно так же, как параметры для других ресурсов
  }
}

A Puppetben a változók be vannak írva. Eszik sok adattípus. Az adattípusokat általában az osztályoknak és definícióknak átadott paraméterértékek érvényesítésére használják. Ha az átadott paraméter nem egyezik a megadott típussal, fordítási hiba lép fel.

A típus közvetlenül a paraméter neve elé kerül:

class example (
  String $param1,
  Integer $param2,
  Array $param3,
  Hash $param4,
  Hash[String, String] $param5,
) {
  ...
}

Osztályok: include classname vs class{'classname':}

Minden osztály egy típusú erőforrás osztály. Mint minden más típusú erőforrás esetében, ugyanazon a csomóponton nem lehet ugyanannak az osztálynak két példánya.

Ha kétszer próbál meg hozzáadni egy osztályt ugyanahhoz a csomóponthoz class { 'classname':} (nincs különbség, eltérő vagy azonos paraméterekkel), fordítási hiba lesz. De ha erőforrásstílusban használ egy osztályt, azonnal beállíthatja az összes paraméterét a jegyzékben.

Ha azonban használ include, akkor az osztály tetszőleges számú alkalommal hozzáadható. A tény az, hogy include egy idempotens függvény, amely ellenőrzi, hogy egy osztály hozzáadva van-e a könyvtárhoz. Ha az osztály nincs a könyvtárban, akkor hozzáadja, és ha már létezik, akkor nem csinál semmit. De használat esetén include Osztálydeklaráció során nem lehet osztályparamétereket beállítani – minden szükséges paramétert külső adatforrásban – Hiera vagy ENC – kell beállítani. A következő cikkben róluk fogunk beszélni.

Meghatározza

Ahogy az előző blokkban elhangzott, ugyanaz az osztály nem lehet többször jelen egy csomóponton. Bizonyos esetekben azonban ugyanazt a kódblokkot különböző paraméterekkel kell tudnia használni ugyanazon a csomóponton. Más szóval, szükség van egy saját erőforrástípusra.

Például a PHP modul telepítéséhez a következőket tesszük az Avitóban:

  1. Telepítse a csomagot ezzel a modullal.
  2. Hozzon létre egy konfigurációs fájlt ehhez a modulhoz.
  3. Létrehozunk egy szimbolikus hivatkozást a php-fpm konfigurációjához.
  4. Létrehozunk egy szimbolikus hivatkozást a php cli konfigurációjához.

Ilyen esetekben egy olyan kialakítás, mint pl meghatározni (define, meghatározott típus, meghatározott erőforrástípus). A Define hasonló egy osztályhoz, de vannak különbségek: először is minden Define egy erőforrástípus, nem pedig egy erőforrás; másodszor, minden definíciónak van egy implicit paramétere $title, ahová az erőforrás neve kerül deklaráláskor. Csakúgy, mint az osztályok esetében, először le kell írni egy definíciót, amely után használható.

Egy egyszerűsített példa egy PHP modullal:

define php74::module (
  $php_module_name = $title,
  $php_package_name = "php7.4-${title}",
  $version = 'installed',
  $priority = '20',
  $data = "extension=${title}.son",
  $php_module_path = '/etc/php/7.4/mods-available',
) {
  package { $php_package_name:
    ensure          => $version,
    install_options => ['-o', 'DPkg::NoTriggers=true'],  # триггеры дебиановских php-пакетов сами создают симлинки и перезапускают сервис php-fpm - нам это не нужно, так как и симлинками, и сервисом мы управляем с помощью Puppet
  }
  -> file { "${php_module_path}/${php_module_name}.ini":
    ensure  => $ensure,
    content => $data,
  }
  file { "/etc/php/7.4/cli/conf.d/${priority}-${php_module_name}.ini":
    ensure  => link,
    target  => "${php_module_path}/${php_module_name}.ini",
  }
  file { "/etc/php/7.4/fpm/conf.d/${priority}-${php_module_name}.ini":
    ensure  => link,
    target  => "${php_module_path}/${php_module_name}.ini",
  }
}

node server3.testdomain {
  php74::module { 'sqlite3': }
  php74::module { 'amqp': php_package_name => 'php-amqp' }
  php74::module { 'msgpack': priority => '10' }
}

A Duplicate deklarációs hiba legegyszerűbb módja a Define. Ez akkor fordul elő, ha a definícióban van egy állandó nevű erőforrás, és ennek a definíciónak két vagy több példánya van valamelyik csomóponton.

Könnyű megvédeni magát ettől: a definíción belül minden erőforrásnak nevet kell adni, attól függően $title. Alternatíva az erőforrások idempotens hozzáadása, a legegyszerűbb esetben elegendő a definíció összes példányára közös erőforrásokat egy külön osztályba áthelyezni, és ezt az osztályt a definíció - függvénybe foglalni. include idempotens.

Vannak más módok is az idempotencia elérésére erőforrások hozzáadásakor, nevezetesen függvények használatával defined и ensure_resources, de erről a következő részben mesélek.

Függőségek és értesítések osztályokhoz és definíciókhoz

Az osztályok és definíciók a következő szabályokkal egészítik ki a függőségek és értesítések kezelését:

  • függőség egy osztálytól/definiálástól függőségeket ad az osztály/define összes erőforrásához;
  • egy osztály/definiált függőség függőséget ad az összes osztály/definiált erőforráshoz;
  • class/define értesítés értesíti az osztály/define összes erőforrását;
  • class/define előfizetés a class/define összes erőforrására előfizet.

Feltételes állítások és szelektorok

Dokumentáció itt.

if

Itt minden egyszerű:

if ВЫРАЖЕНИЕ1 {
  ...
} elsif ВЫРАЖЕНИЕ2 {
  ...
} else {
  ...
}

hacsak nem

hacsak nem az if fordított: a kódblokk végrehajtásra kerül, ha a kifejezés hamis.

unless ВЫРАЖЕНИЕ {
  ...
}

eset

Itt sincs semmi bonyolult. Értékként használhat reguláris értékeket (karakterláncok, számok stb.), reguláris kifejezéseket és adattípusokat.

case ВЫРАЖЕНИЕ {
  ЗНАЧЕНИЕ1: { ... }
  ЗНАЧЕНИЕ2, ЗНАЧЕНИЕ3: { ... }
  default: { ... }
}

Kiválasztók

A szelektor egy nyelvi konstrukció, amelyhez hasonló case, de egy kódblokk végrehajtása helyett egy értéket ad vissza.

$var = $othervar ? { 'val1' => 1, 'val2' => 2, default => 3 }

modulok

Ha a konfiguráció kicsi, könnyen tárolható egy manifestben. De minél több konfigurációt írunk le, annál több osztály és csomópont van a manifestben, az növekszik, és kényelmetlenné válik vele dolgozni.

Ezenkívül felmerül a kód újrafelhasználásának problémája – ha az összes kód egy jegyzékben van, nehéz megosztani ezt a kódot másokkal. E két probléma megoldására a Puppet rendelkezik egy modulok nevű entitással.

modulok - ezek osztályok, definíciók és egyéb Puppet entitások egy külön könyvtárban elhelyezett halmazai. Más szóval, a modul a Puppet logika független része. Például lehet egy modul az nginx-szel való munkához, és azt fogja tartalmazni, ami és csak az nginx-szel való munkához szükséges, vagy lehet egy modul a PHP-vel való munkához stb.

A modulok verziózottak, és a modulok egymáshoz való függősége is támogatott. Van egy nyitott modultár - Bábkovács.

A bábszerveren a modulok a gyökérkönyvtár modules alkönyvtárában találhatók. Minden modulon belül van egy szabványos könyvtárséma - manifestek, fájlok, sablonok, lib stb.

Fájlszerkezet egy modulban

A modul gyökere a következő leíró nevű könyvtárakat tartalmazhatja:

  • manifests - kiáltványokat tartalmaz
  • files - fájlokat tartalmaz
  • templates - sablonokat tartalmaz
  • lib — Ruby kódot tartalmaz

Ez nem a könyvtárak és fájlok teljes listája, de ehhez a cikkhez egyelőre elegendő.

Erőforrások és fájlok nevei a modulban

Dokumentáció itt.

Egy modulban lévő erőforrások (osztályok, definíciók) nem nevezhetők tetszés szerint. Ezenkívül közvetlen összefüggés van egy erőforrás neve és annak a fájlnak a neve között, amelyben a Puppet az adott erőforrás leírását keresi. Ha megsérti az elnevezési szabályokat, akkor a Puppet egyszerűen nem találja meg az erőforrás leírását, és fordítási hibát kap.

A szabályok egyszerűek:

  • A modulban lévő összes erőforrásnak a modul névterében kell lennie. Ha a modult meghívjuk foo, akkor minden benne lévő erőforrást meg kell nevezni foo::<anything>, vagy csak foo.
  • A modul nevű erőforrásnak szerepelnie kell a fájlban init.pp.
  • Más erőforrások esetében a fájl elnevezési séma a következő:
    • a modulnévvel ellátott előtag el lesz vetve
    • az összes kettős kettőspontot, ha van, perjelekre cseréljük
    • bővítmény hozzáadásra kerül .pp

Egy példával mutatom be. Tegyük fel, hogy modult írok nginx. A következő forrásokat tartalmazza:

  • osztály nginx a manifesztben leírtak init.pp;
  • osztály nginx::service a manifesztben leírtak service.pp;
  • meghatározni nginx::server a manifesztben leírtak server.pp;
  • meghatározni nginx::server::location a manifesztben leírtak server/location.pp.

sablonok

Bizonyára Ön is tudja, mik azok a sablonok; itt nem írom le őket részletesen. De minden esetre otthagyom link a Wikipédiára.

A sablonok használata: A sablon jelentése függvény segítségével bővíthető template, amely átadja a sablon elérési útját. típusú erőforrásokhoz filé paraméterrel együtt használjuk content. Például így:

file { '/tmp/example': content => template('modulename/templatename.erb')

Útvonal megtekintése <modulename>/<filename> fájlra utal <rootdir>/modules/<modulename>/templates/<filename>.

Ezen kívül van egy funkció inline_template — a sablon szövegét kapja bemenetként, nem a fájl nevét.

A sablonokon belül az összes Puppet változót használhatja az aktuális hatókörben.

A Puppet támogatja az ERB és EPP formátumú sablonokat:

Röviden az ERB-ről

Vezérlési struktúrák:

  • <%= ВЫРАЖЕНИЕ %> — írja be a kifejezés értékét
  • <% ВЫРАЖЕНИЕ %> — kiszámítja egy kifejezés értékét (beszúrása nélkül). A feltételes utasítások (if) és a ciklusok (mindegyik) általában ide kerülnek.
  • <%# КОММЕНТАРИЙ %>

Az ERB-ben lévő kifejezések Rubyban vannak írva (az ERB valójában beágyazott rubin).

A változók jegyzékből való eléréséhez hozzá kell adnia @ a változó nevére. A vezérlőkonstrukció után megjelenő sortörés eltávolításához záró címkét kell használnia -%>.

Példa a sablon használatára

Tegyük fel, hogy írok egy modult a ZooKeeper vezérléséhez. A konfiguráció létrehozásáért felelős osztály valahogy így néz ki:

class zookeeper::configure (
  Array[String] $nodes,
  Integer $port_client,
  Integer $port_quorum,
  Integer $port_leader,
  Hash[String, Any] $properties,
  String $datadir,
) {
  file { '/etc/zookeeper/conf/zoo.cfg':
    ensure  => present,
    content => template('zookeeper/zoo.cfg.erb'),
  }
}

És a megfelelő sablon zoo.cfg.erb - Így:

<% if @nodes.length > 0 -%>
<% @nodes.each do |node, id| -%>
server.<%= id %>=<%= node %>:<%= @port_leader %>:<%= @port_quorum %>;<%= @port_client %>
<% end -%>
<% end -%>

dataDir=<%= @datadir %>

<% @properties.each do |k, v| -%>
<%= k %>=<%= v %>
<% end -%>

Tények és beépített változók

A konfiguráció konkrét része gyakran attól függ, hogy mi történik éppen a csomóponton. Például attól függően, hogy melyik Debian kiadásról van szó, telepítenie kell a csomag egyik vagy másik verzióját. Mindezt manuálisan is figyelemmel kísérheti, átírhatja a manifeszteket, ha csomópontok változnak. De ez nem komoly megközelítés, az automatizálás sokkal jobb.

A csomópontokról való információszerzéshez a Puppet rendelkezik egy tényeknek nevezett mechanizmussal. Tények - ez a csomópontra vonatkozó információ, amely manifesztekben, hétköznapi változók formájában érhető el a globális névtérben. Például a gazdagép neve, az operációs rendszer verziója, a processzor architektúrája, a felhasználók listája, a hálózati interfészek listája és azok címei, és még sok minden más. A tények jegyzékekben és sablonokban normál változóként érhetők el.

Példa a tényekkel való munkavégzésre:

notify { "Running OS ${facts['os']['name']} version ${facts['os']['release']['full']}": }
# ресурс типа notify просто выводит сообщение в лог

Formálisan egy ténynek van neve (karakterlánc) és értéke (különféle típusok állnak rendelkezésre: karakterláncok, tömbök, szótárak). Eszik beépített tények halmaza. Sajátot is írhat. Leírják a ténygyűjtőket mint a Ruby függvényeivagy hogyan futtatható fájlok. A tények a formában is bemutathatók adatokat tartalmazó szöveges fájlok a csomópontokon.

Működés közben a bábügynök először az összes elérhető ténygyűjtőt átmásolja a pappetszerverről a csomópontra, majd elindítja azokat, és elküldi az összegyűjtött tényeket a szervernek; Ezt követően a szerver megkezdi a katalógus összeállítását.

Tények futtatható fájlok formájában

Az ilyen tények a könyvtár moduljaiban vannak elhelyezve facts.d. Természetesen a fájloknak futtathatónak kell lenniük. Futtatáskor információkat kell kiadniuk a szabványos kimenetre YAML vagy kulcs=érték formátumban.

Ne felejtse el, hogy a tények minden olyan csomópontra vonatkoznak, amelyet az a poppet-kiszolgáló vezérel, amelyre a modul telepítve van. Ezért a szkriptben ügyeljen arra, hogy ellenőrizze, hogy a rendszer rendelkezik-e a tény működéséhez szükséges összes programmal és fájllal.

#!/bin/sh
echo "testfact=success"
#!/bin/sh
echo '{"testyamlfact":"success"}'

Rubin tények

Az ilyen tények a könyvtár moduljaiban vannak elhelyezve lib/facter.

# всё начинается с вызова функции Facter.add с именем факта и блоком кода
Facter.add('ladvd') do
# в блоках confine описываются условия применимости факта — код внутри блока должен вернуть true, иначе значение факта не вычисляется и не возвращается
  confine do
    Facter::Core::Execution.which('ladvdc') # проверим, что в PATH есть такой исполняемый файл
  end
  confine do
    File.socket?('/var/run/ladvd.sock') # проверим, что есть такой UNIX-domain socket
  end
# в блоке setcode происходит собственно вычисление значения факта
  setcode do
    hash = {}
    if (out = Facter::Core::Execution.execute('ladvdc -b'))
      out.split.each do |l|
        line = l.split('=')
        next if line.length != 2
        name, value = line
        hash[name.strip.downcase.tr(' ', '_')] = value.strip.chomp(''').reverse.chomp(''').reverse
      end
    end
    hash  # значение последнего выражения в блоке setcode является значением факта
  end
end

Szöveges tények

Az ilyen tények a címtár csomópontjaira kerülnek /etc/facter/facts.d a régi Bábos ill /etc/puppetlabs/facts.d az új Bábban.

examplefact=examplevalue
---
examplefact2: examplevalue2
anotherfact: anothervalue

Eljutás a tényekhez

Kétféleképpen lehet megközelíteni a tényeket:

  • a szótáron keresztül $facts: $facts['fqdn'];
  • a ténynév használata változónévként: $fqdn.

A legjobb, ha szótárt használsz $facts, vagy még jobb, ha a globális névteret ($::facts).

Itt található a dokumentáció vonatkozó része.

Beépített változók

A tények mellett van még néhány változó, elérhető a globális névtérben.

  • megbízható tények — a kliens tanúsítványából vett változók (mivel a tanúsítványt általában poppet szerveren adják ki, az ügynök nem tudja csak úgy átvenni és megváltoztatni a tanúsítványát, így a változók „megbízhatóak”): a tanúsítvány neve, a tanúsítvány neve gazdagép és tartomány, kiterjesztések a tanúsítványból.
  • szerver tények —a szerverrel kapcsolatos információkkal kapcsolatos változók — verzió, név, szerver IP-címe, környezet.
  • ügynöki tények — közvetlenül a báb-ügynök által hozzáadott változók, nem pedig a tényezõk szerint — a tanúsítvány neve, az ügynök verziója, a bábverzió.
  • master változók - Pappetmaster változók (sic!). Körülbelül ugyanaz, mint benne szerver tények, plusz konfigurációs paraméterértékek állnak rendelkezésre.
  • fordítói változók — az egyes hatókörökben eltérő fordítóváltozók: az aktuális modul neve és annak a modulnak a neve, amelyben az aktuális objektumot elértük. Használhatók például annak ellenőrzésére, hogy a privát osztályaidat nem használják-e közvetlenül más modulokból.

1. kiegészítés: hogyan kell mindezt futtatni és hibakeresni?

A cikk sok példát tartalmazott a bábkódra, de egyáltalán nem árulta el, hogyan futtassuk ezt a kódot. Nos, kijavítom magam.

A Puppet futtatásához elég egy ügynök, de a legtöbb esetben szükség lesz egy szerverre is.

ügynök

Legalábbis az XNUMX-ös verzió óta, a puppet-agent csomagok innen hivatalos Puppetlabs adattár tartalmazzák az összes függőséget (ruby és a megfelelő drágakövek), így nincsenek telepítési nehézségek (Debian-alapú disztribúciókról beszélek - nem használunk RPM-alapú disztribúciókat).

A legegyszerűbb esetben a bábkonfiguráció használatához elegendő az ügynököt szerver nélküli módban elindítani: feltéve, hogy a bábkódot a csomópontba másolták, indítsa el puppet apply <путь к манифесту>:

atikhonov@atikhonov ~/puppet-test $ cat helloworld.pp 
node default {
    notify { 'Hello world!': }
}
atikhonov@atikhonov ~/puppet-test $ puppet apply helloworld.pp 
Notice: Compiled catalog for atikhonov.localdomain in environment production in 0.01 seconds
Notice: Hello world!
Notice: /Stage[main]/Main/Node[default]/Notify[Hello world!]/message: defined 'message' as 'Hello world!'
Notice: Applied catalog in 0.01 seconds

Természetesen jobb, ha démon módban állítja be a szervert és futtatja az ügynököket a csomópontokon - akkor félóránként egyszer alkalmazzák a szerverről letöltött konfigurációt.

Utánozhatja a munka push modelljét - lépjen az Önt érdeklő csomóponthoz, és kezdje el sudo puppet agent -t. Kulcs -t (--test) valójában több opciót is tartalmaz, amelyek külön-külön engedélyezhetők. Ezek a lehetőségek a következőket tartalmazzák:

  • ne futtasson démon módban (alapértelmezés szerint az ügynök démon módban indul);
  • leállítás a katalógus alkalmazása után (alapértelmezés szerint az ügynök továbbra is dolgozik, és félóránként alkalmazza a konfigurációt);
  • írjon részletes munkanaplót;
  • a fájlok változásainak megjelenítése.

Az ügynöknek változtatások nélküli működési módja van - akkor használhatja, ha nem biztos abban, hogy a megfelelő konfigurációt írta-e meg, és szeretné ellenőrizni, hogy az ügynök pontosan mit fog változni működés közben. Ezt a módot a paraméter engedélyezi --noop a parancssorban: sudo puppet agent -t --noop.

Ezenkívül engedélyezheti a munka hibakeresési naplóját - ebben a báb ír minden általa végrehajtott műveletről: az éppen feldolgozott erőforrásról, ennek az erőforrásnak a paramétereiről, arról, hogy milyen programokat indít el. Természetesen ez egy paraméter --debug.

Сервер

Ebben a cikkben nem foglalkozom a pappetszerver teljes beállításával és a kód telepítésével; csak annyit mondok, hogy a kiszolgálónak van egy teljesen működőképes verziója, amely nem igényel további konfigurációt ahhoz, hogy kis számú szerverrel működjön. csomópontok (mondjuk akár száz). Nagyobb számú csomópont hangolást igényel - alapértelmezés szerint a puppetserver legfeljebb négy dolgozót indít el, a nagyobb teljesítmény érdekében növelnie kell a számukat, és ne felejtse el növelni a memóriakorlátokat, különben a szerver az idő nagy részében szemetet gyűjt.

Kódtelepítés – ha gyorsan és egyszerűen szüksége van rá, nézze meg (r10k)[https://github.com/puppetlabs/r10k], kis telepítésekhez elégnek kell lennie.

2. kiegészítés: Kódolási irányelvek

  1. Helyezzen el minden logikát osztályokba és definíciókba.
  2. Az osztályokat és definíciókat tartsa a modulokban, ne a csomópontokat leíró jegyzékekben.
  3. Használd a tényeket.
  4. Ne hozzon létre if-eket gazdagépnevek alapján.
  5. Nyugodtan adjon hozzá paramétereket az osztályokhoz és definíciókhoz – ez jobb, mint az osztály/define törzsében elrejtett implicit logika.

A következő cikkben elmagyarázom, miért ajánlom ezt.

Következtetés

Zárjuk be a bevezetést. A következő cikkben a Hiera-ról, az ENC-ről és a PuppetDB-ről fogok mesélni.

A felmérésben csak regisztrált felhasználók vehetnek részt. Bejelentkezés, kérem.

Sőt, sokkal több anyag van - az alábbi témákban tudok cikkeket írni, szavazni, hogy miről olvasna szívesen:

  • 59,1%Fejlett bábkonstrukciók – néhány következő szintű szar: hurkok, leképezés és egyéb lambda-kifejezések, erőforrás-gyűjtők, exportált erőforrások és gépek közötti kommunikáció a Puppeten keresztül, címkék, szolgáltatók, absztrakt adattípusok.13
  • 31,8%„Én vagyok anyám adminisztrátora”, avagy hogyan barátkoztunk az Avitóban több különböző verziójú poppet szerverrel, és elvileg a poppet szerver adminisztrálására vonatkozó rész.7
  • 81,8%Hogyan írjuk a bábkódot: műszerezés, dokumentáció, tesztelés, CI/CD.18

22 felhasználó szavazott. 9 felhasználó tartózkodott.

Forrás: will.com