Uvod u lutku

Puppet je sustav za upravljanje konfiguracijom. Koristi se za dovođenje hostova u željeno stanje i održavanje tog stanja.

S Puppetom radim više od pet godina. Ovaj tekst je u biti prevedena i preuređena kompilacija ključnih točaka iz službene dokumentacije, koja će početnicima omogućiti da brzo razumiju bit Puppet-a.

Uvod u lutku

Osnovne informacije

Puppetov operativni sustav je klijent-poslužitelj, iako podržava i rad bez poslužitelja s ograničenom funkcionalnošću.

Koristi se model rada povlačenja: prema zadanim postavkama, jednom svakih pola sata, klijenti kontaktiraju poslužitelj za konfiguraciju i primjenjuju je. Ako ste radili s Ansibleom, onda oni koriste drugačiji push model: administrator pokreće postupak primjene konfiguracije, sami klijenti neće ništa primijeniti.

Tijekom mrežne komunikacije koristi se dvosmjerna TLS enkripcija: poslužitelj i klijent imaju svoje privatne ključeve i odgovarajuće certifikate. Obično poslužitelj izdaje certifikate za klijente, ali u načelu je moguće koristiti vanjski CA.

Uvod u manifeste

U lutkarskoj terminologiji poslužitelju lutaka Spojiti čvorovi (čvorovi). Konfiguracija za čvorove je napisana u manifestima u posebnom programskom jeziku – Puppet DSL.

Puppet DSL je deklarativni jezik. Opisuje željeno stanje čvora u obliku deklaracija pojedinačnih resursa, na primjer:

  • Datoteka postoji i ima određeni sadržaj.
  • Paket je instaliran.
  • Usluga je počela.

Resursi mogu biti međusobno povezani:

  • Postoje ovisnosti, one utječu na redoslijed korištenja resursa.
    Na primjer, "prvo instalirajte paket, zatim uredite konfiguracijsku datoteku, a zatim pokrenite uslugu."
  • Postoje obavijesti - ako se resurs promijenio, šalje obavijesti resursima koji su na njega pretplaćeni.
    Na primjer, ako se konfiguracijska datoteka promijeni, možete automatski ponovno pokrenuti uslugu.

Dodatno, Puppet DSL ima funkcije i varijable, kao i uvjetne izjave i selektore. Podržani su i razni mehanizmi šablona - EPP i ERB.

Puppet je napisan u Rubyju, tako da su mnogi konstrukti i pojmovi preuzeti od tamo. Ruby vam omogućuje da proširite Puppet - dodajte složenu logiku, nove vrste resursa, funkcije.

Dok Puppet radi, manifesti za svaki određeni čvor na poslužitelju kompiliraju se u direktorij. Adresar je popis resursa i njihovih odnosa nakon izračuna vrijednosti funkcija, varijabli i proširenja uvjetnih iskaza.

Sintaksa i kodni stil

Ovdje su dijelovi službene dokumentacije koji će vam pomoći razumjeti sintaksu ako navedeni primjeri nisu dovoljni:

Evo primjera kako izgleda manifest:

# Комментарии пишутся, как и много где, после решётки.
#
# Описание конфигурации ноды начинается с ключевого слова node,
# за которым следует селектор ноды — хостнейм (с доменом или без)
# или регулярное выражение для хостнеймов, или ключевое слово default.
#
# После этого в фигурных скобках описывается собственно конфигурация ноды.
#
# Одна и та же нода может попасть под несколько селекторов. Про приоритет
# селекторов написано в статье про синтаксис описания нод.
node 'hostname', 'f.q.d.n', /regexp/ {
  # Конфигурация по сути является перечислением ресурсов и их параметров.
  #
  # У каждого ресурса есть тип и название.
  #
  # Внимание: не может быть двух ресурсов одного типа с одинаковыми названиями!
  #
  # Описание ресурса начинается с его типа. Тип пишется в нижнем регистре.
  # Про разные типы ресурсов написано ниже.
  #
  # После типа в фигурных скобках пишется название ресурса, потом двоеточие,
  # дальше идёт опциональное перечисление параметров ресурса и их значений.
  # Значения параметров указываются через т.н. hash rocket (=>).
  resource { 'title':
    param1 => value1,
    param2 => value2,
    param3 => value3,
  }
}

Uvlake i prijelomi redaka nisu obavezni dio manifesta, ali postoji preporuka stilski vodič. Sažetak:

  • Uvlake s dva razmaka, tabulator se ne koristi.
  • Vitičaste zagrade odvojene su razmakom; dvotočke nisu odvojene razmakom.
  • Zarezi iza svakog parametra, uključujući i zadnji. Svaki parametar je u zasebnom retku. Izuzetak je slučaj bez parametara i jednog parametra: možete pisati u jednom redu i bez zareza (tj. resource { 'title': } и resource { 'title': param => value }).
  • Strelice na parametrima trebaju biti na istoj razini.
  • Strelice odnosa resursa ispisane su ispred njih.

Lokacija datoteka na pappetserveru

Za daljnje objašnjenje, predstavit ću koncept "korijenskog direktorija". Korijenski direktorij je direktorij koji sadrži konfiguraciju lutke za određeni čvor.

Korijenski direktorij varira ovisno o verziji programa Puppet i korištenim okruženjima. Okruženja su neovisni skupovi konfiguracije koji su pohranjeni u zasebnim direktorijima. Obično se koristi u kombinaciji s git-om, u kojem se slučaju okruženja stvaraju iz git-ovih grana. U skladu s tim, svaki se čvor nalazi u jednom ili drugom okruženju. To se može konfigurirati na samom čvoru ili u ENC-u, o čemu ću govoriti u sljedećem članku.

  • U trećoj verziji ("stara lutka") osnovni imenik bio je /etc/puppet. Korištenje okruženja nije obavezno - na primjer, ne koristimo ih sa starom Puppet. Ako se koriste okruženja, obično se u njima pohranjuju /etc/puppet/environments, korijenski direktorij bit će direktorij okruženja. Ako se okruženja ne koriste, korijenski direktorij bit će osnovni direktorij.
  • Počevši od četvrte verzije (“nova lutka”), korištenje okruženja postalo je obavezno, a osnovni direktorij premješten je u /etc/puppetlabs/code. Sukladno tome, okruženja su pohranjena u /etc/puppetlabs/code/environments, korijenski direktorij je direktorij okruženja.

Mora postojati poddirektorij u korijenskom direktoriju manifests, koji sadrži jedan ili više manifesta koji opisuju čvorove. Osim toga, trebao bi postojati poddirektorij modules, koji sadrži module. Malo kasnije ću vam reći što su moduli. Osim toga, stari Puppet također može imati poddirektorij files, koji sadrži razne datoteke koje kopiramo u čvorove. U novom Puppetu sve su datoteke smještene u module.

Datoteke manifesta imaju ekstenziju .pp.

Par borbenih primjera

Opis čvora i resursa na njemu

Na čvoru server1.testdomain mora se kreirati datoteka /etc/issue sa sadržajem Debian GNU/Linux n l. Datoteka mora biti u vlasništvu korisnika i grupe root, prava pristupa moraju biti 644.

Pišemo 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 в начале будет воспринято как записанное в восьмеричной системе, и всё пойдёт не так, как задумано
    }
}

Odnosi između resursa na čvoru

Na čvoru server2.testdomain nginx mora biti pokrenut, raditi s prethodno pripremljenom konfiguracijom.

Razdvojimo problem:

  • Paket treba instalirati nginx.
  • Potrebno je da se konfiguracijske datoteke kopiraju s poslužitelja.
  • Usluga mora biti pokrenuta nginx.
  • Ako se konfiguracija ažurira, uslugu je potrebno ponovno pokrenuti.

Pišemo 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 ovo radilo, potrebna vam je otprilike sljedeća lokacija datoteke na lutkarskom poslužitelju:

/etc/puppetlabs/code/environments/production/ # (это для нового Паппета, для старого корневой директорией будет /etc/puppet)
├── manifests/
│   └── site.pp
└── modules/
    └── example/
        └── files/
            └── nginx-conf/
                ├── nginx.conf
                ├── mime.types
                └── conf.d/
                    └── some.conf

Vrste izvora

Potpuni popis podržanih vrsta resursa možete pronaći ovdje u dokumentaciji, ovdje ću opisati pet osnovnih tipova, koji su u mojoj praksi dovoljni za rješavanje većine problema.

file

Upravlja datotekama, direktorijima, simboličkim vezama, njihovim sadržajem i pravima pristupa.

Mogućnosti:

  • naziv resursa — put do datoteke (nije obavezno)
  • put — put do datoteke (ako nije naveden u nazivu)
  • osigura - Vrsta datoteke:
    • absent - izbrisati datoteku
    • present — mora postojati datoteka bilo koje vrste (ako nema datoteke, stvorit će se obična datoteka)
    • file - obična datoteka
    • directory - imenik
    • link - simbolička veza
  • sadržaj — sadržaj datoteke (prikladno samo za obične datoteke, ne može se koristiti zajedno s izvor ili meta)
  • izvor — poveznica na stazu s koje želite kopirati sadržaj datoteke (ne može se koristiti zajedno s sadržaj ili meta). Može se navesti kao URI sa shemom puppet: (tada će se koristiti datoteke s poslužitelja lutaka), te sa shemom http: (Nadam se da je jasno što će se dogoditi u ovom slučaju), pa čak i s dijagramom file: ili kao apsolutni put bez sheme (tada će se koristiti datoteka iz lokalnog FS-a na čvoru)
  • meta — kamo simbolički link treba pokazivati ​​(ne može se koristiti zajedno s sadržaj ili izvor)
  • vlasnik — korisnik koji bi trebao posjedovati datoteku
  • skupina — skupina kojoj datoteka treba pripadati
  • način — dopuštenja datoteke (kao niz)
  • povratiti - omogućuje rekurzivnu obradu imenika
  • čistka - omogućuje brisanje datoteka koje nisu opisane u Lutki
  • sila - omogućuje brisanje imenika koji nisu opisani u Puppetu

paket

Instalira i uklanja pakete. Može rukovati obavijestima - ponovno instalira paket ako je parametar naveden reinstaliraj_pri_osvježavanju.

