Увядзенне ў Puppet

Puppet - гэта сістэма кіравання канфігурацыяй. Ён выкарыстоўваецца для прывядзення хастоў да патрэбнага стану і падтрыманні гэтага стану.

Я працую з Puppet ўжо больш за пяць гадоў. Гэты тэкст - па сутнасці перакладзеная і пераўпарадкаваная кампіляцыя ключавых момантаў з афіцыйнай дакументацыі, якая дазволіць пачаткоўцам хутка ўглыбіцца ў сутнасць Puppet.

Увядзенне ў Puppet

базавая інфармацыя

Схема працы Puppet – кліент-серверная, хоць падтрымліваецца і варыянт працы без сервера з абмежаванай функцыянальнасцю.

Выкарыстоўваецца pull-мадэль працы: па змаўчанні раз у паўгадзіны кліенты звяртаюцца да сервера за канфігурацыяй і ўжываюць яе. Калі вы працавалі з Ansible, то тамака выкарыстоўваецца іншая, push-мадэль: адміністратар ініцыюе працэс ужывання канфігурацыі, самі па сабе кліенты нічога ўжываць не будуць.

Пры сеткавым узаемадзеянні выкарыстоўваецца двухбаковае TLS-шыфраванне: у сервера і кліента ёсць свае зачыненыя ключы і якія адпавядаюць ім сертыфікаты. Звычайна сервер выпускае сертыфікаты для кліентаў, але ў прынцыпе магчымае выкарыстанне і вонкавага CA.

Знаёмства з маніфестамі

У тэрміналогіі Puppet да папэт-сервера падключаюцца ноды (nodes). Канфігурацыя для нод пішацца у маніфестах на спецыяльнай мове праграмавання - Puppet DSL.

Puppet DSL – дэкларатыўная мова. На ім апісваецца жаданы стан ноды ў выглядзе аб'явы асобных рэсурсаў, напрыклад:

  • Файл існуе, і ў яго пэўны змест.
  • Пакет устаноўлены.
  • Сэрвіс запушчаны.

Рэсурсы могуць быць узаемазвязаны:

  • Ёсць залежнасці, яны ўплываюць на парадак ужывання рэсурсаў.
    Напрыклад, «спачатку ўсталюй пакет, затым папраў канфігурацыйны файл, пасля гэтага запусці сэрвіс».
  • Ёсць апавяшчэнні — калі рэсурс змяніўся, ён адпраўляе апавяшчэнні падпісаным на яго рэсурсам.
    Напрыклад, калі змяняецца канфігурацыйны файл, можна аўтаматычна перазапускаць сэрвіс.

Акрамя таго, у Puppet DSL ёсць функцыі і зменныя, а таксама ўмоўныя аператары і селектары. Таксама падтрымліваюцца розныя механізмы шаблонизации - EPP і ERB.

Puppet напісаны на Ruby, таму многія канструкцыі і тэрміны ўзяты адтуль. Ruby дазваляе пашыраць Puppet - дапісваць складаную логіку, новыя тыпы рэсурсаў, функцыі.

Падчас працы Puppet маніфесты для кожнай канкрэтнай ноды на сэрвэры кампілююцца ў каталог. Каталог - Гэта спіс рэсурсаў і іх узаемасувязяў пасля вылічэння значэння функцый, зменных і раскрыцця ўмоўных аператараў.

Сінтаксіс і кодстайл

Вось раздзелы афіцыйнай дакументацыі, якія дапамогуць разабрацца з сінтаксісам, калі прыведзеных прыкладаў будзе недастаткова:

Вось прыклад таго, як выглядае маніфест:

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

Водступы і пераклады радкоў не з'яўляюцца абавязковай часткай маніфесту, аднак ёсць рэкамендаваны кіраўніцтва па стылі. Кароткі выклад:

  • Двухпрабельныя водступы, табы не выкарыстоўваюцца.
  • Фігурныя дужкі адлучаюцца прабелам, двокроп'е прабелам не адлучаецца.
  • Коскі пасля кожнага параметру, у тым ліку апошняга. Кожны параметр - на асобным радку. Выключэнне робіцца для выпадку без параметраў і аднаго параметра: можна пісаць на адным радку і без коскі (г.зн. resource { 'title': } и resource { 'title': param => value }).
  • Стрэлкі ў параметраў павінны быць на адным узроўні.
  • Стрэлкі ўзаемасувязі рэсурсаў пішуцца перад імі.

Размяшчэнне файлаў на папетсерверы

Для далейшых тлумачэнняў я ўвяду паняцце "каранёвая дырэкторыя". Каранёвая дырэкторыя - гэта дырэкторыя, у якой знаходзіцца Puppet-канфігурацыя для канкрэтнай ноды.

