Puppet ir konfigurācijas pārvaldības sistēma. Tā tiek izmantota, lai hostus nogādātu pareizajā stāvoklī un uzturētu šo stāvokli.
Es strādāju ar Puppet jau vairāk nekā piecus gadus. Šis teksts ir būtībā tulkota un pārstrukturēta svarīgāko punktu kompilācija no oficiālās dokumentācijas, kas ļaus iesācējiem ātri izprast Puppet būtību.

Pamatzināšanas
Puppet darbības shēma ir klients-serveris, lai gan ir iespējama arī serverim bez funkcionalitātes versija.
Tiek izmantota pull darba modeļa pieeja: pēc noklusējuma ik pēc pusstundas klienti savienojas ar serveri, lai iegūtu konfigurāciju un to piemērotu. Ja esat strādājis ar Ansible, tur tiek izmantota cita, push modeļa pieeja: administrators iniciē konfigurācijas piemērošanas procesu, paši klienti neko nepiemēros.
Tīkla mijiedarbībā tiek izmantota abpusēja TLS šifrēšana: serverim un klientam ir savi privātie atslēgas un attiecīgie sertifikāti. Parasti serveris izsniedz sertifikātus klientiem, bet principā ir iespējama arī ārēja CA izmantošana.
Iepazīšanās ar manifestiem
Puppet terminoloģijā pievienojas puppet-serverim savienojas mezglus (nodes). Konfigurācija nodēm tiek rakstīta manifestos īpašā programmēšanas valodā - Puppet DSL.
Puppet DSL ir deklaratīva valoda. Tajā tiek aprakstīts vēlamais stāvoklis node kā atsevišķu resursu deklarācijas, piemēram:
- Fails eksistē, un tam ir noteikts saturs.
- Pakotne ir instalēta.
- Pakalpojums ir palaists.
Resursi var būt savstarpēji saistīti:
- Ir atkarības, tās ietekmē resursu piemērošanas secību.
Piemēram, "vispirms instalējiet pakotni, tad labojiet konfigurācijas failu, pēc tam palaidiet pakalpojumu." - Ir paziņojumi - ja resurss mainās, tas sūta paziņojumus uz tiem resursiem, kas uz to ir parakstījušies.
Piemēram, ja mainās konfigurācijas fails, var automātiski pārstartēt pakalpojumu.
Turklāt Puppet DSL ir funkcijas un mainīgie, kā arī nosacījuma operatori un selektori. Tiek atbalstīti dažādi veidlapu veidošanas mehānismi - EPP un ERB.
Puppet ir uzrakstīts Ruby valodā, tāpēc daudzas konstrukcijas un termini ir ņemti no turienes. Ruby ļauj paplašināt Puppet - pievienot sarežģītu loģiku, jaunus resursu tipus, funkcijas.
Darba laikā Puppet manifesti katrai konkrētajai mezgla serverī tiek kompilēti katalogā. Katalogs — tas ir resursu un to savstarpējo attiecību saraksts pēc funkciju, mainīgo vērtību aprēķināšanas un nosacījumu operatoru atklāšanas.
Sintakse un kodēšanas stils
Šeit ir oficiālās dokumentācijas daļas, kas palīdzēs saprast sintaksi, ja sniegtie piemēri nav pietiekami:
Šeit ir piemērs, kā izskatās manifests:
# Комментарии пишутся, как и много где, после решётки.
#
# Описание конфигурации ноды начинается с ключевого слова node,
# за которым следует селектор ноды — хостнейм (с доменом или без)
# или регулярное выражение для хостнеймов, или ключевое слово default.
#
# После этого в фигурных скобках описывается собственно конфигурация ноды.
#
# Одна и та же нода может попасть под несколько селекторов. Про приоритет
# селекторов написано в статье про синтаксис описания нод.
node 'hostname', 'f.q.d.n', /regexp/ {
# Конфигурация по сути является перечислением ресурсов и их параметров.
#
# У каждого ресурса есть тип и название.
#
# Внимание: не может быть двух ресурсов одного типа с одинаковыми названиями!
#
# Описание ресурса начинается с его типа. Тип пишется в нижнем регистре.
# Про разные типы ресурсов написано ниже.
#
# После типа в фигурных скобках пишется название ресурса, потом двоеточие,
# дальше идёт опциональное перечисление параметров ресурса и их значений.
# Значения параметров указываются через т.н. hash rocket (=>).
resource { 'title':
param1 => value1,
param2 => value2,
param3 => value3,
}
}Iezīmes un jaunu rindu izmantošana nav obligāta manifestā, bet ir ieteikumi Kopsavilkums:
- Divu atstarpu iezīmes, tabulācijas netiek izmantotas.
- Apskarojošās iekavas atdalītas ar atstarpi, kolons netiek atdalīts ar atstarpi.
- Komati pēc katra parametra, ieskaitot pēdējo. Katrs parametrs ir atsevišķā rindā. Izņēmums ir gadījums bez parametriem un viena parametra gadījums: to var rakstīt vienā rindā un bez komata (t.i.
resource { 'title': }unresource { 'title': param => value }). - Bultas parametriem jābūt vienā līmenī.
- Resursu savstarpējās attiecības bultas rakstītas pirms tām.
Failu novietojums uz Puppet servera
Lai sniegtu papildu skaidrojumus, es iepazīstināšu ar jēdzienu "saknes direktorija". Saknes direktorija ir direktorija, kurā atrodas Puppet konfigurācija konkrētajam mezglam.
Saknes direktorija atšķiras atkarībā no Puppet versijas un vidēm. Vides ir neatkarīgi konfigurāciju komplekti, kas glabājas atsevišķās direktorijās. Parasti tās tiek izmantotas kopā ar git, tādējādi vides tiek veidotas no git zariem. Attiecīgi katrs mezgls atrodas kādā no vidēm. Tas tiek konfigurēts pašā mezglā vai ENC, par ko es sīkāk pastāstīšu nākamajā rakstā.
- Trešajā versijā ("vecais Puppet") bāzes direktorija bija
/etc/puppet. Vides izmantošana ir opcija — mēs, piemēram, tās neizmantojam ar veco Puppet. Ja vides tiek izmantotas, tās parasti tiek glabātas/etc/puppet/environments, saknes direktorija būs vides direktorija. Ja vides netiek izmantotas, saknes direktorija būs bāzes. - Sākot ar ceturto versiju ("jaunais Puppet"), vides izmantošana kļuva obligāta, un bāzes direktorija tika pārvietota uz
/etc/puppetlabs/code. Tāpēc vidēm tiek saglabāti/etc/puppetlabs/code/environments, saknes direktorija — vides direktorija.
Saknes direktorijā jābūt apakšdirektorijai manifests, kur glabājas viens vai vairāki manifesti ar mezglu aprakstu. Turklāt tur jābūt apakšdirektorijai modules, kur atrodas moduļi. Par to, kas ir moduļi, es pastāstīšu nedaudz vēlāk. Turklāt vecajā Pappetē var būt arī apakšdirektorija files, kur glabājas dažādi faili, kurus mēs kopējam uz mezgliem. Jaunajā Pappetē visi faili ir izcelti moduļos.
Manifestu faili ir ar paplašinājumu .pp.
Divi reālu piemēru
Mezglu un resursa apraksts uz tā
Uz mezgla server1.testdomain jābūt izveidotam failam /etc/issue ar saturu Debian GNU/Linux n l. Failam jābūt piederīgam lietotājam un grupai root, piekļuves tiesības jābūt 644.
Rakstām manifestu:
node 'server1.testdomain' { # konfigurācijas bloks, kas attiecas uz mezglu server1.testdomain
file { ' /etc/issue': # aprakstām failu /etc/issue
ensure => present, # šim failam jābūt
content => 'Debian GNU/Linux n l', # tā saturam jābūt šādam
owner => root, # lietotājs-īpašnieks
group => root, # grupas-īpašnieks
mode => '0644', # tiesības uz failu. Tās ir norādītas kā virkne (iekavās), jo citādi skaitlis ar 0 sākumā tiks uztverts kā uzrakstīts astoņkārtējā sistēmā, un viss ies ne tā, kā plānots
}
}Resursu saiknes mezglā
Uz mezgla server2.testdomain jādarbojas nginx, kas strādā ar iepriekš sagatavotu konfigurāciju.
Decomponējam uzdevumu:
- Nepieciešams, lai tiktu uzstādīts pakotne
nginx. - Nepieciešams, lai tiktu kopēti konfigurācijas faili no servera.
- Nepieciešams, lai tiktu palaists serviss
nginx. - Ja konfigurācija tiek atjaunināta, nepieciešams restartēt servisu.
Rakstām manifestu:
noda 'server2.testdomain' { # konfigurācijas bloks, kas attiecas uz nodu server2.testdomain
pakotne { 'nginx': # aprakstām pakotni nginx
nodrošināt => instalēts, # tam jābūt uzstādītam
}
# Tiešā bultiņa (-&) norāda, ka resursam zemāk jābūt
# izveidotam pēc augstāk aprakstītā resursa.
# Šādas atkarības ir tranzitīvas.
-> fails { '/etc/nginx': # aprakstām failu /etc/nginx
nodrošināt => direktorija, # tam jābūt direktorijai
avots => 'puppet:///modules/example/nginx-conf', # tās saturs ņemams no puppeta servera norādītajā adresē
rekursīvi => patiesi, # kopēt failus rekursīvi
iznīcēt => patiesi, # jāiznīcina liekie faili (tie, kuri nav avotā)
piespiest => patiesi, # iznīcināt liekās direktorijas
}
# Viļņota bultiņa (~&) norāda, ka resursam zemāk jāparakstās uz izmaiņām augstāk aprakstītajā resursā.
# Viļņota bultiņa ietver tiešo (-&).
~> pakalpojums { 'nginx': # aprakstām pakalpojumu nginx
nodrošināt => darbojas, # tam jābūt iedarbinātam
iespējot => patiesi, # to jāstartē automātiski sistēmas startēšanas laikā
}
# Kad resursam, kura tips ir pakalpojums, tiek paziņots,
# atbilstošais pakalpojums tiek restartēts.
}Lai tas darbotos, vajadzētu būt aptuveni tādai failu organizācijai puppeta serverī:
/etc/puppetlabs/code/environments/production/ # (это для нового Паппета, для старого корневой директорией будет /etc/puppet)
├── manifests/
│ └── site.pp
└── modules/
└── example/
└── files/
└── nginx-conf/
├── nginx.conf
├── mime.types
└── conf.d/
└── some.confResursu tipi
Pilns atbalstīto resursu tipu saraksts atrodas , šeit es aprakstīšu piecus pamata tipus, kuri manā praksē ir pietiekami, lai risinātu lielāko daļu uzdevumu.
file
Pārvalda failus, direktorijas, simboliskos linkus, to saturu, piekļuves tiesības.
Parametri:
- resursa nosaukums — ceļš uz failu (pēc izvēles)
- ceļš — ceļš uz failu (ja tas nav norādīts nosaukumā)
- nodrošināt — faila tips:
nav— dzēst failupresent— jābūt jebkāda veida failam (ja faila nav, tiks izveidots parasts fails)file— parasts failsdirektorija— direktorijasaite— simboliskais links
- content — faila saturs (der tikai parastiem failiem, nevar izmantot kopā ar source vai target)
- source — saite uz ceļu, no kura jākopē faila saturs (nav atļauts izmantot kopā ar content vai target). Var tikt norādīta kā URI ar shēmu
puppet:(tad tiks izmantoti faili no puppeta servera), kā arī ar shēmuhttp:(ceru, ka ir saprotams, kas šajā gadījumā notiks), un pat ar shēmufile:vai kā absolūta ceļa norādījumu bez shēmas (tad tiks izmantots fails no lokālās FS uz nodu) - target — uz kurieni vajadzētu norādīt simboliskais links (nav atļauts izmantot kopā ar content vai source)
- īpašnieks — lietotājs, kuram pieder fails
- grupa — grupa, kurai jāpieder fails
- režīms — tiesības uz failu (virkne)
- rekursīvi — iekļauj direktoriju rekursīvu apstrādi
- notīrīt — iekļauj failu dzēšanu, kas nav aprakstīti Puppet
- spēkā — iekļauj direktoriju dzēšanu, kas nav aprakstīti Puppet
package
Uzstāda un dzēš paketes. Spēj apstrādāt paziņojumus — atjauno paketi, ja norādīts parametrs atjaunot_uz_atjaunināšanu.
Parametri:
- resursa nosaukums — paketes nosaukums (pēc izvēles)
- name — paketes nosaukums (ja nav norādīts nosaukumā)
- pakalpojuma sniedzējs — pakotņu pārvaldnieks, ko izmantot
- nodrošināt — vēlamais paketes stāvoklis:
present,uzstādīts— uzstādīta jebkura versijalatest— uzstādīta jaunākā versijanav— dzēsts (apt-get remove)notīrīts— dzēsts kopā ar konfigurācijas failiem (apt-get purge)tura— paketes versija ir bloķēta (apt-mark hold)jebkura cita virkne— uzstādīta norādītā versija
- atjaunot_uz_atjaunināšanu — ja
true, tad, saņemot paziņojumu, pakete tiks atjaunota. Noderīga source-based distribūcijām, kur pakotņu pārbūve var būt nepieciešama, mainoties kompilācijas parametriem. pēc noklusējumafalse.
service
Pārvalda pakalpojumus. Spēj apstrādāt paziņojumus — restartē pakalpojumu.
Parametri:
- resursa nosaukums — pakalpojums, ko pārvaldīt (pēc izvēles)
- name — pakalpojums, ko pārvaldīt (ja nav norādīts nosaukumā)
- nodrošināt — vēlamais pakalpojuma stāvoklis:
skriešana— darbojasapturēts— apturēts
- iespējot — pārvalda pakalpojuma palaišanas iespēju:
true— automātiskā palaišana ir ieslēgta (systemctl enable)mask— maskēts (systemctl mask)false— automātiskā palaišana ir izslēgta (systemctl disable)
- restartēt — komanda pakalpojuma restartēšanai
- status — komanda pakalpojuma statusa pārbaudīšanai
- irrestartē — norādiet, vai pakalpojuma init skripts atbalsta restartēšanu. Ja
falseun norādīts parametrs restartēt — tiek izmantota šī parametra vērtība. Jafalseun parametrs restartēt nav norādīts — pakalpojums tiek apturēts un restartēts (bet systemd tiek lietota komandasystemctl restart). - irstatus — norādiet, vai pakalpojuma init skripts atbalsta komandu
statusTransakcijafalse, tad tiek izmantota parametra vērtība status. Pēc noklusējumatrue.
exec
Palaidis ārējās komandas. Ja netiek norādīti parametri izveidots, tikaija, izņēmuma gadījumā vai pārlabot tikai, komanda tiks palaista katrā Puppet izpildē. Spēj apstrādāt paziņojumus — palaiž komandu.
Parametri:
- resursa nosaukums — komanda, kuru jāveic (pēc izvēles)
- command — komanda, kuru jāveic (ja tā nav norādīta nosaukumā)
- ceļš — ceļi, kur meklēt izpildāmo failu
- tikaija — ja šajā parametrā norādītā komanda noslēdzās ar nulles atgriešanas kodu, pamata komanda tiks izpildīta
- izņēmuma gadījumā — ja šajā parametrā norādītā komanda noslēdzās ar nenulles atgriešanas kodu, pamata komanda tiks izpildīta
- izveidots — ja norādītais šajā parametrā fails neeksistē, pamata komanda tiks izpildīta
- pārlabot tikai — ja
true, tad komanda tiks uzsākta tikai tad, ja šis exec saņems paziņojumu no citiem resursiem - cwd — direktorija, no kuras uzsākt komandu
- user — lietotājs, no kura uzsākt komandu
- pakalpojuma sniedzējs — ar ko uzsākt komandu:
- posix — vienkārši tiek izveidots bērnu process, obligāti jānorāda ceļš
- shell — komanda tiek uzsākta shellā
/bin/sh, nav obligāti jānorāda ceļš, var izmantot globingu, caurules un citas shell funkcijas. Parasti tiek noteikts automātiski, ja ir dažādi speciālās simboli (|,;,&&,||ut tml).
cron
Pārvalda krondarbus.
Parametri:
- resursa nosaukums — vienkārši kāds identificētājs
- nodrošināt — krondarba stāvoklis:
present— izveidot, ja neeksistēnav— dzēst, ja eksistē
- command — kādu komandu uzsākt
- vidē — kādā vidē uzsākt komandu (mainīgo saraksts un to vērtības caur
=) - user — no kura lietotāja uzsākt komandu
- minūte, stunda, nedēļas diena, mēnesis, mēneša diena — kad uzsākt kronu. Ja kāds no šiem atribūtiem nav norādīts, tā vērtība krontabā būs
*.
Puppet 6.0 cron kā būtu puppetserver, tāpēc nav dokumentācijas kopējā vietnē. Bet tā puppet-agent, tāpēc to nav nepieciešams instalēt atsevišķi. Dokumentāciju var skatīt , vai .
Par resursiem kopumā
Prasības attiecībā uz resursu unikālumu
Visbiežākā kļūda, ar kuru sastopamies — Duplicate declaration. Šī kļūda rodas, kad katalogā nonāk divi vai vairāki resursi ar to pašu veidu un nosaukumu.
Tāpēc vēlreiz rakstīšu: manifestos vienai nodaļai nedrīkst būt resursi ar to pašu veidu un nosaukumu (title)!
Dažreiz nepieciešams instalēt pakotnes ar to pašu nosaukumu, bet ar atšķirīgiem pakotņu pārvaldniekiem. Šādā gadījumā jāizmanto parametrs name, lai izvairītos no kļūdas:
package { 'ruby-mysql':
ensure => installed,
name => 'mysql',
provider => 'gem',
}
package { 'python-mysql':
ensure => installed,
name => 'mysql',
provider => 'pip',
}Citos resursos ir līdzīgi parametri, kas palīdz novērst dublēšanos, — name u service, command u exec, utt.
Metaparametri
Daži īpaši parametri ir katra resursa veida neatkarīgi no tā būtības.
Pilns metaparametru saraksts .
Īss saraksts:
- require — šajā parametrā tiek norādīts, no kādiem resursiem atkarīgs šis resurss.
- before — šajā parametrā tiek norādīts, kuri resursi ir atkarīgi no šī resursa.
- subscribe — šajā parametrā tiek norādīts, no kādiem resursiem saņem paziņojumus šis resurss.
- notify — šajā parametrā tiek norādīts, kuri resursi saņem paziņojumus no šī resursa.
Visi minētie metaparametri pieņem vai nu vienu atsauci uz resursu, vai masīvu atsauču kvadrātveida iekavās.
Atsauces uz resursiem
Atsauce uz resursu ir vienkārši resursa minējums. Tās tiek izmantotas galvenokārt atkarību norādīšanai. Atsauce uz neesošu resursu izraisīs kompilācijas kļūdu.
Atsauces sintakse ir šāda: resursa veids ar lielo burtu (ja tipa nosaukumā ir divi koloni, tad katra daļa starp koloni tiek rakstīta ar lielo burtu), tālāk kvadrātveida iekavās resursa nosaukums (nosaukuma reģistrs netiek mainīts!). Tiek rakstīti bez atstarpēm, kvadrātveida iekavas tiek rakstītas tūlīt pēc tipa nosaukuma.
IsClosed = 0 AND Canceled = 0 AND Enabled = 0
file { '/file1': ensure => present }
file { '/file2':
ensure => directory,
before => File['/file1'],
}
file { '/file3': ensure => absent }
File['/file1'] -> File['/file3']Atkarības un paziņojumi
Kā jau iepriekš minēts, vienkāršas atkarības starp resursiem ir tranzitīvas. Starp citu, esiet piesardzīgi, nosakot atkarības — var izveidot cikliskas atkarības, kas izraisīs kompilācijas kļūdu.
Atšķirībā no atkarībām, paziņojumi nav tranzitīvi. Paziņojumiem ir šādi noteikumi:
- Ja resurss saņem paziņojumu, tas tiek atjaunināts. Darbības atjaunināšanas procesā ir atkarīgas no resursa tipa — exec izpilda komandu, service pārlaiž pakalpojumu, package pārvieto pakotni. Ja resursam nav noteikta darbība atjaunināšanas laikā, tad nekas nenotiek.
- Vienā Puppet izpildē resurss tiek atjaunināts ne vairāk kā vienu reizi. Tas ir iespējams, jo paziņojumi ietver atkarības, un atkarību grafiks nesatur ciklus.
- Ja, ja mainīgais stāvoklis resursam, tad resurss nosūta paziņojumus visiem, kas ir reģistrējušies uz to.
- Ja resurss tiek atjaunināts, tas nosūta paziņojumus visiem, kas ir reģistrējušies uz to.
Nenorādītu parametru apstrāde
Parasti, ja kādam resursa parametram nav noklusējuma vērtības un šis parametrs nav norādīts manifestā, Puppet nemainīs šo īpašību attiecīgajam resursam mezglā. Piemēram, ja resursam tips file nav norādīts parametrs īpašnieks, Puppet nemainīs attiecīgā faila īpašnieku.
Iepazīšanās ar klasēm, mainīgajiem un definīcijām
Pieņem, ka mums ir vairāki mezgli, uz kuriem ir vienāda daļa konfigurācijas, bet ir arī atšķirības - citādi mēs varētu visu aprakstīt vienā blokā node {}. Protams, var vienkārši kopēt vienādas konfigurācijas daļas, bet parasti tas nav labākais risinājums - konfigurācija palielinās, mainot kopējo konfigurācijas daļu, būs jāmaina vienā un tajā pašā vietā daudziem. Jā, ir viegli kļūdīties, un vispār DRY princips (neatkārtojiet sevi) nav bez iemesla izdomāts.
Lai risinātu šo problēmu, ir tāda konstrukcija kā klase.
Klasēs
ir nosaukts Puppet koda bloks. Klasēm ir nepieciešams, lai atkārtoti izmantotu kodu.
Vispirms klasei jābūt aprakstītai. Pašam aprakstam nav jāpaplašina resursi nekur. Klase ir aprakstīta manifestos:
# Описание класса начинается с ключевого слова class и его названия.
# Дальше идёт тело класса в фигурных скобках.
class example_class {
...
}Pēc tam klase var tikt izmantota:
# первый вариант использования — в стиле ресурса с типом class
class { 'example_class': }
# второй вариант использования — с помощью функции include
include example_class
# про отличие этих двух вариантов будет рассказано дальшеPiemērs no iepriekšējās uzdevuma - izdalām nginx uzstādīšanu un konfigurāciju klasē:
class nginx_example {
package { 'nginx':
ensure => installed,
}
-> file { '/etc/nginx':
ensure => directory,
source => 'puppet:///modules/example/nginx-conf',
recurse => true,
purge => true,
force => true,
}
~> service { 'nginx':
ensure => running,
enable => true,
}
}
node 'server2.testdomain' {
include nginx_example
}Mainīgie
Klase no iepriekšējā piemēra nav elastīga, jo tā vienmēr sniedz vienu un to pašu nginx konfigurāciju. Izveidosim, lai ceļš uz konfigurāciju kļūtu par mainīgu, tad šo klasē varēs izmantot, lai uzstādītu nginx ar jebkuru konfigurāciju.
To var izdarīt .
Uzmanību: mainīgie Puppet ir nemainīgi!
Turklāt piekļūt mainīgajam var tikai pēc tam, kad to ir deklarējuši, citādi mainīgā vērtība būs undef.
Piemērs darbam ar mainīgajiem:
# создание переменных
$variable = 'value'
$var2 = 1
$var3 = true
$var4 = undef
# использование переменных
$var5 = $var6
file { '/tmp/text': content => $variable }
# интерполяция переменных — раскрытие значения переменных в строках. Работает только в двойных кавычках!
$var6 = "Variable with name variable has value ${variable}"Puppet ir nosaukumu telpas, un attiecīgi, mainīgajiem ir redzamības joma: mainīgais ar to pašu nosaukumu var tikt definēts dažādās nosaukumu telpās. Izvēloties mainīgā vērtību, tas tiek meklēts pašreizējā nosaukumu telpā, pēc tam kapsētajā un tā tālāk.
Nosaukumu telpu piemēri:
- globālā — tajā nokļūst mainīgie ārpus klases vai mezgla definīcijas;
- mezgla nosaukumu telpa mezgla definīcijā;
- klases nosaukumu telpa klases definīcijā.
Lai izvairītos no neskaidrībām, piekļūstot mainīgajam, var norādīt nosaukumu telpu mainīgā nosaukumā:
# переменная без пространства имён
$var
# переменная в глобальном пространстве имён
$::var
# переменная в пространстве имён класса
$classname::var
$::classname::varSadarīsim, ka ceļš uz nginx konfigurāciju ir mainīgajā $nginx_conf_source. Tādējādi klase izskatīsies šādi:
class nginx_example {
package { 'nginx':
ensure => installed,
}
-> file { '/etc/nginx':
ensure => directory,
source => $nginx_conf_source, # šeit izmantojam mainīgo, nevis fiksētu virkni
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
}Tomēr iepriekšminētais piemērs ir slikts, jo ir kāda "noslēpumaina zināšana" par to, ka kaut kur iekšā klasē tiek izmantots mainīgais ar tādu pašu nosaukumu. Daudz pareizāk būtu padarīt šo zināšanu kopīgu — klasēm var būt parametri.
Klases parametri — tie ir mainīgie klases nosaukumu telpā, tie tiek definēti klases galvenē un var tikt izmantoti kā parasti mainīgie klases ķermenī. Parametru vērtības tiek norādītas, kad klase tiek izmantota manifestā.
Parametram var noteikt noklusējuma vērtību. Ja parametram nav noklusējuma vērtības un vērtība netiek noteikta, izmantojot to, tas izsauks kompilācijas kļūdu.
Apskatīsim parametru klasei no iepriekšminētā piemēra un pievienosim divus parametrus: pirmo, obligāto — ceļu uz konfigurāciju, un otro, neobligāto — paketes nosaukumu ar nginx (Debian, piemēram, ir paketes 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', # задаём параметры класса точно так же, как параметры для других ресурсов
}
}Puppet mainīgie ir tipizēti. Ir . Datu tipi parasti tiek izmantoti, lai validētu parametru vērtības, kas tiek nodotas klasēm un definīcijām. Ja nodotais parameters neatbilst norādītajam tipam, radīsies kompilācijas kļūda.
Tips tiek rakstīts tieši pirms parametra nosaukuma:
class example (
String $param1,
Integer $param2,
Array $param3,
Hash $param4,
Hash[String, String] $param5,
) {
...
}Klasēm: iekļaut klasifikatoru vs klase{‘klasifikators’:}
Katrs klase ir resursu veids klase. Tāpat kā ar jebkura cita veida resursiem, vienā mezglā nevar būt divi eksemplāri no vienas un tās pašas klases.
Ja mēģināsiet pievienot klasi vienam un tam pašam mezglam divreiz ar class { 'klasifikators':} (nav nozīmes, ar dažādiem vai identiskiem parametriem), notiks kompilācijas kļūda. Tomēr, ja izmantosit klasi resursa stilā, var tieši manifestā norādīt visus tās parametrus.
Tomēr, ja izmantosiet include, tad klasi var pievienot tik daudz reižu, cik nepieciešams. Problēma ir tāda, ka include ir idempotenta funkcija, kas pārbauda, vai klase ir pievienota katalogā. Ja klases katalogā nav, tā to pievieno, bet, ja tā jau ir, tad nekas nenotiek. Bet, ja tiek izmantota include nav iespējams norādīt klases parametrus klases deklarēšanas laikā - visi nepieciešamie parametri ir jānorāda ārējā datu avotā - Hiera vai ENC. Par to mēs runāsim nākamajā rakstā.
Definīcijas
Kā tika minēts iepriekšējā blokā, viena un tā pati klase nevar būt mezglā vairāk kā vienu reizi. Tomēr dažos gadījumos ir nepieciešams izmantot vienu un to pašu koda bloku ar dažādiem parametriem vienā mezglā. Citiem vārdiem sakot, ir nepieciešamība pēc sava resursu veida.
Piemēram, lai uzstādītu PHP moduli, mēs Avito darām sekojošo:
- Uzstādam pakotni ar šo moduli.
- Izveidojam konfigurācijas failu šim modulim.
- Izveidojam simbolisko saiti uz konfigurāciju php-fpm.
- Izveidojam simbolisko saiti uz konfigurāciju php cli.
Tādos gadījumos tiek izmantota tāda konstrukcija kā (define, defined type, defined resource type). Definīcija ir līdzīga klasei, taču ir atšķirības: pirmkārt, katra definīcija ir resursu veids, nevis resurss; otrkārt, katrai definīcijai ir implicitais parametrs $title, kur nonāk resursa nosaukums, kad to deklarē. Tāpat kā ar klasēm, definīciju vispirms jāapraksta, pēc tam to var izmantot.
Vienkāršots piemērs ar PHP moduli:
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 package triggers create symlinks and restart the php-fpm service themselves - we don't need that, as we control both symlinks and services via 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' }
}Definē ir visvienkāršāk uztvert kļūdu Duplicate declaration. Tas notiek, ja definīcijā ir resurss ar nemainīgu nosaukumu, un kādā mezglā ir divi vai vairāki šīs definīcijas eksemplāri.
Aizsargāties pret to ir vienkārši: visiem resursiem iekš definīcijas jābūt nosaukumam, kas ir atkarīgs no $title. Alternatīva – idempotentā resursu pievienošana, vienkāršā gadījumā pietiek pārvietot kopīgos visiem definīcijas eksemplāriem resursus atsevišķā klasē un iekļaut šo klasi definīcijā — funkcija include ir idempotenta.
Ir arī citas metodes, kā sasniegt idempotentību resursu pievienošanā, proti, izmantojot funkcijas defined un ensure_resources, bet par to es pastāstīšu nākamajā sērijā.
Atkarības un paziņojumi klasēm un definīcijām
Klasēm un definīcijām pievieno šādus noteikumus atkarību un paziņojumu apstrādei:
- atkarība no klases/definīcijas pievieno atkarības no visiem klases/definīcijas resursiem;
- klases/definīcijas atkarība pievieno atkarības visiem klases/definīcijas resursiem;
- klases/definīcijas paziņojums paziņo visiem klases/definīcijas resursiem;
- pieteikšanās klasei/definīcijai reģistrē visus klases/definīcijas resursus.
Nosacījumu operatori un selektori
if
Te viss ir vienkārši:
if IZSAITE1 {
...
} elsif IZSAITE2 {
...
} else {
...
}izņēmuma gadījumā
unless — tas ir if apgrieztā secībā: koda bloks tiks izpildīts, ja izteiksme ir nepatiesa.
unless IZSAITE {
...
}situācija
Te arī nav nekā sarežģīta. Kā vērtības var izmantot parastas vērtības (virknes, skaitļi un tā tālāk), regulāros izteikumus, kā arī datu tipus.
case IZTEIKUMS {
VĒRTĪBA1: { ... }
VĒRTĪBA2, VĒRTĪBA3: { ... }
noklusējums: { ... }
}Selektori
Selektors ir valodas konstrukcija, kas līdzīga situācija, tikai tā vietā, lai izpildītu kodu, tā atgriež vērtību.
$var = $othervar ? { 'val1' => 1, 'val2' => 2, noklusējums => 3 }Moduļi
Kad konfigurācija ir maza, to viegli var turēt vienā manifestā. Taču, jo lielākas konfigurācijas mēs aprakstām, jo vairāk klašu un mezglu paliek manifestā, tas kļūst apjomīgs, un ar to ir grūti strādāt.
Turklāt, pastāv problēma ar koda atkārtotu izmantošanu — kad viss kods ir vienā manifestā, ir sarežģīti to dalīt ar citiem. Lai risinātu šīs divas problēmas, Puppet ir tāda entitāte kā moduļi.
Moduļi — tie ir klašu, definīciju un citu Puppet entitāšu komplekti, kas izvietoti atsevišķā direktorijā. Citiem vārdiem sakot, mods ir neatkarīgs Puppet loģikas gabals. Piemēram, var būt modulis darbam ar nginx, un tajā būs tieši tas, kas nepieciešams darbam ar nginx, vai arī var būt modulis darbam ar PHP, un tā tālāk.
Moduļi tiek versiju vadīti, kā arī tiek atbalstītas moduļu atkarības cits no cita. Pastāv atvērts moduļu repozitārs — .
Puppet serverī moduļi atrodas apakšdirektorijā modules sakņu direktorijā. Katram modulim iekšā ir standarta direktoriju shēma — manifests, files, templates, lib un tā tālāk.
Failu struktūra modulī
Moduļa saknē var būt šādas direktorijas ar saprotamiem nosaukumiem:
manifests— tajā atrodas manifestifiles— tajā atrodas failitemplates— tajā atrodas šablonilib— tajā atrodas Ruby kods
Tas nav pilns direktoriju un failu saraksts, bet šim rakstam pagaidām pietiek.
Resursu nosaukumi un failu nosaukumi modulī
Resursi (klases, definīcijas) modulī nevar tikt nosaukti kā vien vēlas. Turklāt ir tieša atbilstība starp resursa nosaukumu un faila nosaukumu, kurā Puppet meklēs šī resursa aprakstu. Ja tiek pārkāpti nosaukšanas noteikumi, Puppet vienkārši neatradīs resursa aprakstu, un tas izraisīs kompilācijas kļūdu.
Noteikumi ir vienkārši:
- Visiem resursiem modulī jāatrodas moduļa nosaukuma nospiedumā. Ja modulis tiek saukts
foo, tad visiem resursiem tajā jābūt nosaukumamfoo::, vai arī vienkāršifoo. - Resursam ar moduļa nosaukumu jāatrodas failā
init.pp. - Pārējiem resursiem failu nosaukšanas shēma ir šāda:
- moduļa nosaukuma prefikss tiek atmests
- visas dubultās koloniņas, ja tās ir, tiek aizstātas ar slīpsvītrām
- pievieno paplašinājumu
.pp
Parādīšu piemēru. Pieņemsim, ka rakstu moduli nginx. Tajā ir šādi resursi:
- klase
nginxaprakstīts manifestāinit.pp; - klase
nginx::serviceaprakstīts manifestāservice.pp; - definīcija
nginx::serveraprakstīts manifestāserver.pp; - definīcija
nginx::server::locationaprakstīts manifestāserver/location.pp.
Veidnes
Noteikti jūs paši zināt, kas ir šabloni, nevēlos to šeit detalizēt. Bet uz katru gadījumu atstāšu .
Kā izmantot šablonus: šablona vērtību var atklāt, izmantojot funkciju template, kurai tiek nodota ceļš uz šablonu. Resursiem, kas ir tāda veida file lietojam kopā ar parametru content. Piemēram, tā:
file { '/tmp/example': content => template('modulename/templatename.erb')Ceļš formātā / nozīmē failu /modules//templates/.
Turklāt ir funkcija inline_template — tai tiek nodots šablona teksts, nevis faila nosaukums.
Šablonos var izmantot visas Puppet mainīgās pašreizējā redzamībā.
Puppet atbalsta šablonus ERB un EPP formātā:
Īsumā par ERB
Vadības konstrukcijas:
<%= ВЫРАЖЕНИЕ %>— ievietot izteiksmes vērtību<% ВЫРАЖЕНИЕ %>— aprēķināt izteiksmes vērtību (neievietojot to). Šeit parasti ietilpst nosacījumu operatori (if), cikli (each).<%# КОММЕНТАРИЙ %>
Izteiksmes ERB tiek rakstītas Ruby (īstenībā ERB — tas ir Embedded Ruby).
Lai piekļūtu mainīgajiem no manifesta, jāpievieno @ pie mainīgā nosaukuma. Lai noņemtu jauno rindu, kas parādās pēc vadības konstrukcijas, jāizmanto noslēdzošais tags -%>.
Šablona lietošanas piemērs
Pieņemsim, ka rakstu moduļa administrēšanai ZooKeeper. Klase, kas atbild par konfigurācijas izveidi, izskatās apmēram tā:
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'),
}
}Un atbilstošais tam šablons zoo.cfg.erb izskatās šādi:
0 -%>
server.=:::
dataDir=
=Fakti un iebūvētās mainīgās
Bieži vien konkrētā konfigurācijas daļa ir atkarīga no tā, kas šobrīd notiek uz noda. Piemēram, atkarībā no tā, kurš Debian izlaidums ir uzstādīts, jāinstalē attiecīgā pakotnes versija. To visu var sekot roku rokā, pārkopējot manifestus gadījumos, kad tiek mainīti nudi. Bet tas nav nopietns piegājiens; automatizācija ir daudz labāka.
Lai iegūtu informāciju par nudi Puppet ietvaros, ir tāds mehānisms kā fakti. Fakti — tas ir informācija par nudu, kas pieejama manifestos kā parastas mainīgās globālajā nosaukumu telpā. Piemēram, viesdatora nosaukums, operētājsistēmas versija, procesora arhitektūra, lietotāju saraksts, tīkla interfeisu saraksts un to adreses, un daudz, daudz citu. Fakti ir pieejami manifestos un veidnēs kā parastas mainīgās.
Faktu lietošanas piemērs:
notify { "Running OS ${facts['os']['name']} version ${facts['os']['release']['full']}": }
# notify tipa resurss vienkārši izvada ziņu žurnālāRunājot formāli, faktam ir vārds (virkne) un vērtība (pieejami dažādi veidi: virknes, masīvi, vārdnīcas). Ir . Tāpat var rakstīt arī savus. Faktu apkopošanas funkcijas tiek aprakstītas , vai kā . Tāpat fakti var tikt attēloti kā nudos.
Strādājot, Puppet aģents vispirms nokopē no Puppet servera uz nudu visus pieejamos faktu apkopes mehānismus, pēc tam tos palaiž un nosūta serverim apkopotos faktus; jau pēc tam serveris sāk katalogu kompilāciju.
Fakti kā izpildāmie faili
Šādi fakti tiek ievietoti moduļos direktorijā facts.d. Protams, failiem jābūt izpildāmiem. Palaižot, tie jāizvada standartizētajā izejā vai nu YAML formātā, vai «atslēga=vērtība» formātā.
Neaizmirstiet, ka fakti tiek izplatīti visām nudi, kas atrodas Puppet servera pārvaldībā, uz kuru tiek izvērsts jūsu modulis. Tādēļ skriptā parūpējieties, lai sistēmā būtu visas nepieciešamās jūsu fakta darbības programmas un faili.
#!/bin/sh
echo "testfact=success"#!/bin/sh
echo '{"testyamlfact":"success"}'Fakti Ruby valodā
Šādi fakti tiek ievietoti moduļos direktorijā 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
endTeksta fakti
Šādi fakti tiek saglabāti nudos direktorijā /etc/facter/facts.d vecajā Puppet vai /etc/puppetlabs/facts.d jaunajā Puppet.
examplefact=examplevalue---
examplefact2: examplevalue2
anotherfact: anothervaluePiekļuve faktiem
Faktus var iegūt divos veidos:
- caur vārdnīcu
$facts:$facts['fqdn']; - izmantojot fakta nosaukumu kā mainīgo nosaukumu:
$fqdn.
Vislabāk ir izmantot vārdnīcu $facts, un vēl labāk norādīt globālo nospiedumu ($::facts).
Iebūvētās mainīgās
Papildus faktu iegūšanai, ir arī , kas pieejami globālajā nospiedumā.
- trusted facts — mainīgie, kas tiek ņemti no klienta sertifikāta (jo sertifikāts parasti tiek izsniegts uz puppet-servera, aģents nevar vienkārši mainīt savu sertifikātu, tāpēc mainīgie ir "uzticami"): sertifikāta nosaukums, hosta un domēna nosaukums, paplašinājumi no sertifikāta.
- server facts — mainīgie, kas attiecas uz servera informāciju — versija, nosaukums, servera IP adrese, vide.
- agent facts — mainīgie, ko pievieno tieši puppet-agent, nevis facter — sertifikāta nosaukums, aģenta versija, puppet versija.
- master variables — puppetmaster mainīgie (sic!). Tur ir aptuveni tas pats, kas server facts, plus pieejamas konfigurācijas parametru vērtības.
- compiler variables — kompilatora mainīgie, kas katrā redzamībā atšķiras: pašreizējā moduļa nosaukums un moduļa nosaukums, kurā tika pieprasīts pašreizējais objekts. Tos var izmantot, piemēram, lai pārbaudītu, vai jūsu privātie klases netiek tieši izmantoti no citiem moduļiem.
Pielikums 1: kā to visu palaist un atkļūdot?
Rakstā bija daudz piemēru puppet-kodam, bet nebija stāstīts, kā šo kodu palaist. Nu, es labošu to.
Lai strādātu ar Puppet, pietiek ar aģentu, bet vairumā gadījumu būs nepieciešams arī serveris.
Aģents
Vismaz no piektās versijas puppet-agent pakotnes no ietver visas atkarības (ruby un attiecīgie gem'i), tāpēc nav nekādu grūtību ar instalāciju (es runāju par Debian balstītajām distribūcijām — RPM balstītas distribūcijas mēs nelietojam).
Vienkāršākajā gadījumā, lai piemērotu puppet konfigurāciju, pietiek ar to, lai palaistu aģentu bezservera režīmā: ar nosacījumu, ka puppet kods ir kopēts uz mezglu, palaidiet 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 secondsVislabāk ir uzsākt serveri un palaist aģentus mezglos dežūru režīmā — tad reizi pusstundā tie piemēros konfigurāciju, kas lejupielādēta no servera.
Var imitēt push-mode darbību — ieiet interesējošajā mezglā un palaist sudo puppet agent -t. Parametrs -t (--test) faktiski ietver vairākas opcijas, kuras var ieslēgt arī atsevišķi. Starp šīm opcijām ir šādas:
- nedarboties dežūru režīmā (pēc noklusējuma aģents tiek palaists dežūru režīmā);
- pabeigt darbu pēc katalogu piemērošanas (pēc noklusējuma aģents turpinās darbu un piemēros konfigurāciju reizi pusstundā);
- rakstīt detalizētu darba žurnālu;
- parādīt izmaiņas failos.
Aģentam ir nemainīgu izmaiņu režīms — to var izmantot gadījumos, kad neesat pārliecināts, ka esat uzrakstījis pareizu konfigurāciju, un vēlaties pārbaudīt, ko tieši aģents mainīs darba laikā. Šo režīmu ieslēdz ar parametru --noop komandu rindā: sudo puppet agent -t --noop.
Papildus var ieslēgt debug darba žurnālu — tajā puppet ieraksta visus izpildītos darbus: par resursu, kuru šobrīd apstrādā, šo resursu parametriem, kādas programmas tiek palaistas. Protams, šis ir parametrs --debug.
Serveris
Pilnīgu papeta servera iestatījumu un koda izvietošanu šajā rakstā es neapskatīšu, tikai teikšu, ka no kastes tiek uzstādīta pilnībā funkcionējoša servera versija, nepievienojot papildu iestatījumus darbam nelielā mezglu skaitā (piemēram, līdz simts). Lielāks mezglu skaits jau prasīs tuningu — pēc noklusējuma puppetserver izpilda ne vairāk kā četrus darbiniekus, lai palielinātu veiktspēju, nepieciešams palielināt darbinieku skaitu un neaizmirst palielināt atmiņas limitus, pretējā gadījumā lielāko laiku serveris veiks garbage collection.
Koda izvietošana — ja ir nepieciešams ātri un vienkārši, tad skatieties (r10k)[], nelielām instalācijām tam diezgan vajadzētu pietikt.
Pielikums 2: ieteikumi koda rakstīšanai
- Izvelciet visu loģiku klasēs un definīcijās.
- Turiet klases un definīcijas moduļos, nevis mezglu aprakstu manifestos.
- Izmantojiet faktus.
- Nedari if’us pēc hosta vārdiem.
- Nepieciešams pievienot parametrus klasēm un definīcijām – tas ir labāk nekā neizteiktā loģika, kas slēpjas klases/definīcijas ķermenī.
Un kāpēc es ieteicu to darīt – paskaidrošu nākamajā rakstā.
Secinājums
Šeit noslēdzam ievadu. Nākamajā rakstā pastāstīšu par Hiera, ENC un PuppetDB.
Tikai reģistrētie lietotāji var piedalīties aptaujā. , lūdzu.
Patiesībā materiāla ir daudz vairāk – es varu uzrakstīt rakstus par nākamajām tēmām, balsojiet, par ko jūs vēlētos lasīt:
- 59,1%Advanced puppet constructs – kā daži nākamā līmeņa sūdi: cikli, mappēšana un citas lambda izteiksmes, resursu kolektori, eksportējamie resursi un starp-hosts mijiedarbība caur Puppet, tagi, nodrošinātāji, abstrakti datu tipa.
- 31,8%«Es esmu mājas admin» vai kā mēs Avito saderinājām vairākus Puppet serverus dažādās versijās, nu un principā daļa par Puppet servera administrēšanu.
- 81,8%Kā mēs rakstām Puppet kodu: instrumentu iesaiņojums, dokumentācija, testēšana, CI/CD.
Balsojuši 22 lietotāji. Atturējās 9 lietotāji.
Avots: habr.com
