Sissejuhatus Puppetisse

Puppet on konfigureerimise haldamise süsteem. Seda kasutatakse hostide viimiseks soovitud olekusse ja selle oleku hoidmiseks.

Olen Puppetiga töötanud üle viie aasta. See tekst on põhimõtteliselt tõlgitud ja ümber korraldatud kokkuvõte olulistest punktidest ametlikust dokumentatsioonist, mis aitab algajatel kiiresti Puppetisse süveneda.

Sissejuhatus Puppetisse

Põhiteave

Puppeti töö schema on kliendi-serveri mudel, kuigi toetatakse ka serverita tööversiooni piiratud funktsionaalsusega.

Kasutatakse pull-mudelit: vaikimisi pöörduvad kliendid iga poole tunni tagant serveri poole konfiguratsiooni järele ja rakendavad seda. Kui olete töötanud Ansible'iga, siis seal kasutatakse teistsugust push-mudelit: administraator käivitab konfiguratsiooni rakendamise protsessi, kliendid ei rakenda midagi iseenesest.

Võrgusuhtluses kasutatakse kahepoolses TLS-krüptimist: serveril ja kliendil on oma privaatsed võtmed ja vastavad sertifikaadid. Tavaline on, et server väljastab sertifikaate klientidele, kuid põhimõtteliselt on võimalik kasutada ka välist CA-d.

Tuttavaks saamine manifestidega

Puppet'i terminoloogias pappet-serverisse ühendatakse nodid (nodes). Nodide konfiguratsioon kirjutatakse manifestides spetsiaalses programmeerimiskeeles — Puppet DSL.

Puppet DSL on deklaratiivne keel. Sellel kirjeldatakse soovitud nodi seisundit, määrates välja üksikute ressursside, näiteks:

  • Fail eksisteerib ja sellel on kindel sisu.
  • Pakett on paigaldatud.
  • Teenust on käivitatud.

Ressursid võivad olla omavahel seotud:

  • On sõltuvusi, mis mõjutavad ressursside rakendamist järjekorda.
    Näiteks, "esmajärjekorras paigalda pakett, seejärel muuda konfiguratsioonifaili, pärast seda käivita teenus.”
  • On teated — kui ressurss muutub, saadetakse teadet sellele allkirjastatud ressurssidele.
    Näiteks, kui muutub konfiguratsioonifail, saab teenust automaatselt taaskäivitada.

Lisaks on Puppet DSL-is funktsioonid ja muutujad, samuti tingimuslikud operaatorid ja valijad. Toetatakse ka erinevaid mallimehhanisme — EPP ja ERB.

Puppet on kirjutatud Ruby keeles, seega on paljud konstruktsioonid ja terminid sealt pärit. Ruby võimaldab Puppetit laiendada — kirjutada keerulist loogikat, uusi ressursitüüpe, funktsioone.

Puppet'i töö ajal kompileeritakse iga konkreetse node'i jaoks manifest serveris katalooge. Kataloog — see on loetelu ressurssidest ja nende omavahelistest seostest pärast funktsioonide, muutujate väärtuste ja tingimuslausete avamist.

Süntaks ja koodistiil

Siin on ametliku dokumentatsiooni osad, mis aitavad süntaksiga tutvuda, kui antud näidised ei ole piisavad:

Siin on näide sellest, milline manifest välja näeb:

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

Vahed ja reaüleminekud ei ole manifesti kohustuslik osa, kuid on soovitatav stiilijuhend. Lühikokkuvõte:

  • Kaks vahekohta, tabulatsioonid ei tohi olla.
  • Aroomi sümbolid peavad olema eraldatud vahega, kaksteist ei eraldata vahega.
  • Koma iga parameetri, sealhulgas viimase järel. Iga parameeter on eraldi real. Erand kehtib juhtumite jaoks, kus ei ole parameetreid või on üks parameeter: saab kirjutada ühel real ja ilma komata (st. resource { 'title': } ja resource { 'title': param => value }).
  • Parameetrite nooled peavad olema ühel tasemel.
  • Ressursside omavahelise seose nooled kirjutatakse nende ette.

Failide asukoht Puppet-serveris

Edasiste selgituste jaoks tutvustan mõistet „juurkataloog”. Juurkataloog on kaust, kus asub Puppet-i konfiguratsioon konkreetse sõlme jaoks.

Juurkataloog varieerub sõltuvalt Puppet-i versioonist ja keskkondade kasutamisest. Keskkonnad on iseseisvad konfiguratsioonide komplektid, mis asuvad eraldi kataloogides. Neid kasutatakse tavaliselt koos gitiga, sellisel juhul luuakse keskkonnad git harudest. Vastavalt on iga sõlm ühes või teises keskkonnas. See seadistatakse ise sõlmes või ENC-s, millest räägin järgmisel korral.

  • Kolmandas versioonis („vana Puppet”) oli põhikataloog /etc/puppet. Keskkondade kasutamine on valikuline – näiteks ei kasuta me neid vanas Puppetis. Kui keskkondi kasutatakse, hoitakse neid tavaliselt /etc/puppet/environments, juurkataloogiks on keskkonna kataloog. Kui keskkondi ei kasutata, on juurkataloogiks põhikataloog.
  • Alates neljandast versioonist („uus Puppet”) on keskkondade kasutamine muutunud kohustuslikuks ja põhikataloog on viidud /etc/puppetlabs/code. Vastavalt sellele hoitakse keskkondi /etc/puppetlabs/code/environments, juurkataloog — keskkonna kataloog.

Juurtaskataloogis peab olema alamkataloog manifests, kus asub üks või mitu manifesti, mis kirjeldavad node'e. Lisaks peab seal olema alamkataloog modules, kus asuvad moodulid. Mis on moodulid, selle kohta räägin natuke hiljem. Samuti võib vanas Papetis olla alamkataloog files, kus asuvad erinevad failid, mida me kopeerime node'idele. Uues Papetis on kõik failid aga moodulitesse viidud.

Manifesti failid on laiendiga .pp.

Mõned lahingu näited

Node'i ja selle ressursi kirjeldus

Node'is server1.testdomain peab olema loodud fail /etc/issue sisaldusega Debian GNU/Linux n l. Fail peab kuuluma kasutajale ja grupile root, juur- ja grupi õigused peavad olema 644.

Kirjutame manifesti:

node 'server1.testdomain' {   # server1.testdomain'ile seotud konfiguratsiooni plokk
    file { '/etc/issue':   # kirjeldame faili /etc/issue
        ensure  => present,   # see fail peab olemas olema
        content => 'Debian GNU/Linux n l',   # selle sisu peab olema selline
        owner   => root,   # omanik
        group   => root,   # grupi omanik
        mode    => '0644',   # faili õigused. Need on määratud stringina (üksteise vahel), sest muidu tõlgendatakse algusega 0 olevat numbrit kaheksandana, ja kõik läheb valesti
    }
}

Resursside seosed nodis

Node'is server2.testdomain peab olema käimas nginx, mis töötab ettevalmistatud konfiguratsiooniga.

Teeme ülesande dekompositsiooni:

  • Peab olema installitud pakett nginx.
  • Peab olema kopeeritud konfiguratsioonifailid serverist.
  • Peab olema käimas teenus nginx.
  • Konfiguratsiooni värskendamisel peab teenuse taaskäivitama.

Kirjutame manifesti:

node 'server2.testdomain' {   # server2.testdomain'ile seotud konfiguratsiooniblokk
    package { 'nginx':   # nginx paketi määratlemine
        ensure => installed,   # see peab olema installitud
    }
  # Otsene nool (->) tähendab, et allolev ressurss peab
  # olema loodud peale ülaltoodud ressursi.
  # Need sõltuvused on transitiivsed.
    -> file { '/etc/nginx':   # /etc/nginx faili määratlemine
        ensure => directory,   # see peab olema kaust
        source => 'puppet://modules/example/nginx-conf',   # selle sisu tuleb võeta puppet-serverist antud aadressilt
        recurse => true,   # failide rekursiivne kopeerimine
        purge => true,   # tuleb eemaldada üleliigsed failid (need, mida allikas ei sisalda)
        force => true,   # eemaldada üleliigsed kaustad
    }
  # Lainekujuline nool (~>) tähendab, et allolev ressurss peab
  # olema seotud muutustega ülaltoodud ressursist.
  # Lainekujuline nool sisaldab endas otsest (->).
    ~> service { 'nginx':   # nginx teenuse määratlemine
        ensure => running,   # see peab olema käimas
        enable => true,   # see peab automaatselt käivituma süsteemi käivitamisel
    }
  # Kui teenuse tüüp saab teate,
  # vastav teenus taaskäivitub.
}

Selle toimimiseks peab serveris olema ligikaudu selline failide paigutus:

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

Resursside tüübid

Toetatud ressursside tüüpide täielik nimekiri asub dokumentatsioonis, siin kirjeldan viis põhiliiki, mida minu praktikas on piisavalt enamikus ülesannetes lahendamiseks.

fail

Haldab faile, kaustu, sümboolseid linke, nende sisu ja juurdepääsuõigusi.

Parameetrid:

  • ressursi nimi — failitee (valikuline)
  • failitee — failitee (kui see ei ole määratud nimes)
  • ensure — faili tüüp:
    • absent — kustutada fail
    • present — peab olema mis tahes tüüpi fail (kui faili pole, luuakse tavaline fail)
    • fail — tavaline fail
    • directory — kaust
    • link — sümboolne link
  • content — faili sisu (sobib ainult tavaliste failide jaoks, ei saa kasutada koos source või target)
  • source — link teele, kust tuleb kopeerida faili sisu (ei saa kasutada koos content või target). See võib olla määratud kas URI kujul skeemiga puppet: (siis kasutatakse faile puppetseerverist), samuti skeemiga http: (loodan, et on selge, mis sel juhul juhtub), või isegi skeemita absoluutse teena (siis kasutatakse faili kohaliku failisüsteemi pealt) file: или в виде абсолютного пути без схемы (тогда будет использован файл с локальной ФС на ноде)
  • target — kuhu sümbol link peab osutama (ei tohi koos kasutada content või source)
  • omanik — kasutaja, kellele fail kuuluma peab
  • grupp — grupp, kellele fail kuuluma peab
  • mode — õigused failile (stringina)
  • recurse — kaasab kaustade rekursiivse töötlemise
  • purge — sisaldab failide eemaldamist, mida Puppet ei kirjelda
  • force — sisaldab kaustade eemaldamist, mida Puppet ei kirjelda