Каранёвая дырэкторыя адрозніваецца ў залежнасці ад версіі Puppet і выкарыстанні асяроддзяў. Асяроддзі - гэта незалежныя наборы канфігурацыі, якія захоўваюцца ў асобных дырэкторыях. Звычайна выкарыстоўваюцца ў спалучэнні з гітом, у такім выпадку асяроддзі ствараюцца з галінак гіта. Адпаведна, кожная нода знаходзіцца ў тым ці іншым асяроддзі. Гэта наладжваецца на самой нодзе, альбо ў ENC, пра што я раскажу ў наступным артыкуле.

  • У трэцяй версіі («стары Папэт») базавай дырэкторыяй была /etc/puppet. Выкарыстанне акружэнняў апцыянальнае — мы, напрыклад, іх не выкарыстоўваем са старым Папэтам. Калі асяроддзі выкарыстоўваюцца, то яны звычайна захоўваюцца ў /etc/puppet/environments, каранёвай дырэкторыяй будзе дырэкторыя асяроддзя. Калі асяроддзі не выкарыстоўваюцца, каранёвай дырэкторыяй будзе базавая.
  • Пачынаючы з чацвёртай версіі («новы Папэт») выкарыстанне асяродкаў стала абавязковым, а базавую дырэкторыю перанеслі ў /etc/puppetlabs/code. Адпаведна, асяроддзі захоўваюцца ў /etc/puppetlabs/code/environments, каранёвая дырэкторыя - дырэкторыя асяроддзя.

У каранёвай дырэкторыі павінна быць паддырэкторыя manifests, у якой ляжыць адзін ці некалькі маніфестаў з апісаннем нод. Акрамя таго, там павінна быць паддырэкторыя modules, У якой ляжаць модулі. Што такое модулі, я раскажу крыху пазней. Акрамя таго, у старым Папеце таксама можа быць паддырэкторыя files, У якой ляжаць розныя файлы, якія мы капіюем на ноды. У новым Папэце ж усе файлы вынесены ў модулі.

Файлы маніфэстаў маюць пашырэньне .pp.

Пара баявых прыкладаў

Апісанне ноды і рэсурса на ёй

На нодзе server1.testdomain павінен быць створаны файл /etc/issue са змесцівам Debian GNU/Linux n l. Файл павінен належаць карыстальніку і групе root, правы доступу павінны быць 644.

Пішам маніфест:

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

Узаемасувязі рэсурсаў на нодзе

На нодзе server2.testdomain павінен быць запушчаны nginx, які працуе з падрыхтаванай загадзя канфігурацыяй.

Дэкампазіруем задачу:

  • Трэба, каб быў усталяваны пакет nginx.
  • Трэба, каб былі скапіяваныя канфігурацыйныя файлы з сервера.
  • Трэба, каб быў запушчаны сервіс nginx.
  • У выпадку абнаўлення канфігурацыі трэба перазапускаць сэрвіс.

Пішам маніфест:

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 получает уведомление,
  # соответствующий сервис перезапускается.
}

Каб гэта працавала, трэба прыкладна такое размяшчэнне файлаў на папэт-серверы:

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

Тыпы рэсурсаў

Поўны спіс падтрымліваемых тыпаў рэсурсаў знаходзіцца у дакументацыі, тутака ж я апішу пяць базавых тыпаў, якіх у маёй практыцы хапае для рашэння большасці задач.

файл

Кіруе файламі, дырэкторыямі, сімлінкамі, іх змесцівам, правамі доступу.

параметры:

  • назва рэсурсу - шлях да файла (апцыянальна)
  • шлях - шлях да файла (калі ён не зададзены ў назве)
  • забяспечваць - Тып файла:
    • absent - выдаліць файл
    • present - павінен быць файл любога тыпу (калі файла няма - будзе створаны звычайны файл)
    • file - Звычайны файл
    • directory - Дырэкторыя
    • link - сімлінк
  • змест - змесціва файла (падыходзіць толькі для звычайных файлаў, нельга выкарыстоўваць разам з крыніца або мішэнь)
  • крыніца - спасылка на шлях, з якога трэба капіяваць змесціва файла (нельга выкарыстоўваць разам з змест або мішэнь). Можа быць зададзена як у выглядзе URI са схемай puppet: (тады будуць выкарыстаныя файлы з папэт-сервера), так і са схемай http: (спадзяюся, зразумела, што будзе ў гэтым выпадку), і нават са схемай file: ці ў выглядзе абсалютнага шляху без схемы (тады будзе выкарыстаны файл з лакальнай ФС на нодзе)
  • мішэнь куды павінен паказваць сімлінк (нельга выкарыстоўваць разам з змест або крыніца)
  • ўладальнік - карыстач, якому павінен прыналежаць файл
  • група - група, якой павінен належаць файл
  • рэжым - правы на файл (у выглядзе радка)
  • паўтараць - уключае рэкурсіўную апрацоўку дырэкторый
  • чыстка - уключае выдаленне файлаў, якія не апісаны ў Puppet
  • прымусіць - Уключае выдаленне дырэкторый, якія не апісаны ў Puppet

пакет

Усталёўвае і выдаляе пакеты. Умее апрацоўваць апавяшчэнні — пераўсталёўвае пакет, калі зададзены параметр reinstall_on_refresh.

параметры:

  • назва рэсурсу - назва пакета (апцыянальна)
  • імя - назва пакета (калі не зададзена ў назве)
  • правайдэры - пакетны менеджэр, які трэба выкарыстоўваць
  • забяспечваць - жаданае стан пакета:
    • present, installed - усталявана любая версія
    • latest - усталявана апошняя версія
    • absent - выдалены (apt-get remove)
    • purged - выдалены разам з канфігурацыйнымі файламі (apt-get purge)
    • held - версія пакета заблакаваная (apt-mark hold)
    • любая другая строка - усталявана паказаная версія
  • reinstall_on_refresh - калі true, то пры атрыманні апавяшчэння пакет будзе пераўсталяваны. Карысна для source-based дыстрыбутываў, дзе перазборка пакетаў можа быць неабходна пры змене параметраў зборкі. Па змаўчанні false.

абслугоўванне