Mogućnosti:

  • naziv resursa — naziv paketa (nije obavezno)
  • ime — naziv paketa (ako nije navedeno u nazivu)
  • davatelj — upravitelj paketa za korištenje
  • osigura — željeno stanje paketa:
    • present, installed - bilo koja instalirana verzija
    • latest - najnovija verzija instalirana
    • absent - izbrisano (apt-get remove)
    • purged — izbrisano zajedno s konfiguracijskim datotekama (apt-get purge)
    • held - verzija paketa je zaključana (apt-mark hold)
    • любая другая строка — navedena verzija je instalirana
  • reinstaliraj_pri_osvježavanju - ako true, nakon primitka obavijesti paket će biti ponovno instaliran. Korisno za distribucije temeljene na izvornom kodu, gdje može biti potrebna ponovna izrada paketa prilikom promjene parametara izgradnje. Zadano false.

usluga

Upravlja uslugama. Može obraditi obavijesti - ponovno pokreće uslugu.

Mogućnosti:

  • naziv resursa — usluga kojom se upravlja (opcionalno)
  • ime — uslugu kojom treba upravljati (ako nije navedena u nazivu)
  • osigura — željeno stanje usluge:
    • running - pokrenut
    • stopped - zaustavljeno
  • omogućiti — kontrolira mogućnost pokretanja usluge:
    • true — automatsko pokretanje je omogućeno (systemctl enable)
    • mask - prikriveno (systemctl mask)
    • false — automatsko pokretanje je onemogućeno (systemctl disable)
  • restart - naredba za ponovno pokretanje usluge
  • status — naredba za provjeru statusa usluge
  • ima ponovno pokretanje — označava podržava li inicijalni skript usluge ponovno pokretanje. Ako false a parametar je naveden restart — koristi se vrijednost ovog parametra. Ako false i parametar restart nije navedeno - usluga je zaustavljena i pokrenuta za ponovno pokretanje (ali systemd koristi naredbu systemctl restart).
  • ima status — označava podržava li inicijalni skript usluge naredbu status, ako false, tada se koristi vrijednost parametra status. Zadano true.

exec

Izvodi vanjske naredbe. Ako ne navedete parametre stvara, samo ako, osim ako ili osvježeno, naredba će se pokrenuti svaki put kada se pokrene Puppet. Može obraditi obavijesti - izvršava naredbu.

Mogućnosti:

  • naziv resursa — naredba koju treba izvršiti (neobavezno)
  • naredba — naredba koju treba izvršiti (ako nije navedena u nazivu)
  • put — staze u kojima se traži izvršna datoteka
  • samo ako — ako je naredba navedena u ovom parametru završena s nultim povratnim kodom, izvršit će se glavna naredba
  • osim ako — ako je naredba navedena u ovom parametru završena s povratnim kodom koji nije nula, izvršit će se glavna naredba
  • stvara — ako datoteka navedena u ovom parametru ne postoji, izvršit će se glavna naredba
  • osvježeno - ako true, tada će se naredba pokrenuti samo kada ovaj exec primi obavijest od drugih izvora
  • cwd — direktorij iz kojeg se pokreće naredba
  • korisnik — korisnik od kojeg treba pokrenuti naredbu
  • davatelj - kako pokrenuti naredbu:
    • POSIX — podređeni proces se jednostavno kreira, svakako navedite put
    • školjka - naredba se pokreće u ljusci /bin/sh, možda nije navedeno put, možete koristiti globbing, cijevi i druge značajke školjke. Obično se otkriva automatski ako postoje posebni znakovi (|, ;, &&, || itd.).

cron

Kontrolira cronjobs.

Mogućnosti:

  • naziv resursa - samo neka vrsta identifikatora
  • osigura — stanje krunskog posla:
    • present - stvoriti ako ne postoji
    • absent - izbrisati ako postoji
  • naredba - koju naredbu pokrenuti
  • okolina — u kojem okruženju pokrenuti naredbu (popis varijabli okruženja i njihovih vrijednosti putem =)
  • korisnik — od kojeg korisnika pokrenuti naredbu
  • minuta, sat, radni dan, mjesec, Mjesec dan — kada pokrenuti cron. Ako bilo koji od ovih atributa nije naveden, njegova će vrijednost u crontabu biti *.

U Puppet 6.0 cron kao da izvaditi iz kutije u puppetserveru, tako da nema dokumentacije na općem mjestu. Ali on je u kutiji u puppet-agentu, tako da ga nema potrebe zasebno instalirati. Možete vidjeti dokumentaciju za to u dokumentaciji za petu verziju LutkeIli na GitHubu.

O resursima općenito

Zahtjevi za jedinstvenost izvora

Najčešća pogreška s kojom se susrećemo je Duplikat deklaracije. Ova se pogreška pojavljuje kada se dva ili više resursa iste vrste s istim imenom pojave u direktoriju.

Zato ću opet napisati: manifesti za isti čvor ne bi trebali sadržavati resurse iste vrste s istim naslovom!

Ponekad je potrebno instalirati pakete s istim nazivom, ali s različitim upraviteljima paketa. U ovom slučaju morate koristiti parametar nameda biste izbjegli grešku:

package { 'ruby-mysql':
  ensure   => installed,
  name     => 'mysql',
  provider => 'gem',
}
package { 'python-mysql':
  ensure   => installed,
  name     => 'mysql',
  provider => 'pip',
}

Druge vrste resursa imaju slične opcije za izbjegavanje dupliciranja - name у usluga, command у exec, i tako dalje.

Metaparametri

Svaki tip resursa ima neke posebne parametre, bez obzira na njegovu prirodu.

Potpuni popis meta parametara u dokumentaciji Puppet.

Uži izbor:

  • zahtijevati — ovaj parametar označava o kojim resursima ovisi ovaj resurs.
  • prije - Ovaj parametar specificira koji resursi ovise o ovom resursu.
  • pretplatiti — ovaj parametar navodi iz kojih resursa ovaj resurs prima obavijesti.
  • obavijestiti — Ovaj parametar određuje koji resursi primaju obavijesti od ovog resursa.

Svi navedeni metaparametri prihvaćaju ili jednu vezu resursa ili niz veza u uglatim zagradama.

Linkovi na resurse

Veza na izvor je jednostavno spominjanje izvora. Uglavnom se koriste za označavanje ovisnosti. Upućivanje na nepostojeći izvor uzrokovat će pogrešku kompilacije.

Sintaksa poveznice je sljedeća: tip resursa s velikim slovom (ako naziv tipa sadrži dvotočke, tada je svaki dio imena između dvotočaka velikim slovom), zatim naziv resursa u uglatim zagradama (slučaj naziva ne mijenja se!). Ne smije biti razmaka, uglate zagrade pišu se odmah iza naziva tipa.

Primjer:

file { '/file1': ensure => present }
file { '/file2':
  ensure => directory,
  before => File['/file1'],
}
file { '/file3': ensure => absent }
File['/file1'] -> File['/file3']

Ovisnosti i obavijesti

Dokumentacija ovdje.

Kao što je ranije rečeno, jednostavne ovisnosti između resursa su tranzitivne. Usput, budite oprezni pri dodavanju ovisnosti - možete stvoriti cikličke ovisnosti, što će uzrokovati pogrešku kompilacije.

Za razliku od ovisnosti, obavijesti nisu prolazne. Za obavijesti vrijede sljedeća pravila:

  • Ako resurs primi obavijest, ažurira se. Radnje ažuriranja ovise o vrsti resursa − exec izvodi naredbu, usluga ponovno pokreće uslugu, paket ponovno instalira paket. Ako resurs nema definiranu akciju ažuriranja, ništa se ne događa.
  • Tijekom jednog pokretanja Puppeta, resurs se ne ažurira više od jednom. To je moguće jer obavijesti uključuju ovisnosti, a grafikon ovisnosti ne sadrži cikluse.
  • Ako Puppet promijeni stanje resursa, resurs šalje obavijesti svim resursima koji su na njega pretplaćeni.
  • Ako je resurs ažuriran, on šalje obavijesti svim resursima koji su na njega pretplaćeni.

Rukovanje neodređenim parametrima

U pravilu, ako neki parametar resursa nema zadanu vrijednost i taj parametar nije naveden u manifestu, tada Puppet neće promijeniti ovo svojstvo za odgovarajući resurs na čvoru. Na primjer, ako je resurs vrste file parametar nije naveden owner, tada Puppet neće promijeniti vlasnika odgovarajuće datoteke.

Uvod u klase, varijable i definicije

Pretpostavimo da imamo nekoliko čvorova koji imaju isti dio konfiguracije, ali postoje i razlike - inače bismo mogli sve opisati u jednom bloku node {}. Naravno, možete jednostavno kopirati identične dijelove konfiguracije, ali općenito je to loše rješenje - konfiguracija raste, a ako promijenite opći dio konfiguracije, morat ćete uređivati ​​istu stvar na mnogo mjesta. U isto vrijeme, lako je pogriješiti, a općenito, DRY (ne ponavljajte se) princip je izmišljen s razlogom.

Za rješavanje ovog problema postoji takav dizajn kao razred.

Nastava

Klasa je imenovani blok koda kosilice. Za ponovnu upotrebu koda potrebne su klase.

Prvo je potrebno opisati klasu. Sam opis nigdje ne dodaje nikakve resurse. Klasa je opisana u manifestima:

# Описание класса начинается с ключевого слова class и его названия.
# Дальше идёт тело класса в фигурных скобках.
class example_class {
    ...
}

Nakon ovoga klasa se može koristiti:

# первый вариант использования — в стиле ресурса с типом class
class { 'example_class': }
# второй вариант использования — с помощью функции include
include example_class
# про отличие этих двух вариантов будет рассказано дальше

Primjer iz prethodnog zadatka - premjestimo instalaciju i konfiguraciju nginxa u klasu:

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
}

varijable

Klasa iz prethodnog primjera nije nimalo fleksibilna jer uvijek donosi istu nginx konfiguraciju. Napravimo put do konfiguracijske varijable, a onda se ova klasa može koristiti za instaliranje nginxa s bilo kojom konfiguracijom.

To se može učiniti pomoću varijabli.

Pažnja: varijable u Puppetu su nepromjenjive!

Osim toga, varijabli se može pristupiti tek nakon što je deklarirana, inače će vrijednost varijable biti undef.

Primjer rada s varijablama:

# создание переменных
$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, a varijable, prema tome, imaju područje vidljivosti: Varijabla s istim imenom može se definirati u različitim imenskim prostorima. Prilikom rješavanja vrijednosti varijable, varijabla se pretražuje u trenutnom imenskom prostoru, zatim u okružujućem imenskom prostoru, i tako dalje.

Primjeri prostora imena:

  • globalno - varijable izvan opisa klase ili čvora idu tamo;
  • prostor imena čvora u opisu čvora;
  • imenski prostor klase u opisu klase.

Kako biste izbjegli dvosmislenost pri pristupanju varijabli, možete odrediti prostor imena u nazivu varijable:

# переменная без пространства имён
$var
# переменная в глобальном пространстве имён
$::var
# переменная в пространстве имён класса
$classname::var
$::classname::var

Složimo se da put do nginx konfiguracije leži u varijabli $nginx_conf_source. Tada će klasa izgledati ovako:

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
}

Međutim, navedeni primjer je loš jer postoji neko “tajno znanje” da se negdje unutar klase koristi varijabla s takvim i takvim imenom. Mnogo je ispravnije ovo znanje učiniti općim - klase mogu imati parametre.

Parametri klase su varijable u prostoru imena klase, navedene su u zaglavlju klase i mogu se koristiti kao redovne varijable u tijelu klase. Vrijednosti parametara navedene su pri korištenju klase u manifestu.

Parametar se može postaviti na zadanu vrijednost. Ako parametar nema zadanu vrijednost i vrijednost nije postavljena kada se koristi, to će uzrokovati pogrešku kompilacije.

Parametrizirajmo klasu iz gornjeg primjera i dodajmo dva parametra: prvi, obavezni, je put do konfiguracije, a drugi, opcijski, je naziv paketa s nginxom (u Debianu, na primjer, postoje paketi 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',   # задаём параметры класса точно так же, как параметры для других ресурсов
  }
}

U Puppetu se varijable upisuju. Jesti mnoge vrste podataka. Tipovi podataka obično se koriste za provjeru vrijednosti parametara proslijeđenih klasama i definicijama. Ako proslijeđeni parametar ne odgovara navedenom tipu, pojavit će se pogreška kompilacije.

Tip se piše neposredno prije naziva parametra:

class example (
  String $param1,
  Integer $param2,
  Array $param3,
  Hash $param4,
  Hash[String, String] $param5,
) {
  ...
}

Klase: uključite classname naspram class{'classname':}

Svaka klasa je resurs tipa razred. Kao i kod bilo koje druge vrste resursa, ne mogu postojati dvije instance iste klase na istom čvoru.

Ako pokušate dodati klasu u isti čvor dva puta koristeći class { 'classname':} (bez razlike, s različitim ili identičnim parametrima), doći će do pogreške kompilacije. Ali ako koristite klasu u stilu resursa, možete odmah eksplicitno postaviti sve njene parametre u manifestu.

Međutim, ako koristite include, tada se klasa može dodavati onoliko puta koliko želite. Činjenica je da include je idempotentna funkcija koja provjerava je li klasa dodana u imenik. Ako klasa nije u imeniku, dodaje je, a ako već postoji, ne radi ništa. Ali u slučaju korištenja include ne možete postaviti parametre klase tijekom deklaracije klase - svi potrebni parametri moraju biti postavljeni u vanjskom izvoru podataka - Hiera ili ENC. O njima ćemo govoriti u sljedećem članku.

Definira

Kao što je rečeno u prethodnom bloku, ista klasa ne može biti prisutna na čvoru više od jednom. Međutim, u nekim slučajevima morate moći koristiti isti blok koda s različitim parametrima na istom čvoru. Drugim riječima, postoji potreba za vlastitom vrstom resursa.

Na primjer, da bismo instalirali PHP modul, u Avitu radimo sljedeće:

  1. Instalirajte paket s ovim modulom.
  2. Kreirajmo konfiguracijsku datoteku za ovaj modul.
  3. Stvaramo simboličku vezu na konfiguraciju za php-fpm.
  4. Stvaramo simboličku vezu na konfiguraciju za php cli.

U takvim slučajevima, dizajn kao što je definirati (definirati, definirani tip, definirani tip izvora). Define je sličan klasi, ali postoje razlike: prvo, svaki Define je tip resursa, a ne resurs; drugo, svaka definicija ima implicitni parametar $title, gdje ide ime resursa kada se deklarira. Kao iu slučaju klasa, definicija se prvo mora opisati, nakon čega se može koristiti.

Pojednostavljeni primjer s modulom za 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' }
}

Najlakši način da uhvatite pogrešku Duplicirane deklaracije je u Define. To se događa ako definicija ima resurs s konstantnim imenom, a postoje dvije ili više instanci ove definicije na nekom čvoru.

Lako se zaštititi od ovoga: svi resursi unutar definicije moraju imati naziv ovisno o $title. Alternativa je idempotentno zbrajanje resursa, u najjednostavnijem slučaju dovoljno je resurse zajedničke svim instancama definicije premjestiti u posebnu klasu i tu klasu uključiti u definiciju - funkciju include idempotentan.

Postoje i drugi načini za postizanje idempotencije prilikom dodavanja resursa, naime pomoću funkcija defined и ensure_resources, ali o tome ću vam pričati u sljedećoj epizodi.

Ovisnosti i obavijesti za klase i definicije

Klase i definicije dodaju sljedeća pravila za rukovanje ovisnostima i obavijestima:

  • ovisnost o klasi/definiciji dodaje ovisnosti o svim resursima klase/define;
  • ovisnost klase/define dodaje ovisnosti svim resursima klase/define;
  • obavijest klase/define obavještava sve resurse klase/define;
  • class/define pretplata pretplaćuje se na sve resurse class/define.

Uvjetne naredbe i selektori

Dokumentacija ovdje.

if

Ovdje je jednostavno:

if ВЫРАЖЕНИЕ1 {
  ...
} elsif ВЫРАЖЕНИЕ2 {
  ...
} else {
  ...
}

osim ako

osim ako je if obrnuto: blok koda će se izvršiti ako je izraz lažan.

unless ВЫРАЖЕНИЕ {
  ...
}

slučaj

Ni ovdje nema ništa komplicirano. Kao vrijednosti možete koristiti regularne vrijednosti (stringove, brojeve itd.), regularne izraze i vrste podataka.

case ВЫРАЖЕНИЕ {
  ЗНАЧЕНИЕ1: { ... }
  ЗНАЧЕНИЕ2, ЗНАЧЕНИЕ3: { ... }
  default: { ... }
}

Selektori

Selektor je jezična konstrukcija slična case, ali umjesto izvršavanja bloka koda, vraća vrijednost.

$var = $othervar ? { 'val1' => 1, 'val2' => 2, default => 3 }

moduli

Kada je konfiguracija mala, lako se može zadržati u jednom manifestu. Ali što više konfiguracija opisujemo, to više klasa i čvorova ima u manifestu, on raste i postaje nezgodan za rad.

Osim toga, postoji problem ponovne upotrebe koda - kada je sav kod u jednom manifestu, teško je taj kod dijeliti s drugima. Za rješavanje ova dva problema, Puppet ima entitet koji se zove moduli.

moduli - to su skupovi klasa, definicija i drugih Puppet entiteta smještenih u poseban direktorij. Drugim riječima, modul je neovisni dio lutkarske logike. Na primjer, može postojati modul za rad s nginxom, a on će sadržavati ono i samo ono što je potrebno za rad s nginxom, ili može postojati modul za rad s PHP-om i tako dalje.

Moduli su verzionirani, a podržane su i međusobne ovisnosti modula. Postoji otvoreno spremište modula - Kovačnica lutaka.

Na lutkarskom poslužitelju moduli se nalaze u poddirektoriju modula u korijenskom direktoriju. Unutar svakog modula postoji standardna shema imenika - manifesti, datoteke, predlošci, lib, i tako dalje.

Struktura datoteke u modulu

Korijen modula može sadržavati sljedeće direktorije s opisnim nazivima:

  • manifests - sadrži manifeste
  • files - sadrži datoteke
  • templates - sadrži predloške
  • lib — sadrži Ruby kod

Ovo nije potpuni popis direktorija i datoteka, ali je za sada dovoljan za ovaj članak.

Nazivi resursa i nazivi datoteka u modulu

Dokumentacija ovdje.

Resursi (klase, definicije) u modulu ne mogu se imenovati kako god želite. Osim toga, postoji izravna korespondencija između naziva izvora i naziva datoteke u kojoj će Puppet tražiti opis tog izvora. Ako prekršite pravila imenovanja, Puppet jednostavno neće pronaći opis resursa i dobit ćete pogrešku kompilacije.

Pravila su jednostavna:

  • Svi resursi u modulu moraju biti u prostoru naziva modula. Ako se modul pozove foo, onda bi svi resursi u njemu trebali biti imenovani foo::<anything>, ili samo foo.
  • Resurs s nazivom modula mora biti u datoteci init.pp.
  • Za ostale resurse shema imenovanja datoteke je sljedeća:
    • prefiks s nazivom modula se odbacuje
    • sve dvotočke, ako ih ima, zamjenjuju se kosim crtama
    • dodaje se proširenje .pp

Pokazat ću primjerom. Recimo da pišem modul nginx. Sadrži sljedeće resurse:

  • razred nginx opisano u manifestu init.pp;
  • razred nginx::service opisano u manifestu service.pp;
  • definirati nginx::server opisano u manifestu server.pp;
  • definirati nginx::server::location opisano u manifestu server/location.pp.

Predlošci

Sigurno i sami znate što su predlošci, neću ih ovdje detaljno opisivati. Ali ostavit ću ga za svaki slučaj link na Wikipediju.

Kako koristiti predloške: Značenje predloška može se proširiti pomoću funkcije template, kojem se prosljeđuje put do predloška. Za resurse vrste file koristi se zajedno s parametrom content. Na primjer, ovako:

file { '/tmp/example': content => template('modulename/templatename.erb')

Prikaz staze <modulename>/<filename> podrazumijeva datoteku <rootdir>/modules/<modulename>/templates/<filename>.

Osim toga, postoji i funkcija inline_template — kao ulaz prima tekst predloška, ​​a ne naziv datoteke.

Unutar predložaka možete koristiti sve varijable Puppet u trenutnom opsegu.

Puppet podržava predloške u ERB i EPP formatu:

Ukratko o ERB-u

Kontrolne strukture:

  • <%= ВЫРАЖЕНИЕ %> — unesite vrijednost izraza
  • <% ВЫРАЖЕНИЕ %> — izračunati vrijednost izraza (bez umetanja). Uvjetne naredbe (if) i petlje (each) obično idu ovdje.
  • <%# КОММЕНТАРИЙ %>

Izrazi u ERB-u napisani su u Rubyju (ERB je zapravo Embedded Ruby).

Da biste pristupili varijablama iz manifesta, morate dodati @ na ime varijable. Da biste uklonili prijelom retka koji se pojavljuje nakon kontrolne konstrukcije, trebate upotrijebiti završnu oznaku -%>.

Primjer korištenja predloška

Recimo da pišem modul za kontrolu ZooKeepera. Klasa odgovorna za kreiranje konfiguracije izgleda otprilike ovako:

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'),
  }
}

I odgovarajući predložak zoo.cfg.erb - Dakle:

<% 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 -%>

Činjenice i ugrađene varijable

Često određeni dio konfiguracije ovisi o tome što se trenutno događa na čvoru. Na primjer, ovisno o izdanju Debiana, trebate instalirati jednu ili drugu verziju paketa. Sve to možete pratiti ručno, prepisujući manifeste ako se čvorovi promijene. Ali ovo nije ozbiljan pristup; automatizacija je puno bolja.

Za dobivanje informacija o čvorovima, Puppet ima mehanizam koji se zove činjenice. činjenicama - ovo su informacije o čvoru, dostupne u manifestima u obliku običnih varijabli u globalnom imenskom prostoru. Na primjer, naziv glavnog računala, verzija operativnog sustava, arhitektura procesora, popis korisnika, popis mrežnih sučelja i njihovih adresa i još mnogo, mnogo više. Činjenice su dostupne u manifestima i predlošcima kao regularne varijable.

Primjer rada s činjenicama:

notify { "Running OS ${facts['os']['name']} version ${facts['os']['release']['full']}": }
# ресурс типа notify просто выводит сообщение в лог

Formalno gledano, činjenica ima ime (niz) i vrijednost (dostupni su različiti tipovi: nizovi, nizovi, rječnici). Jesti skup ugrađenih činjenica. Možete i sami napisati. Opisani su sakupljači činjenica poput funkcija u Rubyjubilo kao izvršne datoteke. Činjenice se također mogu prezentirati u obliku tekstualne datoteke s podacima na čvorovima.

Tijekom rada, lutkarski agent prvo kopira sve dostupne kolektore činjenica s pappetservera na čvor, nakon čega ih pokreće i šalje prikupljene činjenice na poslužitelj; Nakon toga poslužitelj počinje sastavljati katalog.

Činjenice u obliku izvršnih datoteka

Takve se činjenice smještaju u module u imeniku facts.d. Naravno, datoteke moraju biti izvršne. Kada se pokreću, moraju ispisati informacije na standardni izlaz u formatu YAML ili ključ=vrijednost.

Ne zaboravite da se činjenice odnose na sve čvorove koje kontrolira pojedinačni poslužitelj na kojem je postavljen vaš modul. Stoga u skripti provjerite ima li sustav sve programe i datoteke potrebne za rad vašeg fakta.

#!/bin/sh
echo "testfact=success"
#!/bin/sh
echo '{"testyamlfact":"success"}'

Činjenice o rubinu

Takve se činjenice smještaju u module u 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
end

Činjenice teksta

Takve se činjenice postavljaju na čvorove u imeniku /etc/facter/facts.d u starom Puppetu ili /etc/puppetlabs/facts.d u novoj Lutki.

examplefact=examplevalue
---
examplefact2: examplevalue2
anotherfact: anothervalue

Doći do činjenica

Postoje dva načina da se pristupi činjenicama:

  • kroz rječnik $facts: $facts['fqdn'];
  • koristeći naziv činjenice kao naziv varijable: $fqdn.

Najbolje je koristiti rječnik $facts, ili još bolje, označite globalni imenski prostor ($::facts).

Ovdje je relevantni odjeljak dokumentacije.

Ugrađene varijable

Osim činjenica, postoji i neke varijable, dostupan u globalnom imenskom prostoru.

  • pouzdane činjenice — varijable koje su preuzete iz certifikata klijenta (budući da se certifikat obično izdaje na poppet poslužitelju, agent ne može samo uzeti i promijeniti njegov certifikat, pa su varijable “pouzdane”): naziv certifikata, naziv hosta i domene, proširenja iz certifikata.
  • činjenice o poslužitelju —varijable koje se odnose na informacije o poslužitelju—verzija, naziv, IP adresa poslužitelja, okruženje.
  • činjenice agenta — varijable dodane izravno od strane lutkarskog agenta, a ne od faktora — ime certifikata, verzija agenta, lutkarska verzija.
  • glavne varijable - Pappetmaster varijable (sic!). Otprilike je isto kao u činjenice o poslužitelju, plus dostupne su vrijednosti konfiguracijskih parametara.
  • varijable prevoditelja — varijable prevoditelja koje se razlikuju u svakom opsegu: naziv trenutnog modula i naziv modula u kojem je pristupljeno trenutnom objektu. Mogu se koristiti, na primjer, za provjeru da se vaši privatni tečajevi ne koriste izravno iz drugih modula.

Dodatak 1: kako sve ovo pokrenuti i otkloniti pogreške?

Članak je sadržavao mnogo primjera lutka koda, ali nam uopće nije rekao kako pokrenuti ovaj kod. Pa ispravljam se.

Za pokretanje Puppeta dovoljan je agent, ali u većini slučajeva trebat će vam i poslužitelj.

zastupnik

Barem od verzije XNUMX, paketi lutkarskog agenta iz službeni repozitorij Puppetlabs sadrže sve ovisnosti (ruby i odgovarajuće dragulje), tako da nema poteškoća s instalacijom (govorim o distribucijama temeljenim na Debianu - ne koristimo distribucije temeljene na RPM-u).

U najjednostavnijem slučaju, za korištenje lutkarske konfiguracije, dovoljno je pokrenuti agenta u načinu rada bez poslužitelja: pod uvjetom da je lutkarski kod kopiran u čvor, pokrenite puppet apply <путь к манифесту>:

atikhonov@atikhonov ~/puppet-test $ cat helloworld.pp 
node default {
    notify { 'Hello world!': }
}
atikhonov@atikhonov ~/puppet-test $ puppet apply helloworld.pp 
Notice: Compiled catalog for atikhonov.localdomain in environment production in 0.01 seconds
Notice: Hello world!
Notice: /Stage[main]/Main/Node[default]/Notify[Hello world!]/message: defined 'message' as 'Hello world!'
Notice: Applied catalog in 0.01 seconds

Bolje je, naravno, postaviti poslužitelj i pokrenuti agente na čvorovima u demonskom načinu rada - tada će svakih pola sata primijeniti konfiguraciju preuzetu s poslužitelja.

Možete imitirati push model rada – idite na čvor koji vas zanima i počnite sudo puppet agent -t. Ključ -t (--test) zapravo uključuje nekoliko opcija koje se mogu pojedinačno uključiti. Ove opcije uključuju sljedeće:

  • ne pokreću se u daemon modu (prema zadanim postavkama agent se pokreće u daemon modu);
  • isključiti nakon primjene kataloga (prema zadanim postavkama, agent će nastaviti raditi i primjenjivati ​​konfiguraciju jednom svakih pola sata);
  • napisati detaljan dnevnik rada;
  • prikazati promjene u datotekama.

Agent ima način rada bez promjena - možete ga koristiti kada niste sigurni da ste napisali ispravnu konfiguraciju i želite provjeriti što će točno agent promijeniti tijekom rada. Ovaj način je omogućen parametrom --noop na naredbenoj liniji: sudo puppet agent -t --noop.

Osim toga, možete omogućiti dnevnik otklanjanja pogrešaka rada - u njemu lutka piše o svim radnjama koje izvodi: o resursu koji trenutno obrađuje, o parametrima ovog resursa, o tome koje programe pokreće. Naravno, ovo je parametar --debug.

Server

U ovom članku neću razmatrati potpunu postavku pappetservera i implementaciju koda na njega; samo ću reći da postoji potpuno funkcionalna verzija poslužitelja koja ne zahtijeva dodatnu konfiguraciju za rad s malim brojem čvorova (recimo, do stotinu). Veći broj čvorova zahtijevat će podešavanje - prema zadanim postavkama, puppetserver ne pokreće više od četiri radnika, za bolje performanse morate povećati njihov broj i ne zaboravite povećati ograničenja memorije, inače će poslužitelj većinu vremena sakupljati smeće.

Implementacija koda - ako vam treba brzo i jednostavno, pogledajte (na r10k)[https://github.com/puppetlabs/r10k], za male instalacije bi to trebalo biti sasvim dovoljno.

Dodatak 2: Smjernice za kodiranje

  1. Smjestite svu logiku u klase i definicije.
  2. Držite klase i definicije u modulima, a ne u manifestima koji opisuju čvorove.
  3. Koristite se činjenicama.
  4. Nemojte stvarati if-ove na temelju naziva hostova.
  5. Slobodno dodajte parametre za klase i definicije - ovo je bolje od implicitne logike skrivene u tijelu klase/define.

Objasnit ću zašto to preporučujem u sljedećem članku.

Zaključak

Završimo s uvodom. U sljedećem članku ću vam reći o Hieri, ENC i PuppetDB.

U anketi mogu sudjelovati samo registrirani korisnici. Prijaviti se, molim.

Zapravo, ima puno više materijala - mogu pisati članke o sljedećim temama, glasovati o onome o čemu biste bili zainteresirani čitati:

  • 59,1%Napredne marionetske konstrukcije - neka sranja sljedeće razine: petlje, mapiranje i drugi lambda izrazi, sakupljači resursa, eksportirani resursi i komunikacija između hostova putem lutke, oznake, pružatelji usluga, apstraktni tipovi podataka.13
  • 31,8%“Ja sam mamin admin” ili kako smo se mi u Avitu sprijateljili s nekoliko poppet servera različitih verzija, te u principu onaj dio o administriranju poppet servera.7
  • 81,8%Kako pišemo kod lutke: instrumentacija, dokumentacija, testiranje, CI/CD.18

Glasovalo je 22 korisnika. Suzdržano je bilo 9 korisnika.

Izvor: www.habr.com