package

Paigaldab ja eemaldab pakette. Suudab töötleda teateid — paigaldab paketi uuesti, kui on määratud parameeter reinstall_on_refresh.

Parameetrid:

  • ressursi nimi — paketi nimi (valikuline)
  • name — paketi nimi (kui ei ole määratud nimetuses)
  • provider — pakihaldur, mida kasutada
  • ensure — soovitud paketi seisund:
    • present, installed — on paigaldatud mõni versioon
    • latest — on paigaldatud viimane versioon
    • absent — on eemaldatud (apt-get remove)
    • purged — eemaldatud koos konfiguratsioonifailidega (apt-get purge)
    • held — paketi versioon on lukustatud (apt-mark hold)
    • iga muu string — on paigaldatud määratud versioon
  • reinstall_on_refresh — kui true, siis tarnimise korral pakett uuendatakse. See on kasulik allika põhiste distributsioonide puhul, kus pakettide uuesti koostamine võib olla vajalik, kui kogumise parameetreid muudetakse. Vaikimisi false.

teenus

Haldab teenuseid. Suudab töödelda teateid — taaskäivitab teenuse.

Parameetrid:

  • ressursi nimi — teenus, mida tuleb hallata (valikuline)
  • name — teenus, mida tuleb hallata (kui ei ole määratud nimes)
  • ensure — soovitud teenuse olek:
    • jooks — käivitatud
    • stopp — peatatud
  • lubama — haldab teenuse käivitamise võimalust:
    • true — on lubatud automaatne käivitamine (systemctl enable)
    • mask — on maskeeritud (systemctl mask)
    • false — automaatne käivitamine on keelatud (systemctl disable)
  • taaskäivitama — käsk teenuse taaskäivitamiseks
  • status — käsk teenuse oleku kontrollimiseks
  • hasrestart — märkida, kas teenuse initsialiseerimiskood toetab taaskäitamist. Kui false ja parameeter on määratud taaskäivitama — kasutatakse selle parameetri väärtust. Kui false ja parameeter taaskäivitama ei ole määratud — teenus peatatakse ja käivitatakse taaskäivitamiseks (kuid systemd kasutab käsku systemctl restart).
  • hasstatus — märkida, kas teenuse initsialiseerimiskood toetab käsku status. Kui false, kasutakse parameetri väärtust status. Vaikimisi true.

exec

Käivitab välised käsud. Kui parameetreid ei ole märgitud loob, ainult_kehtiv, kui_mitte või ainult_uuendus, käsk käivitatakse iga kord, kui Puppet töötab. Suudab hallata teateid – käivitab käsu.

Parameetrid:

  • ressursi nimi — käsk, mida peab täitma (valikuline)
  • command — käsk, mida peab täitma (kui seda ei ole nimekirjas määratud)
  • failitee — teed, kus otsida käivitatavat faili
  • ainult_kehtiv — kui selles parameetris määratud käsk lõpetatakse nullkoodiga, täidetakse peamine käsk
  • kui_mitte — kui selles parameetris määratud käsk lõpetatakse mittekoodiga, täidetakse peamine käsk
  • loob — kui selles parameetris määratud faili ei eksisteeri, täidetakse peamine käsk
  • ainult_uuendus — kui true, siis käsk käivitatakse ainult juhul, kui see exec saab teateid teiste allikate poolt
  • cwd — kataloog, kust käsku käivitada
  • kasutaja — kasutaja, kelle alt käsku käivitada
  • provider — millega käivitada käsk:
    • posix — lihtsalt loodakse alamprotsess, on vaja märkida failitee
    • shell — käsk käivitatakse shellis /bin/sh, ei pea märkima failitee, on saab kasutada globbimist, torusid ja muid shelly funktsioone. Tavaliselt määratakse automaatselt, kui on erinevad erisümbolid (|, ;, &&, || jne).

cron

Haldab cron-tööd.

Parameetrid:

  • ressursi nimi — lihtsalt mingi identifikaator
  • ensure — cron-töö olek:
    • present — loo, kui ei eksisteeri
    • absent — eemalda, kui eksisteerib
  • command — millist käsku käivitada
  • environment — millises keskkonnas käivitada käsk (loetelu keskkonnamuutujatest ja nende väärtustest, eraldatud =)
  • kasutaja — kellelt käivitada käsk
  • minuut, tund, nädalapäev, kuu, kuu päev — millal cron käivitada. Kui mõni neist atribuudist ei ole määratud, on selle väärtus crontabi sees *.

Puppet 6.0-s cron nagu eemaldatud väljaandest puppetserveris, seega ei ole sellele dokumendil üldises veebisaidil dokumendi. Kuid see leiab aset väljaandes puppet-agentis, seega ei pea seda eraldi installima. Dokumentatsiooni selle kohta saab vaadata Puppeti viienda versiooni dokumentatsioonis, või GitHubis.

Ressurssidest üldiselt

Nõuded ressursside unikaalsusele

Kõige levinum viga, millega me silmitsi seisame — Duplicate declaration. See viga tekib, kui katalooge satuvad kaks või enam sama tüüpi ressurssi sama nimega.

Seega kirjutan veel kord: Sama tüüpi ressurssidest ei tohiks ühe sõlme manifestides olla sama nimega (title)!

