Всем привет.

Знакомство с паппетом у меня началось достаточно недавно, году в 2021, и на тот момент когда я решил, что в моем "хозяйстве" стоит попробовать его внедрить, я начал искать всевозможную информацию по нему. Информации только на хабре можно почерпнуть достаточно много, но к сожалению, в самом начале достаточно сложно уложить всё прочитанное в голове, особенно когда в одной статье говорится про использование паппета без ENC (которое тоже не сильно понятно для чего нужно в самом начале), в другой приводятся примеры использования с Hiera, а в третьей статье рассказывается про то, как написать свой собственный манифест, без привязки к пути, по которому выполняется управление хостами.

Лично мне потребовалось достаточно много времени чтобы понять, как всё описанное связывается с конкретными компами, и как вообще я должен всем этим управлять. В итоге собрав свой зоопарк я всё же смог всё уложить в голове, и хочу оставить на хабре всё то, чего я хотел бы узнать в самом начале, когда только начинался мой путь постижения паппета. Если вы только начинаете изучать Puppet (в связке с Foreman с которым я всё это и изучал), то возможно я смогу под катом ответить на возникшие вопросы.

Для начала

Хочу предупредить - всё что я буду описывать является лишь упрощением того как есть на самом деле. В большинстве случаев опущены функции которые также могут выполняться описанным, или например связь одного со вторым может и будет двусторонней, но это должно быть осознано уже при непосредственном использовании, а чтобы непосредственно начать использовать, нужно понять как всё это хотя бы условно связано. Я по мере возможностей буду описывать, что ещё возможно в том или ином случае, но в самом начале всё будет максимально просто, чтобы уложить это в голове.

Что есть что

Первым делом когда я только приступил к изучению паппета, я не мог понять, как в принципе взаимосвязаны Puppet, Foreman, группы хостов и параметры хостов и групп, и как вообще хосты определяют, что именно из написанного предназначается конкретно им, а не соседним хостам.

Для ответа на этот вопрос я предложу следующую схему:

Рисунок 1 - Как всё связано
Рисунок 1 - Как всё связано
  • Admin - условный админ компов, который будет решать, какие конкретно классы должны быть применены на каком компе. Он по сути решает, как делить хосты на группы, какие параметры хостам и группам должны задаваться. Также он "одобряет" подключение агента к серверу.

  • Foreman - та web часть, с которой админ будет всё время работать. В формане хранится весь тот набор "одобренных" хостов, списка групп и принадлежность хостов группам, соответствие классов хостам и группам, а также значения параметров, необходимых для применения классов.

  • Puppet Server - та самая "подкапотная" часть, которая обрабатывает соединения Puppet agent'ов установленных на управляемых хостах. Когда агент подключается к серверу, сервер "спрашивает" у Foreman'а, какие классы необходимо применить на этом самом хосте - форман в соответствии с тем, что настроил Admin уже подготавливает список, какие классы необходимо применять, выдает список значений параметров классов, которые должны использоваться, и сервер после этого выдаёт агенту ответ, что необходимо применить. Сами классы, которые доступны для применения на хостах хранятся именно на нём.

  • Programmer - это в нашем случае будет тот же самый Admin в лице нас, который будет писать свои собственные классы, которые уже будут храниться на Puppet Server. В большинстве примеров предлагается установить уже существующий модуль, список которых вы можете посмотреть на сайте Puppet Forge, однако возможность написания своего класса для настройки всего что угодно является одной из важнейших во всей этой затее с использованием паппета.

  • Puppet agent - та самая клиентская часть, которая устанавливается на хосте и работает в виде сервиса, и раз в 30 минут выполняет подключение к серверу, и каждый раз применяет классы, которые сервер передал агенту на исполнение.

Кратко об установке

Установка вполне описана на офф сайте, всё что нужно это в соответствии со своим дистрибутивом (в моем случае debian 11) пройти по инструкции https://www.theforeman.org/manuals/3.8/quickstart_guide.html

Всё что нужно будет после подготовки сделать, это выполнять инструкции команды foreman-installer (которая по сути установит всё что нужно, в том числе и сервер паппета, и агента паппета для управления этим сервером) до тех пор пока установка не пройдет успешно (с выводом сгенерированного пароля который понадобится для входа).

Спойлер - для установки требуется:

  1. Задать имя хоста в формате fqdn - задать хотя бы домен чтобы имя хоста было puppet-test.local

  2. В файле /etc/hosts добавить строку вида 192.168.0.2 puppet-test.local puppet-test чтобы fqdn был первым в списке имен.

Результирующий вывод, который скажет что всё в порядке, это

Executing: foreman-rake upgrade:run
  Success!
  * Foreman is running at https://puppet-test.local
      Initial credentials are admin / mAeJ2PazP37qHfQm
  * Foreman Proxy is running at https://puppet-test.local:8443

The full log is at /var/log/foreman-installer/foreman.log

В котором как раз и выведен нужный нам для входа пароль. Не забудьте первым делом после входа поменять пароль на нужный (правый верхний угол, через настройки профиля) или же хотя бы его скопипастить.

Беглое знакомство с интерфейсом

Думаю прежде чем пойти объяснять что-то, что работает под капотом, было бы неплохо увидеть это в живую (хотя бы на скринах), чтобы осознавать о чём будет идти речь дальше.

Первое что будет видно при входе это список хостов, и самый первый же хост - наш сервер. Сервер также управляется паппетом, потому можно использовать на нём уже существующие модули и классы.

Рисунок 2 - Первый вход
Рисунок 2 - Первый вход

Я позволил себе чуть-чуть по создавать новых объектов и сразу добавить puppet-test.local в новую, только что созданную группу чтобы было чего показывать.

По началу нам будут интересны списки групп:

Рисунок 3 - Host Groups
Рисунок 3 - Host Groups

И списки классов

Рисунок 4 - Classes
Рисунок 4 - Classes

Как, надеюсь, уже успели понять из прочитанного выше, основной смысл настройки хостов заключается в составлении соответствий между Host Groups и Classes (хотя можно настраивать соответствие классов с одиночными хостами, интерфейс при этом по сути один и тот же).

Но всё же, прежде чем начинать настраивать эти соответствия, давайте немного углубимся в теорию.

Чуть поподробнее об используемом

Foreman в нашем случае является ENC (external node classifier) для Puppet Server, и если вы читали статьи, в которых предлагается что-то настраивать с использованием Hiera (ей я не пользовался), то значит она заменяет Foreman на рисунке 1, и Admin должен будет уже не в вэбе делать всё то что я описал выше, а в текстовых конфигах хиеры, и принципиальная разница от этого не изменится - просто сервер будет запрашивать что применять не у формана а у неё. Ещё как вариант можно описывать хосты без использования ENC, в файлах паппета на сервере, он с этим вполне справится, но это уже другие случаи, которые к статье не относятся.

Также для понимания стоит описать, что же такое класс - это файл который должен быть сохранен в определенном каталоге на сервере и должен быть импортирован в формана для его дальнейшего использования (об этом чуть ниже), который в определенном формате описывает, что должно быть этим классом настроено. Например у нас есть некая прога, которая должна быть установлена и настроена на подключение к определенному серверу в сети - в таком случае мы должны описать, что эта прога должна быть установлена (ресурс package), сервис должен быть добавлен в автозагрузку и запущен всегда (ресурс service), а в конфиге этого сервиса должны быть прописаны нужные нам параметры, которые могут отличаться от хоста к хосту или от группы к группе, что будет реализовано в виде параметров в формане, и потому мы должны будем сгенерировать файл конфига (ресурс file или ресурс ini_setting).

Ресурсы это "объекты" которые доступны для использования в классах и являются независимыми от того какой это дистрибутив, они выполняют настройку - например ресурс package это "объект" который будет устанавливать указанный пакет через доступного в этой операционной системе пакетный менеджер.

Весь набор который нам нужно настроить может быть полностью написан нами, тем самым мы создаем свой собственный модуль - один или несколько классов нужных для этого, либо, если это уже типовая задача, то скачиваем и устанавливаем готовый модуль с Puppet Forge.

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

class install_and_configure_famous_prog ($serverName = 'default.server.example.com') {
  package {'famous_prog':
    ensure => 'latest',
  }

  service {'famous_prog':
    ensure => 'running',
    enable => true,
  }

  file {'famous_prog_config':
    ensure => 'present',
    path => '/etc/famous_prog/config',
    content => "server = $serverName",
    notify => Service['famous_prog'],
  }
}

ресурс package с помощью apt или yum (или даже chocolatey - менджер пакетов в винде, но с дополнительной установкой "зависимостей" при установке агента на винде) установит нужный пакет, ресурс service через команды systemctl (systemd) или service (system V) сделает enable и start (или в случае винды через виндовую команду service всё аналогичное), а file создаст файл с требуемым содержимым, и благодаря notify запросит рестарт сервиса famous_prog после изменения конфига (если файл поменялся).

Итоговый класс будет под этим именем (Рисунок 4) присваиваться хосту или группе, и таким образом при следующем подключении агента данный класс применится, и на всех хостах входящих в хостгруппу (Рисунок 3) кому класс был присвоен программа будет установлена и настроена.

То как именно правильно писать свои классы (модули) нужно будет читать и практиковаться отдельно, я же лишь хочу прояснить, что именно эти написанные или скачанные модули и будут тем самым, что Admin будет в Foreman привязывать хосту или группе хостов на исполнение.

Присвоение класса хост группе

Давайте вернемся к примерам со скринами - допустим мы хотим хостгруппе repo-servers (Рисунок 3) присвоить класс install_and_configure_famous_prog, который будет выполнять установку нужной нам программы на всех хостах из этой группы - для этого нужно открыть хостгруппу на редактирование и на вкладке Puppet ENC просто выбрать нужный нам класс нажатием "+" - всё, после сохранения написанный нами класс будет применён в течении получаса (дефолтное время через которое агенты паппета на хостах синхронизируются с сервером) на всех хостах группы.

Рисунок 5 - Присвоение класса хостгруппе
Рисунок 5 - Присвоение класса хостгруппе

Раз уж на примере уже посмотрели как происходит присвоение классов, пойдем дальше в теорию.

Окружения

Ко всему описанному ещё нужно добавить такое понятие как environment (окружение), создание которых также доступно и должно быть использовано, в дополнении к дефолтному под названием production, в которое все новые хосты добавляются. Основной функцией окружений я бы считал возможность установки или создания модулей для каждого окружения по отдельности. Как пример, к которму пришел я, могу привести создание доп окружения testing, в котором хранятся копии всех модулей, что хранятся в окружении production, и которые можно спокойно править не боясь сломать то, что сейчас уже работает, а после опробования взять и скопировать в прод.

Пример файла с классом install_and_configure_famous_prog описанным выше, который будет доступен в окружении testing должен быть сохранен по пути:

/etc/puppetlabs/code/environments/testing/modules/install_and_configure_famous_progs/manifests/init.pp

Паппет опирается на определенную иерархию папок, в которых названия имеют значения - прежде всего это папка где хранится по сути всё связанное с модулями и классами - каталог /etc/puppetlabs/code:

.
├── environments
│   ├── common
│   └── production
│       ├── data
│       ├── environment.conf
│       ├── hiera.yaml
│       ├── manifests
│       └── modules
│           └── install_and_configure_famous_prog
│               └── manifests
│                   └── init.pp
└── modules

В папке ./modules будут появляться все установленные "глобально" модули, т.е. доступные для любого environment.

В папке ./environments содержатся каталоги с названиями окружений. Если требуется создать новое окружение, то оно будет создаваться новой папкой внутри ./environments с созданием иерархии аналогичной ./environments/production.

Папка ./environments/production это по сути дефолтное окружение, которое будет присваиваться всем вновь добавленным хостам (это настраиваемо), и оно будет ограничивать список классов доступных для присвоения хостгруппам лишь теми, что лежат в этой папке (или из числа глобальных).

Весь список мы рассматривать не будем (так как часть относится как вы догадались к Hiera - другой ENC которая также доступна для использования), потому обратим внимание лишь на два каталога:

  1. ./environments/production/manifests

  2. ./environments/production/modules

Отличие первого от второго в том, что в modules можно хранить в одной "куче" всё что относится к нашему модулю, в то время как в manifests можно сразу создавать файлы с классами, которые также будут доступны для присвоения хостгруппам.

В ./environments/production/modules/{module_name} как в примере с install_and_configure_famous_prog можно группировать связанные с нашим классом файлы и шаблоны, которые в этой статье не рассматривались, но можно было бы сделать иерархию вида

environments/
└── production
    └── modules
        └── install_and_configure_famous_prog
            ├── files
            │   └── file.jpg
            ├── manifests
            │   └── init.pp
            └── templates
                └── config_template.erb

и все файлы и шаблоны будут доступны для использования в коде нашего класса.

В принципе создавать классы сразу в папке ./environments/production/manifests никто не запрещает, но и создавать всё в виде модулей с расчетом что в будущем может понадобиться расширение функционала тоже никто не запрещает - я лично создаю всё модулями.

Самое главное, что как только добавляется новый класс или меняется его список переменных, то форману нужно сообщить об этих изменениях через импорт окружений, что заставляет его перечитать весь список классов с предложением подтвердить изменения - делается это через страницу Configure -> Puppet ENC -> Environments и нажать кнопку "Import environments from puppet-test.local". Повторный импорт класса не требуется при изменении внутренностей класса, а только лишь при изменении списка параметров.

Локации и организации

Под конец, для создания общей картины формана хотелось бы вкратце упомянуть о таких вещах как Location и Organization.

Это достаточно мощные по функционалу объекты, которые при продвинутом использовании позволяют значительно упростить администрирование большого числа хостов, однако в случае нашей статьи, когда нужно лишь понять основы, локации и организации могут восприниматься как способы фильтрации списка хостов - в формане по дефолту идут уже созданные Default Location и Default Organization в которые вновь добавленные хосты прописываются, а новые можно создать соответственно в меню Administration -> Locations и Administration -> Organizations, после чего назначить хосты в эти самые локации и организации - в самом простом варианте это будет работать как отдельные "контейнеры" хостов, к которым можно регулировать доступ для разных ролей.

В более сложном варианте для локаций и организаций можно прописывать ещё и параметры, которые будут доступны внутри кода классов, что с учетом наследования и переопределения параметров между организацией, локацией, хостгруппой и хостом может дать дополнительные возможности в коде классов, но это уже не относится к теме статьи.

Подключение нового хоста

Думаю что описанного достаточно для того, чтобы начать свои собственные эксперименты с написанием собственных классов и применять их на тестовой виртуалке, которая будет выступать в качестве нашего подопытного - тем ни менее, подключение хоста к паппету это не самый лёгкий квест, и я постараюсь вкратце описать как это провернуть, чтобы наконец подопытный приступил к своим обязанностям.

В принципе паппет сервер вполне способен работать с агентами разных веток, и лучше всего это описывает страница https://www.puppet.com/docs/puppet/7/primary_agent_compatibility.html

Для примера я возьму в качестве подопытного всё тот же Debian 11, хотя никто не запрещает взять и другие дистрибутивы, даже RPM-based. В идеале даже я бы предложил использовать двух подопытных, DEB-based и RPM-based и одновременно на них проверять сотворенное.

Первым делом нужно раздобыть пакет агента паппета - в моём случае я пойду в репо https://apt.puppet.com/pool/bullseye/puppet7/p/puppet-agent/index.html и скачаю последнюю версию пакета, который при соответствии релиза дебиана поставится одним пакетом без проблем. В идеале конечно стоит подключить репозиторий паппета, чтобы очередные обновления агента прилетали обычным apt upgrade, но для быстрого старта нам пойдет и разовая установка агента.

После того как были выполнены команды wget и apt install нужно подготовить нашего подопытного для знакомства с сервером - во первых, в /etc/hosts нужно прописать соответствие адреса сервера и его ip - паппету важно работать по fqdn - прописываем ту же самую строку что мы прописывали на сервере в момент запуска foreman-installer.

Агент паппета устанавливается не в /bin потому чтобы можно было вызывать его не по полному пути нужно обновить переменные окружения перелогинившись, либо использовать полный путь /opt/puppetlabs/bin/puppet.

Далее нужно откорректировать конфиг агента, чтобы указать к какому серверу с какими параметрами нужно подключаться - для нашего простого случая будет достаточно в файле /etc/puppetlabs/puppet/puppet.conf прописать:

server = puppet-test.local
ca_server = puppet-test.local

И выполнить первую синхронизацию агента с сервером:

/opt/puppetlabs/bin/puppet agent --test --onetime

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

Info: Creating a new RSA SSL key for agent-test
Info: csr_attributes file loading from /etc/puppetlabs/puppet/csr_attributes.yaml
Info: Creating a new SSL certificate request for agent-test
Info: Certificate Request fingerprint (SHA256): D9:93:CE:72:B9:28:81:6E:E5:78:75:AF:BD:C5:D0:CF:95:27:58:9A:80:49:64:D4:09:73:4D:48:7E:C6:EC:B2
Info: Certificate for agent-test has not been signed yet
Couldn't fetch certificate from CA server; you might still need to sign this agent's certificate (agent-test).
Exiting now because the waitforcert setting is set to 0.

Подписать можно и через команду в консоли сервера, но давайте сделаем это через веб в формане - Infrastructure -> Smart Proxies -> puppet-test.local -> Puppet CA -> Certificates - там появится новый сертификат нашего подопытного, которого нужно подписать нажатием кнопки sign (на будущее - есть certificate autosign по маске сертификата):

После того как мы со стороны сервера подписали сертификат на стороне агента нужно ещё раз выполнить синхронизацию:

/opt/puppetlabs/bin/puppet agent --test --onetime

В этот раз синхронизация пойдет дальше а подтверждением того что всё прошло как нужно будут строки:

Info: Using environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Caching catalog for agent-test
Info: Applying configuration version '1699555233'
Info: Creating state file /opt/puppetlabs/puppet/cache/state/state.yaml
Notice: Applied catalog in 0.01 seconds

После этого можно будет пойти в список хостов формана и удостовериться что новый хост там появился.

После этого самое главное не забыть добавить в автозагрузку и запустить сервис паппета:

systemctl enable puppet && systemctl start puppet

Наш подопытный готов к исполнению своих обязанностей.

Первый эксперимент

Чтобы не гадать над моим абстрактным классом install_and_configure_famous_prog предлагаю сделать первые шаги на конкретном примере:

mkdir -p /etc/puppetlabs/code/environments/production/modules/my_first_class/manifests

После этого создаем новый файл /etc/puppetlabs/code/environments/production/modules/my_first_class/manifests/init.pp и указать его содержимое:

class my_first_class {
  package {'htop':
    ensure => 'installed',
  }
}

После чего выполнить импорт нового класса нажатием кнопки "Import environments from puppet-test.local":

Подтверждаем добавление нового класса

После чего новый класс готов к использованию в хостгруппах.

Всё, для того чтобы создать новую хостгруппу привязать к ней новый класс, назначить эту хостгруппу нашему подопытному, и выполнить принудительную синхронизацию для того чтобы пакет htop установился автоматически (что на дебиане, что на федоре) нашим тестовым классом вы уже должны были узнать из этой статьи - начинайте пробовать отредактировать класс my_first_class добавляя в него использование разных ресурсов (https://www.puppet.com/docs/puppet/7/cheatsheet_core_types#cheatsheet_core_types - встроенные, не требующие установки новых модулей), посмотрите что можно увидеть на странице Monitor -> Dashboard, зайдите на страницу с информацией о хосте и посмотрите отчеты о синхронизации, попробуйте установить первый понравившийся модуль с Puppet Forge - наш небольшой полигон для этого готов.

Заключение

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

Надеюсь что на часть вопросов, ради которых вы зашли под кат я смог ответить, если нет - быть может смогу ответить в комментариях?

Комментарии (0)