Puppet je sistem za upravljanje konfiguracije. Uporablja se za konfiguriranje gostiteljev, da dosežejo in vzdržujejo želeno stanje.
S Puppetom delam že več kot pet let. To besedilo je v bistvu prevedena in reorganizirana zbirka ključnih točk iz uradne dokumentacije, zasnovana tako, da pomaga novincem hitro razumeti bistvo Puppet-a.

Osnovne informacije
Puppet deluje na osnovi odjemalca in strežnika, čeprav je podprta tudi različica brez strežnika z omejeno funkcionalnostjo.
Uporablja se model vlečenja (pull): privzeto odjemalci vsake pol ure zahtevajo konfiguracijo od strežnika in jo uporabijo. Če ste že delali z Ansibleom, ta uporablja drugačen model, model vlečenja (push): skrbnik sproži postopek konfiguracijske aplikacije; odjemalci sami ne uporabijo ničesar.
Omrežna komunikacija uporablja dvosmerno šifriranje TLS: strežnik in odjemalec imata vsak svoj zasebni ključ in ustrezna potrdila. Običajno strežnik izda potrdila odjemalcem, možna pa je tudi uporaba zunanjega overitelja potrdil.
Uvod v manifeste
V lutkovni terminologiji na lutkovni strežnik povezati vozlišča (vozlišča). Konfiguracija za vozlišča je zapisana v manifestih v posebnem programskem jeziku - Puppet DSL.
Puppet DSL je deklarativni jezik. Opisuje želeno stanje vozlišča z deklariranjem posameznih virov, na primer:
- Datoteka obstaja in ima določeno vsebino.
- Paket je nameščen.
- Storitev je bila zagnana.
Viri so lahko med seboj povezani:
- Obstajajo odvisnosti, ki vplivajo na vrstni red uporabe virov.
Na primer, »najprej namestite paket, nato uredite konfiguracijsko datoteko in nato zaženite storitev.« - Obstajajo obvestila – če se vir spremeni, pošlje obvestila virom, ki so nanj naročeni.
Na primer, če se konfiguracijska datoteka spremeni, se lahko storitev samodejno znova zažene.
Poleg tega Puppet DSL vključuje funkcije in spremenljivke ter pogojne operatorje in izbirnike. Podpira tudi različne mehanizme za predloge, kot sta EPP in ERB.
Puppet je napisan v Rubyju, zato je veliko konstruktov in terminologije prevzetih od tam. Ruby omogoča razširitev Puppet-a, kar omogoča dodajanje kompleksne logike, novih tipov virov in funkcij.
Ko se Puppet zažene, se manifesti za vsako določeno vozlišče na strežniku zberejo v imenik. Imenik — je seznam virov in njihovih odnosov po izračunu vrednosti funkcij, spremenljivk in razširitvenih pogojnih stavkov.
Sintaksa in slog kode
Tukaj je nekaj razdelkov uradne dokumentacije, ki vam lahko pomagajo razumeti sintakso, če navedeni primeri niso dovolj:
Tukaj je primer, kako izgleda manifest:
# Комментарии пишутся, как и много где, после решётки.
#
# Описание конфигурации ноды начинается с ключевого слова node,
# за которым следует селектор ноды — хостнейм (с доменом или без)
# или регулярное выражение для хостнеймов, или ключевое слово default.
#
# После этого в фигурных скобках описывается собственно конфигурация ноды.
#
# Одна и та же нода может попасть под несколько селекторов. Про приоритет
# селекторов написано в статье про синтаксис описания нод.
node 'hostname', 'f.q.d.n', /regexp/ {
# Конфигурация по сути является перечислением ресурсов и их параметров.
#
# У каждого ресурса есть тип и название.
#
# Внимание: не может быть двух ресурсов одного типа с одинаковыми названиями!
#
# Описание ресурса начинается с его типа. Тип пишется в нижнем регистре.
# Про разные типы ресурсов написано ниже.
#
# После типа в фигурных скобках пишется название ресурса, потом двоеточие,
# дальше идёт опциональное перечисление параметров ресурса и их значений.
# Значения параметров указываются через т.н. hash rocket (=>).
resource { 'title':
param1 => value1,
param2 => value2,
param3 => value3,
}
}Zamiki in prelomi vrstic niso obvezni del manifesta, vendar je priporočeno Povzetek:
- Dvojni presledki pri zamikih, tabulatorji se ne uporabljajo.
- Zaviti oklepaji so ločeni s presledkom, dvopičja pa ne.
- Vejice za vsakim parametrom, vključno z zadnjim. Vsak parameter je v ločeni vrstici. Izjema je v primeru, da parametrov ni in je en: lahko jih zapišete v eno vrstico brez vejice (tj.
resource { 'title': }иresource { 'title': param => value }). - Puščice na parametrih morajo biti na isti ravni.
- Puščice med viri so napisane pred njimi.
Lokacije datotek na strežniku PuppetServer
Za nadaljnjo razlago bom predstavil koncept "korenskega imenika". Korenski imenik je imenik, ki vsebuje konfiguracijo Puppet za določeno vozlišče.
Korenski imenik se razlikuje glede na različico Puppet in ali se uporabljajo okolja. Okolja so neodvisni nabori konfiguracij, shranjeni v ločenih imenikih. Običajno se uporabljajo skupaj z gitom, pri čemer se okolja ustvarijo iz vej gita. Posledično se vsako vozlišče nahaja v specifičnem okolju. To je konfigurirano na samem vozlišču ali v ENC, kar bom obravnaval v naslednjem članku.
- V tretji različici ("stari Puppet") je bil osnovni imenik
/etc/puppetUporaba okolij ni obvezna – na primer, s starim Puppetom jih ne uporabljamo. Če se okolja uporabljajo, so običajno shranjena v/etc/puppet/environments, bo korenski imenik imenik okolja. Če okolja niso uporabljena, bo korenski imenik osnovni imenik. - Od četrte različice (»nova lutka«) naprej je uporaba okolij postala obvezna, osnovni imenik pa je bil premaknjen v
/etc/puppetlabs/codeV skladu s tem so okolja shranjena v/etc/puppetlabs/code/environments, korenski imenik - imenik okolja.
V korenskem imeniku mora biti podimenik manifests, ki vsebuje enega ali več manifestov z opisi vozlišč. Poleg tega mora obstajati poddirektorij modules, ki vsebuje module. Kasneje bom razložil, kaj so moduli. Poleg tega ima lahko stari Puppet tudi poddirektorij. files, ki vsebuje različne datoteke, ki jih kopiramo v vozlišča. V novem Puppetu so vse datoteke premaknjene v module.
Datoteke manifesta imajo končnico .pp.
Nekaj primerov bojevanja
Opis vozlišča in vira na njem
Na vozlišču server1.testdomain datoteka mora biti ustvarjena /etc/issue z vsebino Debian GNU/Linux n lDatoteka mora biti v lasti uporabnika in skupine. root, pravice dostopa morajo biti 644.
Napišimo 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 в начале будет воспринято как записанное в восьмеричной системе, и всё пойдёт не так, как задумано
}
}Razmerja med viri na vozlišču
Na vozlišču server2.testdomain nginx mora biti zagnan in delovati s predhodno pripravljeno konfiguracijo.
Razčlenimo problem:
- Paket je treba namestiti
nginx. - Potrebno je kopirati konfiguracijske datoteke s strežnika.
- Storitev mora delovati.
nginx. - Če je konfiguracija posodobljena, je treba storitev znova zagnati.
Napišimo 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 получает уведомление,
# соответствующий сервис перезапускается.
}Da bi to delovalo, morate imeti datoteke na strežniku lutk urejene takole:
/etc/puppetlabs/code/environments/production/ # (это для нового Паппета, для старого корневой директорией будет /etc/puppet)
├── manifests/
│ └── site.pp
└── modules/
└── example/
└── files/
└── nginx-conf/
├── nginx.conf
├── mime.types
└── conf.d/
└── some.confVrste virov
Celoten seznam podprtih vrst virov najdete Tukaj bom opisal pet osnovnih tipov, ki v moji praksi zadostujejo za rešitev večine težav.
datoteka
Upravlja datoteke, imenike, simbolične povezave, njihovo vsebino in pravice dostopa.
Parametri:
- ime vira — pot do datoteke (neobvezno)
- pot — pot do datoteke (če ni navedena v imenu)
- zagotovitev — vrsta datoteke:
absent- izbrisati datotekopresent- mora obstajati datoteka katere koli vrste (če datoteke ni, bo ustvarjena navadna datoteka)file- navadna datotekadirectory— imeniklink— simbolična povezava
- vsebina — vsebina datoteke (primerno samo za navadne datoteke, ni mogoče uporabljati z vir ali ciljna)
- vir — sklic na pot, s katere naj se kopira vsebina datoteke (ni mogoče uporabiti skupaj z vsebina ali ciljna). Lahko se določi kot URI s shemo
puppet:(potem bodo uporabljene datoteke s strežnika lutk), kot tudi s shemohttp:(Upam, da je jasno, kaj se bo v tem primeru zgodilo), in celo z diagramomfile:ali kot absolutna pot brez sheme (potem bo uporabljena datoteka iz lokalnega FS na vozlišču) - ciljna — kam naj bi kazala simbolična povezava (ni mogoče uporabljati skupaj z vsebina ali vir)
- Lastnik — uporabnik, ki bi moral biti lastnik datoteke
- skupina — skupina, ki ji mora datoteka pripadati
- Način — pravice do datotek (kot niz)
- recidiv — omogoča rekurzivno obdelavo imenikov
- purge - vključuje odstranitev datotek, ki niso opisane v Puppetu
- moč - vključuje odstranitev imenikov, ki niso opisani v Puppetu
paket
Namesti in odstrani pakete. Lahko obravnava obvestila – ponovno namesti paket, če je parameter določen. ponovna_ponovna_osvežitev.
Parametri:
- ime vira — ime paketa (neobvezno)
- Ime — ime paketa (če ni navedeno v imenu)
- Ponudnik — upravitelj paketov, ki ga je treba uporabiti
- zagotovitev — želeno stanje paketa:
present,installed- katera koli nameščena različicalatest— nameščena je najnovejša različicaabsent— izbrisano (apt-get remove)purged— odstranjeno skupaj s konfiguracijskimi datotekami (apt-get purge)held— različica paketa je zaklenjena (apt-mark hold)любая другая строка— nameščena je navedena različica
- ponovna_ponovna_osvežitev - Če
true, bo paket po prejemu obvestila ponovno nameščen. To je uporabno za distribucije, ki temeljijo na izvorni kodi, kjer je pri spreminjanju parametrov gradnje morda potrebna ponovna izgradnja paketov. Privzetofalse.
Storitev
Upravlja storitve. Lahko obravnava obvestila – znova zažene storitev.
Parametri:
- ime vira — storitev, ki jo je treba upravljati (neobvezno)
- Ime — storitev, ki jo je treba upravljati (če ni navedena v imenu)
- zagotovitev — želeno stanje storitve:
running— lansiranstopped- ustavljen
- omogočajo — nadzoruje možnost zagona storitve:
true— samodejni zagon je omogočen (systemctl enable)mask- prikrito (systemctl mask)false— samodejni zagon je onemogočen (systemctl disable)
- restart — ukaz za ponovni zagon storitve
- Status — ukaz za preverjanje stanja storitve
- je ponovno zagnal — označite, ali initscript storitve podpira ponovni zagon. Če
falsein parameter je določen restart — uporabi se vrednost tega parametra. Čefalsein parameter restart ni določeno - storitev se ustavi in zažene za ponovni zagon (vendar se v systemd uporablja ukazsystemctl restart). - imastanje — navedite, ali initscript storitve podpira ukaz
status. Čefalse, nato se uporabi vrednost parametra StatusPrivzetotrue.
exec
Izvaja zunanje ukaze. Če niso navedeni nobeni parametri ustvari, samo če, če ali samo osveži, ukaz se bo zagnal vsakič, ko se zažene Puppet. Lahko obravnava obvestila in izvede ukaz.
Parametri:
- ime vira — ukaz za izvedbo (neobvezno)
- ukaz — ukaz, ki ga je treba izvesti (če ni naveden v imenu)
- pot — poti, v katerih naj se išče izvedljiva datoteka
- samo če - če se ukaz, naveden v tem parametru, zaključi z ničelno povratno kodo, se bo izvedel glavni ukaz
- če - če se ukaz, naveden v tem parametru, zaključi z neničelno povratno kodo, se bo izvedel glavni ukaz
- ustvari - če datoteka, navedena v tem parametru, ne obstaja, se bo izvedel glavni ukaz
- samo osveži - Če
true, potem se bo ukaz zagnal le, ko bo ta izvršilna naprava prejela obvestilo iz drugih virov - cwd — imenik, iz katerega se bo zagnal ukaz
- uporabnik — uporabnik, od katerega se izvede ukaz
- Ponudnik — kako zagnati ukaz:
- posix - podrejeni proces se preprosto ustvari, potrebno ga je določiti pot
- shell — ukaz se izvaja v lupini
/bin/sh, lahko ga izpustite pot, lahko uporabite globiranje, cevne vrstice in druge funkcije lupine. Običajno se samodejno zazna, če so prisotni kakšni posebni znaki (|,;,&&,||itd.).
cron
Upravlja kronske zaposlitve.
Parametri:
- ime vira - samo nekaj identifikatorja
- zagotovitev — stanje kronske zaposlitve:
present- ustvari, če ne obstajaabsent- izbriši, če obstaja
- ukaz — kateri ukaz zagnati
- okolje — v kakšnem okolju zagnati ukaz (seznam okoljskih spremenljivk in njihovih vrednosti prek
=) - uporabnik — pod katerim imenom naj se izvede ukaz
- min, uro, dan v tednu, mesec, dan v mesecu — kdaj zagnati cron. Če kateri od teh atributov ni določen, bo njegova vrednost v crontab-u
*.
V Lutki 6.0 cron kot da v puppetserverju, zato na splošni strani ni dokumentacije. Vendar pa v puppet-agentu, zato ga ni treba posebej nameščati. Njegovo dokumentacijo najdete tukaj. Ali .
O virih na splošno
Zahteve glede edinstvenosti virov
Najpogostejša napaka, s katero se srečujemo, je Podvojena deklaracijaDo te napake pride, ko sta v imenik dodana dva ali več virov iste vrste z istim imenom.
Torej bom še enkrat napisal/a: Manifesti za eno vozlišče ne smejo vsebovati virov iste vrste z istim imenom (naslovom)!
Včasih morate namestiti pakete z istim imenom, vendar z uporabo različnih upravljalnikov paketov. V tem primeru morate uporabiti parameter nameda se izognete napaki:
package { 'ruby-mysql':
ensure => installed,
name => 'mysql',
provider => 'gem',
}
package { 'python-mysql':
ensure => installed,
name => 'mysql',
provider => 'pip',
}Druge vrste virov imajo podobne parametre, da se prepreči podvajanje, name у Storitev, command у exec, in tako naprej.
Metaparametri
Vsaka vrsta vira ima nekaj posebnih parametrov, ne glede na njeno naravo.
Celoten seznam metaparametrov .
Ožji seznam:
- zahteva — ta parameter določa, od katerih virov je ta vir odvisen.
- pred — ta parameter določa, kateri viri so odvisni od tega vira.
- naročiti — ta parameter določa, iz katerih virov ta vir prejema obvestila.
- obvestiti — Ta parameter določa, kateri viri prejemajo obvestila od tega vira.
Vsi navedeni metaparametri sprejemajo bodisi posamezno povezavo do vira bodisi niz povezav v oglatih oklepajih.
Povezave do virov
Referenca vira je preprosto sklic na vir. Uporablja se predvsem za določanje odvisnosti. Sklicevanje na neobstoječ vir bo povzročilo napako pri prevajanju.
Sintaksa povezave je naslednja: tip vira se začne z veliko začetnico (če ime tipa vsebuje dvojna dvopičja, se vsak del imena med dvopičji napiše z veliko začetnico), sledi pa ime vira v oglatih oklepajih (velika in velika začetnica imena se ne spreminja!). Presledkov ne sme biti, oglati oklepaji pa so zapisani takoj za imenom tipa.
Primer:
file { '/file1': ensure => present }
file { '/file2':
ensure => directory,
before => File['/file1'],
}
file { '/file3': ensure => absent }
File['/file1'] -> File['/file3']Odvisnosti in obvestila
Kot smo že omenili, so preproste odvisnosti med viri tranzitivne. Mimogrede, bodite previdni pri določanju odvisnosti – ustvarite lahko ciklične odvisnosti, kar bo povzročilo napako pri prevajanju.
Za razliko od odvisnosti obvestila niso tranzitivna. Za obvestila veljajo naslednja pravila:
- Če vir prejme obvestilo, se posodobi. Dejanja posodobitve so odvisna od vrste vira: exec izvede ukaz, Storitev ponovno zažene storitev, paket Ponovno namesti paket. Če vir nima definirane posodobitvene akcije, se ne zgodi nič.
- Vir se posodobi največ enkrat na zagon Puppet-a. To je mogoče, ker obvestila vključujejo odvisnosti, graf odvisnosti pa je brez ciklov.
- Če Puppet spremeni stanje vira, vir pošlje obvestila vsem virom, ki so nanj naročeni.
- Če je vir posodobljen, pošlje obvestila vsem virom, ki so nanj naročeni.
Obravnavanje nedoločenih parametrov
Praviloma, če parameter vira nima privzete vrednosti in ta parameter ni določen v manifestu, Pappet ne bo spremenil te lastnosti za ustrezni vir na vozlišču. Na primer, če vir tipa datoteka parameter ni določen owner, potem Pappet ne bo spremenil lastnika ustrezne datoteke.
Uvod v razrede, spremenljivke in definicije
Recimo, da imamo več vozlišč, ki imajo enak del konfiguracije, vendar obstajajo tudi razlike - sicer bi lahko vse opisali v enem bloku. node {}Seveda lahko preprosto kopirate iste dele konfiguracije, vendar je to na splošno slaba rešitev – konfiguracija raste in ko spremenite del konfiguracije, boste morali isto stvar urejati na več mestih. Zlahka se zmotimo in načelo DRY (ne ponavljaj se) ni bilo izumljeno kar tako.
Za rešitev tega problema obstaja zasnova, kot je Razred.
Классы
— je poimenovani blok lutkovne kode. Za ponovno uporabo kode so potrebni razredi.
Najprej je treba razred opisati. Sam opis ne dodaja nobenih virov. Razred je opisan v manifestih:
# Описание класса начинается с ключевого слова class и его названия.
# Дальше идёт тело класса в фигурных скобках.
class example_class {
...
}Po tem se lahko razred uporabi:
# первый вариант использования — в стиле ресурса с типом class
class { 'example_class': }
# второй вариант использования — с помощью функции include
include example_class
# про отличие этих двух вариантов будет рассказано дальшеPrimer iz prejšnje naloge: namestitev in konfiguracijo nginxa premaknimo v razred:
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
}Spremenljivke
Razred iz prejšnjega primera ni zelo prilagodljiv, ker vedno prinaša isto konfiguracijo nginx. Naredimo pot konfiguracije spremenljivo, tako da lahko ta razred uporabimo za namestitev nginx s katero koli konfiguracijo.
To je mogoče storiti .
Opomba: spremenljivke v Puppetu so nespremenljive!
Poleg tega je do spremenljivke mogoče dostopati šele po njeni deklaraciji, sicer bo vrednost spremenljivke undef.
Primer dela s spremenljivkami:
# создание переменных
$variable = 'value'
$var2 = 1
$var3 = true
$var4 = undef
# использование переменных
$var5 = $var6
file { '/tmp/text': content => $variable }
# интерполяция переменных — раскрытие значения переменных в строках. Работает только в двойных кавычках!
$var6 = "Variable with name variable has value ${variable}"Lutka ima imenski prostori, in spremenljivke imajo ustrezno obsegSpremenljivka z istim imenom je lahko definirana v različnih imenskih prostorih. Pri razreševanju vrednosti spremenljivke se spremenljivka poišče v trenutnem imenskem prostoru, nato v imenskem prostoru, ki ga obdaja, in tako naprej.
Primeri imenskih prostorov:
- globalno - spremenljivke zunaj opisa razreda ali vozlišča gredo sem;
- imenski prostor vozlišča v opisu vozlišča;
- imenski prostor razreda v opisu razreda.
Da bi se izognili dvoumnosti pri dostopu do spremenljivke, lahko v imenu spremenljivke določite imenski prostor:
# переменная без пространства имён
$var
# переменная в глобальном пространстве имён
$::var
# переменная в пространстве имён класса
$classname::var
$::classname::varStrinjajmo se, da je pot do konfiguracije nginx v spremenljivki $nginx_conf_sourcePotem bo razred videti takole:
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
}Vendar je navedeni primer problematičen, ker implicira neko "skrivno znanje" o tem, za kaj se spremenljivka z določenim imenom uporablja nekje znotraj razreda. Veliko bolj primerno je, da to znanje postane javno – razredi imajo lahko parametre.
Parametri razreda To so spremenljivke v imenskem prostoru razreda; definirane so v glavi razreda in se lahko uporabljajo kot običajne spremenljivke v telesu razreda. Vrednosti parametrov so določene v manifestu pri uporabi razreda.
Parametru je mogoče dodeliti privzeto vrednost. Če parameter nima privzete vrednosti in vrednost ni določena ob uporabi, bo prišlo do napake pri prevajanju.
Давайте параметризуем класс из примера выше и добавим два параметра: первый, обязательный — путь к конфигурации, и второй, необязательный — название пакета с nginx (в Debian, например, есть пакеты 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 Puppetu so spremenljivke tipizirane. Obstajajo Tipi podatkov se običajno uporabljajo za preverjanje vrednosti parametrov, posredovanih razredom in definicijam. Če posredovani parameter ne ustreza določenemu tipu, pride do napake pri prevajanju.
Tip je zapisan tik pred imenom parametra:
class example (
String $param1,
Integer $param2,
Array $param3,
Hash $param4,
Hash[String, String] $param5,
) {
...
}Razredi: vključi ime razreda v primerjavi z razredom {'ime razreda':}
Vsak razred je vir tipa razredKot pri kateri koli drugi vrsti vira tudi na enem vozlišču ne more biti dveh primerkov istega razreda.
Če poskušate dvakrat dodati razred istemu vozlišču z uporabo class { 'classname':} (ne glede na to, ali imajo enake ali različne parametre) bo povzročil napako pri prevajanju. Če pa razred uporabljate v slogu vira, lahko vse njegove parametre izrecno določite kar v manifestu.
Če pa uporabljate include, potem lahko razred dodate tolikokrat, kot želite. Bistvo je, da include — idempotentna funkcija, ki preveri, ali je bil razred dodan v katalog. Če razreda ni v katalogu, ga doda, če pa že je, ne naredi ničesar. V primeru uporabe pa include Parametrov razreda ni mogoče določiti med deklaracijo razreda – vsi zahtevani parametri morajo biti določeni v zunanjem viru podatkov, kot je Hiera ali ENC. O tem bomo razpravljali v naslednjem članku.
Definira
Kot je bilo omenjeno v prejšnjem razdelku, isti razred ne more biti na vozlišču prisoten več kot enkrat. Vendar pa je v nekaterih primerih potrebno omogočiti uporabo istega bloka kode z različnimi parametri na enem vozlišču. Z drugimi besedami, potreben je tip vira po meri.
Na primer, za namestitev PHP modula v podjetju Avito naredimo naslednje:
- Paket namestimo s tem modulom.
- Ustvarimo konfiguracijsko datoteko za ta modul.
- Ustvarite simbolično povezavo do konfiguracije za php-fpm.
- Ustvarite simbolično povezavo do konfiguracije za PHP cli.
V takih primerih je zasnova, kot je (define, definirani tip, definirani tip vira). Define je podoben razredu, vendar obstajajo razlike: prvič, vsak define je tip vira in ne vir; drugič, vsak define ima implicitni parameter. $title, kamor se ime vira vpiše ob njegovi deklaraciji. Tako kot pri razredih je treba definicijo najprej deklarirati, preden jo je mogoče uporabiti.
Poenostavljen primer z modulom 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' }
}V Defineju je najlažja napaka, ki jo je mogoče odkriti, podvojena deklaracija. To se zgodi, če je v definiciji vir s konstantnim imenom in če na nekem vozlišču obstajata dva ali več primerkov te definicije.
Pred tem se je enostavno zaščititi: vsi viri znotraj definicije morajo imeti ime, ki je odvisno od $titleKot alternativa je možno idempotentno seštevanje virov. V najpreprostejšem primeru je dovolj, da vire, skupne vsem instancam definicije, premaknemo v ločen razred in ta razred vključimo v definicijo - funkcijo include idempotenten.
Obstajajo tudi drugi načini za doseganje idempotentnosti pri dodajanju virov, in sicer z uporabo funkcij defined и ensure_resources, ampak o tem vam bom povedal v naslednji epizodi.
Odvisnosti in obvestila za razrede in definicije
Razredi in definicije dodajo naslednja pravila za obravnavo odvisnosti in obvestil:
- Odvisnost razreda/definicije doda odvisnosti vsem virom razreda/definicije;
- Odvisnost razreda/definicije doda odvisnosti vsem virom razreda/definicije;
- Obvestilo razreda/define obvesti vse vire razreda/define;
- Z naročnino na razred/definicijo se naročite na vse vire razreda/definice.
Pogojni operatorji in selektorji
if
Tukaj je vse preprosto:
if ВЫРАЖЕНИЕ1 {
...
} elsif ВЫРАЖЕНИЕ2 {
...
} else {
...
}če
unless je nasprotje od if: blok kode se bo izvedel, če je izraz napačen.
unless ВЫРАЖЕНИЕ {
...
}primeru
Tudi tukaj ni nič zapletenega. Kot vrednosti lahko uporabite regularne vrednosti (nize, številke itd.), regularne izraze in podatkovne tipe.
case ВЫРАЖЕНИЕ {
ЗНАЧЕНИЕ1: { ... }
ЗНАЧЕНИЕ2, ЗНАЧЕНИЕ3: { ... }
default: { ... }
}Selektorji
Selektor je jezikovna konstrukcija, podobna case, le da namesto izvedbe bloka kode vrne vrednost.
$var = $othervar ? { 'val1' => 1, 'val2' => 2, default => 3 }Moduli
Ko je konfiguracija majhna, jo je mogoče enostavno shraniti v en sam manifest. Več konfiguracij pa opišemo, več razredov in vozlišč se pojavi v manifestu, zaradi česar je večji in težje z njim delati.
Poleg tega obstaja problem ponovne uporabe kode – ko je vsa koda v enem samem manifestu, jo je težko deliti z drugimi. Za reševanje teh dveh težav Puppet uvaja module.
Moduli — so nabori razredov, definicij in drugih entitet Puppet, ki se nahajajo v ločenem imeniku. Z drugimi besedami, modul je neodvisen del logike Puppet. Na primer, lahko obstaja modul za delo z NGINX, ki bi vseboval le tisto, kar je potrebno za delo z NGINX, ali pa lahko obstaja modul za delo s PHP in tako naprej.
Moduli so različicovani in podprte so medmodulne odvisnosti. Na voljo je odprt repozitorij modulov – .
Na strežniku lutk so moduli shranjeni v poddirektoriju modules korenskega imenika. Vsak modul ima standardno strukturo imenikov: manifesti, datoteke, predloge, knjižnice itd.
Struktura datotek v modulu
Koren modula lahko vsebuje naslednje imenike z imeni, ki so samoumevna:
manifests— v njem so manifestifiles— v njem so datoteketemplates- v njem so predlogelib— vsebuje kodo Ruby
To ni popoln seznam imenikov in datotek, vendar je za ta članek dovolj.
Imena virov in imena datotek v modulu
Virov (razredov, definicij) v modulu ni mogoče poimenovati. Poleg tega obstaja neposredna povezava med imenom vira in imenom datoteke, v kateri bo Puppet iskal opis vira. Če kršite pravila poimenovanja, Puppet preprosto ne bo našel opisa vira, kar bo povzročilo napako pri prevajanju.
Pravila so preprosta:
- Vsi viri v modulu morajo biti v imenskem prostoru modula. Če se modul pokliče
foo, potem je treba poklicati vse vire v njemfoo::<anything>ali preprostofoo. - Vir z imenom modula mora biti v datoteki
init.pp. - Za druge vire je shema poimenovanja datotek naslednja:
- predpona z imenom modula se izpusti
- Vsa morebitna dvojna dvopičja se nadomestijo s poševnicami.
- razširitev se dodaja
.pp
Naj ponazorim s primerom. Recimo, da pišem modul. nginxVsebuje naslednje vire:
- Razred
nginxopisano v manifestuinit.pp; - Razred
nginx::serviceopisano v manifestuservice.pp; - Definiraj
nginx::serveropisano v manifestuserver.pp; - Definiraj
nginx::server::locationopisano v manifestuserver/location.pp.
Predloge
Verjetno že veste, kaj so predloge, zato tukaj ne bom šel v podrobnosti. Ampak za vsak slučaj jih bom pustil tukaj. .
Kako uporabljati predloge: Vrednost predloge je mogoče razširiti s funkcijo template, ki se posreduje pot do predloge. Za vire tipa datoteka Uporabljamo ga skupaj s parametrom contentNa primer, takole:
file { '/tmp/example': content => template('modulename/templatename.erb')Pot vrste <modulename>/<filename> pomeni datoteko <rootdir>/modules/<modulename>/templates/<filename>.
Poleg tega obstaja funkcija inline_template — kot vhod se mu posreduje besedilo predloge, ne ime datoteke.
Vse spremenljivke Puppet v trenutnem obsegu se lahko uporabljajo znotraj predlog.
Puppet podpira predloge ERB in EPP:
Na kratko o ERB
Nadzorne strukture:
<%= ВЫРАЖЕНИЕ %>— vstavite vrednost izraza<% ВЫРАЖЕНИЕ %>— oceni vrednost izraza (brez vstavljanja). Pogojni operatorji (if) in zanke (each) se običajno nahajajo tukaj.<%# КОММЕНТАРИЙ %>
Izrazi v ERB so napisani v Rubyju (pravzaprav ERB pomeni Embedded Ruby).
Za dostop do spremenljivk iz manifesta morate dodati @ na ime spremenljivke. Če želite odstraniti prelom vrstice, ki se pojavi za kontrolno strukturo, morate uporabiti zaključno oznako. -%>.
Primer uporabe predloge
Recimo, da pišem modul za upravljanje ZooKeeperja. Razred, ki je odgovoren za ustvarjanje konfiguracije, je videti nekako takole:
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'),
}
}In ustrezna predloga zoo.cfg.erb - Torej:
<% 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 -%>Dejstva in vgrajene spremenljivke
Зачастую конкретная часть конфигурации зависит от того, что в данный момент происходит на ноде. Например, в зависимости от того, какой релиз Debian стоит, нужно установить ту или иную версию пакета. Можно следить за этим всем вручную, переписывая манифесты в случае изменения нод. Но это несерьёзный подход, автоматизация гораздо лучше.
Za pridobivanje informacij o vozliščih ima Puppet mehanizem, imenovan dejstva. dejstva — To so informacije o vozlišču, ki so na voljo v manifestih kot regularne spremenljivke v globalnem imenskem prostoru. Na primer ime gostitelja, različica operacijskega sistema, arhitektura procesorja, seznam uporabnikov, seznam omrežnih vmesnikov in njihovi naslovi ter še veliko, veliko več. Dejstva so na voljo v manifestih in predlogah kot regularne spremenljivke.
Primer dela z dejstvi:
notify { "Running OS ${facts['os']['name']} version ${facts['os']['release']['full']}": }
# ресурс типа notify просто выводит сообщение в логFormalno gledano ima dejstvo ime (niz) in vrednost (na voljo so različni tipi: nizi, polja, slovarji). Obstaja Lahko pa napišete tudi svoje. Zbiralci dejstev so opisani , ali takole Dejstva se lahko predstavijo tudi v obliki na vozliščih.
Med delovanjem lutkovni agent najprej kopira vse razpoložljive zbiralnike dejstev iz lutkovnega strežnika na vozlišče, nato jih zažene in pošlje zbrana dejstva strežniku; po tem strežnik začne sestavljati katalog.
Dejstva v obliki izvedljivih datotek
Takšna dejstva so shranjena v modulih v imeniku facts.dSeveda morajo biti datoteke izvedljive. Ko jih zaženete, morajo izpisati podatke na standardni izhod bodisi v obliki YAML bodisi v obliki ključ-vrednost.
Ne pozabite, da se dejstva distribuirajo vsem vozliščem, ki jih upravlja lutkovni strežnik, na katerega je nameščen vaš modul. Zato v svojem skriptu zagotovite, da ima sistem vse programe in datoteke, potrebne za delovanje vašega dejstva.
#!/bin/sh
echo "testfact=success"#!/bin/sh
echo '{"testyamlfact":"success"}'Dejstva o rubinu
Takšna dejstva so shranjena v modulih v imeniku 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
endDejstva o besedilu
Takšna dejstva so postavljena na vozlišča v imeniku /etc/facter/facts.d v starem Pappetu ali /etc/puppetlabs/facts.d v novem Pappetu.
examplefact=examplevalue---
examplefact2: examplevalue2
anotherfact: anothervalueSklicevanje na dejstva
Do dejstev lahko dostopamo na dva načina:
- skozi slovar
$facts:$facts['fqdn']; - z uporabo imena dejstva kot imena spremenljivke:
$fqdn.
Najbolje je uporabiti slovar $factsin še bolje, določite globalni imenski prostor ($::facts).
Vgrajene spremenljivke
Poleg dejstev obstaja tudi , ki je na voljo v globalnem imenskem prostoru.
- zaupnih dejstev — spremenljivke, ki so vzete iz odjemalskega potrdila (ker je potrdilo običajno izdano na lutkovnem strežniku, agent ne more preprosto spremeniti svojega potrdila, zato so spremenljivke »zaupanja vredne«): ime potrdila, ime gostitelja in domene, končnice iz potrdila.
- dejstva o strežniku —spremenljivke, povezane s podatki o strežniku — različica, ime, IP-naslov strežnika, okolje.
- dejstva o agentu — spremenljivke, ki jih doda neposredno lutkovni agent, ne pa faktor — ime potrdila, različica agenta, lutkovna različica.
- glavne spremenljivke - Spremenljivke Puppetmaster (sic!). Približno enako je kot v dejstva o strežniku, poleg tega pa so na voljo tudi vrednosti konfiguracijskih parametrov.
- spremenljivke prevajalnika — spremenljivke prevajalnika, ki se razlikujejo v posameznem obsegu: ime trenutnega modula in ime modula, v katerem je bil dostop do trenutnega objekta. Te se lahko uporabijo na primer za zagotovitev, da vaši zasebni razredi niso neposredno uporabljeni s strani drugih modulov.
Dodatek 1: Kako vse to zagnati in odpraviti napake?
Članek je vseboval veliko primerov kode Puppet, vendar ni bilo nobene razlage, kako to kodo zagnati. No, to bom popravil.
Za zagon Puppet zadostuje agent, v večini primerov pa boste potrebovali tudi strežnik.
Agent
Vsaj od različice 5 naprej, paketi puppet-agent iz содержат в себе все зависимости (ruby и соответствующие gem’ы), поэтому сложностей с установкой никаких нет (говорю про Debian-based дистрибутивы — RPM-based дистрибутивами мы не пользуемся).
V najpreprostejšem primeru je za uporabo konfiguracije lutke dovolj, da agenta zaženete v načinu brez strežnika: pod pogojem, da je koda lutke kopirana na vozlišče, zaženite 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 secondsSeveda je bolje nastaviti strežnik in na vozliščih zagnati agente v demonskem načinu – potem bodo vsake pol ure uporabili konfiguracijo, preneseno s strežnika.
Lahko simulirate model dela s potiskom - pojdite na vozlišče, ki vas zanima, in ga zaženite sudo puppet agent -tKljuč -t (--test) dejansko vključuje več možnosti, ki jih je mogoče omogočiti posamično. Te možnosti vključujejo naslednje:
- ne izvajajte v demonskem načinu (privzeto se agent izvaja v demonskem načinu);
- zaključi po uporabi kataloga (privzeto bo agent nadaljeval z delom in uporabil konfiguracijo vsake pol ure);
- napišite podroben delovni dnevnik;
- prikaži spremembe v datotekah.
Agent ima način »brez sprememb« – uporabite ga lahko, če niste prepričani, ali ste napisali pravilno konfiguracijo, in želite preveriti, kaj točno bo agent spremenil med delovanjem. Ta način omogoči parameter --noop v ukazni vrstici: sudo puppet agent -t --noop.
Omogočite lahko tudi dnevnik odpravljanja napak – Puppet vanj zapisuje vsa svoja dejanja: vir, ki ga trenutno obdeluje, parametre vira in programe, ki jih izvaja. To je seveda parameter. --debug.
Strežnik
V tem članku ne bom obravnaval celotne nastavitve PuppetServerja in nameščanja kode nanj. Povedal bom le, da je strežnik vnaprej nameščen s popolnoma delujočo različico, ki za majhno število vozlišč (recimo do 100) ne zahteva dodatne konfiguracije. Večje število vozlišč bo zahtevalo nekaj nastavitev – privzeto PuppetServer ne uporablja več kot štirih delavcev. Za boljšo zmogljivost morate povečati njihovo število in ne pozabite povečati omejitev pomnilnika, sicer bo strežnik večino časa porabil za zbiranje smeti.
Uvajanje kode - če jo potrebujete hitro in enostavno, potem poglejte (na r10k)[], za majhne namestitve bi to moralo biti povsem zadostno.
Dodatek 2: Priporočila za kodiranje
- Vso logiko premaknite v razrede in definicije.
- Razrede in definicije hranite v modulih, ne v manifestih vozlišč.
- Uporabi dejstva.
- Ne uporabljajte IF-jev na podlagi imen gostiteljev.
- Razredom in definicijam lahko prosto dodajate parametre – to je bolje kot imeti implicitno logiko skrito v telesu razreda/definicije.
In zakaj priporočam to početje, bom razložil v naslednjem članku.
Zaključek
S tem zaključujem uvod. V naslednjem članku bom obravnaval Hiero, ENC in PuppetDB.
V anketi lahko sodelujejo samo registrirani uporabniki. , prosim.
Pravzaprav je gradiva še veliko več – lahko pišem članke o naslednjih temah. Prosim, glasujte za to, kaj bi vas zanimalo:
- 59,1%Napredne konstrukcije Puppet so nekaj naslednje ravni: zanke, preslikave in drugi lambda izrazi, zbiralniki virov, izvoženi viri in komunikacija med gostitelji prek Puppet, oznake, ponudniki, abstraktni podatkovni tipi.13
- 31,8%»Sem mamin administrator,« ali kako smo se pri Avitu spoprijateljili z več različnimi različicami lutkovnih strežnikov in načeloma del o administraciji lutkovnih strežnikov.
- 81,8%Kako pišemo lutkovno kodo: orodja, dokumentacija, testiranje, CI/CD.18
Glasovalo je 22 uporabnikov. 9 uporabnikov se je vzdržalo.
Vir: www.habr.com
