Puppet je systém riadenia konfigurácie. Používa sa na privedenie hostiteľov do požadovaného stavu a udržiavanie tohto stavu.
S Puppet pracujem už viac ako päť rokov. Tento text je v podstate preložený a preusporiadaný kompilácia kľúčových bodov z oficiálnej dokumentácie, ktorá začiatočníkom umožní rýchlo pochopiť podstatu Puppet.
Základné informácie
Operačný systém Puppet je klient-server, aj keď podporuje aj bezserverovú prevádzku s obmedzenou funkčnosťou.
Používa sa ťahový model prevádzky: štandardne raz za pol hodinu klienti kontaktujú server kvôli konfigurácii a použijú ju. Ak ste pracovali s Ansible, potom používajú iný model push: správca iniciuje proces aplikácie konfigurácie, samotní klienti nič nepoužijú.
Počas sieťovej komunikácie sa používa obojsmerné šifrovanie TLS: server a klient majú svoje vlastné súkromné kľúče a zodpovedajúce certifikáty. Typicky server vydáva certifikáty pre klientov, ale v zásade je možné použiť externú CA.
Úvod do manifestov
V bábkarskej terminológii na bábkový server pripojiť uzly (uzly). Konfigurácia pre uzly je napísaná v manifestoch v špeciálnom programovacom jazyku - Puppet DSL.
Bábkové DSL je deklaratívny jazyk. Popisuje požadovaný stav uzla vo forme deklarácií jednotlivých zdrojov, napr.
- Súbor existuje a má špecifický obsah.
- Balík je nainštalovaný.
- Služba sa začala.
Zdroje môžu byť vzájomne prepojené:
- Existujú závislosti, ovplyvňujú poradie, v ktorom sa zdroje využívajú.
Napríklad „najprv nainštalujte balík, potom upravte konfiguračný súbor a potom spustite službu“. - Existujú upozornenia - ak sa zdroj zmenil, odošle upozornenia na zdroje, ktoré sú k nemu prihlásené.
Ak sa napríklad zmení konfiguračný súbor, službu môžete automaticky reštartovať.
Okrem toho má Bábkové DSL funkcie a premenné, ako aj podmienené príkazy a selektory. Podporované sú aj rôzne šablónovacie mechanizmy – EPP a ERB.
Bábka je napísaná v Ruby, takže veľa konštruktov a výrazov je prevzatých odtiaľ. Ruby vám umožňuje rozšíriť Puppet - pridať komplexnú logiku, nové typy zdrojov, funkcie.
Keď je spustená Puppet, manifesty pre každý konkrétny uzol na serveri sa skompilujú do adresára. adresár je zoznam zdrojov a ich vzťahov po výpočte hodnoty funkcií, premenných a rozšírenia podmienených príkazov.
Syntax a kódový štýl
Tu sú časti oficiálnej dokumentácie, ktoré vám pomôžu pochopiť syntax, ak uvedené príklady nestačia:
Tu je príklad toho, ako manifest vyzerá:
# Комментарии пишутся, как и много где, после решётки.
#
# Описание конфигурации ноды начинается с ключевого слова node,
# за которым следует селектор ноды — хостнейм (с доменом или без)
# или регулярное выражение для хостнеймов, или ключевое слово default.
#
# После этого в фигурных скобках описывается собственно конфигурация ноды.
#
# Одна и та же нода может попасть под несколько селекторов. Про приоритет
# селекторов написано в статье про синтаксис описания нод.
node 'hostname', 'f.q.d.n', /regexp/ {
# Конфигурация по сути является перечислением ресурсов и их параметров.
#
# У каждого ресурса есть тип и название.
#
# Внимание: не может быть двух ресурсов одного типа с одинаковыми названиями!
#
# Описание ресурса начинается с его типа. Тип пишется в нижнем регистре.
# Про разные типы ресурсов написано ниже.
#
# После типа в фигурных скобках пишется название ресурса, потом двоеточие,
# дальше идёт опциональное перечисление параметров ресурса и их значений.
# Значения параметров указываются через т.н. hash rocket (=>).
resource { 'title':
param1 => value1,
param2 => value2,
param3 => value3,
}
}
Odsadenie a zlomy riadkov nie sú povinnou súčasťou manifestu, ale odporúča sa
- Dvojmiestne zarážky, tabulátory sa nepoužívajú.
- Zložené zátvorky sú oddelené medzerou, dvojbodky nie sú oddelené medzerou.
- Čiarky za každým parametrom vrátane posledného. Každý parameter je na samostatnom riadku. Výnimku tvorí prípad bez parametrov a jedného parametra: môžete písať na jeden riadok a bez čiarky (t.j.
resource { 'title': }
иresource { 'title': param => value }
). - Šípky na parametroch by mali byť na rovnakej úrovni.
- Pred nimi sú napísané šípky vzťahu zdrojov.
Umiestnenie súborov na pappetserver
Pre ďalšie vysvetlenie uvediem pojem „koreňový adresár“. Koreňový adresár je adresár, ktorý obsahuje konfiguráciu Puppet pre konkrétny uzol.
Koreňový adresár sa líši v závislosti od verzie Puppet a použitých prostredí. Prostredia sú nezávislé sady konfigurácií, ktoré sú uložené v samostatných adresároch. Zvyčajne sa používa v kombinácii s git, v takom prípade sú prostredia vytvorené z vetiev git. Podľa toho je každý uzol umiestnený v jednom alebo inom prostredí. Toto je možné nakonfigurovať na samotnom uzle alebo v ENC, o ktorom budem hovoriť v ďalšom článku.
- V tretej verzii ("stará bábka") bol základný adresár
/etc/puppet
. Použitie prostredí je voliteľné – napríklad pri starom Puppet ich nepoužívame. Ak sa používajú prostredia, zvyčajne sa ukladajú/etc/puppet/environments
, koreňovým adresárom bude adresár prostredia. Ak sa prostredia nepoužívajú, koreňový adresár bude základným adresárom. - Počnúc štvrtou verziou („nová bábka“) sa používanie prostredí stalo povinným a základný adresár bol presunutý do
/etc/puppetlabs/code
. Podľa toho sú prostredia uložené v/etc/puppetlabs/code/environments
, koreňový adresár je adresár prostredia.
V koreňovom adresári musí byť podadresár manifests
, ktorý obsahuje jeden alebo viac manifestov popisujúcich uzly. Okrem toho by mal existovať podadresár modules
, ktorý obsahuje moduly. Aké moduly sú, vám poviem o niečo neskôr. Okrem toho môže mať starý Puppet aj podadresár files
, ktorý obsahuje rôzne súbory, ktoré kopírujeme do uzlov. V novom Puppet sú všetky súbory umiestnené v moduloch.
Súbory manifestu majú príponu .pp
.
Pár príkladov boja
Popis uzla a zdroja na ňom
Na uzle server1.testdomain
musí byť vytvorený súbor /etc/issue
s obsahom Debian GNU/Linux n l
. Súbor musí vlastniť používateľ a skupina root
, musia byť prístupové práva 644
.
Píšeme manifest:
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 в начале будет воспринято как записанное в восьмеричной системе, и всё пойдёт не так, как задумано
}
}
Vzťahy medzi zdrojmi na uzle
Na uzle server2.testdomain
nginx musí byť spustený a musí pracovať s predtým pripravenou konfiguráciou.
Rozložme si problém:
- Balík je potrebné nainštalovať
nginx
. - Je potrebné, aby konfiguračné súbory boli skopírované zo servera.
- Služba musí byť spustená
nginx
. - Ak je konfigurácia aktualizovaná, služba sa musí reštartovať.
Píšeme manifest:
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 получает уведомление,
# соответствующий сервис перезапускается.
}
Aby to fungovalo, potrebujete približne nasledujúce umiestnenie súboru na bábkovom serveri:
/etc/puppetlabs/code/environments/production/ # (это для нового Паппета, для старого корневой директорией будет /etc/puppet)
├── manifests/
│ └── site.pp
└── modules/
└── example/
└── files/
└── nginx-conf/
├── nginx.conf
├── mime.types
└── conf.d/
└── some.conf
Typy zdrojov
Kompletný zoznam podporovaných typov zdrojov nájdete tu
rezeň
Spravuje súbory, adresáre, symbolické odkazy, ich obsah a prístupové práva.
Parametre:
- názov zdroja — cesta k súboru (voliteľné)
- cesta — cesta k súboru (ak nie je uvedená v názve)
- zabezpečiť - typ súboru:
absent
- vymazať súborpresent
— musí existovať súbor akéhokoľvek typu (ak súbor neexistuje, vytvorí sa bežný súbor)file
- bežný súbordirectory
- adresárlink
- symbolický odkaz
- obsah — obsah súboru (vhodné len pre bežné súbory, nemožno ho použiť spolu s zdroj alebo terč)
- zdroj — odkaz na cestu, z ktorej chcete skopírovať obsah súboru (nedá sa použiť spolu s obsah alebo terč). Môže byť špecifikovaný buď ako URI so schémou
puppet:
(potom sa použijú súbory z bábkového servera) a so schémouhttp:
(Dúfam, že je jasné, čo sa stane v tomto prípade) a dokonca aj s diagramomfile:
alebo ako absolútna cesta bez schémy (vtedy sa použije súbor z lokálneho FS na uzle) - terč — kam má smerovať symbolický odkaz (nedá sa použiť spolu s obsah alebo zdroj)
- majiteľ — používateľ, ktorý by mal súbor vlastniť
- skupina — skupina, do ktorej by mal súbor patriť
- spôsob — oprávnenia na súbor (ako reťazec)
- opakovať - umožňuje rekurzívne spracovanie adresárov
- purge - umožňuje mazanie súborov, ktoré nie sú popísané v Bábke
- pevnosť - umožňuje mazanie adresárov, ktoré nie sú popísané v Puppet
balíček
Nainštaluje a odstráni balíky. Schopný spracovať upozornenia - preinštaluje balík, ak je zadaný parameter reinstall_on_refresh.
Parametre:
- názov zdroja — názov balíka (voliteľné)
- názov — názov balíka (ak nie je uvedený v názve)
- Poskytovateľ — správca balíkov na použitie
- zabezpečiť — požadovaný stav balenia:
present
,installed
- nainštalovaná akákoľvek verzialatest
- nainštalovaná najnovšia verziaabsent
- odstránené (apt-get remove
)purged
— odstránené spolu s konfiguračnými súbormi (apt-get purge
)held
- verzia balíka je uzamknutá (apt-mark hold
)любая другая строка
— je nainštalovaná špecifikovaná verzia
- reinstall_on_refresh - keby
true
, potom sa po prijatí upozornenia balík preinštaluje. Užitočné pre distribúcie založené na zdrojovom kóde, kde môže byť potrebné prebudovanie balíkov pri zmene parametrov zostavenia. Predvolenéfalse
.
služba
Spravuje služby. Schopný spracovať upozornenia - reštartuje službu.
Parametre:
- názov zdroja — služba, ktorá sa má spravovať (voliteľné)
- názov — služba, ktorú je potrebné spravovať (ak nie je uvedená v názve)
- zabezpečiť — požadovaný stav služby:
running
- spustenýstopped
- zastavený
- umožniť — ovláda schopnosť spustiť službu:
true
— automatické spustenie je povolené (systemctl enable
)mask
- zamaskovaný (systemctl mask
)false
— automatické spúšťanie je zakázané (systemctl disable
)
- reštart - príkaz na reštartovanie služby
- postavenie — príkaz na kontrolu stavu služby
- hasrestart — uveďte, či initscript služby podporuje reštart. Ak
false
a parameter je špecifikovaný reštart — použije sa hodnota tohto parametra. Akfalse
a parametrom reštart nie je zadané - služba sa zastaví a spustí sa reštart (ale systemd používa príkazsystemctl restart
). - hasstatus — uveďte, či initscript služby podporuje príkaz
status
, akfalse
, potom sa použije hodnota parametra postavenie. Predvolenétrue
.
exec
Spúšťa externé príkazy. Ak neuvediete parametre vytvorí, iba ak, ak alebo len osvieženie, príkaz sa spustí pri každom spustení Puppet. Schopný spracovať upozornenia - spustí príkaz.
Parametre:
- názov zdroja — príkaz, ktorý sa má vykonať (voliteľné)
- povel — príkaz, ktorý sa má vykonať (ak nie je uvedený v názve)
- cesta — cesty, v ktorých sa má hľadať spustiteľný súbor
- iba ak — ak sa príkaz špecifikovaný v tomto parametri dokončí s nulovým návratovým kódom, vykoná sa hlavný príkaz
- ak — ak sa príkaz špecifikovaný v tomto parametri dokončí s nenulovým návratovým kódom, vykoná sa hlavný príkaz
- vytvorí — ak súbor špecifikovaný v tomto parametri neexistuje, vykoná sa hlavný príkaz
- len osvieženie - keby
true
, potom sa príkaz spustí len vtedy, keď tento exec dostane upozornenie z iných zdrojov - cwd — adresár, z ktorého sa má príkaz spustiť
- užívateľ — používateľ, od ktorého sa má príkaz spustiť
- Poskytovateľ - ako spustiť príkaz:
- POSIX — jednoducho sa vytvorí podriadený proces, nezabudnite uviesť cesta
- škrupina - príkaz sa spustí v shelli
/bin/sh
, nemusí byť špecifikované cesta, môžete použiť globbing, potrubia a ďalšie funkcie shellu. Zvyčajne sa automaticky zistí, ak existujú nejaké špeciálne znaky (|
,;
,&&
,||
atď).
cron
Ovláda cronjobs.
Parametre:
- názov zdroja - len nejaký druh identifikátora
- zabezpečiť — stav korunného zamestnania:
present
- vytvoriť, ak neexistujeabsent
- odstrániť, ak existuje
- povel - aký príkaz spustiť
- prostredie — v akom prostredí spustiť príkaz (zoznam premenných prostredia a ich hodnôt cez
=
) - užívateľ — od ktorého používateľa spustiť príkaz
- minúta, hodina, všedný deň, mesiac, mesačný deň — kedy spustiť cron. Ak niektorý z týchto atribútov nie je zadaný, jeho hodnota v crontab bude
*
.
V Bábke 6.0 cron ako keby
O zdrojoch všeobecne
Požiadavky na jedinečnosť zdrojov
Najčastejšou chybou, s ktorou sa stretávame, je Duplicitné vyhlásenie. Táto chyba sa vyskytuje, keď sa v adresári objavia dva alebo viac zdrojov rovnakého typu s rovnakým názvom.
Preto ešte raz napíšem: manifesty pre rovnaký uzol by nemali obsahovať zdroje rovnakého typu s rovnakým názvom!
Niekedy je potrebné nainštalovať balíky s rovnakým názvom, ale s rôznymi správcami balíkov. V tomto prípade musíte použiť parameter name
aby sa predišlo chybe:
package { 'ruby-mysql':
ensure => installed,
name => 'mysql',
provider => 'gem',
}
package { 'python-mysql':
ensure => installed,
name => 'mysql',
provider => 'pip',
}
Ostatné typy zdrojov majú podobné možnosti, aby sa vyhli duplicite − name
у služba, command
у exec, a tak ďalej.
Metaparametre
Každý typ zdroja má nejaké špeciálne parametre, bez ohľadu na jeho povahu.
Úplný zoznam meta parametrov
Krátky zoznam:
- vyžadovať — tento parameter označuje, od ktorých zdrojov tento zdroj závisí.
- pred - Tento parameter určuje, ktoré zdroje závisia od tohto zdroja.
- predplatiť — tento parameter určuje, z ktorých zdrojov tento zdroj dostáva upozornenia.
- oznámiť — Tento parameter určuje, ktoré zdroje dostávajú upozornenia z tohto zdroja.
Všetky uvedené metaparametre akceptujú buď odkaz na jeden zdroj alebo pole odkazov v hranatých zátvorkách.
Odkazy na zdroje
Odkaz na zdroj je jednoducho zmienka o zdroji. Používajú sa hlavne na označenie závislostí. Odkazovanie na neexistujúci zdroj spôsobí chybu kompilácie.
Syntax odkazu je nasledovná: typ zdroja s veľkým písmenom (ak názov typu obsahuje dvojité dvojbodky, potom každá časť názvu medzi dvojbodkami je veľká), potom názov zdroja v hranatých zátvorkách (veľké písmená názvu sa nemení!). Nemali by byť žiadne medzery, hranaté 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 uvedené, jednoduché závislosti medzi zdrojmi sú prechodné. Mimochodom, buďte opatrní pri pridávaní závislostí - môžete vytvárať cyklické závislosti, čo spôsobí chybu pri kompilácii.
Na rozdiel od závislostí nie sú upozornenia prechodné. Pre oznámenia platia nasledujúce pravidlá:
- Ak zdroj dostane upozornenie, aktualizuje sa. Akcie aktualizácie závisia od typu zdroja − exec spustí príkaz, služba reštartuje službu, balíček preinštaluje balík. Ak zdroj nemá definovanú akciu aktualizácie, nič sa nestane.
- Počas jedného spustenia Puppet sa zdroj neaktualizuje viac ako raz. Je to možné, pretože oznámenia obsahujú závislosti a graf závislostí neobsahuje cykly.
- Ak Puppet zmení stav zdroja, zdroj odošle upozornenia všetkým zdrojom, ktoré má na odber.
- Ak je zdroj aktualizovaný, odošle upozornenia všetkým zdrojom, ktoré sú k nemu prihlásené.
Manipulácia s nešpecifikovanými parametrami
Spravidla, ak niektorý parameter zdroja nemá predvolenú hodnotu a tento parameter nie je špecifikovaný v manifeste, potom Puppet nezmení túto vlastnosť pre zodpovedajúci zdroj v uzle. Napríklad, ak zdroj typu rezeň parameter nie je zadaný owner
, potom Puppet nezmení vlastníka príslušného súboru.
Úvod do tried, premenných a definícií
Predpokladajme, že máme niekoľko uzlov, ktoré majú rovnakú časť konfigurácie, ale existujú aj rozdiely - inak by sme to všetko mohli opísať v jednom bloku node {}
. Samozrejme, môžete jednoducho skopírovať identické časti konfigurácie, ale vo všeobecnosti je to zlé riešenie - konfigurácia rastie a ak zmeníte všeobecnú časť konfigurácie, budete musieť na mnohých miestach upraviť to isté. Zároveň je ľahké urobiť chybu a vo všeobecnosti bol princíp DRY (neopakujte sa) vynájdený z nejakého dôvodu.
Na vyriešenie tohto problému existuje taký dizajn ako trieda.
Vyučovanie
Najprv je potrebné opísať triedu. Samotný popis nikde nepridáva žiadne zdroje. Trieda je opísaná v manifestoch:
# Описание класса начинается с ключевого слова class и его названия.
# Дальше идёт тело класса в фигурных скобках.
class example_class {
...
}
Potom je možné triedu použiť:
# первый вариант использования — в стиле ресурса с типом class
class { 'example_class': }
# второй вариант использования — с помощью функции include
include example_class
# про отличие этих двух вариантов будет рассказано дальше
Príklad z predchádzajúcej úlohy - presunieme inštaláciu a konfiguráciu 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 vôbec flexibilná, pretože vždy prináša rovnakú konfiguráciu nginx. Urobme cestu ku konfiguračnej premennej, potom túto triedu možno použiť na inštaláciu nginx s akoukoľvek konfiguráciou.
Dá sa to zvládnuť
Pozor: premenné v Puppet sú nemenné!
Navyše k premennej je možné pristupovať až po jej deklarovaní, inak bude hodnota premennej rovnaká 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}"
Bábka má menné priestorya premenné podľa toho majú oblasť viditeľnosti: Premenná s rovnakým názvom môže byť definovaná v rôznych menných priestoroch. Pri riešení hodnoty premennej sa premenná hľadá v aktuálnom mennom priestore, potom v obklopujúcom mennom priestore atď.
Príklady menného priestoru:
- globálne - tam idú premenné mimo popisu triedy alebo uzla;
- menný priestor uzla v popise uzla;
- priestor názvov triedy v popise triedy.
Aby ste sa vyhli nejednoznačnosti pri prístupe k premennej, môžete v názve premennej zadať priestor názvov:
# переменная без пространства имён
$var
# переменная в глобальном пространстве имён
$::var
# переменная в пространстве имён класса
$classname::var
$::classname::var
Dohodnime sa, že cesta ku konfigurácii nginx leží v premennej $nginx_conf_source
. Potom bude trieda vyzerať takto:
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
}
Uvedený príklad je však zlý, pretože existuje nejaký „tajný poznatok“, že niekde vo vnútri triedy sa používa premenná s takým a takým názvom. Oveľa správnejšie je tieto znalosti zovšeobecniť – triedy môžu mať parametre.
Parametre triedy sú premenné v mennom priestore triedy, sú špecifikované v hlavičke triedy a možno ich použiť ako bežné premenné v tele triedy. Hodnoty parametrov sú špecifikované pri použití triedy v manifeste.
Parameter je možné nastaviť na predvolenú hodnotu. Ak parameter nemá predvolenú hodnotu a táto hodnota nie je pri použití nastavená, spôsobí to chybu kompilácie.
Parametrizujme triedu z vyššie uvedeného príkladu a pridáme dva parametre: prvý, povinný, je cesta ku konfigurácii a druhý, voliteľný, je názov balíka s nginx (napríklad v Debiane sú 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 sa zadávajú premenné. Jedzte
Typ sa zapisuje bezprostredne pred názov parametra:
class example (
String $param1,
Integer $param2,
Array $param3,
Hash $param4,
Hash[String, String] $param5,
) {
...
}
Classes: include classname vs class{'classname':}
Každá trieda je zdrojom určitého typu trieda. Rovnako ako pri akomkoľvek inom type zdroja, na rovnakom uzle nemôžu existovať dve inštancie rovnakej triedy.
Ak sa pokúsite pridať triedu do rovnakého uzla dvakrát pomocou class { 'classname':}
(bez rozdielu, s rôznymi alebo rovnakými parametrami), dôjde k chybe pri kompilácii. Ale ak použijete triedu v štýle prostriedku, môžete okamžite explicitne nastaviť všetky jej parametre v manifeste.
Ak však použijete include
, potom môže byť trieda pridaná toľkokrát, koľkokrát chcete. Faktom je, že include
je idempotentná funkcia, ktorá kontroluje, či bola trieda pridaná do adresára. Ak trieda nie je v adresári, pridá ju, a ak už existuje, neurobí nič. Ale v prípade použitia include
Počas deklarácie triedy nemôžete nastaviť parametre triedy - všetky požadované parametre musia byť nastavené v externom zdroji údajov - Hiera alebo ENC. O nich si povieme v ďalšom článku.
Definuje
Ako bolo povedané v predchádzajúcom bloku, tá istá trieda nemôže byť prítomná na uzle viac ako raz. V niektorých prípadoch však musíte byť schopní použiť rovnaký blok kódu s rôznymi parametrami na rovnakom uzle. Inými slovami, existuje potreba vlastného typu zdroja.
Napríklad, aby sme nainštalovali modul PHP, v Avito urobíme nasledovné:
- Nainštalujte balík s týmto modulom.
- Vytvorme konfiguračný súbor pre tento modul.
- Vytvoríme symbolický odkaz na konfiguráciu pre php-fpm.
- Vytvoríme symbolický odkaz na konfiguráciu pre php cli.
V takýchto prípadoch je vhodný dizajn ako napr $title
, kam sa dostane názov zdroja, keď je deklarovaný. Rovnako ako v prípade tried, definícia musí byť najprv opísaná a potom môže byť použitá.
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'], # триггеры дебиановских 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' }
}
Najjednoduchší spôsob, ako zachytiť chybu Duplicitnej deklarácie, je v Define. Toto sa stane, ak má definícia zdroj s konštantným názvom a na niektorom uzle existujú dve alebo viac inštancií tejto definície.
Je ľahké sa pred tým chrániť: všetky zdroje v definícii musia mať názov v závislosti od $title
. Alternatívou je idempotentné pridávanie zdrojov, v najjednoduchšom prípade stačí zdroje spoločné pre všetky inštancie definície presunúť do samostatnej triedy a túto triedu zahrnúť do definície - funkcie include
idempotentný.
Existujú aj iné spôsoby, ako dosiahnuť idempotenciu pri pridávaní zdrojov, a to pomocou funkcií defined
и ensure_resources
, ale o tom vám poviem v ďalšej epizóde.
Závislosti a upozornenia pre triedy a definície
Triedy a definície pridávajú nasledujúce pravidlá na spracovanie závislostí a upozornení:
- závislosť na triede/definovať pridáva závislosti na všetkých zdrojoch triedy/definovať;
- závislosť triedy/definovať pridáva závislosti ku všetkým zdrojom triedy/definovať;
- upozornenie na triedu/definovať upozorní všetky zdroje triedy/definovať;
- class/define predplatné prihlási na odber všetkých zdrojov triedy/define.
Podmienené príkazy a selektory
if
Tu je to jednoduché:
if ВЫРАЖЕНИЕ1 {
...
} elsif ВЫРАЖЕНИЕ2 {
...
} else {
...
}
ak
if je if inverzne: blok kódu sa vykoná, ak je výraz nepravdivý.
unless ВЫРАЖЕНИЕ {
...
}
puzdro
Ani tu nie je nič zložité. Ako hodnoty môžete použiť regulárne hodnoty (reťazce, čísla atď.), regulárne výrazy a typy údajov.
case ВЫРАЖЕНИЕ {
ЗНАЧЕНИЕ1: { ... }
ЗНАЧЕНИЕ2, ЗНАЧЕНИЕ3: { ... }
default: { ... }
}
Selektory
Selektor je jazyková konštrukcia podobná case
, ale namiesto vykonania bloku kódu vráti hodnotu.
$var = $othervar ? { 'val1' => 1, 'val2' => 2, default => 3 }
moduly
Keď je konfigurácia malá, môže sa ľahko uchovávať v jednom manifeste. Ale čím viac konfigurácií popisujeme, tým viac tried a uzlov je v manifeste, rastie a práca s ním sa stáva nepohodlnou.
Okrem toho je tu problém opätovného použitia kódu – keď je všetok kód v jednom manifeste, je ťažké zdieľať tento kód s ostatnými. Na vyriešenie týchto dvoch problémov má Puppet entitu nazývanú moduly.
moduly - sú to sady tried, definícií a iných entít Bábka umiestnené v samostatnom adresári. Inými slovami, modul je nezávislá časť Bábkovej logiky. Napríklad môže existovať modul na prácu s nginx a bude obsahovať to, čo a len to, čo je potrebné na prácu s nginx, alebo môže existovať modul na prácu s PHP atď.
Moduly sú verzované a podporované sú aj vzájomné závislosti modulov. Existuje otvorené úložisko modulov -
Na bábkovom serveri sú moduly umiestnené v podadresári modules koreňového adresára. Vo vnútri každého modulu je štandardná adresárová schéma - manifesty, súbory, šablóny, lib atď.
Štruktúra súboru v module
Koreň modulu môže obsahovať nasledujúce adresáre s popisnými názvami:
manifests
- obsahuje manifestyfiles
- obsahuje súborytemplates
- obsahuje šablónylib
- obsahuje kód Ruby
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 pomenovať akokoľvek chcete. Okrem toho existuje priama zhoda medzi názvom zdroja a názvom súboru, v ktorom bude Puppet hľadať popis tohto zdroja. Ak porušíte pravidlá pomenovania, Puppet jednoducho nenájde popis zdroja a dostanete chybu kompilácie.
Pravidlá sú jednoduché:
- Všetky prostriedky v module musia byť v mennom priestore modulu. Ak sa modul volá
foo
, potom by mali byť pomenované všetky zdroje v ňomfoo::<anything>
, alebo len takfoo
. - Zdroj s názvom modulu musí byť v súbore
init.pp
. - Pre ostatné zdroje je schéma pomenovania súborov nasledovná:
- predpona s názvom modulu sa zahodí
- všetky dvojité dvojbodky, ak nejaké existujú, sa nahradia lomkami
- je pridané rozšírenie
.pp
Ukážem na príklade. Povedzme, že píšem modul nginx
. Obsahuje nasledujúce zdroje:
- trieda
nginx
popísané v manifesteinit.pp
; - trieda
nginx::service
popísané v manifesteservice.pp
; - definovať
nginx::server
popísané v manifesteserver.pp
; - definovať
nginx::server::location
popísané v manifesteserver/location.pp
.
šablóny
Určite sami viete, čo sú šablóny, nebudem ich tu podrobne popisovať. Ale pre každý prípad to nechám
Ako používať šablóny: Význam šablóny možno rozšíriť pomocou funkcie template
, ktorým je odovzdaná cesta k šablóne. Pre zdroje typu rezeň používa sa spolu s parametrom content
. Napríklad takto:
file { '/tmp/example': content => template('modulename/templatename.erb')
Zobraziť cestu <modulename>/<filename>
znamená súbor <rootdir>/modules/<modulename>/templates/<filename>
.
Okrem toho existuje funkcia inline_template
— prijme ako vstup text šablóny, nie názov súboru.
V rámci šablón môžete použiť všetky premenné bábky v aktuálnom rozsahu.
Puppet podporuje šablóny vo formáte ERB a EPP:
Stručne o ERB
Riadiace štruktúry:
<%= ВЫРАЖЕНИЕ %>
— vložte hodnotu výrazu<% ВЫРАЖЕНИЕ %>
— vypočítať hodnotu výrazu (bez jeho vloženia). Sem sa zvyčajne nachádzajú podmienené príkazy (if) a cykly (každý).<%# КОММЕНТАРИЙ %>
Výrazy v ERB sú napísané v Ruby (ERB je vlastne Embedded Ruby).
Ak chcete získať prístup k premenným z manifestu, musíte pridať @
na názov premennej. Ak chcete odstrániť zalomenie riadku, ktoré sa objaví po konštrukcii ovládacieho prvku, musíte použiť uzatváraciu značku -%>
.
Príklad použitia šablóny
Povedzme, že píšem modul na ovládanie ZooKeepera. Trieda zodpovedná za vytvorenie konfigurácie vyzerá asi 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 príslušná šablóna zoo.cfg.erb
-Takže:
<% 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 -%>
Fakty a vstavané premenné
Konkrétna časť konfigurácie často závisí od toho, čo sa práve deje na uzle. Napríklad v závislosti od vydania Debianu musíte nainštalovať jednu alebo druhú verziu balíka. Toto všetko môžete sledovať manuálne, prepisovanie manifestov, ak sa uzly zmenia. Ale to nie je seriózny prístup, automatizácia je oveľa lepšia.
Na získanie informácií o uzloch má Puppet mechanizmus nazývaný fakty. fakty - sú to informácie o uzle dostupné v manifestoch vo forme obyčajných premenných v globálnom mennom priestore. Napríklad názov hostiteľa, verzia operačného systému, architektúra procesora, zoznam používateľov, zoznam sieťových rozhraní a ich adries a oveľa, oveľa viac. Fakty sú dostupné v manifestoch a šablónach ako bežné premenné.
Príklad práce s faktami:
notify { "Running OS ${facts['os']['name']} version ${facts['os']['release']['full']}": }
# ресурс типа notify просто выводит сообщение в лог
Formálne povedané, fakt má názov (reťazec) a hodnotu (k dispozícii sú rôzne typy: reťazce, polia, slovníky). Jedzte
Počas prevádzky bábkový agent najskôr skopíruje všetky dostupné kolektory faktov z pappetserveru do uzla, potom ich spustí a odošle zozbierané fakty na server; Potom server začne kompilovať katalóg.
Fakty vo forme spustiteľných súborov
Takéto skutočnosti sú umiestnené v moduloch v adresári facts.d
. Samozrejme, súbory musia byť spustiteľné. Keď sú spustené, musia vydávať informácie na štandardný výstup buď vo formáte YAML alebo kľúč=hodnota.
Nezabudnite, že tieto fakty platia pre všetky uzly, ktoré sú riadené poppet serverom, na ktorý je váš modul nasadený. Preto v skripte skontrolujte, či má systém všetky programy a súbory potrebné na to, aby váš fakt fungoval.
#!/bin/sh
echo "testfact=success"
#!/bin/sh
echo '{"testyamlfact":"success"}'
Fakty o rubíne
Takéto skutočnosti sú umiestnené v moduloch 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
end
Textové fakty
Takéto skutočnosti sú umiestnené na uzloch v adresári /etc/facter/facts.d
v starej Bábke resp /etc/puppetlabs/facts.d
v novej Bábke.
examplefact=examplevalue
---
examplefact2: examplevalue2
anotherfact: anothervalue
Dostať sa k faktom
Existujú dva spôsoby, ako pristupovať k faktom:
- cez slovník
$facts
:$facts['fqdn']
; - použitie názvu faktu ako názvu premennej:
$fqdn
.
Najlepšie je použiť slovník $facts
alebo ešte lepšie, uveďte globálny menný priestor ($::facts
).
Vstavané premenné
Okrem faktov existuje aj
- dôveryhodné fakty — premenné, ktoré sú prevzaté z certifikátu klienta (keďže certifikát je zvyčajne vydaný na poppet serveri, agent nemôže len vziať a zmeniť jeho certifikát, takže premenné sú „dôveryhodné“): názov certifikátu, názov certifikátu hostiteľa a domény, prípony z certifikátu.
- fakty zo servera —premenné súvisiace s informáciami o serveri — verzia, názov, IP adresa servera, prostredie.
- fakty o agentovi — premenné pridané priamo bábkovým agentom a nie faktorom — názov certifikátu, verzia agenta, verzia bábky.
- hlavné premenné - Premenné Pappetmaster (sic!). Je to približne rovnaké ako v fakty zo servera, plus hodnoty konfiguračných parametrov sú k dispozícii.
- premenné kompilátora — premenné kompilátora, ktoré sa líšia v každom rozsahu: názov aktuálneho modulu a názov modulu, v ktorom sa pristupovalo k aktuálnemu objektu. Môžu byť použité napríklad na kontrolu, či sa vaše súkromné triedy nepoužívajú priamo z iných modulov.
Dodatok 1: ako to všetko spustiť a ladiť?
Článok obsahoval veľa príkladov bábkového kódu, ale vôbec nám nepovedal, ako tento kód spustiť. No opravujem sa.
Na spustenie Puppet stačí agent, ale vo väčšine prípadov budete potrebovať aj server.
agent
Minimálne od verzie XNUMX, balíčky bábkového agenta od
V najjednoduchšom prípade na použitie konfigurácie bábky stačí spustiť agenta v režime bez servera: za predpokladu, že kód bábky je skopírovaný do uzla, spustite 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
Je samozrejme lepšie nastaviť server a spustiť agentov na uzloch v režime démona - potom raz za pol hodinu použijú konfiguráciu stiahnutú zo servera.
Môžete napodobniť push model práce - prejdite na uzol, ktorý vás zaujíma, a začnite sudo puppet agent -t
... kľúč -t
(--test
) v skutočnosti obsahuje niekoľko možností, ktoré je možné povoliť jednotlivo. Tieto možnosti zahŕňajú nasledujúce možnosti:
- nespúšťajte v režime démona (v predvolenom nastavení sa agent spúšťa v režime démona);
- vypnúť po použití katalógu (v predvolenom nastavení bude agent pokračovať v práci a použije konfiguráciu raz za pol hodinu);
- napísať podrobný pracovný denník;
- zobraziť zmeny v súboroch.
Agent má prevádzkový režim bez zmien - môžete ho použiť, keď si nie ste istí, že ste napísali správnu konfiguráciu a chcete si skontrolovať, čo presne agent počas prevádzky zmení. Tento režim je povolený parametrom --noop
na príkazovom riadku: sudo puppet agent -t --noop
.
Okrem toho môžete povoliť denník ladenia práce - v ňom bábka píše 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úšťa. Toto je samozrejme parameter --debug
.
Server
V tomto článku sa nebudem zaoberať úplným nastavením pappetserveru a nasadením kódu naň, poviem len, že po vybalení je k dispozícii plne funkčná verzia servera, ktorá nevyžaduje dodatočnú konfiguráciu na prácu s malým počtom uzly (povedzme až sto). Väčší počet uzlov bude vyžadovať ladenie - štandardne bábkový server nespúšťa viac ako štyroch pracovníkov, pre väčší výkon musíte zvýšiť ich počet a nezabudnite zvýšiť limity pamäte, inak bude server väčšinu času zbierať odpadky.
Nasadenie kódu – ak to potrebujete rýchlo a jednoducho, pozrite sa (na r10k)[
Dodatok 2: Usmernenia pre kódovanie
- Umiestnite všetku logiku do tried a definícií.
- Triedy a definície uchovávajte v moduloch, nie v manifestoch popisujúcich uzly.
- Použite fakty.
- Nevytvárajte if na základe názvov hostiteľov.
- Neváhajte pridať parametre pre triedy a definície – je to lepšie ako implicitná logika skrytá v tele triedy/definície.
Prečo to odporúčam urobiť, vysvetlím v nasledujúcom článku.
Záver
Skončíme s úvodom. V ďalšom článku vám poviem o Hiere, ENC a PuppetDB.
Do prieskumu sa môžu zapojiť iba registrovaní užívatelia.
V skutočnosti je toho oveľa viac – môžem písať články na nasledujúce témy, hlasovať o tom, o čom by ste si chceli prečítať:
- 59,1%Pokročilé bábkové konštrukcie – nejaké sračky ďalšej úrovne: slučky, mapovanie a iné lambda výrazy, kolektory zdrojov, exportované zdroje a komunikácia medzi hostiteľmi cez bábku, značky, poskytovatelia, abstraktné dátové typy.13
- 31,8%„Som admin mojej matky“ alebo ako sme sa v Avite spriatelili s niekoľkými poppet servermi rôznych verzií a v princípe časť o správe poppet servera.7
- 81,8%Ako píšeme bábkový kód: prístrojové vybavenie, dokumentácia, testovanie, CI/CD.18
Hlasovalo 22 užívateľov. 9 užívateľov sa zdržalo hlasovania.
Zdroj: hab.com