Кіруе сэрвісамі. Умее апрацоўваць апавяшчэнні — перазапускае сэрвіс.

параметры:

  • назва рэсурсу - сэрвіс, якім трэба кіраваць (апцыянальна)
  • імя - сэрвіс, якім трэба кіраваць (калі не зададзена ў назве)
  • забяспечваць - жаданае стан сэрвісу:
    • running - запушчаны
    • stopped - спынены
  • уключыце - Кіруе магчымасцю запуску сэрвісу:
    • true - уключаны аўтазапуск (systemctl enable)
    • mask - замаскіраваны (systemctl mask)
    • false - выключаны аўтазапуск (systemctl disable)
  • перазапуск - каманда для перазапуску сэрвісу
  • статус - каманда для праверкі статусу сэрвісу
  • hasrestart - паказаць, ці падтрымлівае ініскрыпт сэрвісу перазапуск. Калі false і паказаны параметр перазапуск - Выкарыстоўваецца значэнне гэтага параметру. Калі false і параметр перазапуск не паказаны - сэрвіс спыняецца і запускаецца для перазапуску (але ў systemd выкарыстоўваецца каманда systemctl restart).
  • hasstatus - паказаць, ці падтрымлівае ініскрыпт сэрвісу каманду status. Калі false, то выкарыстоўваецца значэнне параметру статус. Па змаўчанні true.

Exec

Запускае вонкавыя каманды. Калі не паказваць параметры стварае, onlyif, калі не або refreshonly, каманда будзе запускацца пры кожным прагоне Папэта. Умее апрацоўваць апавяшчэнні — запускае каманду.

параметры:

  • назва рэсурсу - каманда, якую трэба выканаць (апцыянальна)
  • каманда - каманда, якую трэба выканаць (калі яна не зададзена ў назве)
  • шлях - шляхі, у якіх шукаць выкананы файл
  • onlyif - калі паказаная ў гэтым параметры каманда завяршылася з нулявым кодам звароту, асноўная каманда будзе выканана
  • калі не - калі паказаная ў гэтым параметры каманда завяршылася з ненулявым кодам звароту, асноўная каманда будзе выканана
  • стварае - калі паказаны ў гэтым параметры файл не існуе, асноўная каманда будзе выканана
  • refreshonly - калі true, то каманда будзе запушчана толькі ў тым выпадку, калі гэты exec атрымлівае апавяшчэнне ад іншых рэсурсаў
  • cwd - Дырэкторыя, з якой запускаць каманду
  • карыстальнік - карыстач, ад якога запускаць каманду
  • правайдэры - з дапамогай чаго запускаць каманду:
    • поз - проста ствараецца даччыны працэс, абавязкова паказваць шлях
    • абалонка - каманда запускаецца ў шеле /bin/sh, можна не паказваць шлях, можна выкарыстоўваць глабінг, пайпы і іншыя фічы шелла. Звычайна вызначаецца аўтаматычна, калі ёсць усякія спецзнакі (|, ;, &&, || і гэтак далей).

крон

Кіруе кранджобамі.

параметры:

  • назва рэсурсу - проста нейкі ідэнтыфікатар
  • забяспечваць - стан кранджоба:
    • present - стварыць, калі не існуе
    • absent - выдаліць, калі існуе
  • каманда - якую каманду запускаць
  • навакольнае асяроддзе - у якім асяроддзі запускаць каманду (спіс зменных асяроддзі і іх значэнняў праз =)
  • карыстальнік - ад якога карыстальніка запускаць каманду
  • хвілін, гадзіну, будні дзень, месяц, monthday - калі запускаць крон. Калі нейкі з гэтых аттрыбутаў не пазначаны, яго значэннем у кронтабе будзе *.

У Puppet 6.0 крон як бы выдалілі са скрынкі у puppetserver, таму няма дакументацыі на агульным сайце. Але ён ёсць у скрынцы у puppet-agent, таму ставіць яго асобна не трэба. Дакументацыю па ім можна паглядзець у дакументацыі да пятай версіі Папета, Альбо на Гітхабе.

Пра рэсурсы ўвогуле

Патрабаванні да ўнікальнасці рэсурсаў

Самая частая памылка, з якой мы сустракаемся - Duplicate declaration. Гэтая памылка ўзнікае, калі ў каталог трапляюць два і больш рэсурсы аднолькавага тыпу з аднолькавай назвай.

Таму яшчэ раз напішу: у маніфестах для адной ноды не павінна быць рэсурсаў аднолькавага тыпу з аднолькавай назвай (title)!

Часам ёсць неабходнасць паставіць пакеты з аднолькавай назвай, але рознымі пакетнымі мэнэджэрамі. У такім разе трэба карыстацца параметрам name, каб пазбегнуць памылкі:

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

У іншых тыпах рэсурсаў ёсць аналагічныя параметры, якія дапамагаюць пазбегнуць дублікацыі, - name у абслугоўванне, command у Exec, і гэтак далей.

Метапараметры

Некаторыя спецыяльныя параметры ёсць у кожнага тыпу рэсурса, незалежна ад яго сутнасці.

Поўны спіс метапараметраў у дакументацыі Puppet.

Кароткі спіс:

  • патрабаваць - У гэтым параметры паказваецца, ад якіх рэсурсаў залежыць дадзены рэсурс.
  • да - У гэтым параметры паказваецца, якія рэсурсы залежаць ад дадзенага рэсурсу.
  • падпісвацца - У гэтым параметры паказваецца, ад якіх рэсурсаў атрымлівае апавяшчэння дадзены рэсурс.
  • паведаміць - У гэтым параметры паказваецца, якія рэсурсы атрымліваюць апавяшчэнні ад дадзенага рэсурсу.

Усе пералічаныя метапараметры прымаюць альбо адну спасылку на рэсурс, альбо масіў спасылак у квадратных дужках.

Спасылкі на рэсурсы

Спасылка на рэсурс - гэта проста згадванне рэсурсу. Выкарыстоўваюцца яны ў асноўным для ўказання залежнасцяў. Спасылка на неіснуючы рэсурс выкліча памылку кампіляцыі.

Сінтаксіс у спасылкі наступны: тып рэсурсу з вялікай літары (калі ў назве тыпу змяшчаюцца падвойныя двукроп'я, то з вялікай літары пішацца кожная частка назвы паміж двукроп'ямі), далей у квадратных дужках назва рэсурсу (рэгістра назвы не мяняецца!). Прабелаў быць не павінна, квадратныя дужкі пішуцца адразу пасля назвы тыпу.

Прыклад:

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

Залежнасці і апавяшчэнні

Дакументацыя тут.

Як ужо было сказана раней, простыя залежнасці паміж рэсурсамі транзітыўныя. Дарэчы, будзьце ўважлівыя пры прастаўленні залежнасцяў - можна зрабіць цыклічныя залежнасці, што выкліча памылку кампіляцыі.

У адрозненне ад залежнасцяў, апавяшчэнні не транзітыўныя. Для апавяшчэнняў дзейнічаюць наступныя правілы:

  • Калі рэсурс атрымлівае апавяшчэнне, ён абнаўляецца. Дзеянні пры абнаўленні залежаць ад тыпу рэсурсу. Exec запускае каманду, абслугоўванне перазапускае сэрвіс, пакет пераўсталёўвае пакет. Калі для рэсурса не вызначана дзеянне пры абнаўленні, то нічога не адбываецца.
  • За адзін прагон Папэта рэсурс абнаўляецца не больш за адзін раз. Гэта магчыма, так як апавяшчэння ўключаюць у сябе залежнасці, а граф залежнасцяў не змяшчае цыклаў.
  • Калі Папэт змяняе стан рэсурсу, то рэсурс адпраўляе апавяшчэнні ўсім падпісаным на яго рэсурсам.
  • Калі рэсурс абнаўляецца, то ён адпраўляе апавяшчэнні ўсім падпісаным на яго рэсурсам.

Апрацоўка неўказаных параметраў

Як правіла, калі ў нейкага параметра рэсурсу няма значэння па змаўчанні і гэты параметр не паказаны ў маніфесце, то Паппет не будзе змяняць гэтую ўласцівасць у які адпавядае рэсурсу на нодзе. Напрыклад, калі ў рэсурсу тыпу файл не паказаны параметр owner, то Паппет не будзе мяняць уладальніка ў адпаведнага файла.

Знаёмства з класамі, зменнымі і дэфайнамі

Выкажам здагадку, у нас некалькі нод, на якіх ёсць аднолькавая частка канфігурацыі, але ёсць і адрозненні - інакш мы маглі б апісаць гэта ўсё ў адным блоку node {}. Вядома, можна проста скапіяваць аднолькавыя часткі канфігурацыі, але ў агульным выпадку гэта дрэннае рашэнне - канфігурацыя разрастаецца, пры змене агульнай часткі канфігурацыі давядзецца кіраваць адно і тое ж у мностве месцаў. Пры гэтым лёгка памыліцца, ну і наогул прынцып DRY (don't repeat yourself) не проста так прыдумалі.

Для вырашэння такой праблемы ёсць такая канструкцыя, як клас.

Класы

Клас - гэта найменны блок папэт-кода. Класы патрэбны для перавыкарыстоўвання кода.

Спачатку клас трэба апісаць. Само па сабе апісанне не дадае нікуды ніякіх рэсурсаў. Клас апісваецца ў маніфестах:

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

Пасля гэтага клас можна выкарыстоўваць:

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

Прыклад з папярэдняй задачы - вынесем усталёўку і наладу nginx у клас:

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
}

зменныя

Клас з папярэдняга прыкладу зусім не гнуткі, таму што ён заўсёды прыносіць адну і тую ж канфігурацыю nginx. Давайце зробім так, каб шлях да канфігурацыі стаў зменным, тады гэты клас можна будзе выкарыстоўваць для ўсталёўкі nginx з любой канфігурацыяй.

Гэта можна зрабіць з дапамогай зменных.

Увага: зменныя ў Puppet нязменныя!

Акрамя таго, звяртацца да зменнай можна толькі пасля таго, як яе абвясцілі, інакш значэннем зменнай апынецца undef.

Прыклад працы са зменнымі:

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

У Puppet ёсць прасторы імён, а ў зменных, адпаведна, ёсць вобласць бачнасці: зменная з адным і тым жа імем можа быць вызначана ў розных прасторах імён. Пры дазволе значэння зменнай пераменная шукаецца ў бягучым неймспейсе, потым у аб'ёмным, і гэтак далей.

Прыклады прасторы імён:

  • глабальнае - туды трапляюць зменныя па-за апісаннем класа або ноды;
  • прастора імёнаў ноды ў апісанні ноды;
  • прастора імён класа ў апісанні класа.

Каб пазбегнуць неадназначнасці пры звароце да зменнай, можна паказваць прастору імёнаў у імені зменнай:

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

Дамовімся, што шлях да канфігурацыі nginx ляжыць у зменнай $nginx_conf_source. Тады клас будзе выглядаць наступным чынам:

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
}

Аднак прыведзены прыклад дрэнны тым, што ёсць нейкія «таемныя веды» аб тым, што дзесьці ўнутры класа выкарыстоўвае зменная з такім імем. Значна больш правільна зрабіць гэтыя веды агульнымі - у класаў могуць быць параметры.

Параметры класа - Гэта зменныя ў прасторы імёнаў класа, яны задаюцца ў загалоўку класа і могуць быць скарыстаны як звычайныя зменныя ў целе класа. Значэнні параметраў паказваецца пры выкарыстанні класа ў маніфесце.

Параметру можна задаць значэнне па змаўчанні. Калі ў параметру няма значэння па змаўчанні і значэнне не зададзена пры выкарыстанні, гэта выкліча памылку кампіляцыі.

Давайце параметрызуем клас з прыкладу вышэй і дадамо два параметры: першы, абавязковы - шлях да канфігурацыі, і другі, неабавязковы - назоў пакета з nginx (у Debian, напрыклад, ёсць пакеты nginx, nginx-light, nginx-full).

# переменные описываются сразу после имени класса в круглых скобках
class nginx_example (
  $conf_source,
  $package_name = 'nginx-light', # параметр со значением по умолчанию
) {
  package { $package_name:
    ensure => installed,
  }
  -> file { '/etc/nginx':
    ensure  => directory,
    source  => $conf_source,
    recurse => true,
    purge   => true,
    force   => true,
  }
  ~> service { 'nginx':
    ensure => running,
    enable => true,
  }
}

node 'server2.testdomain' {
  # если мы хотим задать параметры класса, функция include не подойдёт* — нужно использовать resource-style declaration
  # *на самом деле подойдёт, но про это расскажу в следующей серии. Ключевое слово "Hiera".
  class { 'nginx_example':
    conf_source => 'puppet:///modules/example/nginx-conf',   # задаём параметры класса точно так же, как параметры для других ресурсов
  }
}

У Puppet зменныя тыпізаваны. Ёсць шмат тыпаў дадзеных. Тыпы дадзеных звычайна выкарыстоўваюцца для валідацыі значэнняў параметраў, якія перадаюцца ў класы і дэфайны. Калі перададзены параметр не адпавядае паказанаму тыпу, адбудзецца памылка кампіляцыі.

Тып пішацца непасрэдна перад імем параметра:

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

Класы: include classname vs class{'classname':}

Кожны клас з'яўляецца рэсурсам тыпу клас. Як і ў выпадку з любымі іншымі тыпамі рэсурсаў, на адной нодзе не можа прысутнічаць два экзэмпляры аднаго і таго ж класа.

Калі паспрабаваць дадаць клас на адну і тую ж ноду два разы з дапамогай class { 'classname':} (без розніцы, з рознымі ці з аднолькавымі параметрамі), будзе памылка кампіляцыі. Затое ў выпадку выкарыстання класа ў стылі рэсурсу можна тут жа ў маніфесце відавочна задаць усе яго параметры.

Аднак калі выкарыстоўваць include, то клас можна дадаваць колькі заўгодна разоў. Справа ў тым што include - ідэмпатэнтная функцыя, якая правярае, ці дададзены клас у каталог. Калі класа ў каталогу няма - дадае яго, а калі ўжо ёсць, то нічога не робіць. Але ў выпадку выкарыстання include нельга задаць параметры класа падчас аб'явы класа - усе абавязковыя параметры павінны быць зададзены ў вонкавай крыніцы дадзеных - Hiera або ENC. Пра іх мы пагаворым у наступным артыкуле.

Дэфайны

Як было сказана ў папярэднім блоку, адзін і той жа клас не можа прысутнічаць на нодзе больш за адзін раз. Аднак у некаторых выпадках трэба мець магчымасць ужываць адзін і той жа блок кода з рознымі параметрамі на адной нодзе. Іншымі словамі, ёсць запатрабаванне ва ўласным тыпе рэсурсу.

Напрыклад, для таго, каб усталяваць модуль PHP, мы ў Авіта робім наступнае:

  1. Усталеўваны пакет з гэтым модулем.
  2. Ствараем канфігурацыйны файл для гэтага модуля.
  3. Ствараем сімлінк на канфіг для php-fpm.
  4. Ствараем сімлінк на канфіг для php cli.

У такіх выпадках выкарыстоўваецца такая канструкцыя, як дэфайн (define, defined type, defined resource type). Дэфайн падобны на клас, але ёсць адрозненні: па-першае, кожны дэфайн з'яўляецца тыпам рэсурсу, а не рэсурсам; па-другое, у кожнага дэфайну ёсць няяўны параметр $title, куды трапляе імя рэсурсу пры яго аб'яўленні. Гэтак жа як і ў выпадку з класамі, дэфайн спачатку трэба апісаць, пасля гэтага яго можна выкарыстоўваць.

Спрошчаны прыклад з модулем для 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' }
}

У дэфайне прасцей за ўсё злавіць памылку Duplicate declaration. Гэта адбываецца, калі ў дэфайне ёсць рэсурс з канстантным імем, і на нейкай нодзе два і больш асобніка гэтага дэфайну.

Абараніцца ад гэтага проста: усе рэсурсы ўнутры дэфайну павінны мець назву, якая залежыць ад $title. У якасці альтэрнатывы - ідэмпатэнтнае даданне рэсурсаў, у найпростым выпадку досыць вынесці агульныя для ўсіх асобнікаў дэфайна рэсурсы ў асобны клас і інклюдзіць гэты клас у дэфайне - функцыя include ідэмпатэнтная.

Ёсць і іншыя спосабы дасягнуць ідэмпатэнтнасці пры даданні рэсурсаў, а менавіта выкарыстанне функцый defined и ensure_resources, Але пра гэта раскажу ў наступнай серыі.

Залежнасці і апавяшчэнні для класаў і дэфайнаў

Класы і дэфайны дадаюць наступныя правілы да апрацоўкі залежнасцяў і апавяшчэнняў:

  • залежнасць ад класа/дэфайну дадае залежнасці ад усіх рэсурсаў класа/дэфайну;
  • залежнасць класа/дэфайну дадае залежнасці ўсім рэсурсам класа/дэфайну;
  • апавяшчэнне класа/дэфайну паведамляе ўсе рэсурсы класа/дэфайну;
  • падпіска на клас/дэфайн падпісвае на ўсе рэсурсы класа/дэфайну.

Умоўныя аператары і селектары

Дакументацыя тут.

if

Тут усё проста:

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

калі не

unless - гэта if наадварот: блок кода будзе выкананы, калі выраз ілжыва.

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

выпадак

Тут таксама нічога складанага. У якасці значэнняў можна выкарыстоўваць звычайныя значэнні (радкі, лікі і гэтак далей), рэгулярныя выразы, а таксама тыпы дадзеных.

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

Селектар

Селектар - гэта моўная канструкцыя, падобная на case, толькі замест выканання блока кода яна вяртае значэнне.

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

модулі

Калі канфігурацыя маленькая, яе лёгка можна трымаць у адным маніфэсце. Але чым больш канфігурацыі мы апісваем, тым больш класаў і нод становіцца ў маніфесце, ён разрастаецца, з ім становіцца няёмка працаваць.

Акрамя таго, ёсць праблема перавыкарыстання кода - калі ўвесь код у адным маніфесце, складана гэтым кодам дзяліцца з іншымі. Для вырашэння гэтых двух праблем у Puppet ёсць такая сутнасць, як модулі.

модулі - Гэта наборы класаў, дэфайнаў і іншых Puppet-сутнасцяў, вынесеных у асобную дырэкторыю. Іншымі словамі, модуль - гэта незалежны кавалак Puppet-логікі. Напрыклад, можа быць модуль для працы з nginx, і ў ім будзе тое і толькі тое, што трэба для працы з nginx, а можа быць модуль для працы з PHP і гэтак далей.

Модулі версіянуюцца, таксама падтрымліваюцца залежнасці модуляў сябар ад сябра. Ёсць адкрыты рэпазітар модуляў. Лялечная кузня.

На папэт-серверы модулі ляжаць у паддырэкторыі modules каранёвай дырэкторыі. Унутры кожнага модуля стандартная схема дырэкторый – manifests, files, templates, lib і гэтак далей.

Структура файлаў у модулі

У корані модуля могуць быць наступныя дырэкторыі з размаўлялымі назовамі:

  • manifests - у ёй ляжаць маніфесты
  • files - У ёй ляжаць файлы
  • templates - У ёй ляжаць шаблоны
  • lib - у ёй ляжыць Ruby-код

Гэта не поўны спіс дырэкторый і файлаў, але для гэтага артыкула пакуль дастаткова.

Назвы рэсурсаў і імёны файлаў у модулі

Дакументацыя тут.

Рэсурсы (класы, дэфайны) у модулі нельга называць як заўгодна. Акрамя таго, ёсць прамое адпаведнасць паміж назвай рэсурсу і імем файла, у якім Puppet будзе шукаць апісанне гэтага рэсурсу. Калі парушаць правілы наймення, то Puppet проста не знойдзе апісанне рэсурсаў, і атрымаецца памылка кампіляцыі.

Правілы простыя:

  • Усе рэсурсы ў модулі павінны быць у неймспейсе модуля. Калі модуль называецца foo, то ўсе рэсурсы ў ім павінны называцца foo::<anything>, альбо проста foo.
  • Рэсурс з назвай модуля павінен быць у файле init.pp.
  • Для астатніх рэсурсаў схема наймення файлаў наступная:
    • прэфікс з імем модуля адкідаецца
    • усе падвойныя двукроп'я, калі яны ёсць, замяняюцца на сляшы
    • дапісваецца пашырэнне .pp

Прадэманструю на прыкладзе. Выкажам здагадку, я пішу модуль nginx. У ім ёсць наступныя рэсурсы:

  • клас nginx апісаны ў маніфесце init.pp;
  • клас nginx::service апісаны ў маніфесце service.pp;
  • дэфайн nginx::server апісаны ў маніфесце server.pp;
  • дэфайн nginx::server::location апісаны ў маніфесце server/location.pp.

шаблоны

Напэўна, вы і самі ведаеце, што такое шаблоны, не буду распісваць тут падрабязна. Але на ўсялякі выпадак пакіну спасылку на Вікіпедыю.

Як выкарыстоўваць шаблоны: значэнне шаблона можна расчыніць з дапамогай функцыі template, якой перадаецца шлях да шаблону. Для рэсурсаў тыпу файл выкарыстоўваем разам з параметрам content. Напрыклад, так:

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