Mõnikord on vajalik installida pakette sama nimega, kuid erinevate paketihalduritega. Sellisel juhul tuleks kasutada parameetrit name, et vältida viga:

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

Teistes ressursside tüüpides on sarnased parameetrid, mis aitavad vältida dubleerimist, — name on teenus, command on exec, ja nii edasi.

Metaparameetrid

Igal ressursi tüübival on mõned spetsiaalsed parameetrid, sõltumata tema olemusest.

Täpne nimekiri metaparameetritest Puppeti dokumentatsioonis.

Lühike nimekiri:

  • require — selles parameetris määratakse, millistest ressurssidest sõltub antud ressurss.
  • before — selles parameetris määratakse, millised ressursid sõltuvad antud ressursist.
  • subscribe — selles parameetris määratakse, millistest ressurssidest saab antud ressurss teateid.
  • notify — selles parameetris määratakse, millised ressursid saavad teateid antud ressursilt.

Kõik nimetatud meta-parameetrid aktsepteerivad kas ühte viidet ressursile või massiivi viiteid külgmistes nurksulgudes.

Viidatud ressursid

Viide ressursile on lihtsalt ressursi mainimine. Neid kasutatakse peamiselt sõltuvuste näitamiseks. Viide mittesegadud ressursile põhjustab kompileerimisviga.

Viite süntaks on järgmine: ressursi tüüp suure tähega (kui tüpi nimetus sisaldab kaht pidevat koolonit, siis kirjutatakse iga osa nimetust koolonite vahel suure tähega), seejärel külgmistes nurksulgudes ressursi nimi (nime registreerimise eest ei muuda!). Tühikuid ei tohi olla, külgsulgud kirjutatakse kohe pärast tüübi nimetust.

Näide:

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

Sõltuvused ja teavitused

Dokumentatsioon on siin.

Nagu eespool öeldi, on lihtsad sõltuvused ressursside vahel transitiivsed. Ole ettevaatlik, kui määrad sõltuvusi — võib luua tsüklilisi sõltuvusi, mis põhjustavad kompileerimisviga.

Erinevalt sõltuvustest ei ole teadete edastamine transitiivne. Teadetele kehtivad järgmised reeglid:

  • Kui ressurss saab teate, siis see uuendatakse. Uuendamise toimingud sõltuvad ressursi tüübist — exec käivitab käsu, teenus taaskäivitab teenuse, package uuendab paketti. Kui ressursi jaoks ei ole uuendamise toimingut määratud, siis ei toimu midagi.
  • Ühe Puppeetri jooksu jooksul uuendatakse ressursse mitte rohkem kui üks kord. See on võimalik seetõttu, et teated sisaldavad sõltuvusi ning sõltuvuste diagramm ei sisalda silmuseid.
  • Kui Puppeet muudab ressursi olekut, saadab ressurss teated kõigile sellele registreerunud ressurssidele.
  • Kui ressurss uuendatakse, saadab ta teated kõigile sellele registreerunud ressurssidele.

Määratlemata parameetrite töötlemine

Üldiselt, kui mõnel ressursi parameetril ei ole vaikimisi väärtust ja seda parameetrit ei ole manifestis määratud, ei muuda Puppeet vastava ressursi omadust sõlmes. Näiteks, kui ressursi tüüp fail ei ole parameeter määratud, omaniksiis Puppeet ei muuda vastava faili omanikku.

Tutvumine klasside, muutuja ja defineeringutega

Oletame, et meil on mitu sõlme, millel on sama konfiguratsiooni osa, kuid ka erinevusi — muidu võiksime kõik selle ühes plokis kirjeldada node {}. Muidugi, võiksite lihtsalt kopeerida samad konfiguratsiooni osad, kuid üldjuhul on see halb lahendus — konfiguratsioon kasvab, kui ühisosa konfiguratsioonis muutub, tuleb sama asja muuta paljudes kohtades. On lihtne eksida, ja üldine DRY (ärge korrake end) põhimõte ei ole niisama välja mõeldud.

Selle probleemi lahendamiseks on olemas konstruktsioon, nagu klass.

Klassid

Klass on nimetatud plokk puppeti koodist. Klassid on vajalikud koodi taaskasutamiseks.

Esiteks tuleb klass kirjeldada. Kirjeldus iseenesest ei lisa kuhugi ressursse. Klass kirjeldatakse manifestides:

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

Pärast seda saab klassi kasutada:

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

Eelmisest ülesandest näide — viime nginx-i installimise ja seadistamise klassi:

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
}

Muutujad

Eelmine klass on üsna jäik, sest see toob alati sama nginx konfiguratsiooni. Teeme nii, et konfiguratsioonitee muutub muutujaks, millega saab seda klassi kasutada nginx'i paigaldamiseks mis tahes konfiguratsiooniga.

Seda saab teha muutujate abil.

Tähelepanu: Puppet'is on muutujad muutumatud!

Lisaks saab muutujaid kasutada ainult pärast nende deklareerimist, vastasel juhul on muutuja väärtuseks undef.

Muutujatega töötamise näide:

# создание переменных
$variable = 'value'
$var2 = 1
$var3 = true
$var4 = undef
# использование переменных
$var5 = $var6
file { '/tmp/text': content => $variable }
# интерполяция переменных — раскрытие значения переменных в строках. Работает только в двойных кавычках!
$var6 = "Variable with name variable has value ${variable}"

Puppet'is on nimetuste ruumid, ja muutujaid iseloomustab vastavalt ulatus: muutuja sama nimega võib olla määratud erinevatesse nimetuste ruumidesse. Muutuja väärtuse lahendamisel otsitakse muutuja esiteks praegusest nimeladast, seejärel ümbritsevatest jne.

Nimetuste ruumi näidised:

  • globaalne — sinna kuuluvad muutujad, mis ei kuulu klassi või sõlme kirjelduse alla;
  • sõlme nimetu sõlme kirjelduse juures;
  • klassi nimetu klassi kirjelduse juures.

Ebamugavuste vältimiseks muutuja juurde viitamisel saab muutujate nimedes näidata nimeliini:

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

Leppime kokku, et nginx-i konfiguratsiooni tee asub muutuja $nginx_conf_source. Klass näeb siis välja järgmiselt:

class nginx_example {
    package { 'nginx':
        ensure => installed,
    }
    -> file { '/etc/nginx':
        ensure => directory,
        source => $nginx_conf_source,   # siin kasutame muutujat, mitte fikseeritud rida
        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
}

Kuid esitatud näide on halb, kuna seal on mingi 'salajane teadmine' selle kohta, et kuskil klassis kasutatakse muutujat teatud nimega. Palju õigustatum on see teadmine muuta avalikuks — klassidel võivad olla parameetrid.

Klassi parameetrid — need on klassi nimede ruum, mis määratakse klassi pealkirjas ja mida saab kasutada nagu tavalisi muutujaid klassi kehas. Parameetrite väärtused määratakse klassi kasutamisel manifestis.

Parameetrile saab määrata vaikesätte. Kui parameetril ei ole vaikemäära ja väärtust ei määrata kasutamisel, põhjustab see kompileerimisvea.

Vaatame, kuidas parametriseerida ülaltoodud klassi ja lisada sellele kaks parameetrit: esimene, kohustuslik — tee seadistusele, ja teine, valikuline — nginx-paketi nimi (Debianis, näiteks, on pakette 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',   # задаём параметры класса точно так же, как параметры для других ресурсов
  }
}

Puppetis on muutujad tüübitud. On palju andmetüüpe. Andmetüüpe kasutatakse tavaliselt parameetrite väärtuste valideerimiseks, mis antakse klassidesse ja defineeringutesse. Kui antud parameeter ei vasta määratud tüübile, tekib kompileerimisviga.

Tüüp kirjutatakse parameetri nime ette:

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

Klassid: include classname vs class{‘classname’:}

Iga klass on ressursitüüp class. Nagu teiste ressursside puhul ei saa ühel noodil olla kahte sama klassi eksemplari.

Kui proovida klassi samale noodile kaks korda lisada, kasutades class { 'classname':} (olgu need erinevad või samad parameetrid), siis tekib kompileerimisviga. Siiski, kui kasutada klassi ressursistiilis, saab selle parameetreid manifestis selgelt määrata.

Kuid kui kasutada include, siis saab klassi lisada nii palju kordi kui soovite. Probleem on selles, et include on idempotentne funktsioon, mis kontrollib, kas klass on kataloogis olemas. Kui klassi ei ole, lisab see selle, kuid kui ta juba on, siis ei tee midagi. Kuid klassi kasutamisel include ei saa klassi parameetreid klassi deklareerimise ajal määrata - kõik kohustuslikud parameetrid peavad olema määratud välises andmeallikas - Hiera või ENC. Nendest räägime järgmises artiklis.

Defineerib

Nagu eelnevas plokis mainitud, ei saa sama klass nodis olla rohkem kui üks kord. Siiski on teatud olukordades vajalik rakendada sama koodiplokki erinevate parameetritega ühel nodil. Teisisõnu, on vajadus spetsiifilise ressursitüübi järele.

Näiteks PHP mooduli installimiseks teeme me Avitos järgmist:

  1. Paigaldame paketi koos selle mooduliga.
  2. Loo selle mooduli konfiguratsioonifail.
  3. Loo sümbolsid php-fpmi konfigureerimiseks.
  4. Loo sümbolsid php cli konfigureerimiseks.

Sellistes olukordades kasutatakse konstruktsiooni, nagu define (define, defineeritud tüüp, defineeritud ressursitüüp). Define on sarnane klassiga, kuid seda eristavad mõned asjad: esiteks, iga define on ressursitüüp, mitte ressurss; teiseks, igal define'il on varjatud parameeter $title, kuhu paigutatakse ressursi nimi selle kuulutamisel. Nagu klasside puhul, tuleb define kõigepealt kirjeldada, seejärel saab seda kasutada.

Lihtsustatud näide PHP moodulist:

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-pakkide triggereid luuakse automaatselt sümbolsid ja käivitatakse php-fpm teenus - see pole meile vajalik, kuna hallame nii sümboleid kui teenust Puppet'i abil
  }
  -> 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' }
}

Definitsioonis on kõige lihtsam tabada viga Duplicate declaration. See juhtub, kui definitsioonis on ressurss konstantsest nimest, ja mingil sõlmel on kaks või enam seda definitsiooni eksemplari.

