Skip to content

Puppet with Hiera/Hierarchy [Best Practice]

Вернёмся к нашим баранам. В прошлой статье мы представили, что у нас есть 2 физических сервера и поставили на них puppetmaster и puppetdb. В этой статье, попробуем так же по-пацански (aka best practice) правильно настроить hiera, “напишем” наш первый модуль и установим нормальный из реп форджа. Ну, поехали…

Теория


Что такое hiera? Это просто структурирование хранилище key/value в текстовом виде в файлах. Чтоб не расплываться в воде красивых и бесполезных слов теории как это любят делать разработчики в своей документации, скажу лишь, что мы будет использовать её для объявления классов в пределах хостов (нод, на языке puppet) и передачи чего-либо в переменные для этих классов. Кто мало-мальски знаком с программированием поймёт о чём речь.

Зачем нам нужны модули и при чём они тут вообще? По-сути, грубо говоря, модуль – это просто класс которым удобно управлять с точки зрения разработчика. Вообще классы можно писать в любой точке любого манифеста и объявлять их там же, но это вносит путаницу. Ну и так просто “правильно” по мнению разработчиков puppet, по-этому будем делать именно так. Например, puppetdb который мы ставили в прошлой статье, это тоже всего лишь модуль содержащий один одноимённый класс с кучей зависимостей, подклассов, функций и т.д.

Вообще, наш puppet-сервис может уже работать и без hiera, все старые его версии именно так и делают, в них hiera выступает не обязательным модулем всего лишь, но с недавних пор она стала обязательной части puppet’a. Ну ок, пусть будет обязательной, раз мы взялись воспроизводить best practice, значит будет делать от и до, чтоб по-пацански. Вот как в итоге всё будет работать.

2
Всё очень просто

Практика


Запускаем наши хосты puppet и puppetdb. По возможности обновляем их (Обновлять свои сервера лучше вообще как можно чаще. Мы противники устаревшего софта. Фу-фу-фу) и проверяем что наши демоны успешно стартовали.

puppet # ps ax | grep pupp
  804 ?        Ssl    2:34 /usr/bin/java -Xms4g -Xmx4g -XX:MaxPermSize=1g -XX:OnOutOfMemoryError=kill -9 %p -Djava.security.egd=/dev/urandom -cp /opt/puppetlabs/server/apps/puppetserver/puppet-server-release.jar clojure.main -m puppetlabs.trapperkeeper.main --config /etc/puppetlabs/puppetserver/conf.d -b /etc/puppetlabs/puppetserver/bootstrap.cfg
11088 pts/2    S+     0:00 grep --color=auto pupp


puppetdb # ps ax | grep pupp
  822 ?        Ssl    1:53 /usr/bin/java -Xmx2g -XX:OnOutOfMemoryError=kill -9 %p -Djava.security.egd=/dev/urandom -cp /opt/puppetlabs/server/apps/puppetdbpuppetdb.jar clojure.main -m puppetlabs.puppetdb.main --config /etc/puppetlabs/puppetdb/conf.d -b /etc/puppetlabs/puppetdb/bootstrap.cfg
 5823 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53518) idle
 5825 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53520) idle
 5826 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53522) idle
 5827 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53524) idle
 5828 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53526) idle
 5829 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53528) idle
 5830 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53530) idle
 5831 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53532) idle
 5832 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53534) idle
 5840 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53536) idle
 5842 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53538) idle
 5843 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53540) idle
 5844 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53542) idle
 5845 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53544) idle
 5846 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53546) idle
 5847 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53548) idle
 5848 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53550) idle
 5849 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53552) idle
 5850 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53554) idle
 5858 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53556) idle
 5860 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53558) idle
 5861 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53560) idle
 5862 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53562) idle
 5863 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53564) idle
 5864 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53566) idle
 5865 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53568) idle
 5866 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53570) idle
 5867 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53572) idle
 5868 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53574) idle
 5869 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53576) idle
 5870 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53578) idle
 5871 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53580) idle
 5872 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53582) idle
 5873 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53584) idle
 5884 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53586) idle
 5885 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53588) idle
 5886 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53590) idle
 5887 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53592) idle
 5888 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53594) idle
 5889 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53596) idle
 5890 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53598) idle
 5891 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53600) idle
 5892 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53602) idle
 5893 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53604) idle
 5894 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53606) idle
 5895 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53608) idle
 5902 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53610) idle
 5904 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53612) idle
 5905 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53614) idle
 5906 ?        Ss     0:00 postgres: puppetdb puppetdb 192.168.0.2(53616) idle
 5934 pts/0    S+     0:00 grep --color=auto pupp
 

Можно ещё логи почитать для верности, но мне лень, я уверен у нас всё норм.
Приступим. Начнём с простого: Установим какой-нить простенький модуль и заюзаем его на ноде puppetdb с помощью hiera. Модуль поставим как в официальной доке – ntp.

puppet # puppet module install puppetlabs-ntp
Notice: Preparing to install into /etc/puppetlabs/code/environments/production/modules ...
Notice: Downloading from https://forgeapi.puppetlabs.com ...
Notice: Installing -- do not interrupt ...
/etc/puppetlabs/code/environments/production/modules
L-T puppetlabs-ntp (v4.2.0)
  L-- puppetlabs-stdlib (v4.11.0)

Смотрим список для пущей убедительности

puppet # puppet module list
/etc/puppetlabs/code/environments/production/modules
+-- puppetlabs-apt (v2.2.2)
+-- puppetlabs-concat (v2.1.0)
+-- puppetlabs-firewall (v1.8.0)
+-- puppetlabs-inifile (v1.5.0)
+-- puppetlabs-ntp (v4.2.0)
+-- puppetlabs-postgresql (v4.7.1)
+-- puppetlabs-puppetdb (v5.1.2)
+-- puppetlabs-stdlib (v4.11.0)
/etc/puppetlabs/code/modules (no modules installed)
/opt/puppetlabs/puppet/modules (no modules installed)

Отлично! Теперь давайте протестируем наш модуль, но пока без использования hiera, как это делали все нормальные люди раньше.
Создаём файл в котором мы объявим все наши ноды /etc/puppetlabs/code/environments/production/manifests/site.pp если он ещё не был создан. Вообще, название файла роли не играет, по-сути мы создаём обычный манифест, любой манифест помещённый в директорию манифестов будет прочитан и выполнен в рамках текущего environment, но раз уж мы решили следовать букве best practice, будем называть всё своими именами.
Помещаем в него строки:

node 'puppetdb.my.domain' {
  class { "ntp":
    servers    => [ '0.ru.pool.ntp.org iburst','1.ru.pool.ntp.org iburst','2.ru.pool.ntp.org iburst','3.ru.pool.ntp.org iburst'],
    autoupdate => false,
    restrict   => [],
  }
}

Всё, с этого момента он вступает в силу, не нужно ничего перезапускать и перечитывать какие-то конфиги. Мастер не запоминает их (манифесты) в память, но обращается к ним в момент обращения клиента к нему.
Запускаем на puppetdb клиент и наблюдаем.

puppetdb # puppet agent --no-daemonize --onetime --verbose
Info: Using configured environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Loading facts
Info: Caching catalog for puppetdb.my.domain
Info: Applying configuration version '1467304459'
Notice: /Stage[main]/Ntp::Config/File[/etc/ntp.conf]/ensure: defined content as '{md5}30c98a829448d2b680bb48abd595887f'
Info: Class[Ntp::Config]: Scheduling refresh of Class[Ntp::Service]
Info: Class[Ntp::Service]: Scheduling refresh of Service[ntp]
Notice: /Stage[main]/Ntp::Service/Service[ntp]: Triggered 'refresh' from 1 events
Notice: Applied catalog in 0.17 seconds

Всё получилось! Удивительно!
Теперь давайте то же самое сделаем при помощи hiera. Для этого правим /etc/puppetlabs/code/hiera.yaml приведя файл к следующему виду

---
:backends:
  - yaml
:yaml:
:datadir: "/etc/puppetlabs/code/environments/%{environment}/hieradata"
:hierarchy:
  - "nodes/%{::trusted.certname}"
  - "nodes/%{::hostname}"
  - common

Создаём необходимые директории

mkdir -p /etc/puppetlabs/code/environments/production/hieradata/nodes/

Создаём конфиг нашей ноды puppetdb /etc/puppetlabs/code/environments/production/hieradata/nodes/puppetdb.my.domain.yaml и пишем в нём:

# /etc/puppetlabs/code/environments/production/hieradata/nodes/puppetdb.my.domain.yaml
---
# Declare our classes
classes: ntp

# After declare, work with it, we can transfer any var that supports to it
ntp::restrict:
 -
ntp::autoupdate: false
ntp::enable: true
ntp::servers:
 - 0.jp.pool.ntp.org iburst
 - 1.jp.pool.ntp.org iburst
 - 2.jp.pool.ntp.org iburst
 - 3.jp.pool.ntp.org iburst

Теперь поправим наш /etc/puppetlabs/code/environments/production/manifests/site.pp

node 'puppetdb.my.domain' {
    hiera_include('classes')
}

Сим действом, мы заставляем мастера обращаться к hiera и искать там все значения с ключём classes. Ключ может быть любой, но опять же, совершенно надуманно, разработчики просят называть его именно classes, просто потому что они так захотели… пардон, потому что так правильно. Ну ок, удовлетворим их желание.
Запускаем клиент на puppetdb

puppetdb # puppet agent --no-daemonize --onetime --verbose
Info: Using configured environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Loading facts
Info: Caching catalog for puppetdb.my.domain
Info: Applying configuration version '1467305326'
Info: Computing checksum on file /etc/ntp.conf
Info: /Stage[main]/Ntp::Config/File[/etc/ntp.conf]: Filebucketed /etc/ntp.conf to puppet with sum 30c98a829448d2b680bb48abd595887f
Notice: /Stage[main]/Ntp::Config/File[/etc/ntp.conf]/content: content changed '{md5}30c98a829448d2b680bb48abd595887f' to '{md5}f65100dd021d444720d9c0442594c8c8'
Info: Class[Ntp::Config]: Scheduling refresh of Class[Ntp::Service]
Info: Class[Ntp::Service]: Scheduling refresh of Service[ntp]
Notice: /Stage[main]/Ntp::Service/Service[ntp]: Triggered 'refresh' from 1 events
Notice: Applied catalog in 0.20 seconds

2

Смех смехом и кажется что всё очень просто и логично, но это лишь потому, что это объясняю я. Чтоб достичь этого результата я потратил уйму своего времени. Всё потому, что в их дурацкой документации 1000 строк описания того “как это работает” aka теории и 1 строка с примером которую ещё надо найти. Я не разработчик и мне, как быдлоадмину, абсолютно это не интересно и я был бы рад 1000 примерам и одной строке теории, но увы. Ну да ладно, а то чёт у меня бомбить снова начинает…

Следующий наш шаг – напишем какой-нибудь простенький свой модуль, чтоб поместить туда необходимую нам логику. Есть несколько путей: Правильный и Быстрый. При правильном нас учат собирать свой модуль с нуля, упаковывать его, заливать на репу, поддерживать и ставить с помощью module install. Но как мы не девелоперы, а админы всего лишь, так мы выбираем быстрый: “Херак-херак и в продакшн” (с)

Существуют модули глобальные, действующие на все environment’ы, и локальные, соответственно работающие только в том environment’е, в какой вы его установите. Мы будем делать локальный. Создаём директорию содержащую название модуля, у нас он будет называться tm (Сокр. Test Module):

mkdir /etc/puppetlabs/code/environments/production/modules/tm

Очень важна иерархия директорий. Модули подчиняются той же иерархии что и environment’ы – манифесты в manifests, файлы в files, шаблоны в templates и т.п. Нам понадобится только manifests. Создаём её:

mkdir /etc/puppetlabs/code/environments/production/modules/tm/manifests

И помещаем туда наш единственный файлик init.pp содержащий

# Class: tm
#

class tm ($parameter_one = "default text") {
 file {'/tmp/test_module':
  ensure  => file,
  content => $parameter_one,
 }
}

И тут же проверяем наши модули:

puppet # puppet module list
/etc/puppetlabs/code/environments/production/modules
+-- puppetlabs-apt (v2.2.2)
+-- puppetlabs-concat (v2.1.0)
+-- puppetlabs-firewall (v1.8.0)
+-- puppetlabs-inifile (v1.5.0)
+-- puppetlabs-ntp (v4.2.0)
+-- puppetlabs-postgresql (v4.7.1)
+-- puppetlabs-puppetdb (v5.1.2)
+-- puppetlabs-stdlib (v4.11.0)
L-- tm (???)
/etc/puppetlabs/code/modules (no modules installed)
/opt/puppetlabs/puppet/modules (no modules installed)

Видим что у нас появился новый модуль со знаками вопросов в скобках. Это потому что он сделан не очень правильно и не содержит версии, но это не страшно, для тестов самое то.
Переписываем /etc/puppetlabs/code/environments/production/hieradata/nodes/puppetdb.my.domain.yaml

# /etc/puppetlabs/code/environments/production/hieradata/nodes/puppetdb.my.domain.yaml
---
# Declare our classes
classes: 
 - ntp
 - tm

# After declare, work with it, we can transfer any var that supports to it
ntp::restrict:
 -
ntp::autoupdate: false
ntp::enable: true
ntp::servers:
 - 0.ch.pool.ntp.org iburst
 - 1.ch.pool.ntp.org iburst
 - 2.ch.pool.ntp.org iburst
 - 3.ch.pool.ntp.org iburst

# Here is example our module "Test Module" via /etc/puppetlabs/code/environments/production/modules/tm
tm::parameter_one: "There is no other way, and there never was. I lost in this place. Pls, i beg u, help me if u can" 

Запускаем клиент на puppetdb

puppetdb # puppet agent --no-daemonize --onetime --verbose
Info: Using configured environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Loading facts
Info: Caching catalog for puppetdb.my.domain
Info: Applying configuration version '1467306854'
Notice: /Stage[main]/Ntp::Config/File[/etc/ntp.conf]/ensure: defined content as '{md5}f65100dd021d444720d9c0442594c8c8'
Info: Class[Ntp::Config]: Scheduling refresh of Class[Ntp::Service]
Info: Class[Ntp::Service]: Scheduling refresh of Service[ntp]
Notice: /Stage[main]/Ntp::Service/Service[ntp]: Triggered 'refresh' from 1 events
Notice: /Stage[main]/Tm/File[/tmp/test_module]/ensure: defined content as '{md5}ef4186a646a01838b0427bdffeae7cc6'
Notice: Applied catalog in 0.18 seconds

Похоже что-то получилось. Давайте посмотрим содержимое /tmp/test_module

puppetdb # cat /tmp/test_module
There is no other way, and there never was. I lost in this place. Pls, i beg u, help me if u can

В точности то, что мы писали в нашем конфиге. Если не передавать параметр parameter_one , будет браться параметр по-умолчанию из класса.

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

Published inIT

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *