Puppet je systém správy konfigurácie. Používa sa na uvedenie hostiteľov do požadovaného stavu a na jeho udržanie.
Pracujem s Puppetom už viac ako päť rokov. Tento text je v podstate preložená a preusporiadaná kompilácia kľúčových momentov z oficiálnej dokumentácie, ktorá umožní nováčikom rýchlo preniknúť do podstaty Puppetu.

Základné informácie
Schéma práce Puppetu je klient-serverová, aj keď je podporovaná aj možnosť práce bez servera s obmedzenou funkčnosťou.
Používa sa pull-model práce: predvolený interval je pol hodiny, kedy sa klienti obracajú na server za konfiguráciou a túto aplikujú. Ak ste pracovali s Ansible, tam sa používa iný, push-model: administrátor iniciuje proces aplikácie konfigurácie, klienti sami nič neaplikujú.
Pri sieťovej interakcii sa používa obojsmerné šifrovanie TLS: server a klient majú svoje súkromné kľúče a príslušné certifikáty. Zvyčajne server vydáva certifikáty pre klientov, ale v zásade je možné používať aj externé CA.
Zoznámenie sa s manifestmi
V terminológii Puppetu k puppet-serveru pripojujú uzly (uzly). Konfigurácia pre uzly sa píše v manifestoch v špeciálnom programovacom jazyku - Puppet DSL.
Puppet DSL je deklaratívny jazyk. V ňom sa popisuje želaný stav uzla vo forme vyhlásenia jednotlivých zdrojov, napríklad:
- Súbor existuje a má určité obsah.
- Balík je nainštalovaný.
- Služba je spustená.
Zdroje môžu byť vzájomne prepojené:
- Existujú závislosti, ktoré ovplyvňujú poradie aplikácie zdrojov.
Napríklad, "najprv nainštaluj balík, potom oprav konfiguračný súbor, po tom spusti službu." - Existujú upozornenia - ak sa zdroj zmení, posiela upozornenia prihláseným naň zdrojom.
Napríklad, ak sa mení konfiguračný súbor, službu je možné automaticky reštartovať.
Okrem toho má Puppet DSL funkcie a premenné, ako aj podmienené operátory a selektory. Podporujú sa aj rôzne mechanizmy šablonizácie - EPP a ERB.
Puppet je napísaný v Ruby, preto sú mnohé konštrukcie a termíny prevzaté odtiaľ. Ruby umožňuje rozširovať Puppet - dopisovať zložitú logiku, nové typy zdrojov, funkcie.
Počas práce s Puppet sa manifesty pre každú konkrétnu uzol na serveri kompilujú do adresára. Adresár — to je zoznam zdrojov a ich vzťahov po výpočte hodnôt funkcií, premenných a rozvinutí podmienkových operátorov.
Syntax a kódový štýl
Tu sú sekcie oficiálnej dokumentácie, ktoré pomôžu pochopiť syntax, ak poskytnuté príklady nebudú dostatočné:
Tu je príklad toho, ako vyzerá manifest:
# Комментарии пишутся, как и много где, после решётки.
#
# Описание конфигурации ноды начинается с ключевого слова node,
# за которым следует селектор ноды — хостнейм (с доменом или без)
# или регулярное выражение для хостнеймов, или ключевое слово default.
#
# После этого в фигурных скобках описывается собственно конфигурация ноды.
#
# Одна и та же нода может попасть под несколько селекторов. Про приоритет
# селекторов написано в статье про синтаксис описания нод.
node 'hostname', 'f.q.d.n', /regexp/ {
# Конфигурация по сути является перечислением ресурсов и их параметров.
#
# У каждого ресурса есть тип и название.
#
# Внимание: не может быть двух ресурсов одного типа с одинаковыми названиями!
#
# Описание ресурса начинается с его типа. Тип пишется в нижнем регистре.
# Про разные типы ресурсов написано ниже.
#
# После типа в фигурных скобках пишется название ресурса, потом двоеточие,
# дальше идёт опциональное перечисление параметров ресурса и их значений.
# Значения параметров указываются через т.н. hash rocket (=>).
resource { 'title':
param1 => value1,
param2 => value2,
param3 => value3,
}
}Odstupy a konce riadkov nie sú povinnou súčasťou manifestu, avšak existuje odporúčaná . Stručné zhrnutie:
- Dvojpriestorové odsadenia, tabulátory sa nepoužívajú.
- Zložené zátvorky sú oddelené medzerou, dvojbodka nie je oddelená medzerou.
- Čiarky sú po každom parametri, vrátane posledného. Každý parameter je na samostatnom riadku. Výnimkou sú prípady bez parametrov a jedného parametra: môžu byť napísané na jednom riadku bez čiarky (t.j.
resource { 'title': }aresource { 'title': param => value }). - Šípky pri parametroch musia byť na jednej úrovni.
- Šípky vzťahov medzi zdrojmi sa píšu pred nimi.
Umiestnenie súborov na puppetserveri
Pre ďalšie vysvetlenia zavediem pojem „rootový adresár“. Rootový adresár je adresár, v ktorom sa nachádza Puppet konfigurácia pre konkrétny uzol.
Rootový adresár sa líši v závislosti od verzie Puppet a používania prostredí. Prostredia sú nezávislé sady konfigurácií, ktoré sú uložené v samostatných adresároch. Zvyčajne sa používajú v kombinácii s gitom, v takom prípade sa prostredia vytvárajú z vetiev gitu. Podľa toho sa každý uzol nachádza v jednom alebo inom prostredí. Toto je nastavené na samotnom uzle alebo v ENC, o čom budem hovoriť v ďalšom článku.
- V tretej verzii („starý Puppet“) bola základnou adresárou
/etc/puppet. Použitie prostredí je voliteľné – napríklad ich nepoužívame so starým Puppetom. Ak sa prostredia používajú, zvyčajne sa ukladajú v/etc/puppet/environments, rootová adresár bude adresár prostredia. Ak sa prostredia nepoužívajú, rootovou adresárom bude základná. - Od štvrtej verzie („nový Puppet“) sa používanie prostredí stalo povinným a základná adresáre bola presunutá do
/etc/puppetlabs/code. Prislúchajúce prostredia sa ukladajú v/etc/puppetlabs/code/environments, koreňový adresár je adresár prostredia.
V koreňovom adresári musí byť podadresár manifests, v ktorom sú jeden alebo viaceré manifesty s popisom uzlov. Okrem toho by tam mal byť podadresár modules, v ktorom sa nachádzajú moduly. Čo sú moduly, vysvetlím neskôr. Okrem toho v starom Pappete môže byť aj podadresár files, v ktorom sú rôzne súbory, ktoré kopírujeme na uzly. V novom Pappete sú však všetky súbory presunuté do modulov.
Súbory manifestov majú príponu .pp.
Príklady z praxe
Popis uzla a zdroja na ňom
Na uzle server1.testdomain musí byť vytvorený súbor /etc/issue so obsahom Debian GNU/Linux n l. Súbor musí patriť používateľovi a skupine root, práva prístupu musia byť 644.
Píšeme manifest:
node 'server1.testdomain' { # blok konfigurácie, ktorý sa týka uzla server1.testdomain
file { 'etc/issue': # popisujeme súbor /etc/issue
ensure => present, # tento súbor musí existovať
content => 'Debian GNU/Linux n l', # mal by mať takýto obsah
owner => root, # používateľ-vlastník
group => root, # skupina-vlastník
mode => '0644', # práva na súbor. Sú zadávané ako reťazec (v úvodzovkách), pretože inak sa číslo so 0 na začiatku bude považovať za zapísané v osmičkovej sústave, a všetko pôjde inak, ako bolo zamýšľané
}
}Vzťahy zdrojov na uzle
Na uzle server2.testdomain musí byť spustený nginx, ktorý pracuje s pripravenou konfiguráciou.
Rozloženie úlohy:
- Je potrebné nainštalovať balík
nginx. - Je potrebné skopírovať konfiguračné súbory zo servera.
- Je potrebné spustiť službu
nginx. - V prípade aktualizácie konfigurácie je potrebné reštartovať službu.
Píšeme manifest:
node 'server2.testdomain' { # konfiguračný blok týkajúci sa nody server2.testdomain
package { 'nginx': # popisujeme balík nginx
ensure => installed, # musí byť nainštalovaný
}
# Priama šípka (->) hovorí, že zdroj nižšie by mal
# byť vytvorený po zdroji, ktorý je uvedený vyššie.
# Takéto závislosti sú tranzitívne.
-> file { '/etc/nginx': # popisujeme súbor /etc/nginx
ensure => directory, # má to byť adresár
source => 'puppet:///modules/example/nginx-conf', # jeho obsah sa musí brať z puppetského servera na uvedenej adrese
recurse => true, # kopírovať súbory rekurzívne
purge => true, # treba odstrániť nadbytočné súbory (tie, ktoré nie sú v zdroji)
force => true, # odstraňovať nadbytočné adresáre
}
# Vlna šípka (~>) hovorí, že zdroj nižšie by mal
# sledovať zmeny zdroja, ktorý je uvedený vyššie.
# Vlna šípka zahŕňa priamu (->).
~> service { 'nginx': # popisujeme službu nginx
ensure => running, # má byť spustená
enable => true, # má sa spúšťať automaticky pri štarte systému
}
# Keď zdroj typu service dostane oznámenie,
# príslušná služba sa reštartuje.
}Aby to fungovalo, je potrebné približne takéto usporiadanie súborov na puppetskom serveri:
/etc/puppetlabs/code/environments/production/ # (это для нового Паппета, для старого корневой директорией будет /etc/puppet)
├── manifests/
│ └── site.pp
└── modules/
└── example/
└── files/
└── nginx-conf/
├── nginx.conf
├── mime.types
└── conf.d/
└── some.confTypy zdrojov
Úplný zoznam podporovaných typov zdrojov sa nachádza , tu popíšem päť základných typov, ktoré v mojej praxi stačia na vyriešenie väčšiny úloh.
file
Spravuje súbory, adresáre, symbolické odkazy, ich obsah a práva prístupu.
Parametre:
- názov zdroja — cesta k súboru (voliteľne)
- path — cesta k súboru (ak nie je zadaná v názve)
- ensure — typ súboru:
neprítomný— odstrániť súborpresent— má to byť súbor akéhokoľvek typu (ak súbor neexistuje, bude vytvorený bežný súbor)file— bežný súbordirectory— adresárlink— symbolický odkaz
- content — obsah súboru (vhodné len pre bežné súbory, nemožno používať spolu s source alebo target)
- source — odkaz na cestu, z ktorej sa má kopírovať obsah súboru (nemožno používať spolu s content alebo target). Môže byť zadaný buď vo forme URI so schémou
puppet:(potom sa použijú súbory z puppetského servera), alebo so schémouhttp:(dúfam, že je jasné, čo bude v tomto prípade), a dokonca so schémoufile:alebo vo forme absolútnej cesty bez schémy (potom sa použije súbor z lokálneho FS na node) - target — kam by mal smerovať symbolický odkaz (nemožno používať spolu s content alebo source)
- owner — používateľ, ktorému by mal súbor patriť
- skupina — skupina, ktorej by mal súbor patriť
- mode — práva na súbor (vo forme reťazca)
- rekurzívne — zahrňuje rekurzívne spracovanie adresárov
- vyčistiť — zahrňuje odstránenie súborov, ktoré nie sú uvedené v Puppet
- násilím — zahrňuje odstránenie adresárov, ktoré nie sú uvedené v Puppet
package
Inštaluje a odstraňuje balíky. Vie spracovávať upozornenia — preinštaluje balík, ak je zadaný parameter preinštalovať_po_osvežení.
Parametre:
- názov zdroja — názov balíka (voliteľné)
- name — názov balíka (ak nie je zadaný v názve)
- poskytovateľ — správca balíkov, ktorý by sa mal použiť
- ensure — požadovaný stav balíka:
present,installed— nainštalovaná akákoľvek verzialatest— nainštalovaná najnovšia verzianeprítomný— odstránené (apt-get remove)vyčistené— odstránené spolu s konfiguračnými súbormi (apt-get purge)zablokované— verzia balíka je zablokovaná (apt-mark hold)akýkoľvek iný reťazec— nainštalovaná uvedená verzia
- preinštalovať_po_osvežení — ak
true, pri prijatí upozornenia sa balík preinštaluje. Užitečné pre distribúcie založené na zdrojoch, kde môže byť potrebné prekompilovať balíky pri zmene parametr&ov; kompilácie. Predvolenenepravda.
service
Spravuje služby. Vie spracovávať upozornenia — reštartuje službu.
Parametre:
- názov zdroja — služba, ktorou treba spravovať (voliteľné)
- name — služba, ktorou treba spravovať (ak nie je zadaná v názve)
- ensure — požadovaný stav služby:
beží— spustenézastavené— zastavené
- povoliť — spravuje možnosť spustenia služby:
true— povolené automatické spustenie (systemctl enable)maskovať— zamaskované (systemctl mask)nepravda— automatické spustenie je vypnuté (systemctl disable)
- reštart — príkaz na reštartovanie služby
- status — príkaz na kontrolu stavu služby
- máreštart — určiť, či initskript služby podporuje reštart. Ak
nepravdaa je zadaný parameter reštart — použije sa hodnota tohto parametra. Aknepravdaa parameter reštart nie je zadaný — služba sa zastaví a spustí na reštart (ale v systemd sa používa príkazsystemctl restart). - mástatus — určiť, či initskript služby podporuje príkaz
status. Aknepravda, použije sa hodnota parametra status. Predvolenetrue.
exec
Spúšťa externé príkazy. Ak sa parametre nezadávajú vytvoriť, ibaak, pokial nie alebo refreshonly, príkaz sa spustí pri každom behu Puppetu. Vie spracovávať upozornenia — spúšťa príkaz.
Parametre:
- názov zdroja — príkaz, ktorý treba vykonať (voliteľné)
- command — príkaz, ktorý treba vykonať (ak nie je zadaný v názve)
- path — cesty, kde hľadať spustiteľný súbor
- ibaak — ak príkaz uvedený v tomto parametri skončil s nulovým kódom návratu, hlavný príkaz sa vykoná
- pokial nie — ak príkaz uvedený v tomto parametri skončil s nenulovým kódom návratu, hlavný príkaz sa vykoná
- vytvoriť — ak zadaný súbor v tomto parametri neexistuje, hlavný príkaz sa vykoná
- refreshonly — ak
true, príkaz sa spustí len vtedy, keď tento exec dostane upozornenie od iných zdrojov - cwd — adresár, z ktorého sa spúšťa príkaz
- user — používateľ, ktorý spúšťa príkaz
- poskytovateľ — pomocou čoho spustiť príkaz:
- posix — jednoducho sa vytvorí podproces, je potrebné uviesť path
- shell — príkaz sa spúšťa v shelli
/bin/sh, nie je potrebné uvádzať path, možno použiť globbing, rúrky a iné funkcie shellu. Zvyčajne sa určuje automaticky, ak sú prítomné rôzne špeciálne znaky (|,;,&&,||a podobne).
cron
Riadi crontaby.
Parametre:
- názov zdroja — jednoducho nejaký identifikátor
- ensure — stav crontabu:
present— vytvoriť, ak neexistujeneprítomný— odstrániť, ak existuje
- command — aký príkaz spúšťať
- environment — v akom prostredí spúšťať príkaz (zoznam premenných prostredia a ich hodnôt cez
=) - user — od ktorého používateľa spúšťať príkaz
- minúta, hodina, deň v týždni, mesiac, deň v mesiaci — kedy spúšťať cron. Ak niektorý z týchto atribútov nie je uvedený, jeho hodnota v crontabe bude
*.
V Puppet 6.0 cron ako keby v puppetserver, preto nie je dokumentácia na všeobecnej stránke. Ale je v puppet-agent, takže jeho inštalácia nie je potrebná. Dokumentáciu o ňom si môžete pozrieť , alebo .
O zdrojoch vo všeobecnosti
Požiadavky na jedinečnosť zdrojov
Najčastejšia chyba, s ktorou sa stretávame — Duplicitné vyhlásenie. Táto chyba nastáva, keď do adresára vstúpi dva a viac zdrojov rovnakého typu s rovnakým názvom.
Preto ešte raz napíšem: v manifestoch pre jednu uzol by nemali byť zdroje rovnakého typu s rovnakým názvom (title)!
Niekedy je potrebné nainštalovať balíčky s rovnakým názvom, ale rôznymi správami balíkov. V takom prípade je potrebné používať parameter name, aby sa predišlo chybám:
package { 'ruby-mysql':
ensure => installed,
name => 'mysql',
provider => 'gem',
}
package { 'python-mysql':
ensure => installed,
name => 'mysql',
provider => 'pip',
}V iných typoch zdrojov existujú podobné parametre, ktoré pomáhajú predchádzať duplicite, — name u service, command u exec, a tak ďalej.
Metaparametre
Niektoré špeciálne parametre má každý typ zdroja, nezávisle od jeho podstaty.
Úplný zoznam metaparametrov .
Stručný zoznam:
- require — v tomto parametre sa uvádza, od akých zdrojov závisí daný zdroj.
- pred — v tomto parametre sa uvádza, aké zdroje závisia od daného zdroja.
- subscribe — v tomto parametre sa uvádza, od akých zdrojov dostáva upozornenia daný zdroj.
- notify — v tomto parametre sa uvádza, aké zdroje dostávajú upozornenia od daného zdroja.
Všetky uvedené metaparametre prijímajú buď jeden odkaz na zdroj, alebo pole odkazov v zátvorkách.
Odkazy na zdroje
Odkaz na zdroj je jednoducho spomenutie zdroja. Používajú sa hlavne na uvádzanie závislostí. Odkaz na neexistujúci zdroj vyvolá chybu kompilácie.
Syntax odkazu je nasledovná: typ zdroja s veľkým písmenom (ak názov typu obsahuje dvojité dvojbodky, každá časť názvu medzi dvojbodkami sa píše s veľkým písmenom), ďalej v zátvorkách názov zdroja (veľkost písmen sa nemení!). Medzi nimi nesmú byť medzery, zátvorky sa píšu hneď za názvom typu.
Príklad:
file { '/file1': ensure => present }
file { '/file2':
ensure => directory,
before => File['/file1'],
}
file { '/file3': ensure => absent }
File['/file1'] -> File['/file3']Závislosti a upozornenia
Ako už bolo povedané, jednoduché závislosti medzi zdrojmi sú tranzitívne. Mimochodom, buďte opatrní pri nastavovaní závislostí — môžete vytvoriť cyklické závislosti, čo vyvolá chybu kompilácie.
Na rozdiel od závislostí, upozornenia nie sú tranzitívne. Pre upozornenia platia nasledujúce pravidlá:
- Ak zdroj dostane upozornenie, aktualizuje sa. Ako sa aktualizuje, závisí od typu zdroja — exec spúšťa príkaz, service reštartuje službu, package preinštaluje balík. Ak pre zdroj nie je definované žiadne konanie pri aktualizácii, nič sa nestane.
- Pri jednom behu Papeta sa zdroj aktualizuje maximálne raz. To je možné, pretože upozornenia obsahujú závislosti a graf závislostí neobsahuje cykly.
- Ak sa Pappet zmení stav zdroja, zdroj odošle upozornenia všetkým, ktorí sú naňho prihlásení.
- Ak sa zdroj aktualizuje, odošle upozornenia všetkým, ktorí sú naňho prihlásení.
Spracovanie nešpecifikovaných parametrov
Vo všeobecnosti, ak nemá nejaký parameter zdroja hodnotu predvolenú a tento parameter nie je špecifikovaný v manifeste, Pappet nezmení túto vlastnosť príslušného zdroja na nódě. Napríklad, ak parametru typu file nie je uvedený parameter owner, Pappet nezmení vlastníka príslušného súboru.
Zoznámenie sa s triedami, premennými a definíciami
Predpokladajme, že máme niekoľko nodov, na ktorých je rovnaká časť konfigurácie, ale existujú aj rozdiely - inak by sme mohli toto všetko opísať v jednom bloku. node {}. Samozrejme, môžeme jednoducho skopírovať rovnaké časti konfigurácie, ale vo všeobecnosti je to zlé riešenie - konfigurácia sa rozrastie, pri zmene spoločnej časti konfigurácie bude potrebné upraviť to isté na viacerých miestach. Takto je ľahké sa zmýliť, a vo všeobecnosti je princíp DRY (neopakuj sa) vymyslený nie bezdôvodne.
Na vyriešenie takého problému existuje konštrukcia ako třída.
Triedy
sú pomenované bloky kódu Pappet. Triedy sú potrebné na znovupoužitie kódu.
Najprv je potrebné triedu opísať. Samotný popis nepridáva žiadne zdroje nikde. Trieda sa opisuje v manifestoch:
# Описание класса начинается с ключевого слова class и его названия.
# Дальше идёт тело класса в фигурных скобках.
class example_class {
...
}Potom túto triedu môžeme použiť:
# первый вариант использования — в стиле ресурса с типом class
class { 'example_class': }
# второй вариант использования — с помощью функции include
include example_class
# про отличие этих двух вариантов будет рассказано дальшеPríklad z predchádzajúcej úlohy - presunieme inštaláciu a nastavenie nginx do triedy:
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
}Premenné
Trieda z predchádzajúceho príkladu nie je veľmi flexibilná, pretože vždy prináša jednu a tú istú konfiguráciu nginx. Poďme urobiť tak, aby bola cesta ku konfigurácii premenná, potom túto triedu môžeme použiť na inštaláciu nginx s akoukoľvek konfiguráciou.
To je možné urobiť .
Pozor: premenné v Pappete sú nemenné!
Okrem toho je možné k premennej pristupovať iba po tom, čo bola vyhlásená, inak bude hodnota premennej undef.
Príklad práce s premennými:
# создание переменных
$variable = 'value'
$var2 = 1
$var3 = true
$var4 = undef
# использование переменных
$var5 = $var6
file { '/tmp/text': content => $variable }
# интерполяция переменных — раскрытие значения переменных в строках. Работает только в двойных кавычках!
$var6 = "Variable with name variable has value ${variable}"V Pappete existujú menové priestory, a premenne majú oblast viditeľnosti: premenná s rovnakým menom môže byť definovaná v rôznych menných priestoroch. Pri riešení hodnoty premennej sa premenná najprv hľadá v aktuálnom mennom priestore, potom v obklopujúcom a tak ďalej.
Príklady menných priestorov:
- globálny — sem spadajú premenné mimo popisu triedy alebo uzla;
- menový priestor uzla v opise uzla;
- menový priestor triedy v opise triedy.
Aby sa predišlo nejednoznačnosti pri prístupe k premennej, môžeme uviesť menový priestor v názve premennej:
# переменная без пространства имён
$var
# переменная в глобальном пространстве имён
$::var
# переменная в пространстве имён класса
$classname::var
$::classname::varDohodneme sa, že cesta k konfigurácii nginx je v premennej $nginx_conf_source. Trieda potom bude vyzerať nasledovne:
class nginx_example {
package { 'nginx':
ensure => installed,
}
-> file { '/etc/nginx':
ensure => directory,
source => $nginx_conf_source, # tu používame premennú namiesto pevného reťazca
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
}Avšak uvedený príklad je zlý v tom, že existuje akékoľvek "tajné poznanie" o tom, že niekde vnútri triedy sa používa premenná s takýmto menom. Oveľa správnejšie je spraviť toto poznanie spoločným — triedy môžu mať parametre.
Parametre triedy — to sú premenné v menovom priestore triedy, sú definované v hlavičke triedy a môžu byť použité ako bežné premenné v tele triedy. Hodnoty parametrov sa uvádzajú pri použití triedy v manifeste.
Parametru môžeme určiť hodnotu predvolenú. Ak parameter nemá predvolenú hodnotu a hodnota nie je zadaná pri použití, spôsobí to chybu kompilácie.
Poďme parametrize triedu z predchádzajúceho príkladu a pridajme dva parametre: prvý, povinný — cesta k konfigurácii, a druhý, voliteľný — názov balíka s nginx (v Debiane napríklad existujú balíky 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', # задаём параметры класса точно так же, как параметры для других ресурсов
}
}V Puppet sú premenné typované. Existuje . Typy údajov sa zvyčajne používajú na validáciu hodnôt parametrov, ktoré sa predávajú do tried a definícií. Ak predaný parameter nevyhovuje uvedenému typu, dôjde k chybe kompilácie.
Typ sa píše priamo pred názov parametra:
class example (
String $param1,
Integer $param2,
Array $param3,
Hash $param4,
Hash[String, String] $param5,
) {
...
}Triedy: include classname vs class{‘classname’:}
Každá trieda je zdrojom typu class. Tak ako pri všetkých ostatných typoch zdrojov, na jednom uzle nemôžu byť dve inštancie tej istej triedy.
Ak sa pokúsite pridať triedu na ten istý uzol dvakrát pomocou class { 'classname':} (bez ohľadu na to, či s rôznymi alebo rovnakými parametrami), dôjde k chybám pri kompilácii. Na druhej strane, ak používate triedu v štýle zdroja, môžete priamo v manifeste zadať všetky jej parametre.
Avšak ak použijete include, tak triedu môžete pridávať koľkokrát chcete. Ide o to, že include – idempotentná funkcia, ktorá kontroluje, či bola trieda pridaná do adresára. Ak trieda nie je v adresári – pridá ju, a ak už existuje, tak nič nerobí. Ale pri použití include nie je možné zadať parametre triedy pri jej deklarácii – všetky povinné parametre musia byť zadané z externého zdroja údajov – Hiera alebo ENC. O nich sa porozprávame v ďalšom článku.
Definy
Ako bolo povedané v predchádzajúcej časti, tá istá trieda nemôže byť na uzle viac ako raz. Avšak v niektorých prípadoch je potrebné mať možnosť aplikovať ten istý blok kódu s rôznymi parametrami na jednom uzle. Inými slovami, existuje potreba vlastného typu zdroja.
Napríklad, na inštaláciu modulu PHP v Avito robíme nasledovné:
- Inštalujeme balík s týmto modulom.
- Vytvárame konfiguračný súbor pre tento modul.
- Vytvárame symlink na konfig pre php-fpm.
- Vytvárame symlink na konfig pre php cli.
V týchto prípadoch sa používa konštrukcia, ako je (define, defined type, defined resource type). Definícia je podobná triede, ale sú tu rozdiely: po prvé, každá definícia je typom zdroja, nie zdrojom; po druhé, každá definícia má implicitný parameter $title, do ktorého sa dostáva názov zdroja pri jeho deklarácii. Rovnako ako pri triedach, definíciu najprv treba popísať, potom ju môžete používať.
Zjednodušený príklad s modulom pre PHP:
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'], # debian php packages automatically create symlinks and restart the php-fpm service - we do not need this, as we manage both the symlinks and the service with 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' }
}V definícii je najjednoduchšie zachytiť chybu Duplicate declaration. K tomu dochádza, ak v definícii existuje zdroj s konštantným názvom a na nejakej node sú dva alebo viac inštancií tejto definície.
Ochrana pred týmto je jednoduchá: všetky zdroje vo vnútri definície by mali mať názov závislý od $title. Ako alternatívu - idempotentné pridanie zdrojov, v najjednoduchšom prípade stačí vyčleniť spoločné pre všetky inštancie definície zdroje do samostatnej triedy a zahrnúť túto triedu do definície - funkcia include je idempotentná.
Existujú aj iné spôsoby, ako dosiahnuť idempotentnosť pri pridávaní zdrojov, a to pomocou funkcií defined a ensure_resources, ale o tom sa zmienim v ďalšej časti.
Závislosti a upozornenia pre triedy a definície
Triedy a definície pridávajú nasledujúce pravidlá k spracovaniu závislostí a upozornení:
- závislosť od triedy/definície pridáva závislosti od všetkých zdrojov triedy/definície;
- závislosť triedy/definície pridáva závislosti všetkým zdrojom triedy/definície;
- upozornenie triedy/definície upozorňuje všetky zdroje triedy/definície;
- prihlásenie na triedu/definíciu prihlásí na všetky zdroje triedy/definície.
Podmienečné operátory a selektory
ak
Tu je to jednoduché:
if VYRAZENIE1 {
...
} elsif VYRAZENIE2 {
...
} else {
...
}pokial nie
unless — to je if naopak: blok kódu sa vykoná, ak je výraz nepravdivý.
unless VYRAZENIE {
...
}prípad
Tu tiež nič zložité. Ako hodnoty môžete použiť bežné hodnoty (reťazce, čísla a tak ďalej), regulárne výrazy, ako aj dátové typy.
case VYRAZENIE {
HODNOTA1: { ... }
HODNOTA2, HODNOTA3: { ... }
default: { ... }
}Selektory
Selektor je jazyková konštrukcia podobná prípad, len namiesto vykonávania bloku kódu vracia hodnotu.
$var = $othervar ? { 'val1' => 1, 'val2' => 2, default => 3 }Moduly
Keď je konfigurácia malá, je ľahko ovládateľná v jednom manifeste. Ale čím viac konfigurácie popisujeme, tým viac tried a nodov sa objavuje v manifeste, zväčšuje sa a stáva sa nepraktickým na prácu.
Okrem toho existuje problém opätovného použitia kódu — keď je celý kód v jednom manifeste, je ťažké ho zdieľať s inými. Na riešenie týchto dvoch problémov v Puppet existuje entita nazývaná moduly.
Moduly — sú to súbory tried, definícií a ďalších Puppet entít, ktoré sú presunuté do samostatného adresára. Inými slovami, modul je nezávislá časť Puppet logiky. Napríklad môže byť modul na prácu s nginx, ktorý bude obsahovať iba to, čo je potrebné na prácu s nginx, a môže existovať aj modul na prácu s PHP a tak ďalej.
Moduly sú verziované a tiež podporujú závislosti medzi modulmi. Existuje otvorený repozitár modulov — .
Na Puppet serveri sú moduly uložené v podadresári modules koreňového adresára. Vnútri každého modulu je štandardná schéma adresárov — manifests, files, templates, lib a tak ďalej.
Štruktúra súborov v module
V koreňovej časti modulu môžu byť nasledujúce adresáre s popisnými názvami:
manifests— v nej sú uložené manifestyfiles— v nej sú uložené súborytemplates— v nej sú uložené šablónylib— v nej je uložený Ruby kód
Toto nie je úplný zoznam adresárov a súborov, ale pre tento článok to zatiaľ stačí.
Názvy zdrojov a názvy súborov v module
Zdroje (triedy, definície) v module nemožno nazývať ľubovoľne. Okrem toho existuje priamy vzťah medzi názvom zdroja a názvom súboru, v ktorom Puppet vyhľadá popis tohto zdroja. Ak porušíte pravidlá naming, Puppet jednoducho nenájde popis zdrojov a vznikne chyba pri kompilácii.
Pravidlá sú jednoduché:
- Všetky zdroje v module musia byť v namespacu modulu. Ak sa modul nazýva
foo, všetky zdroje v ňom musia byť pomenovanéfoo::, alebo jednoduchofoo. - Zdroje s názvom modulu musia byť v súbore
init.pp. - Pre ostatné zdroje je schéma pomenovania súborov nasledujúca:
- prefix s menom modulu sa odhodiť
- všetky dvojité dvojbodky, ak existujú, sa nahrádzajú lomkami
- pridáva sa prípona
.pp
Demonštrujem na príklade. Predpokladajme, že píšem modul nginx. V ňom sú nasledujúce zdroje:
- třída
nginxopísaný v manifesteinit.pp; - třída
nginx::serviceopísaný v manifesteservice.pp; - definícia
nginx::serveropísaný v manifesteserver.pp; - definícia
nginx::server::locationopísaný v manifesteserver/location.pp.
Šablóny
Určite viete, čo sú šablóny, nebudem to tu podrobne rozoberať. Ale pre každý prípad pridám .
Ako používať šablóny: význam šablóny sa dá odhaliť pomocou funkcie template, ktorej sa predáva cesta k šablóne. Pre zdroje typu file používame spolu s parametrom content. Napríklad takto:
file { '/tmp/example': content => template('modulename/templatename.erb')Cesta vo forme / predpokladá súbor /modules//templates/.
Okrem toho existuje funkcia inline_template — tej sa ako vstup predáva text šablóny, nie názov súboru.
V rámci šablón je možné používať všetky premenné Puppet v aktuálnom rozsahu viditeľnosti.
Puppet podporuje šablóny vo formáte ERB a EPP:
Stručne o ERB
Riadiace konštrukcie:
<%= ВЫРАЖЕНИЕ %>— vložiť hodnotu výrazu<% ВЫРАЖЕНИЕ %>— vypočítať hodnotu výrazu (bez vloženia). Sem patrí bežne podmienkové operátory (if), cykly (each).<%# КОММЕНТАРИЙ %>
Výrazy v ERB sú písané v Ruby (vlastne, ERB — je Embedded Ruby).
Na prístup k premenným z manifiestu je potrebné doplniť @ k názvu premennej. Aby ste odstránili novú riadku, ktorá sa objavuje po riadiacej konštrukcii, je potrebné použiť uzatváraciu značku -%>.
Príklad použitia šablóny
Predpokladajme, že píšem modul na správu ZooKeeper. Trieda zodpovedná za vytvorenie konfigurácie vyzerá približne takto:
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'),
}
}A zodpovedajúca šablóna zoo.cfg.erb — vyzerá takto:
0 -%>
server.=::;
dataDir=
=Fakty a vstavané premenné
Často konkrétna časť konfigurácie závisí od toho, čo sa práve deje na node. Napríklad v závislosti od toho, akú verziu Debianu máte, je potrebné nainštalovať tú alebo onú verziu balíka. Môžete sledovať všetko ručne, prepisovaním manifestov v prípade zmeny nodov. Ale to je neseriózny prístup, automatizácia je oveľa lepšia.
Na získanie informácií o node v Puppet existuje mechanizmus nazývaný fakty. Fakty sú informácie o node, dostupné v manifestoch ako bežné premenné v globálnom priestore názvov. Napríklad, meno hostiteľa, verzia operačného systému, architektúra procesora, zoznam používateľov, zoznam sieťových rozhraní a ich adries, a mnoho, mnoho ďalších. Fakty sú dostupné v manifestoch a šablónach ako bežné premenné.
Príklad práce s faktami:
notify { "Beží OS ${facts['os']['name']} verzia ${facts['os']['release']['full']}" : }
# typ zdroja notify jednoducho vypíše správu do loguFormálne povedané, fakt má meno (reťazec) a hodnotu (sú dostupné rôzne typy: reťazce, polia, slovníky). Existuje . Taktiež si môžete napísať vlastné. Zbierky faktov sú popísané , alebo ako . Fakty môžu byť tiež predstavené vo forme na nodoch.
Počas práce Puppet agent najprv skopíruje zo servera Puppet na node všetky dostupné zbierky faktov, následne ich spustí a odosiela na server zozbierané fakty; až potom server začína s kompiláciou adresára.
Fakty vo forme spustiteľných súborov
Takéto fakty sú umiestnené do modulov v adresári facts.d. Je samozrejmé, že súbory musia byť spustiteľné. Pri spustení by mali na štandardný výstup vypísať informácie buď vo formáte YAML, alebo v formáte "kľúč=hodnota".
Nezabúdajte, že fakty sa rozšíria na všetky nody, ktoré sú pod správou Puppet servera, na ktorý sa nasadzuje váš modul. Preto v skripte dbajte na kontrolu toho, že v systéme sú všetky potrebné programy a súbory na fungovanie vášho faktu.
#!/bin/sh
echo "testfact=success"#!/bin/sh
echo '{"testyamlfact":"success"}'Fakty na Ruby
Takéto fakty sú umiestnené do modulov v adresári 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
endTextové fakty
Takéto fakty sú umiestnené na nodoch v adresári /etc/facter/facts.d v starom Puppet alebo /etc/puppetlabs/facts.d v novom Puppe.
examplefact=examplevalue---
examplefact2: examplevalue2
anotherfact: anothervaluePrístup k faktom
K faktom sa môžeme obrátiť dvomi spôsobmi:
- cez slovník
$facts:$facts['fqdn']; - použitím názvu faktu ako názvu premennej:
$fqdn.
Najlepšie je použiť slovník $facts, a ešte lepšie uviesť globálny priestoru názvov ($::facts).
Zabudované premenné
Okrem faktov existujú aj , dostupné v globálnom priestore názvov.
- trusted facts — premenné, ktoré sa berú zo certifikátu klienta (keďže certifikát sa zvyčajne vydáva na puppet-serveri, agent nemôže len tak zmeniť svoj certifikát, preto sú premenné a „dôveryhodné“): názov certifikátu, meno hostiteľa a domény, rozšírenia z certifikátu.
- server facts — premenné, ktoré sa týkajú informácií o serveri — verzia, názov, IP adresa servera, prostredie.
- agent facts — premenné, ktoré pridáva priamo puppet-agent, a nie facter — názov certifikátu, verzia agenta, verzia puppetu.
- master variables — premenné puppetmastera (sic!). Tam je približne to isté, čo v server facts, plus sú dostupné hodnoty konfiguračných parametrov.
- compiler variables — premenné kompilátora, ktoré sa líšia v každom rozsahu viditeľnosti: názov aktuálneho modulu a názov modulu, v ktorom bolo pristupované k aktuálnemu objektu. Môžu sa použiť, napríklad na kontrolu, že vaše súkromné triedy nie sú priamo používané z iných modulov.
Doplnok 1: ako to všetko spúšťať a odlaďovať?
V článku bolo veľa príkladov puppet-kódu, ale vôbec nebola reč o tom, ako tento kód spúšťať. Takže, opravujem sa.
Na prácu s Puppetom stačí agent, ale v mnohých prípadoch bude potrebný aj server.
Agent
Aspoň s piatou verziou balíky puppet-agent z obsahujú všetky závislosti (ruby a príslušné gemy), preto nie sú žiadne ťažkosti s inštaláciou (hovorím o Debian-based distribúciách — RPM-based distribúciami my nepoužívame).
V najjednoduchšom prípade pre použitie puppet konfigurácie stačí spustiť agenta v bezz serverovom režime: za predpokladu, že puppet kód je skopírovaný na uzol, spustíte 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 secondsJe lepšie, samozrejme, zdvihnúť server a spustiť agentov na nódach v režime démona — potom raz za polhodinu budú aplikovať konfiguráciu stiahnutú zo servera.
Môžete simulovať model push prácou — prejsť na zaujímavú nód a spustiť sudo puppet agent -t. Kľúč -t (--test) v skutočnosti zahrňuje niekoľko možností, ktoré je možné zapnúť aj samostatne. Medzi tieto možnosti patrí:
- nepracovať v režime démona (podľa predvolené nastavenia agent beží v režime démona);
- skončiť prácu po aplikácii katalógu (podľa predvolené nastavenia agent pokračuje v práci a bude aplikovať konfiguráciu raz za polhodinu);
- písať podrobný log práce;
- ukazovať zmeny v súboroch.
Agent má režim práce bez zmien — môže sa používať v prípade, ak si nie ste istí, že ste napísali správnu konfiguráciu, a chcete skontrolovať, čo presne agent zmení počas práce. Tento režim sa aktivuje parametrom --noop v príkazovom riadku: sudo puppet agent -t --noop.
Okrem toho je možné zapnúť ladenie logov — v ňom puppet zapisuje o všetkých akciách, ktoré vykonáva: o zdroji, ktorý práve spracováva, o parametroch tohto zdroja, o tom, aké programy spúšta. Samozrejme, toto je parameter --debug.
Server
Plnohodnotnú konfiguráciu puppetsERVERu a deploy naň kód v tomto článku nebudem rozoberať, poviem len, že z krabice sa inštaluje celkom funkčná verzia servera, ktorá nevyžaduje ďalšie nastavenia pre prácu v podmienkach malého počtu nód (povedzme do sto). Väčšie množstvo nód už bude vyžadovať ladenie — podľa predvolené nastavenia puppetserver nespúšťa viac ako štyroch pracovníkov, pre vyšší výkon je potrebné zvýšiť ich počet a nezabudnúť zvýšiť limity pamäte, inak väčšinu času bude server robiť garbage collect.
Deploy kódu — ak potrebujete rýchlo a jednoducho, pozrite sa (na r10k)[], pre malé inštalácie by to malo stačiť.
Doplnok 2: odporúčania na písanie kódu
- Vyberte celú logiku do tried a definícií.
- Udržujte triedy a definície v moduloch, nie v manifestoch s popisom uzlov.
- Používajte fakty.
- Nerobte if-y podľa hostname-ov.
- Nebojte sa pridávať parametre pre triedy a definície — je to lepšie, než implicitná logika skrytá v tele triedy/definície.
Prečo to odporúčam — vysvetlím v nasledujúcom článku.
Záver
Na tomto ukončíme úvod. V nasledujúcom článku sa budem venovať Hiera, ENC a PuppetDB.
Len registrovaní používatelia sa môžu zúčastniť prieskumu. , prosím.
V skutočnosti je materiálu omnoho viac — môžem napísať články na nasledujúce témy, hlasujte, o čom by vás zaujímalo čítať:
- 59,1%Pokročilé konštrukty puppetu — niektoré vysoké úrovne: cykly, mapovanie a ďalšie lambda výrazy, kolektory zdrojov, exportované zdroje a medziuzlové interakcie cez Puppet, tagy, poskytovatelia, abstraktné dátové typy.
- 31,8%„Som admin u mámy“ alebo ako sme v Avite spojili niekoľko serverov puppetov rôznych verzií, no a vlastne aj časť o správe puppet servera.
- 81,8%Ako píšeme puppet kód: nástrojová oblasť, dokumentácia, testovanie, CI/CD.
Hlasovalo 22 používateľov. Zdržali sa 9 používateľov.
Zdroj: habr.com