Шлях віду <modulename>/<filename> мае на ўвазе файл <rootdir>/modules/<modulename>/templates/<filename>.

Акрамя таго, ёсць функцыя inline_template - ёй на ўваход перадаецца тэкст шаблона, а не імя файла.

Усярэдзіне шаблонаў можна выкарыстоўваць усе зменныя Puppet у бягучай вобласці бачнасці.

Puppet падтрымлівае шаблоны ў фармаце ERB і EPP:

Сцісла пра ERB

Кіравальныя канструкцыі:

  • <%= ВЫРАЖЕНИЕ %> - Уставіць значэнне выразу
  • <% ВЫРАЖЕНИЕ %> - Вылічыць значэнне выраз (не ўстаўляючы яго). Сюды просты ідуць умоўныя аператары (if), цыклы (each).
  • <%# КОММЕНТАРИЙ %>

Выразы ў ERB пішуцца на Ruby (уласна, ERB – гэта Embedded Ruby).

Для доступу да пераменных з маніфэсту трэба дапісаць @ да імя зменнай. Каб прыбраць пераклад радка, які з'яўляецца пасля кіравальнай канструкцыі, трэба выкарыстоўваць які зачыняе тэг -%>.

Прыклад выкарыстання шаблона

Выкажам здагадку, я пішу модуль для кіравання ZooKeeper. Клас, які адказвае за стварэнне канфіга, выглядае прыкладна так:

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

А адпаведны яму шаблон zoo.cfg.erb - так:

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

Факты і ўбудаваныя зменныя

Часцяком пэўная частка канфігурацыі залежыць ад таго, што ў дадзены момант адбываецца на нодзе. Напрыклад, у залежнасці ад таго, які рэліз Debian варта, трэба ўсталяваць тую ці іншую версію пакета. Можна сачыць за гэтым усім уручную, перапісваючы маніфесты ў выпадку змены нод. Але гэта несур'ёзны падыход, аўтаматызацыя значна лепшая.

Для атрымання інфармацыі аб нодах у Puppet ёсць такі механізм, як факты. факты - гэта інфармацыя аб нодзе, даступная ў маніфестах у выглядзе звычайных зменных у глабальнай прасторы імёнаў. Напрыклад, імя хаста, версія аперацыйнай сістэмы, архітэктура працэсара, спіс карыстачоў, спіс сеткавых інтэрфейсаў і іх адрасоў, і шматлікае, шматлікае іншае. Факты даступныя ў маніфестах і шаблонах як звычайныя зменныя.

Прыклад працы з фактамі:

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

Калі казаць фармальна, то ў факту ёсць імя (радок) і значэнне (даступныя розныя тыпы: радкі, масівы, слоўнікі). Ёсць набор убудаваных фактаў. Таксама можна пісаць уласныя. Зборшчыкі фактаў апісваюцца як функцыі на Ruby, Альбо як выкананыя файлы. Таксама факты могуць быць прадстаўлены ў выглядзе тэкставых файлаў з дадзенымі на нодах.

Падчас працы папэт-агент спачатку капіюе з папетсервера на ноду ўсе даступныя зборшчыкі фактаў, пасля чаго запускае іх і адпраўляе на сервер сабраныя факты; ужо пасля гэтага сервер пачынае кампіляцыю каталога.

Факты ў выглядзе выкананых файлаў

Такія факты кладуцца ў модулі ў дырэкторыю facts.d. Зразумела, файлы павінны быць выкананымі. Пры запуску яны павінны выводзіць на стандартную выснову інфармацыю альбо ў фармаце YAML, альбо ў фармаце "ключ = значэнне".

Не забывайце, што факты распаўсюджваюцца на ўсе ноды, якія знаходзяцца пад кіраваннем папэт-сервера, на які выкочваецца ваш модуль. Таму ў скрыпце заклапоціцеся праверкай таго, што ў сістэме ёсць усе неабходныя для працы вашага факту праграмы і файлы.

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

Факты на Ruby

Такія факты кладуцца ў модулі ў дырэкторыю 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

Тэкставыя факты

Такія факты кладуцца на ноды ў дырэкторыю /etc/facter/facts.d у старым Папеце ці /etc/puppetlabs/facts.d у новым Папеце.

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

Зварот да фактаў

Звярнуцца да фактаў можна двума спосабамі:

  • праз слоўнік $facts: $facts['fqdn'];
  • выкарыстоўваючы імя факта як імя зменнай: $fqdn.

Лепш за ўсё выкарыстоўваць слоўнік $facts, а яшчэ лепш паказваць глабальны неймспейс ($::facts).

Вось патрэбны раздзел дакументацыі.

Убудаваныя зменныя

Акрамя фактаў, ёсць яшчэ некаторыя зменныя, даступныя ў глабальным прасторы імёнаў.

  • trusted facts - зменныя, якія бяруцца з сертыфіката кліента (бо сертыфікат звычайна выпускаецца на папэт-серверы, агент не можа проста так узяць і памяняць свой сертыфікат, таму зменныя і «давераныя»): назва сертыфіката, імя хаста і дамена, пашырэнні з сертыфіката.
  • server facts -пераменныя, якія адносяцца да інфармацыі аб серверы - версія, імя, IP-адрас сервера, асяроддзе.
  • agent facts - зменныя, якія дадаюцца непасрэдна puppet-agent'ам, а не facter'ом - назва сертыфіката, версія агента, версія паппета.
  • master variables - зменныя папетмайстра (sic!). Там прыкладна тое ж самае, што ў server facts, плюс даступныя значэння канфігурацыйных параметраў.
  • compiler variables - зменныя кампілятара, якія адрозніваюцца ў кожнай вобласці бачнасці: імя бягучага модуля і імя модуля, у якім было зварот да бягучага аб'екта. Іх можна выкарыстоўваць, напрыклад, каб правяраць, што вашыя прыватныя класы не выкарыстоўваюць напрамую з іншых модуляў.

Дадатак 1: як гэта ўсё запускаць і дэбажыць?

У артыкуле было шмат прыкладаў puppet-кода, але зусім не было расказана, як жа гэты код запускаць. Што ж, выпраўляюся.

Для працы Puppet дастаткова агента, але для большасці выпадкаў патрэбен будзе і сервер.

агент

Як мінімум з пятай версіі пакеты puppet-agent з афіцыйнага рэпазітара Puppetlabs утрымоўваюць у сабе ўсе залежнасці (ruby і якія адпавядаюць gem'ы), таму складанасцяў з усталёўкай ніякіх няма (кажу пра Debian-based дыстрыбутывы — RPM-based дыстрыбутывамі мы не карыстаемся).

У найпростым выпадку для ўжывання puppet-канфігурацыі дастаткова запусціць агент у бессерверным рэжыме: пры ўмове, што puppet-код скапіяваны на ноду, запускаеце 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

Лепш, вядома, падняць сервер і запусціць агенты на нодах у рэжыме дэмана - тады раз у паўгадзіны яны будуць ужываць канфігурацыю, запампаваную з сервера.

Можна імітаваць push-мадэль працы - зайсці на цікавую для вас ноду і запусціць sudo puppet agent -t. Ключ -t (--test) на самой справе ўключае некалькі опцый, якія можна ўключаць і па асобнасці. Сярод гэтых опцый наступныя:

  • не працаваць у рэжыме дэмана (па змаўчанні агент запускаецца ў рэжыме дэмана);
  • завяршыць працу пасля ўжывання каталога (па змаўчанні агент працягне працу і будзе ўжываць канфігурацыю раз у паўгадзіны);
  • пісаць падрабязны лог працы;
  • паказваць змены ў файлах.

У агента ёсць рэжым працы без змен - ім можна карыстацца ў выпадку, калі вы не ўпэўненыя, што напісалі карэктную канфігурацыю, і жадаеце праверыць, што менавіта памяняе агент падчас працы. Уключаецца гэты рэжым параметрам --noop у камандным радку: sudo puppet agent -t --noop.

Акрамя таго, можна ўключыць адладкавы лог працы - у ім puppet піша пра ўсе дзеянні, якія ён вырабляе: аб рэсурсе, які ў дадзены момант апрацоўвае, аб параметрах гэтага рэсурсу, аб тым, якія праграмы запускае. Зразумела, гэта параметр --debug.

Сервер

Паўнавартасную наладу папетсервера і дэплой на яго кода ў гэтым артыкуле я не буду разглядаць, скажу толькі, што са скрынкі ставіцца суцэль працаздольная версія сервера, не патрабавальная дадатковай налады для працы ва ўмовах невялікай колькасці нод (скажам, да ста). Большая колькасць нод ужо запатрабуе цюнінгу – па змаўчанні puppetserver запускае не больш за чатырох воркераў, для большай прадукцыйнасці трэба павялічыць іх лік і не забыцца павялічыць ліміты памяці, інакш большую частку часу сервер будзе garbage collect'ить.

Дэплой кода - калі трэба хутка і проста, то глядзіце (на r10k)https://github.com/puppetlabs/r10k], для невялікіх усталёвак яго суцэль павінна хапіць.

Дадатак 2: рэкамендацыі па напісанні кода

  1. Выносьце ўсю логіку ў класы і дэфайны.
  2. Трымайце класы і дэфайны ў модулях, а не ў маніфестах з апісаннем нод.
  3. Карыстайцеся фактамі.
  4. Не рабіце if'ов па хостнеймам.
  5. Не саромейцеся дадаваць параметры для класаў і дэфайнаў - гэта лепш, чым няяўная логіка, схаваная ў целе класа / дэфайну.

А чаму я рэкамендую так рабіць - растлумачу ў наступным артыкуле.

Заключэнне

На гэтым скончым з увядзеннем. У наступным артыкуле раскажу пра Hiera, ENC і PuppetDB.

Толькі зарэгістраваныя карыстачы могуць удзельнічаць у апытанні. Увайдзіце, Калі ласка.

Насамрэч, матэрыялу значна больш — я магу напісаць артыкулы на наступныя тэмы, прагаласуйце, пра што вам цікава было б пачытаць:

  • 59,1%Advanced puppet constructs — some next-level shit: цыклы, мэпінг і іншыя лямбда-выразы, калектары рэсурсаў, экспартуемыя рэсурсы і міжхаставое ўзаемадзеянне праз Puppet, тэгі, правайдэры, абстрактныя тыпы дадзеных.
  • 31,8%«Я ў мамкі адмін» ці як мы ў Авіта пасябравалі некалькі папэт-сервераў розных версій, ну і ў прынцыпе частка пра адміністраванне паппет-сервера.7
  • 81,8%Як мы пішам папэт-код: інструментальная абвязка, дакументацыя, тэсціраванне, CI/CD.18

Прагаласавалі 22 карыстальніка. Устрымаліся 9 карыстальнікаў.

Крыніца: habr.com