A Puppet egy konfigurációkezelő rendszer. Használják a hosztok kívánt állapotának elérésére és fenntartására.
Több mint öt éve dolgozom a Puppet-tel. Ez a szöveg lényegében a hivatalos dokumentáció kulcspontjainak fordított és átrendezett gyűjteménye, amely lehetővé teszi a kezdők számára, hogy gyorsan megértsék a Puppet lényegét.

Alapinformációk
A Puppet működési sémája kliens-szerver alapú, bár létezik korlátozott funkcionalitással bíró nélküli szerver opció is.
Pull modellt használ: alapértelmezés szerint a kliensek félóránként hívják a szervert a konfigurációért és alkalmazzák azt. Ha dolgozott már az Ansible-lal, ott push modellt használnak: az adminisztrátor kezdeményezi a konfiguráció alkalmazásának folyamatát, a kliensek maguktól nem fognak semmit alkalmazni.
Hálózati interakció során kölcsönös TLS titkosítást alkalmaznak: a szervernek és a kliensnek van saját titkos kulcsa és a megfelelő tanúsítványai. Általában a szerver adja ki a tanúsítványokat a klienseknek, de elvileg lehetséges külső CA használata is.
Megismerkedés a mítoszokkal
A Puppet terminológiájában a papet szerverhez csatlakoznak nódok (nodes). A nódok konfigurációját mint a mítoszokban egy speciális programozási nyelven, a Puppet DSL-en írják.
A Puppet DSL deklaratív nyelv. Ezen írják le a nódok kívánt állapotát, például különböző erőforrások bejelentésével:
- A fájl létezik, és van meghatározott tartalma.
- A csomag telepítve van.
- A szolgáltatás fut.
Az erőforrások összefonódhatnak:
- Vannak függőségek, amelyek befolyásolják az erőforrások alkalmazásának sorrendjét.
Például: "először telepítsd a csomagot, aztán módosítsd a konfigurációs fájlt, majd indítsd el a szolgáltatást." - Vannak értesítések – ha egy erőforrás megváltozik, értesítéseket küld a légikísérőknek.
Például, ha a konfigurációs fájl változik, automatikusan újra lehet indítani a szolgáltatást.
Ezen kívül a Puppet DSL-nek vannak funkciói és változói, valamint feltételes operátorai és kiválasztói. Támogatják a különböző sablonmechanizmusokat is – EPP és ERB.
A Puppet Ruby nyelven íródott, ezért sok szerkezet és kifejezés innen származik. A Ruby lehetővé teszi a Puppet kiterjesztését – bonyolult logika és új erőforrástípusok, funkciók hozzáadását.
A Puppet működése során a szerveren minden konkrét nóda mánifesztjei a könyvtárba kerülnek. Mappa — ez az erőforrások és azok kapcsolatai listája, a függvények, változók értékeinek kiszámítása és a feltételes operátorok kibővítése után.
Szintaxis és kódstílus
Itt vannak az hivatalos dokumentáció szakaszai, amelyek segítenek megérteni a szintaxist, ha a megadott példák nem elegendőek:
Itt van egy példa arra, hogyan néz ki a mánifeszt:
# Комментарии пишутся, как и много где, после решётки.
#
# Описание конфигурации ноды начинается с ключевого слова node,
# за которым следует селектор ноды — хостнейм (с доменом или без)
# или регулярное выражение для хостнеймов, или ключевое слово default.
#
# После этого в фигурных скобках описывается собственно конфигурация ноды.
#
# Одна и та же нода может попасть под несколько селекторов. Про приоритет
# селекторов написано в статье про синтаксис описания нод.
node 'hostname', 'f.q.d.n', /regexp/ {
# Конфигурация по сути является перечислением ресурсов и их параметров.
#
# У каждого ресурса есть тип и название.
#
# Внимание: не может быть двух ресурсов одного типа с одинаковыми названиями!
#
# Описание ресурса начинается с его типа. Тип пишется в нижнем регистре.
# Про разные типы ресурсов написано ниже.
#
# После типа в фигурных скобках пишется название ресурса, потом двоеточие,
# дальше идёт опциональное перечисление параметров ресурса и их значений.
# Значения параметров указываются через т.н. hash rocket (=>).
resource { 'title':
param1 => value1,
param2 => value2,
param3 => value3,
}
}A behúzások és sortörések nem kötelező részét képezik a mánifesztnek, azonban van ajánlott Rövid összefoglaló:
- Kettős szóköz behúzások, tabulátorok nem használják.
- A kapcsos zárójeleknek szóköznek kell lenniük, a kettőspontnak nem.
- Vesszők minden paraméter után, beleértve az utolsót is. Minden paraméter külön sorban. Kivétel az esetben, ha nincs paraméter vagy egyetlen paraméter van: egy sorban és vessző nélkül is írhatsz (pl.
resource { 'title': }ésresource { 'title': param => value }). - A paraméterek nyilait egy szinten kell elhelyezni.
- Az erőforrások közötti nyilakat előttük kell írni.
Fájlok elhelyezkedése a papetszerveren
A további magyarázatokhoz bevezetek egy fogalmat: "gyökérkönyvtár". A gyökérkönyvtár az a könyvtár, ahol a Puppete konfiguráció található egy adott nodához.
A gyökérkönyvtár verziótól és környezetek használatától függően változik. A környezetek független konfigurációs készletek, amelyeket külön könyvtárakban tárolnak. Általában együtt használják a git-tel, ilyenkor a környezetek git-ágakból készülnek. Ennek megfelelően minden nóda valamilyen környezetben van. Ez a nóda magán, vagy az ENC-ben van beállítva, amiről a következő cikkben mesélek.
- A harmadik verzióban ("régi Puppeteer") az alapértelmezett könyvtár a
/etc/puppet. A környezetek használata opcionális — például mi nem használjuk a régi Puppete-vel. Ha a környezeteket használják, akkor általában a/etc/puppet/environmentskönyvtárban lesz a gyökérkönyvtár. Ha a környezeteket nem használják, a gyökérkönyvtár az alapértelmezett. - A negyedik verziótól kezdve ("új Puppeteer") a környezetek használata kötelezővé vált, és az alapértelmezett könyvtárat áthelyezték.
/etc/puppetlabs/code. A környezetek így tárolódnak/etc/puppetlabs/code/environments, a gyökérkönyvtár a környezet könyvtára.
A gyökérkönyvtárban lennie kell egy alkönyvtárnak manifests, amelyben egy vagy több manifest található a csomópontok leírásával. Ezen kívül kell lennie egy alkönyvtárnak modules, amelyben a modulok találhatók. A modulok mit is jelentenek, arról később fogok mesélni. Ezen kívül a régi Puppetben is lehet egy alkönyvtár files, amelyben különböző fájlok találhatók, amelyeket a csomópontokra másolunk. Az új Puppetben viszont minden fájl a modulokba került.
A manifest fájlok kiterjesztése .pp.
Egy pár gyakorlati példa
A csomópont és az azon lévő erőforrás leírása
A csomóponton server1.testdomain létre kell hozni egy fájlt /etc/issue a következő tartalommal Debian GNU/Linux n l. A fájlnak a felhasználó és csoport birtokában kell lennie , felhasználó nevében fut, mivel alapvető rendszerbeállításokat módosítanak, de a WordPress közvetlenül egy normál felhasználóként működik., a hozzáférési jogoknak pedig 644.
Manifest írása:
node 'server1.testdomain' { # a server1.testdomain csomópontra vonatkozó konfigurációs blokk
file { ' /etc/issue': # a /etc/issue fájl leírása
ensure => present, # ennek a fájlnak léteznie kell
content => 'Debian GNU/Linux n l', # ennek a tartalomnak kell lennie
owner => root, # a tulajdonos felhasználó
group => root, # a tulajdonos csoport
mode => '0644', # a fájlra vonatkozó jogok. Ezeket karakterláncként kell megadni (idézőjelek között), különben a 0-át tartalmazó számot nyolcas számrendszerbeli számként értelmezi a rendszer, és nem úgy alakulnak a dolgok, ahogy terveztük
}
}Az erőforrások összefonódása a csomóponton
A csomóponton server2.testdomain el kell indulnia az nginx-nek, amely az előre elkészített konfigurációval dolgozik.
A feladat dekomponálása:
- Szükség van arra, hogy a csomagot telepítve legyen
nginx. - Szükség van arra, hogy a konfigurációs fájlok átmásolva legyenek a szerverről.
- Szükség van arra, hogy a szolgáltatás elinduljon
nginx. - A konfiguráció frissítése esetén a szolgáltatást újra kell indítani.
Manifest írása:
node 'server2.testdomain' { # a konfigurációs blokk, amely a server2.testdomain-hez tartozik
package { 'nginx': # az nginx csomagot írjuk le
ensure => installed, # telepítve kell lennie
}
# A közvetlen nyíl (->) azt jelzi, hogy az alább lévő erőforrást
# az itt leírt erőforrás után kell létrehozni.
# Az ilyen függőségek áthaladók.
-> file { '\/etc\/nginx': # az \/etc\/nginx fájlt írjuk le
ensure => directory, # ennek könyvtárnak kell lennie
source => 'puppet:\/\/\/modules\/example\/nginx-conf', # a tartalmát a megadott címről kell beszerezni a puppetszerverről
recurse => true, # fájlok másolása rekurzívan
purge => true, # felesleges fájlokat (amik nincsenek a forrásban) el kell távolítani
force => true, # felesleges könyvtárakat el kell távolítani
}
# A hullámos nyíl (~>) azt jelzi, hogy az alább lévő erőforrásnak
# fel kell iratkoznia a fent leírt erőforrás változásaira.
# A hullámos nyíl magában foglalja a közvetlen nyilat (->).
~> service { 'nginx': # az nginx szolgáltatást írjuk le
ensure => running, # futnia kell
enable => true, # automatikusan el kell indítani a rendszer indításakor
}
# Amikor egy service típusú erőforrást értesítenek,
# a megfelelő szolgáltatás újraindul.
}Ahhoz, hogy ez működjön, körülbelül így kell elhelyezni a fájlokat a puppetszerveren:
/etc/puppetlabs/code/environments/production/ # (это для нового Паппета, для старого корневой директорией будет /etc/puppet)
├── manifests/
│ └── site.pp
└── modules/
└── example/
└── files/
└── nginx-conf/
├── nginx.conf
├── mime.types
└── conf.d/
└── some.confErőforrás típusok
A támogatott erőforrások teljes listája itt található , itt öt alapvető típust írok le, amelyek a gyakorlatomban elegendőek a legtöbb feladat megoldásához.
file
Fájlok, könyvtárak, szimbolikus linked, azok tartalma, és jogosultságok kezelése.
Paraméterek:
- az erőforrás neve — a fájl elérési útja (opcionális)
- path — a fájl elérési útja (ha nem lett megadva a névben)
- ensure — a fájl típusa:
nem létezik— fájl törlésepresent— bármilyen típusú fájl kell, hogy legyen (ha a fájl nem létezik, egy sima fájl fog létrejönni)file— sima fájldirectory— könyvtárlink— szimbolikus link
- content — a fájl tartalma (csak sima fájlokhoz, nem használható együtt a forrás vagy target)
- forrás — hivatkozás arra az útra, ahonnan a fájl tartalmát másolni kell (nem használható együtt a content vagy target). Megadható URI formájában a bármelyik sémával
puppet:(akkor a puppetszerver fájljait fogja használni), így is megadhatóhttp:(remélem, ezt így világosan érthető), és még az alábbi sémávalfile:vagy abszolút útként séma nélkül (akkor a fájl a noden lévő helyi fájlrendszerről lesz kiolvasva) - target — hová kell mutatnia a szimlinknek (nem használható együtt a content vagy forrás)
- tulajdonos — felhasználó, akinek a fájl tulajdonlása tartozik
- csoport — a fájl, amelyhez a csoport tartozik
- mód — jogosultságok a fájlra (sztring formájában)
- rekurzív — rekurszív feldolgozást aktivál
- töröl — a Puppetben nem leírt fájlok eltávolítását aktiválja
- kényszerít — a Puppetben nem leírt könyvtárak eltávolítását aktiválja
csomag
Csomagok telepítése és eltávolítása. Képes kezelni az értesítéseket - újratelepíti a csomagot, ha meg van adva a paraméter újratelepítés_frissítéskor.
Paraméterek:
- az erőforrás neve — a csomag neve (opcionális)
- name — a csomag neve (ha nincs megadva a névben)
- szolgáltató — a használni kívánt csomagkezelő
- ensure — a csomag kívánt állapota:
present,telepítve— bármely verzió telepítve vanlatest— a legújabb verzió telepítve vannem létezik— eltávolítva (apt-get remove)törölve— eltávolítva a konfigurációs fájlokkal együtt (apt-get purge)tartva— a csomag verziója le van zárva (apt-mark hold)bármilyen más sztring— a megadott verzió telepítve van
- újratelepítés_frissítéskor — ha
igaz, akkor az értesítéskor a csomag újra telepítve lesz. Hasznos forrás-alapú disztribúciók esetén, ahol a csomagok újbóli összeállítása szükséges lehet a build paraméterek megváltozása miatt. Alapértelmezés szerinthamis.
a SystemD inicializálási rendszer számára:
Szolgáltatások kezelése. Képes kezelni az értesítéseket - újraindítja a szolgáltatást.
Paraméterek:
- az erőforrás neve — a szolgáltatás, amelyet kezelni kell (opcionális)
- name — a szolgáltatás, amelyet kezelni kell (ha nincs megadva a névben)
- ensure — a szolgáltatás kívánt állapota:
fut— futleállítva— leállítva
- engedélyez — a szolgáltatás indításának kezelése:
igaz— engedélyezett az automatikus indítás (systemctl enable)álarc— el van rejtve (systemctl mask)hamis— le van állítva az automatikus indítás (systemctl disable)
- újraindít — parancs a szolgáltatás újraindítására
- status — parancs a szolgáltatás állapotának ellenőrzésére
- van_újraindítás — megadja, hogy a szolgáltatás init scriptje támogatja-e az újraindítást. Ha
hamisés a paraméter meg van adva újraindít — ennek a paraméternek az értéke használatos. Hahamisés a paraméter újraindít nincs megadva - a szolgáltatás leáll és újraindul az újraindításhoz (de a systemd-ben a parancssystemctl restart). - van_állapot — megadni, hogy a szolgáltatás init scriptje támogatja-e a parancsot
status. Hahamis, akkor a paraméter értéke használatos status. Alapértelmezés szerintigaz.
exec
Külső parancsok indítása. Ha nem ad meg paramétereket létrehoz, csak_ha, kivéve vagy csak_frissítéskor, a parancs minden Puppetrun során végrehajtásra kerül. Képes kezelni az értesítéseket - elindítja a parancsot.
Paraméterek:
- az erőforrás neve – a parancs, amit végre kell hajtani (opcionális)
- command – a parancs, amit végre kell hajtani (ha nincs megadva a nevében)
- path – az elérési utak, ahol a végrehajtható fájlt keresik
- csak_ha – ha a megadott parancs a nullás visszatérési kóddal zárul, a fő parancs végrehajtásra kerül
- kivéve – ha a megadott parancs a nem nullás visszatérési kóddal zárul, a fő parancs végrehajtásra kerül
- létrehoz – ha a megadott fájl nem létezik, a fő parancs végrehajtásra kerül
- csak_frissítéskor — ha
igaz, a parancs csak akkor fog elindulni, amikor ez az exec értesítést kap más forrásoktól - cwd – az a könyvtár, ahonnan a parancsot futtatni kell
- user – az a felhasználó, akitől a parancsot futtatni kell
- szolgáltató – mivel indítjuk a parancsot:
- posix – csak létrehozza a gyermeket, kötelező megadni path
- shell – a parancs a shell-ben fut
/bin/sh, nem szükséges megadni path, globbingot, pipe-okat és egyéb shell funkciókat lehet használni. Általában automatikusan meghatározza, ha vannak különféle speciális karakterek (|,;,&&,||stb.).
cron
Kezeli a cron feladatokat.
Paraméterek:
- az erőforrás neve – csak valami azonosító
- ensure – a cron feladat állapota:
present– létrehozni, ha nem léteziknem létezik– törölni, ha létezik
- command – milyen parancsot kell futtatni
- környezet – milyen környezetben kell futtatni a parancsot (a környezeti változók és azok értékei listája
=) - user – melyik felhasználótól kell futtatni a parancsot
- perc, óra, hét napja, hónap, hónap napja – mikor kell futtatni a cron-t. Ha bármelyik ezek közül az attribútum közül nincs megadva, annak értéke a crontabban
*.
A Puppet 6.0-ban cron mintha a puppetserver-ből, ezért nincs dokumentáció a közös weboldalon. De van a puppet-agent-ben, tehát külön telepíteni nem szükséges. A dokumentációját meg lehet nézni , vagy .
Az erőforrásokról általánosságban
A források egyediségével szemben támasztott követelmények
A leggyakoribb hiba, amivel találkozunk – Duplikált deklaráció. Ez a hiba akkor fordul elő, amikor egy könyvtárba két vagy több azonos típusú erőforrás ugyanazzal a névvel kerül.
Ezért még egyszer megerősítem: a manifesztumokban egy nód számára nem lehet azonos típusú erőforrást azonos névvel (title)!
Néha szükséges ugyanolyan nevű csomagokat telepíteni, de különböző csomagkezelőkkel. Ilyen esetben a nameparamétert kell használni, hogy elkerüljük a hibát:
csomag { 'ruby-mysql':
ensure => telepítve,
name => 'mysql',
provider => 'gem',
}
csomag { 'python-mysql':
ensure => telepítve,
name => 'mysql',
provider => 'pip',
}Más típusú erőforrások esetében hasonló paraméterek találhatók, amelyek segítenek elkerülni a duplikációt – name sa-logic-subsets-canary-vs.yaml a SystemD inicializálási rendszer számára:, command sa-logic-subsets-canary-vs.yaml exec, és így tovább.
Meta-paraméterek
Minden erőforrás típusnak vannak speciális paraméterei, függetlenül a lényegétől.
A meta-paraméterek teljes listája .
Rövid lista:
- require — ebben a paraméterben meg van határozva, hogy mely erőforrásoktól függ ez az erőforrás.
- before — ebben a paraméterben meg van határozva, hogy mely erőforrások függenek ettől az erőforrástól.
- subscribe — ebben a paraméterben meg van határozva, hogy mely erőforrásoktól kap értesítést ez az erőforrás.
- notify — ebben a paraméterben meg van határozva, hogy mely erőforrások kapnak értesítést ettől az erőforrástól.
Minden felsorolt meta-paraméter fogadhat egy erőforrás hivatkozását, vagy egy hivatkozások tömbjét négyzetes zárójelekben.
Hivatkozások erőforrásokra
Az erőforrás hivatkozása egyszerűen egy erőforrás említést jelent. Elsősorban a függőségek megadására használják. A nem létező erőforrás hivatkozása fordítási hibát fog okozni.
A hivatkozás szintaxisa a következő: az erőforrás típusa nagybetűvel (ha a névben kettős kettőspont található, akkor minden részt nagybetűvel kell írni, ami a kettős kettőspontok között helyezkedik el), aztán négyzetes zárójelekben az erőforrás neve (a név kis- és nagybetűszerűsége nem változik!). Nincs hely a hivatkozásban, a négyzetes zárójelek közvetlenül a típusszót követik.
Példa:
file { '/file1': ensure => jelen van }
file { '/file2':
ensure => könyvtár,
before => File['/file1'],
}
file { '/file3': ensure => hiányzik }
File['/file1'] -> File['/file3']Függőségek és értesítések
Mint már említettük, az erőforrások közötti egyszerű függőségek tranzitívak. Egyébként ügyeljen a függőségek megadására — ciklikus függőségeket hozhat létre, ami fordítási hibát idézhet elő.
A függőségekkel ellentétben az értesítések nem tranzitívak. Az értesítések esetén a következő szabályok érvényesek:
- Ha egy erőforrás értesítést kap, frissül. A frissítéskor végrehajtott műveletek típusa az erőforrástól függ — exec parancsot indít el, a SystemD inicializálási rendszer számára: szolgáltatást indít újra, csomag csomagot telepít újra. Ha az erőforráshoz nem tartozik frissítési művelet, akkor semmi sem történik.
- Egy futás során a Puppet erőforrása legfeljebb egy alkalommal frissül. Ez lehetséges, mivel az értesítések tartalmazzák a függőségeket, és a függőségek grafikonjában nincsenek ciklusok.
- Ha a Puppet megváltoztatja az erőforrás állapotát, akkor az erőforrás értesítéseket küld minden olyan erőforrásnak, amely fel van iratkozva rá.
- Ha az erőforrás frissül, akkor értesítéseket küld minden olyan erőforrásnak, amely fel van iratkozva rá.
Nem megadott paraméterek kezelése
Általában, ha egy erőforrás paraméterének nincs alapértelmezett értéke, és ez a paraméter nincs megadva a manifestben, a Puppet nem fogja megváltoztatni ezt a tulajdonságot a megfelelő erőforráson a node-on. Például, ha egy erőforrás típusa file nincs megadva a paraméter tulajdonos, akkor a Puppet nem fogja megváltoztatni a megfelelő fájl tulajdonosát.
Megismerkedés osztályokkal, változókkal és definíciókkal
Tegyük fel, hogy van néhány node-unk, amelyek azonos konfigurációs részekkel rendelkeznek, de vannak különbségek is – különben mindent leírhatnánk egy blokkon belül. node {}. Természetesen egyszerűen lemásolhatjuk az azonos konfigurációs részeket, de általában ez rossz megoldás – a konfiguráció nő, és ha a közös konfigurációs rész megváltozik, akkor ugyanazt számos helyen kell javítani. Emellett könnyű hibázni, és egyáltalán, a DRY (don’t repeat yourself) elv nem véletlenül vált népszerűvé.
Egy ilyen probléma megoldására létezik egy konstrukció, mint osztály.
Osztályok
— ez egy nevén nevezett blokk a Puppet kódban. Az osztályok a kód újrahasználásához szükségesek.
Először az osztályt le kell írni. Maga a leírás nem ad hozzá semmilyen erőforrást. Az osztályokat a manifestelemekben írják le:
# Описание класса начинается с ключевого слова class и его названия.
# Дальше идёт тело класса в фигурных скобках.
class example_class {
...
}Ezután az osztályt fel lehet használni:
# первый вариант использования — в стиле ресурса с типом class
class { 'example_class': }
# второй вариант использования — с помощью функции include
include example_class
# про отличие этих двух вариантов будет рассказано дальшеElőző feladat példája – különítsük el az nginx telepítését és beállítását egy osztályba:
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
}Változók
Az előző példából származó osztály teljesen rugalmatlan, mivel mindig ugyanazt az nginx konfigurációt hozza. Tegyünk úgy, hogy a konfigurációs útvonal változóvá váljon, így ezt az osztályt bármilyen konfigurációjú nginx telepítésére felhasználhatjuk.
Ezt meg lehet tenni .
Figyelem: a Puppet változók immutable-ek!
Továbbá, egy változóhoz hozzáférni csak akkor lehet, miután deklarálták, különben a változó értéke undef.
Példa a változók használatára:
# создание переменных
$variable = 'value'
$var2 = 1
$var3 = true
$var4 = undef
# использование переменных
$var5 = $var6
file { '/tmp/text': content => $variable }
# интерполяция переменных — раскрытие значения переменных в строках. Работает только в двойных кавычках!
$var6 = "Variable with name variable has value ${variable}"A Puppet-ben vannak névterek, és a változóknak ennek megfelelően van hatókörük: egy változó ugyanazzal a névvel különböző névterekben van definiálva. A változó értékének meghatározásakor a változót a jelenlegi névtérben keresik, majd a külsőben, és így tovább.
Néhány névtér példa:
- globális — ide tartoznak a változók, amelyek a osztály vagy csomópont leírásán kívül helyezkednek el;
- csomópont névtér a csomópont leírásában;
- osztály névtér az osztály leírásában.
A változóra való hivatkozás ambiguitásának elkerülése érdekében meg lehet határozni a névteret a változó nevében:
# переменная без пространства имён
$var
# переменная в глобальном пространстве имён
$::var
# переменная в пространстве имён класса
$classname::var
$::classname::varTegyük fel, hogy az nginx konfiguráció elérési útja a változóban található $nginx_conf_source. Így a osztály a következőképpen néz ki:
class nginx_example {
package { 'nginx':
ensure => installed,
}
-> file { 'etc/nginx':
ensure => directory,
source => $nginx_conf_source, # itt a váltó nevet használjuk a fix szöveg helyett
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
}Azonban a fenti példa rossz, mert van egyfajta „titkos tudás” arról, hogy a osztályon belül valahol egy változót használunk ezzel a névvel. Sokkal helyesebb ezt a tudást közkinccsé tenni — az osztályoknak lehetnek paramétereik.
Osztály paraméterek — ezek a változók az osztály névterében találhatók, a osztály fejléce alatt határozzák meg őket, és szokásos változókként használhatók a osztály törzsében. A paraméterek értékeit a osztály manifestben történő használatakor adják meg.
A paraméternek be lehet állítani egy alapértelmezett értéket. Ha a paraméternek nincs alapértelmezett értéke, és az értéket nem adják meg a használatkor, fordítási hibát vált ki.
Parametrizáljuk az előző példa osztályt, és adjunk hozzá két paramétert: az első, kötelező — a konfiguráció elérési útja, és a második, opcionális — az nginx csomag neve (Debianban például léteznek csomagok nginx, nginx-light, nginx-full).
# переменные описываются сразу после имени класса в круглых скобках
class nginx_example (
$conf_source,
$package_name = 'nginx-light', # параметр со значением по умолчанию
) {
package { $package_name:
ensure => installed,
}
-> file { '/etc/nginx':
ensure => directory,
source => $conf_source,
recurse => true,
purge => true,
force => true,
}
~> service { 'nginx':
ensure => running,
enable => true,
}
}
node 'server2.testdomain' {
# если мы хотим задать параметры класса, функция include не подойдёт* — нужно использовать resource-style declaration
# *на самом деле подойдёт, но про это расскажу в следующей серии. Ключевое слово "Hiera".
class { 'nginx_example':
conf_source => 'puppet:///modules/example/nginx-conf', # задаём параметры класса точно так же, как параметры для других ресурсов
}
}A Puppet-ben a változók típusosak. Vannak . Az adattípusokat általában a paraméterek értékeinek validálására használják, amelyek a osztályokba és definiálásokba kerülnek. Ha a megadott paraméter nem felel meg a megadott típusnak, fordítási hiba lép fel.
A típus közvetlenül a paraméter neve elé kerül:
class example (
String $param1,
Integer $param2,
Array $param3,
Hash $param4,
Hash[String, String] $param5,
) {
...
}Osztályok: include classname vs class{‘classname’:}
Mindegyik osztály erőforrás típusú class. Ahogyan más erőforrástípusok esetén, egy node-on nem lehet két azonos osztály példány.
Ha megpróbáljuk ugyanazt az osztályt kétszer hozzáadni ugyanahhoz a node-hoz a class { 'classname':} (függetlenül attól, hogy eltérő vagy azonos paraméterekkel), fordítási hiba lép fel. Azonban ha az osztály erőforrás stílusban van használva, akkor a manifestben az összes paramétert azonnal megadhatjuk.
Ha azonban használja a include, akkor az osztályt annyiszor lehet hozzáadni, ahányszor csak akarjuk. A lényeg az, hogy include — idempotens funkció, amely ellenőrzi, hogy az osztály hozzá lett-e adva a könyvtárhoz. Ha az osztály nincs a könyvtárban — hozzáadja, ha már létezik, semmit sem csinál. De ha az include használatakor nem lehet megadni az osztály paramétereit az osztály bejelentése során — az összes kötelező paramétert külső adatforrásban kell megadni — Hiera vagy ENC. Ezekről a következő cikkben beszélünk.
Definíciók
Ahogyan az előző blokkban említésre került, ugyanaz az osztály nem lehet jelen egy node-on többször. Bizonyos esetekben azonban szükség van arra, hogy ugyanazt a kódblokkot különböző paraméterekkel alkalmazzuk egy node-on. Más szóval, szükség van egy saját erőforrás típusra.
Például, ha PHP modult szeretnénk telepíteni, mi az Aviton a következőt tesszük:
- Telepítjük a csomagot ezzel a modullal.
- Konfigurációs fájlt hozunk létre ehhez a modulhoz.
- Létrehozunk egy symlinket a php-fpm konfigurációhoz.
- Létrehozunk egy symlinket a php cli konfigurációhoz.
Ezekben az esetekben olyan konstrukciót használunk, mint (define, defined type, defined resource type). A definíció hasonló az osztályhoz, de van néhány különbség: először is, minden definíció erőforrás típus, nem erőforrás; másodszor, minden definíciónak van egy implicit paramétere, $title, ahová a forrás neve kerül az bejelentés során. Ahogyan az osztályok esetében, a definíciót is először le kell írni, majd használni lehet.
Egyszerű példa PHP modul használatával:
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'], # a debianos php csomagok saját maguk hoznak létre szimlinkeket és indítják újra a php-fpm szolgáltatást - erre nekünk nincs szükségünk, mivel a szimlinkeket és a szolgáltatást Puppet segítségével kezeljük
}
-> file { "${php_module_path}/${php_module_name}.ini":
ensure => $ensure,
content => $data,
}
file { "etc/php/7.4/cli/conf.d/${priority}-${php_module_name}.ini":
ensure => link,
target => "${php_module_path}/${php_module_name}.ini",
}
file { "etc/php/7.4/fpm/conf.d/${priority}-${php_module_name}.ini":
ensure => link,
target => "${php_module_path}/${php_module_name}.ini",
}
}
node server3.testdomain {
php74::module { 'sqlite3': }
php74::module { 'amqp': php_package_name => 'php-amqp' }
php74::module { 'msgpack': priority => '10' }
}A define-ben a legkönnyebb elkapni a Duplicate declaration hibát. Ez akkor fordul elő, ha a definícióban van egy erőforrás állandó névvel, és valahol két vagy több példány van ebből a definícióból.
Emiatt könnyen védekezhetünk: minden erőforrás, ami a definícióban van, olyan nevet kell kapjon, ami attól függ, $title. Alternatívaként - idempotens erőforrás hozzáadás, ami a legegyszerűbb esetben elegendő, ha az összes példány számára közös erőforrásokat egy külön osztályba emeljük, és ezt az osztályt inkludáljuk a definícióba - a funkció include idempotens.
Más módszerek is léteznek az idempotencia elérésére erőforrások hozzáadásánál, ezek közé tartozik a defined és ensure_resources, de erről a következő részből fogok beszélni.
Osztályok és definíciók függőségei és értesítései
Az osztályok és definíciók a következő szabályokat adják a függőségek és értesítések kezeléséhez:
- osztály/definíció függőségei minden erőforrás függőségét hozzáadják az osztályhoz/definícióhoz;
- osztály/definíció függősége minden erőforrást a class/define-hoz fűz;
- az osztály/definíció értesítése értesít minden erőforrást az osztályban/definícióban;
- osztály/definíció feliratkozás minden erőforrást feliratkoztat az osztályban/definícióban.
Feltételes operátorok és szelektorok
if
Ez itt nagyon egyszerű:
if KIFEJEZÉS1 {
...
} elsif KIFEJEZÉS2 {
...
} else {
...
}kivéve
unless - ez az if ellentéte: a kód blokk akkor fut le, ha a kifejezés hamis.
unless KIFEJEZÉS {
...
}case
Itt semmi bonyolult nincs. A végervényeket használhatunk: bíztos értékek (stringek, számok és így tovább), reguláris kifejezések, és adathordozó típusok.
case KIFEJEZÉS {
ÉRTÉK1: { ... }
ÉRTÉK2, ÉRTÉK3: { ... }
default: { ... }
}Kiválasztók
A kiválasztó egy nyelvi konstrukció, amely hasonlít a case, de a kódrészlet végrehajtása helyett értéket ad vissza.
$var = $othervar ? { 'val1' => 1, 'val2' => 2, default => 3 }Modulok
Amikor a konfiguráció kicsi, könnyen kezelhető egyetlen manifestben. De minél több konfigurációt írunk le, annál több osztály és elem kerül a manifestbe, és egyre nehezebben kezelhetővé válik.
Ezen kívül van a kód újrafelhasználásának problémája - amikor az összes kód egyetlen manifestben található, nehezen osztható meg másokkal. E két problémára a Puppetben van egy entitás, amit moduloknak hívnak.
Modulok – ezek osztályok, definíciók és egyéb Puppet-entitások készletei, amelyek egy külön könyvtárba vannak kiemelve. Más szóval, a modul egy független darab Puppet-logika. Például lehet egy modul az nginx kezelésére, ami csak az ahhoz szükséges dolgokat tartalmazza, és lehet egy modul a PHP kezelésére, és így tovább.
A modulok verziózva vannak, és támogatják egymás moduljainak függőségeit. Van egy nyílt modulrepositoriohűv .
A puppet-szerveren a modulok a gyökérkönyvtár modules almappájában találhatók. Minden modulon belül a standard mappaszerkezet - manifests, files, templates, lib és így tovább.
A modul fájlstruktúrája
A modul gyökerében a következő, beszédes nevű mappák találhatók:
manifests– itt találhatók a manifesztokfiles– itt találhatók a fájloktemplates– itt találhatók a sablonoklib– itt található a Ruby-kód
Ez nem teljes listája a könyvtáraknak és fájloknak, de ennek a cikknek elegendő.
Az erőforrások nevei és a fájlnevek a modulban
Az erőforrásokat (osztályok, definíciók) a modulban nem lehet tetszőlegesen elnevezni. Ezenkívül közvetlen megfelelés van a forrás neve és a fájl neve között, amelyben a Puppet keresni fogja ennek az erőforrásnak a leírását. Ha megsértjük a névadási szabályokat, a Puppet egyszerűen nem találja meg az erőforrások leírását, és fordítási hibát okoz.
A szabályok egyszerűek:
- Minden erőforrásnak a modul névtérben kell lennie. Ha a modul neve
foo, akkor az összes erőforrásnak így kell neveződnie:foo::, vagy egyszerűen csakfoo. - A modul nevével rendelkező erőforrásnak a következő fájlban kell lennie:
init.pp. - A többi erőforrás esetén a fájlnevek elnevezési séma a következő:
- a modul nevét tartalmazó előtag elhagyásra kerül.
- minden kettős kettőspontot, ha van, perjelre cserélnek
- kiterjesztés kerül hozzárendelésre
.pp
Példával fogom bemutatni. Tegyük fel, hogy írok egy modult nginx. Ebben a következő erőforrások találhatók:
- osztály
nginxa manifestben van leírvainit.pp; - osztály
nginx::servicea manifestben van leírvaservice.pp; - definíció
nginx::servera manifestben van leírvaserver.pp; - definíció
nginx::server::locationa manifestben van leírvaserver/location.pp.
Sablonok
Valószínűleg jól tudja, mi az a sablon, ezért nem fogom itt részletezni. De minden esetre itt hagyok egy .
Hogyan használjunk sablonokat: a sablon értékét a template, amelynek átadjuk a sablonhoz vezető utat. Olyan erőforrások esetén, mint a file együtt kell használni a paraméterrel content. Például így:
file { '/tmp/example': content => template('modulename/templatename.erb')Az alábbi formátumú útvonal / azt jelenti, hogy a fájl /modules//templates/.
Ezen kívül van egy inline_template — ehhez a sablon szövegét adja meg, nem a fájl nevét.
A sablonokban használhatók az összes Puppet változó az aktuális láthatósági tartományban.
A Puppet támogatja az ERB és EPP formátumú sablonokat:
Röviden az ERB-ről
vezérlő konstrukciók:
<%= ВЫРАЖЕНИЕ %>— a kifejezés értékének beszúrása<% ВЫРАЖЕНИЕ %>— a kifejezés értékének kiszámítása (anélkül, hogy beszúrná). Ide tartoznak a szokásos feltételes operátorok (if), ciklusok (each).<%# КОММЕНТАРИЙ %>
A kifejezések az ERB-ben Ruby-ban íródnak (tulajdonképpen az ERB az Embedded Ruby).
A manifestből való változók eléréséhez hozzá kell adni @ a változó nevéhez. A vezérlő konstrukció után megjelenő új sort a záró tag használatával lehet eltüntetni -%>.
A sablon használatának példája
Tegyük fel, hogy írok egy modult a ZooKeeper kezelésére. A konfiguráció létrehozásáért felelős osztály körülbelül így néz ki:
class zookeeper::configure (
Array[String] $nodes,
Integer $port_client,
Integer $port_quorum,
Integer $port_leader,
Hash[String, Any] $properties,
String $datadir,
) {
file { '/etc/zookeeper/conf/zoo.cfg':
ensure => present,
content => template('zookeeper/zoo.cfg.erb'),
}
}A megfelelő sablon zoo.cfg.erb — így:
0 -%>
server.=::;
dataDir=
=Tények és beépített változók
Gyakran a konfiguráció egy konkrét része attól függ, hogy mi történik éppen a noden. Például a Debian aktuális kiadásától függően szükség van a megfelelő csomagverzió telepítésére. Mindez manuálisan is nyomon követhető, a node-k változásakor újra kell írni a manifesztusokat. De ez nem egy komoly megközelítés, az automatizálás sokkal jobb.
A nodok információinak megismerésére a Puppet-ben van egy mechanizmus, amit tényeknek hívnak. Tények a node-ra vonatkozó, a manifesztusokban globális változókként elérhető információ. Például a gazdagép neve, az operációs rendszer verziója, a processzor architektúrája, a felhasználók listája, a hálózati interfészek és azok címének listája, és még sok más. A tények a manifesztusokban és a sablonokban szokásos változókként elérhetőek.
Példa a tények használatára:
notify { "Futó OS ${facts['os']['name']} verzió ${facts['os']['release']['full']}" : }
# A notify típusú erőforrás egyszerűen egy üzenetet ír ki a naplóba.Formálisan szólva, a ténynek van egy neve (string) és egy értéke (különböző típusok elérhetőek: stringek, tömbök, szótárak). Van . Emellett saját tényeket is lehet írni. A ténygyűjtők Ruby nyelven , vagy . A tények megjelenhetnek a nodon.
A Puppet ügynök működése közben először lemásolja a Puppet szerverről az összes elérhető ténygyűjtőt a node-ra, majd futtatja őket és elküldi a szervernek a begyűjtött tényeket; ezt követően a szerver megkezdi a katalógus összeállítását.
Végrehajtható fájlok formájában lévő tények
Ezek a tények a modulokban a facts.dkönyvtárba kerülnek. Nyilvánvalóan a fájlok végrehajthatóknak kell lenniük. Futáskor a standard kimenetre kell kibővíteniük információt vagy YAML, vagy "kulcs=érték" formátumban.
Ne feledje, hogy a tények minden node-ra kiterjednek, amelyek a Puppet szerver irányítása alatt állnak, ahová a modulját telepíti. Ezért ügyeljen a szkriptben arra, hogy a rendszerben minden a tény működéséhez szükséges program és fájl megtalálható legyen.
#!/bin/sh
echo "testfact=success"#!/bin/sh
echo '{"testyamlfact":"success"}'Ruby nyelvű tények
Ezek a tények a modulokban a 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
endSzöveges tények
Ezek a tények a nodon a /etc/facter/facts.d régi Puppet-ben vagy /etc/puppetlabs/facts.d új Puppet-ben.
examplefact=examplevalue---
examplefact2: examplevalue2
anotherfact: anothervalueA tények elérése
A tényekhez kétféleképpen lehet hozzáférni:
- szótáron keresztül
$facts:$facts['fqdn']; - a tény nevét használva mint változónevet:
$fqdn.
A legjobban a szótár használata ajánlott $facts, és még jobb, ha globális névtér-t használsz ($::facts).
Beépített változók
A tényeken kívül még vannak , amelyek elérhetőek a globális névtérben.
- trusted facts — olyan változók, amelyek az ügyfél tanúsítványából származnak (mivel a tanúsítványt általában a puppet-szerveren állítják ki, az ügynök nem tudja csak úgy megváltoztatni a saját tanúsítványát, ezért ezek a változók "megbízhatóak": tanúsítvány neve, gazdagép neve és domain neve, a tanúsítvány kiterjesztései.
- server facts — a szerverrel kapcsolatos információkat tartalmazó változók — verzió, név, IP-cím, környezet.
- agent facts — a puppet-agent által közvetlenül hozzáadott változók, nem a facter által — tanúsítvány név, ügynök verzió, puppet verzió.
- master variables — a puppetmaster változói (sic!). Hasonlóak a fentiekhez, server facts, plusz elérhetőek a konfigurációs paraméterek értékei.
- compiler variables — a fordító változói, amelyek minden láthatóságban eltérnek: a jelenlegi modul neve és a modul neve, ahonnan a jelenlegi objektumra hivatkoztak. Ezeket például arra lehet használni, hogy ellenőrizzék, hogy a privát osztályaik ne legyenek közvetlenül más modulokból használva.
Kiegészítés 1: hogyan indítsuk és hibakeressük mindezt?
A cikkben sok példa volt a puppet-kódra, de arról nem volt szó, hogyan lehet ezt a kódot futtatni. Nos, korrigálom magam.
A Puppet működéséhez elegendő az ügynök, de a legtöbb esetben szükség lesz a szerverre is.
Ügynök
Legalább az ötödik verziótól kezdve a puppet-agent csomagok minden szükséges függőséget tartalmaznak (ruby és a megfelelők gem-ek), ezért telepítési problémák nem állnak fenn (Debian-alapú disztribúciókra beszélek — az RPM-alapú disztribúciókat nem használjuk).
A legegyszerűbb esetben a puppet-konfiguráció alkalmazásához elegendő az ügynököt futtatni süket szerver módba: feltételezve, hogy a puppet-kód a nodára lett másolva, futtatni kell puppet apply:
atikhonov@atikhonov ~/puppet-test $ cat helloworld.pp
node default {
notify { 'Helló világ!': }
}
atikhonov@atikhonov ~/puppet-test $ puppet apply helloworld.pp
Értesítés: Összeállított katalógus az atikhonov.localdomain számára az production környezetben 0.01 másodperc alatt
Értesítés: Helló világ!
Értesítés: /Stage[main]/Main/Node[default]/Notify[Helló világ!]/message: a 'message' definálva: 'Helló világ!'
Értesítés: Katalógus alkalmazva 0.01 másodperc alattTermészetesen jobb egy szervert indítani és a nodokon ügynököket daemon módban futtatni — ekkor félóránként alkalmazni fogják a szerverről letöltött konfigurációt.
Lehet szimulálni a push-modell működését — lépjen be a kívánt nodera és indítsa el sudo puppet agent -t. A kulcs - folyamatosan „pingelni” (a leállításhoz nyomjuk meg a Ctrl+C kombinációt) (--test) valójában több opciót tartalmaz, amelyeket külön is bekapcsolhat. Ezek között a következők találhatók:
- nem működik daemon módban (alapértelmezés szerint az ügynök daemon módban indul);
- a katalógus alkalmazása után leáll (alapértelmezés szerint az ügynök folytatja a működést, és félóránként alkalmazza a konfigurációt);
- részletes működési naplót ír;
- mutatja a fájlokban bekövetkezett változásokat.
Az ügynöknek van egy változatlan üzemmódja — ezt akkor lehet használni, ha nem biztos abban, hogy helyes konfigurációt írt, és szeretné ellenőrizni, pontosan mit változtat az ügynök a működés során. Ezt az üzemmódot a parancssorban a következő paraméter kapcsolja be: --noop : sudo puppet agent -t --noop.
Továbbá kapcsolhat be debug naplózást is — ebben a puppet minden műveletéről ír, amelyet végrehajt: az éppen feldolgozott erőforrásról, annak paramétereiről, arról, hogy milyen programokat indít. Természetesen ez a paraméter --debug.
Szerver
A puppetserver teljes körű beállítása és a kód telepítése nem a cikk témája, csak annyit mondok, hogy gyári állapotban egy jól működő verzió települ, amely nem igényel további konfigurációt a kis mennyiségű nod működtetéséhez (mondjuk százig). Nagyobb számú nod már finomhangolást igényel — alapértelmezés szerint a puppetserver nem indít el többet négy munkásnál, a nagyobb teljesítményhez növelni kell a számukat, és nem szabad elfelejteni a memóriahatárokat növelni, különben a szerver többségében garbage collect’eli az időt.
A kód telepítése — ha gyorsan és egyszerűen akarja, nézze meg (r10k)[], a kis telepítésekhez ez bőven elegendő kell, hogy legyen.
Kiegészítés 2: Kódírási ajánlások
- Minden logikát helyezzen el osztályokban és definíciókban.
- Tartsuk a classokat és definíciókat modulokban, ne pedig a csomópontok leírásában található manifestekben.
- Használja a tényeket.
- Ne készítsen if-utasításokat hosztnevek alapján.
- Ne habozzon hozzáadni paramétereket a classokhoz és definíciókhoz - ez jobb, mint a rejtett logika a class/definíció testében.
Amiért javaslom ezt, azt a következő cikkben elmagyarázom.
Zárás
Ezzel befejeztük az bevezetőt. A következő cikkben a Hieráról, az ENC-ről és a PuppetDB-ről fogok beszélni.
Csak a regisztrált felhasználók vehetnek részt a szavazáson. , kérem.
Valójában sokkal több anyag van - tudok cikkeket írni a következő témákban, szavazzanak, hogy miről szeretnének olvasni:
- 59,1%Fejlett Puppet konstruktumok - néhány következő szintű dolog: ciklusok, leképezések és egyéb lambda kifejezések, erőforrásgyűjtők, exportált erőforrások és hosztok közötti interakció Puppet-en keresztül, címkék, szolgáltatók, absztrakt adattípusok.
- 31,8%» Én a mamának vagyok admin« vagy hogyan barátkoztunk össze több különböző verziójú Puppet-szerverrel az Avitónál, nos, és általában egy rész a Puppet-szerver adminisztrálásáról.
- 81,8%Hogyan írjuk a Puppet kódot: eszközláncolás, dokumentáció, tesztelés, CI/CD.
22 felhasználó szavazott. 9 felhasználó tartózkodott.
Forrás: habr.com
