Puppet

Basics

/etc/puppet/puppet.conf в блоке [main] (либо в [agent]) должен иметь две важных директивы

certname=puppetclient # <- имя клиента которым он будет представляться мастеру
server=puppetserver # <- доменное имя мастера

Также клиенту можно например указать runinterval=20m и он будет самостоятельно запускаться (без крона)


На мастере puppet.conf должен также иметь две важных директивы но уже в секции [master]

certname=puppetserver
server=puppetserver

(то же самое что и в [main])


Чтобы на мастере подписать сертификат нового клиента нужно сделать так

$ puppet cert list # сначала смотрим какие есть неподписанные
$ puppet cert sign hostname # hostname будет тот, который прописан у клиента в директиве certname 

--all - полезный ключ


Во время работы puppet, манифесты преобразуются в каталоги. Каталог - это список ресурсов и их взаимосвязей после обработки (компиляции) логики манифестов


Комментарии начинаются с решетки

Нода, ресурс

Конфигурация ноды начинается с ключевого слова node за которым следует хостнэйм ноды или нод, или регулярка, или ключевое слово default
Далее в фигурных скобках описывается конфигурация

Имя ноды может совпадать сразу с несколькими описаниями, но паппет выберет только одно. Про приоритет выбора есть в доке

Конфигурация является перечислением ресурсов и их параметров (не может быть двух одинаковых ресурсов с одним именем)


Описание ресурса начинается с его типа, тип указывается в нижнем регистре. После типа ресурса в фигурных скобках пишется его имя, потом двоеточие, а дальше идет опциональное перечисление его параметров (параметры указываются как хэш-таблица в ruby)

node 'phobos.vandud.ru' {  
  file { "/var/www/html/test.html":
    owner => "www-data",
    group => "www-data",
    content => "nigamazafaka, sosi zhopu",
  }
}

Отступы в манифестах не являются обязательными, но приветствуются. Табы не используются, нужно использовать двойной пробел
Фигурные скобки отделяются пробелом, а двоеточие нет. Запятые нужны после каждого параметра, в том числе и после последнего. Каждый параметр на отдельной строке. Но если параметров нет или он всего один, то можно и в одну строку

resorce { 'title': param => value, }

Все стрелки у параметров должны быть на одном уровне


Корневая директория это директория с манифестами
Она может отличаться в зависимости от окружения (puppet окружения, т.е. production, etc), окружения обычно используются вместе с гитом

В новых версиях паппета стало обязательным использование окружений

В старых паппетах корневой директорией была /etc/puppet, а в новых стала /etc/puppetlabs/code/ENV

В корневой директории должна быть папка manifests

Зависимости

Зависимости определяются стрелкой ->

node 'phobos.vandud.ru' {  
  file { "/var/www/html/test.html":
    owner => "www-data",
    group => "www-data",
    content => "nigamazafaka, sosi zhopu",
  }
  
  -> resorce { 'title': param => value, }
}

Это означает, что ресурс 'title' будет обработан строго после файла 'test.html'


Также можно подписывать ресурсы на события других ресурсов (важно: не все ресурсы умеют обрабатывать такие события). Это делается также как описано выше, но через волнистую стрелку ~>. Волнистая стрелка включает в себя прямую
Например ресурс 'service' описаный после волнистой стрелки, будет перезапущен если описаный перед ним ресурс будет изменен

Такая стрелка может быть заменена с помощью метапараметров subscribe или notify


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

file { '/file1': ensure => present }
file { '/file2':
  ensure => directory,
  before => File['/file1'], # before означает что file2 будет выполнен перед file1, так как file1 зависит от file2
}
file { '/file3': ensure => absent }
File['/file1'] -> File['/file3'] # такая стрелка означает: сначала левый, потом правый, также может быть и волнистая
                                 # она означает: сначала левый, и если он изменился, то сделать notify для правого
                                 # может быть цепочка из более чем двух ресурсов

В этом примере (насколько я понял) сначала скопируется директория 2, потом создастся файл 1, а потом 3


Классы, переменные и дефайны

Для того чтобы не повторять одно и то же много раз (и чтобы было удобнее редактировать) - придумали сабж

Класс - это именованный блок паппет кода, классы нужны для переиспользования кода

class class_name {
...
}

Сам по себе он ни на что не влияет пока его не вызовут


Вызвать класс можно двумя способами

class { 'class_name': }
include class_name # import'ы выпилили с 4 версии

Можно вынести установку и настройку какого-либо сервиса в класс и заинклудить этот класс для нужных нод

import'ы нужны для импорта манифестов, а include'ы для классов


У классов могут быть параметры
Параметры класса - это переменные в пространстве имен класса, они задаются в заголовке класса и могут быть использованы как обычные переменные в теле класса
Значения параметров указываются в момент использования класса в манифесте (можно задать умолчание)

Если нет умолчания и в манифесте параметр не указан, то будет ошибка

Параметры указываются так

class class_name ( $param1, $param2 = 'zhopa2', ) {
...
}

node 'hostname' {
  class { 'class_name':
    param1 => 'zhopa1',
  }
}

То есть это выглядит как параметры ресурса


Можно явно указывать типы параметров (тип данных), и если при вызове класса ему переданы параметры неверного типа, то произойдет ошибка

class class_name (
  String $param1,
  Integer $param2 = 3,
  ...
  ) {
  ...
}

Классы могут наследоваться друг от друга и переопределять друг друга

class passwd {
  file { "/etc/passwd":
    owner => "root",
    group => "root",
  }
}

class passwd-bsd inherits passwd {
  File["/etc/passwd"] { group => "wheel" }
}

Можно описать какой-то базовый класс с базовыми настройками (настройки доступа, безопасности и прочее) и наследоваться от него в зависимости от назначения ноды (base+web или base+db)


Дефайн похож на класс, но: каждый дефайн является типом ресурса, а не ресурсом, а также он имеет неявный параметр $title в который попадает имя ресурса при его объявлении

define zhopa::piska (
  $param1 = $title,
  $param2 = 'param',
  ...
  ) {
  ... # а тут например описывается логика установки и копирования/создания конфигов
}

node host_name {
  zhopa::piska { 'package1': }
  zhopa::piska { 'package2': }
  zhopa::piska { 'package3': param2 => 'param-zhopa', }

}

Дефайн нужен например для описанного выше случая (когда мы описали свой ресурс и хотим многократно его использовать в рамках одной ноды)
Класс такого не позвляет, так как у класса нет чего-то вроде $title


Переменные в puppet неизменяемые и обращаться к ним можно строго после их объявления

В паппете есть пространства имен и области видимости

Например: пространство класса, пространство ноды, глобальное пространство

Чтобы избежать неоднозначности можно явно указывать нужное пространство

$var

$::var

$class_name::var

$::class_name::var

Условные операторы и селекторы

if

if EXPR1 {
  ...
} elseif EXPR2 {
  ...
} else {
  ...
}

unless

unless - это if наоборот (блок кода будет выполнен если выражение ложно)

unless EXPR {
  ...
}

case

case EXPR {
  VALUE1: { ... } # в качестве значений можно использовать обычные значения, а также регулярки и типы данных
  VALUE2, VALUE3: { ... }
  default: { ... }
}

Пример:

case $operatingsystem {
  redhat: { service { "httpd": ensure => running }}
  debian: { service { "apache": ensure => running }}
  default: { service { "apache2": ensure => running }}
}

Селекторы

Селектор это как case, только вместо выполнения кода она возвращает значение

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

То есть var будет равна единице если var2 равна строке 'val1'


Еще пример с селектором

file { "/etc/passwd":
  owner => "root",
  group => $kernel ? {
    Linux => "root",
    FreeBSD => "wheel",
  },
}

Модули

Модуль это набор классов, дефайнов и прочих сущностей вынесенных в отдельную директорию. То есть это независимый кусок логики, который например нужен для работы с nginx

Есть открытый репозиторий модулей https://forge.puppet.com

На паппет-сервере модули лежат в папке modules относительно корневой директории
Внутри каждого модуля стандартная схема директорий: manifests, files, templates, lib, etc


Существует прямое соответствие между именами ресурсов и именами файлов в которых паппет будет их искать (поэтому нельзя называть ресурсы как попало)
Правила:

Например модуль nginx:

Шаблоны

Шаблоны можно раскрывать с помощью функции template(), например для ресурса file

В template путь вида <modulename>/<filename> подразумевает под собой путь <rootdir>/modules/<modulename>/templates/<filename>

Также существует функция inline_template которая принимает сам текст шаблона, а не путь к файлу

Внутри шаблонов можно использовать переменные в рамках области видимости


ERB

Управляющие конструкции:

Выражения пишутся на Ruby

Для доступа к переменным из манифеста нужно дописывать @ перед именем переменной

Чтобы убрать перевод строки, который создается закрывающим тэгом, нужно использовать закрывающий тэг с минусом -%>


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

class module::configure (
  $var1,
  ... # много переменных которые используются в шаблоне
) {
  file { '/etc/config':
    ensure => present,
    content => template('config.erb'),
  }
}

Факты и встроенные переменные

Факты это информация о ноде доступная в виде переменных в глобальном пространстве

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

Обращаться к фактам можно:

Лучше использовать словарь (а еще лучше с глобольным неймспейсом - $::facts['aoeuaeu']


Кроме фактов о клиенте есть еще некоторые переменные которые можно использовать


fileserver

Для доступа клиентам к файлам в паппете предусмотрен файлсервер
В файле /etc/puppet/fileserver.conf описываются его настройки

[files]
path /etc/puppet/files
allow *.vandud.ru

И далее в манифестах можно вызывать файлы используя путь до этого файлсервера

file { "/super/file":
  source => "puppet:///files/super-file"
}

Skills

Выключен ли puppet

Следующей командой получить директорию временных файлов puppet'a:

puppet config print vardir

Далее в полученной директории посмотреть файл state/agent_disabled.lock, то есть полный путь (обычно) будет выглядеть так:

/var/lib/puppet/state/agent_disabled.lock

Внутри этого файла будет сообщение (которое было указано при отключении паппета puppet agent --disable 'message')
Если файла нет, значит паппет не выключен


Также есть более простой способ, но он не так надежен как предыдущий
Можно просто посмотреть в /var/log/daemon.log и там будут записи от попыток запуска паппета. Там будут либо успешные записи с результатом работы, либо записи о том что паппет выключен. Но это не сработает если паппет был выключен недавно и он еще не запускался после выключения

Список файлов под паппетом

На каждом хосте есть специальный файл в котором задекларированно состояние сервера
Его можно вывести вот так

sudo cat `sudo puppet config print statedir`/resources.txt

Применение манифестов без сервера

Можно применять манифесты на клиенте без использования сервера

$ puppet apply manifest.pp

Дефолтные конфиги

Можно сгенерировать дефолтные конфиги чтобы посмотреть что там к чему и что вообще есть (как для клиента, так и для сервера)

$ puppet master --genconfig > puppetd.conf.default 
$ puppet agent --genconfig > puppet-agent.conf.default

Установка

Версия 7.0 6


1. Подключение репозитория

https://apt.puppet.com/ - вот тут найти нужный пакет и установить руками через dpkg

Автоматом добавится нужный репозиторий

root@phobos:~# wget https://apt.puppet.com/puppet7-release-buster.deb
--2020-12-26 03:32:57--  https://apt.puppet.com/puppet7-release-buster.deb
Resolving apt.puppet.com (apt.puppet.com)... 13.32.240.20, 13.32.240.14, 13.32.240.53, ...
Connecting to apt.puppet.com (apt.puppet.com)|13.32.240.20|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 11748 (11K) [application/x-debian-package]
Saving to: ‘puppet7-release-buster.deb’
puppet7-release-buster.deb            100%[========================================================================>]  11.47K  --.-KB/s    in 0s      
2020-12-26 03:32:57 (230 MB/s) - ‘puppet7-release-buster.deb’ saved [11748/11748]

root@phobos:~# dpkg -i puppet7-release-buster.deb 
Selecting previously unselected package puppet7-release.
(Reading database ... 32694 files and directories currently installed.)
Preparing to unpack puppet7-release-buster.deb ...
Unpacking puppet7-release (7.0.0-1buster) ...
Setting up puppet7-release (7.0.0-1buster) ...

root@phobos:~# apt update
Hit:1 http://security.debian.org/debian-security buster/updates InRelease
Hit:2 http://mirror.yandex.ru/debian buster InRelease                      
Get:3 http://apt.puppetlabs.com buster InRelease [99.2 kB]
Get:4 http://apt.puppetlabs.com buster/puppet7 amd64 Packages [8,108 B]
Get:5 http://apt.puppetlabs.com buster/puppet7 all Packages [2,328 B]
Fetched 110 kB in 1s (123 kB/s)
Reading package lists... Done
Building dependency tree       
Reading state information... Done
All packages are up to date.
root@phobos:~# 

Суть этого пакета вот в чем

root@two:~/puppet6# dpkg -c puppet6-release-buster.deb
drwxr-xr-x root/root         0 2021-02-04 22:48 ./
drwxr-xr-x root/root         0 2021-02-04 22:48 ./etc/
drwxr-xr-x root/root         0 2021-02-04 22:48 ./etc/apt/
drwxr-xr-x root/root         0 2021-02-04 22:48 ./etc/apt/sources.list.d/
-rw-r--r-- root/root        74 2021-02-04 22:48 ./etc/apt/sources.list.d/puppet6.list
drwxr-xr-x root/root         0 2021-02-04 22:48 ./etc/apt/trusted.gpg.d/
-rw-r--r-- root/root     10352 2021-02-04 22:48 ./etc/apt/trusted.gpg.d/puppet6-keyring.gpg
drwxr-xr-x root/root         0 2021-02-04 22:48 ./usr/
drwxr-xr-x root/root         0 2021-02-04 22:48 ./usr/share/
drwxr-xr-x root/root         0 2021-02-04 22:48 ./usr/share/doc/
drwxr-xr-x root/root         0 2021-02-04 22:48 ./usr/share/doc/puppet6-release/
-rw-r--r-- root/root        44 2021-02-04 22:48 ./usr/share/doc/puppet6-release/bill-of-materials
-rw-r--r-- root/root       147 2021-02-04 22:48 ./usr/share/doc/puppet6-release/changelog.Debian.gz

Установка сервера

apt install puppetserver

Автоматом подтянутся нужные java-пакеты

systemctl start/enable puppetserver

Может не стартануть, смотри ниже про память

$ cat /var/log/puppetlabs/puppetserver/puppetserver_err_pid24969.log
...
There is insufficient memory for the Java Runtime Environment to continue
...

Конфигурирование JVM для puppetserver

Так как паппет на джаве, то нужно обязательно изменить настройки потребления памяти для него (а то не запустится на слабой машине, а на мощной отберет неоправданно много ресурсов)
Настройки джавы указываются в /etc/default/puppetserver

Меняем

JAVA_ARGS="-Xms2g -Xmx2g -Djruby.logger.class=com.puppetlabs.jruby_utils.jruby.Slf4jLogger"

На

JAVA_ARGS="-Xms256m -Xmx256m -Djruby.logger.class=com.puppetlabs.jruby_utils.jruby.Slf4jLogger"

Установка агента

apt install puppet-agent

Следом за установкой предлагается выполнить такую команду

root@phobos:~# sudo /opt/puppetlabs/bin/puppet resource service puppet ensure=running enable=true
Notice: /Service[puppet]/ensure: ensure changed 'stopped' to 'running'
service { 'puppet':
  ensure   => 'running',
  enable   => 'true',
  provider => 'systemd',
}

Конфигурирование PATH

При установке добавляется скрипт /etc/profile.d/puppet-agent.sh, он автоматом правит PATH при логине
Но можно и руками добавить export PATH=/opt/puppetlabs/bin:$PATH в какой-нибудь .bashrc

Конфигурирование агента

Изменить конфигурацию агента можно изменив файл /etc/puppetlabs/puppet/puppet.conf
Или можно воспользоваться командой puppet config set

puppet config set server puppet.vandud.ru --section main
puppet config set dns_alt_names phobos.vandud.ru --section main

Подключение агента к серверу и подпись сертификатов

На клиенте с настроенным конфигом выполнить

puppet ssl bootstrap

На сервере подписать сертификат

sudo puppetserver ca sign --certname <name>

И еще раз на клиенте

puppet ssl bootstrap

root@phobos:/opt/puppetlabs/bin# ls -lh
total 0
lrwxrwxrwx 1 root root 24 Dec 14 11:35 facter -> ../puppet/bin/wrapper.sh
lrwxrwxrwx 1 root root 24 Dec 14 11:35 hiera -> ../puppet/bin/wrapper.sh
lrwxrwxrwx 1 root root 24 Dec 14 11:35 puppet -> ../puppet/bin/wrapper.sh
lrwxrwxrwx 1 root root 44 Dec 12 06:50 puppetserver -> ../server/apps/puppetserver/bin/puppetserver

Конфигурирование

Все настройки в /etc/puppetlabs/puppet/puppet.conf