Kaitsmine selle eest on lihtne: kõik definitsioonis olevad ressursid peavad olema nimest sõltuvad $title. Alternatiivina on idempotentne resource'i lisamine, lihtsaimal juhul piisab, kui tuua kõik instantside ühised määratlused eraldi klassi ja sisaldada see klass määratluses — funktsioon include on idempotentne.

On ka teisi viise, kuidas saavutada idempotentsus resource'ide lisamisel, nimelt funktsioonide kasutamine määratud ja ensure_resources, kuid sellest räägin järgmisel korral.

Sõltuvused ja teated klasside ja määratluste jaoks

Klassid ja määratlused lisavad sõltuvuste ja teatiste töötlemiseks järgmised reeglid:

  • klassist/määratlusest sõltuvus lisab sõltuvused kõigile klassi/määratluse ressurssidele;
  • klass/määratlus lisab sõltuvused kõigile klassi/määratluse ressurssidele;
  • klass/määratlus teatab kõikidele klassi/määratluse ressurssidele;
  • klass/määratlus tõmbab kõiki klassi/määratluse ressursside peale.

Tingimuslikud operaatorid ja selektorid

Dokumentatsioon on siin.

if

Siin on kõik lihtne:

if VYRASTUS1 {
  ...
} elsif VYRASTUS2 {
  ...
} else {
  ...
}

kui_mitte

unless — see on vastupidine if'ile: koodiblokk täidetakse, kui väljend on vale.

unless VYRASTUS {
  ...
}

case

Siin pole samuti midagi keerulist. Väärtustena saab kasutada tavapäraseid väärtusi (märke, numbreid jne), regulaaravaldisi ning andmetüüpe.

case Väljendus {
  VAARTUS1: { ... }
  VAARTUS2, VAARTUS3: { ... }
  vaikimisi: { ... }
}

Valijad

Valija on keele konstruktsioon, mis sarnaneb case, kuid selle asemel, et koodiblokki täita, tagastab see väärtuse.

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

Moodulid

Kui konfiguratsioon on väike, on seda lihtne hoida ühes manifestis. Kuid mida rohkem konfiguratsiooni me kirjeldate, seda rohkem klasse ja nõudeid manifestis tekib, see paisub ja muutub selle haldamine ebamugavaks.

Lisaks on koodide taaskasutamise probleem — kui kogu kood on ühes manifestis, on sellega raske teistega jagada. Nende kahe probleemi lahendamiseks on Puppetis selline entiteet nagu moodulid.

Moodulid — see on klasside, define'ide ja muude Puppet-üksuste kogum, mis on eraldi kausta viidud. Teisisõnu, moodul on iseseisev tükk Puppet-loogikast. Näiteks võib olla nginxiga töötamiseks mõeldud moodul, mis sisaldab ainult seda, mida on vajalik nginxiga töötamiseks, ja võib olla moodul PHP jaoks ning nii edasi.

Mooduleid versioonitakse ja toetatakse ka moodulitevahelist sõltuvust. On avatud moodulite hoidla — Puppet Forge.

Puppet-serveris asuvad moodulid juurkatalooge moodulite alakomplektis. Iga mooduli sees on standardne kataloogiskeem — manifests, files, templates, lib ja nii edasi.

Moodi struktuur

Mooduli juures võivad olla järgmised selgelt nimetatud kataloogid:

  • manifests — seal asuvad manifestid
  • files — seal asuvad failid
  • templates — seal asuvad mallid
  • lib — seal asub Ruby-kood

See ei ole täielik kataloogide ja failide loetelu, kuid sellele artiklile on see piisav.

Ressursside nimed ja failide nimed moodulis

Dokumentatsioon on siin.

Moodulis ei saa ressursse (klassid, määrangud) suvaliselt nimetada. Lisaks on ressurssi nime ja faili nime vahel otsene seos, kus Puppet otsib selle ressursi kirjeldust. Nimetamise reeglite rikkumise korral ei leia Puppet lihtsalt ressursside kirjeldust ning see põhjustab kompileerimisviga.

Reeglid on lihtsad:

  • Kõik ressursid moodulis peavad olema mooduli nimespetsifikaadis. Kui mooduli nimi on foo, siis kõik selle ressursid peavad olema nimetatud foo::, või lihtsalt foo.
  • Mooduli nimega ressurss peab olema failis init.pp.
  • Ülejäänud ressursside failinimede skeem on järgmine:
    • mooduli nimega prefiks jäetakse välja
    • kõik topeltkahekohalised kaksikud, kui need on olemas, asendatakse kaldkriipsudega
    • lisa laiendus .pp

Demonstreerin näite abil. Oletame, et kirjutan mooduli nginx. Selles on järgmised ressursid:

  • klass nginx mooduli init.pp;
  • klass nginx::service mooduli service.pp;
  • define nginx::server mooduli server.pp;
  • define nginx::server::location mooduli server/location.pp.

Mallid

Te ilmselt teate, mis on mallid, seega ei hakka ma siia süvendama. Kuid igaks juhuks jätan linki Wikimedia.

Kuidas malli kasutada: malli väärtust saab avada funktsiooni abil template, mille edastatakse tee mallile. Ressursside tüüpide jaoks fail kasutame koos parameetriga content. Näiteks nii:

fail { '/tmp/example': sisu => template('modulename/templatename.erb')

Tee vormis / tähendab faili /modules//templates/.

Lisaks on olemas funktsioon inline_template — millele edastatakse mallitekst, mitte faili nimi.

Mallides saab kasutada kõiki Puppet'i muutujaid praeguses nähtavuses.

Puppet toetab malli formaate ERB ja EPP:

Lühidalt ERB-st

Juhtimisstruktsioonid:

  • <%= ВЫРАЖЕНИЕ %> — sisestada väljendi väärtus
  • <% ВЫРАЖЕНИЕ %> — arvutada väljendi väärtust (ilma selle sisestamiseta). Siia kuuluvad tavaliselt tingimuslikud operaatorid (if), tsüklid (each).
  • <%# КОММЕНТАРИЙ %>

Väljendid ERB-s on kirjutatud Ruby-s (tegelikult on ERB Embedded Ruby).

Muutujatele, mis on mäleta, tuleb lisada @ muutuja nime ette. Rida vahetuse eemaldamiseks, mis tekib juhtimisstruktsiooni järel, tuleb kasutada sulgemissegmenti -%>.

Malli kasutamise näide

Oletame, et ma arendan moodulit ZooKeeperi haldamiseks. Klassis, mis vastutab konfiguratsiooni loomise eest, on midagi sellist:

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

Ja vastav mall zoo.cfg.erb — selliselt:

0 -%>

server.=:::



dataDir=


=

Faktid ja sisseehitatud muutujad

Sageli sõltub konkreetne konfiguratsiooniosas sellest, mis konkreetselt sõlmes toimub. Näiteks, sõltuvalt sellest, milline Debian'i versioon on paigaldatud, tuleb installida see või teine pakett. Saate seda käsitsi jälgida, kirjutades vajadusel manifesti uuesti, kuid see on ebaprofessionaalne lähenemine, automatiseerimine on palju parem.

Sõlmede teabe saamiseks on Puppetis selline mehhanism nagu faktid. Faktid — see on sõlme teave, mis on kergesti kättesaadav manifeetides tavapäraste muutujate kujul globaalses nimede ruumis. Näiteks hostinimi, operatsioonisüsteemi versioon, protsessori arhitektuur, kasutajate loetelu, võrgu liideste ja nende aadresside loetelu ning palju muud. Faktid on manifeetides ja mallides kergesti kätte saadavad tavapäraste muutujatena.

Näide faktidega töötamisest:

notify { "Töötav OS ${facts['os']['name']} versioon ${facts['os']['release']['full']}": }
# notify tüüpi ressurss lihtsalt kirjutab sõnumi logisse

Kuidas öelda, faktidel on nimi (string) ja väärtus (saadaval on erinevaid tüüpe: stringid, massiivid, sõnastikud). On kogum sisseehitatud fakte. Samuti saab kirjutada oma. Faktide kogujad on kirjeldatud Ruby funktsioonidena, või kui täidetavad failid. Samuti võivad faktid olla esitatud tekstifailide andmetena sõlmedes.

Töötamise ajal kopeerib puppet-agent esmalt puppet-serverist sõlme kõik kergesti kättesaadavad faktikogujad, seejärel käivitab need ja saadab serverisse kogutud faktid; alles pärast seda alustab server katalooge genereerimist.

Faktid täidetavate failidena

Need faktid pannakse moodulitesse kausta facts.d. Kui te käivitate, peavad failid olema täidetavad. Käivitamisel peavad nad standardväljal välja printima teavet kas YAML-vormingus või «võti=väärtus» vormingus.

Ärge unustage, et faktid levivad kõikidele sõlmedele, mis on hallatavad papeti-serveri all, kuhu teie moodul tõugatakse. Seetõttu veenduge skriptis, et süsteemis on kõik vajalikud programmid ja failid teie fakti tööks.

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

Faktid Ruby's

Need faktid pannakse moodulitesse kausta 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

Tekstifaktid

Need faktid pannakse sõlmedesse kausta /etc/facter/facts.d vana Papetis või /etc/puppetlabs/facts.d uus Papetis.

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

Faktide kasutamine

Faktidele pääseb ligi kahte moodi:

  • sõnastiku kaudu $facts: $facts['fqdn'];
  • kasutades fakti nime muutujana: $fqdn.

Parim on kasutada sõnastikku $facts, aga veel parem on määrata globaalne nimespetsiifika ($::facts).

Siin on vajalik jaotis dokumentatsioonist.

Sisseehitatud muutujad

Peale faktide on veel mõned muutujad, mis on saadaval globaalses nimespetsiifikas.

  • usaldusväärsed faktid — kliendi sertifikaadist saadud muutujad (kuna sertifikaat väljastatakse tavaliselt puppet-serveris, ei saa agent lihtsalt oma sertifikaati vahetada, mistõttu muutujad ja 'usaldusväärsed'): sertifikaadi nimi, hosti- ja domeeninimi, sertifikaadi laiendused.
  • server facts — serveri teabe muutujad — versioon, nimi, serveri IP-aadress, keskkond.
  • agent facts — muutujad, mida lisab otse puppet-agent, mitte facter — sertifikaadi nimi, agendi versioon, puppet'i versioon.
  • master variables — puppet-master'i muutujad (sic!). Seal on enam-vähem sama, mis server facts, lisaks on saadaval konfiguratsiooni parameetrite väärtused.
  • compiler variables — kompilaatori muutujad, mis erinevad igas ulatuses: praeguse mooduli nimi ja mooduli nimi, kus pöörduti praeguse objekti poole. Neid saab kasutada näiteks selleks, et kontrollida, et teie privaatklasse ei kasutata otse teistest moodulitest.

Lisand 1: kuidas seda kõike käivitada ja siluda?

Artiklis oli palju puppet-koodi näiteid, kuid üldse ei räägitud, kuidas seda koodi käivitada. Noh, parandan ennast.

Puppet'i tööks piisab agentidest, kuid enamiku juhtumite jaoks on vaja ka serverit.

Agent

Alates viiendast versioonist sisaldavad puppet-agent'i paketid Puppetlabs'i ametlikust allikast kõiki sõltuvusi (ruby ja vastavaid gem'e), seega ei ole installimisel mingeid raskusi (räägin Debian'i põhistest distributiividest - RPM-põhiseid distributiive me ei kasuta).

Lihtsaimatel juhtudel piisab, kui käivitada puppet-konfiguratsioon agent ilma serverita: kui puppet-kood on nodis kopeeritud, käivitada 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

Parim on siiski serveri tõstmine ja agentide käivitamine nodides deemonirežiimis — siis rakendavad nad iga poole tunni järel konfiguratsiooni, mis on serverist alla laaditud.

Saate simuleerida push-mudelit - minge huvipakkuvale nodile ja käitage sudo puppet agent -t. Võti -t (--test) sisaldab tegelikult mitmeid valikuid, mida saab sisse ja välja lülitada eraldi. Nende valikute hulka kuuluvad järgmised:

  • töötada mitte demonirežiimis (vaikimisi agent käivitatakse demonirežiimis);
  • lõpetada töö pärast katalooge rakendamist (vaikimisi agent jätkab tööd ja rakendab konfiguratsiooni iga poole tunni järel);
  • kirjutada üksikasjalikku töölinti;
  • näidata muudatusi failides.

Agendil on muudatusvaba töörežiim — seda saab kasutada, kui te ei ole kindel, et olete kirjutanud õige konfiguratsiooni ja soovite kontrollida, mida täpselt agent tööl teeb. See režiim aktiveeritakse parameetriga --noop komandireas: sudo puppet agent -t --noop.

Lisaks on võimalik lubada veateade logimine — seal kirjutab puppet kõigist tegevustest, mida ta sooritab: ressursist, mida ta hetkel töötleb, selle ressursi parameetritest, ning milliseid programme ta käivitab. Loomulikult on see parameeter --debug.

Server

Puppetserveri täielikku seadistamist ja koodi selle peale ärajätmist ma selles artiklis ei käsitle. Ütlen vaid, et välja paketis on täiesti toimiv serveri versioon, mis ei nõua täiendavat seadistamist, et töötada väikese arvu sõlmedega (ütleme, kuni saja). Suurema sõlmede arvu puhul on seadistamine vajalik — puppetserver käivitab vaikimisi mitte rohkem kui neli töötajat, suurema tootlikkuse saavutamiseks tuleb nende arvu suurendada ja mitte unustada suurendada mälu limiite, muidu veedab server suurema osa ajast garbage collect’ides.

Koodi ärajätmine — kui on vaja kiiresti ja lihtsalt, siis vaadake (r10k)[https://github.com/puppetlabs/r10k], väikeste instalatsioonide jaoks peaks seda täiesti piisama.

Täiendav informatsioon 2: koodi kirjutamise soovitused

  1. Viige kogu loogika klassidesse ja defineerimisse.
  2. Hoidke klassid ja defineerimised moodulites, mitte sõlmede kirjeldustega manifeetides.
  3. Kasutage fakte.
  4. Ärge tehke if-eid hostinimede järgi.
  5. Ärge kartke klasside ja defineerimiste parameetreid lisada — see on parem kui varjatud loogika, mis on peidetud klassi/defineerimise kehasse.

Ja miks ma soovitan seda teha — selgitan järgmises artiklis.

Kokkuvõte

Lõpetame sissejuhatusega. Järgmises artiklis räägin Hierast, ENC-st ja PuppetDB-st.

Ainult registreeritud kasutajad saavad küsitluses osaleda. Logige sisse, palun.

Tegelikult on materjali palju rohkem — ma saan kirjutada artikleid järgmistele teemadele, hääletage, mis teid huvitab:

  • 59,1%Edasijõudnud Puppet'i konstruktsioonid — mõned järgmise taseme teemad: silmustamine, kaardistamine ja teised lambda-väljendid, ressursside kogumine, eksporditavad ressursid ja hostidevaheline suhtlemine Puppet'i kaudu, sildid, pakkujad, abstraktsed andmetüübid.
  • 31,8%"Ma olen administraator" või kuidas me Avitos ühendasime mitmeid erinevate versioonide Puppet-servereid, samuti osa Puppet-serveri haldamisest.
  • 81,8%Kuidas me kirjutame Puppet'i koodi: tööriistade raamistamine, dokumentatsioon, testimine, CI/CD.

Hääletas 22 kasutajat. Ükski ei hääletanud.

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster