Вкратце:
- cfpuppetserver — модуль автоматической настройки Puppet Server + PuppetDB + PostgreSQL + r10k + librarian-puppet
- Краткое введение в Puppet
- Описывается изначальное развёртывание с нуля
Тематический цикл:
- Часть I: сеть и сетевой фильтр (cfnetwork + cffirehol)
- Часть II: доступ и стандартное окружение (cfauth + cfsystem)
- Часть III: установка Puppet Server (cfpuppetserver)
Немного лирики. Казалось бы с этой статьи следует начинать весь цикл, но всё же целевой аудиторией являются более опытные пользователи Open Source продуктов Puppet Labs, которых не устраивают отдельные малоинтегрированные модули с Puppet Forge. Как и с любым случаем "library vs. framework", расплатой является следование мировоззрению автора интегрированного решения.
Немного о принципе работы Puppet
Puppet — это в первую очередь специфичный язык декларативного задания конечного состояния системы. Для сравнения крайне подойдёт GNU Makefile, где помимо непосредственного описания зависимостей есть возможность начудить на полную катушку.
Абстракция Puppet примерно следующая (срыв шаблонов — забудьте всё, что вы знали о терминах в программировании!).
- Узел (node) — это совокупность конфигурации для конкретной целевой системы. На деле это частный случай класса.
- Класс (class) — это набор декларативной логики, которая включается в конфигурацию узла или другие классы. У класса нет ни экземпляров, ни методов, зато есть параметры и переменные, определённые внутри логики. На деле, это скорее процедура, которая может наследовать другую процедуру банальным наращиванием кода и не совсем банальной областью видимости переменных.
- Тип (type) — а вот это уже больше смахивает на классический класс — у него предполагаются экземпляры с именем и определённо заданными параметрами, но ничего более. Конкретная реализация типа может быть написана в виде Puppet скрипта через
define
, который создаёт экземпляры других типов или же в виде расширения на Ruby с полётом фантазии. - Ресурс (resource) — собственно это и есть именованные экземпляры Типов. Имя каждого ресурса уникально в рамках конкретного типа в пределах конфигурации узла (каталога).
- Переменные (variable) — ну, короче это константы… До версии Puppet 4 с их областью видимости было всё ещё хуже. Теперь она адекватна: для использования извне места определения требуется задавать полностью квалифицированный идентифиактор, за исключением случая наследования классов.
Puppet может использоваться для локального развёртывания без сети и соответствующей инфраструктуры. Это может использоваться для создания образов контейнеров. Есть даже целое направление ратующих за отказ от централизованного сервера.
В идеологически правильном ключе, инфраструктура Puppet состоит из агента — привилегированного сервиса на целевой системе и сервера, раздающего ценные указания в виде декларативных каталогов ресурсов по запросу от агентов. Безопасность реализована на уровне приватной инфраструктуры публичного ключа (X.509). Попросту говоря, тех же механизмов, что и в HTTPS, но с собственным CA и обязательной проверкой клиентского сертификата.
В упрощённом виде процедура развёртывания выглядит примерно так:
- Обработка по TLS и X.509 (установка соединения, обновление CRL, проверка ограничений сертификата и т.д)
- Агент получает генераторы фактов с сервера с кэшированием и всеми делами (точнее вытягивается всё из папок lib/ в модулях). Не составляет никакого труда добавить свой Ruby скрипт для сбора интересующей информации.
- Агент собирает факты о целевой системе и отправляет на сервер. Все факты легко просмотреть вручную через вызов
puppet facts
. Эти факты доступны в качестве глобальных переменных. - Сервер составляет каталог ресурсов и отправляет агенту. Под этим скрывается целый пласт различных концепций.
- Агент вытягивает всё необходимое с сервера и приводит систему к указанном виду. Сам агент при этом не знает как поступать с ресурсами, он полагается на реализацию provider'ов (смысловой перевод будет "воплотитель", а не поставщик) конкретных типов ресурсов. Часть provider'ов являются стандартными и входят в пакеты Puppet, а остальные вытягиваются из модулей.
Для вкушения всех прелестей, существует дополнительные плюшки в виде:
- Модуль — совокупность декларативных скриптов Puppet, Ruby расширений для Puppet, файлов, шаблонов файлов, Hiera данных и многого другого. Более правильный термин был бы "пакет".
- Среда (Environment) — совокупность скриптов, модулей и Hiera данных. С усложнением инфраструктуры неминуемо потребовалось разделять конфигурацию далее стандартного деления по узлам. В основном, это требуется для пилотных нововведений и банального контроля доступа (когда не все админы имеют доступ ко всем узлам IT инфраструктуры).
- Hiera — иерархическая база данных. Такая формулировка может сильно отпугивать. Вероятно поэтому её и поменяли в документации более поздних версий. На деле это крайне простой и удобный механизм вытягивать конфигурацию из YAML или JSON файлов. Иерархия заключается в возможности задать порядок чтения множества конфигурационных файлов — т.е. иерархию/приоритет этих файлов.
- Помимо вытягивания данных по вызову функции, Puppet вытягивает параметры классов по умолчанию, что является главной изюминкой.
- Разумеется, Hiera поддерживает интерполяцию фактов и даже вызов специальных функций.
- В Puppet 4.3 реализовали этот же функционал ещё раз для поддержки не только глобальной базы данных, но и локальной для Среды и Модуля, правда автор уже нашёл несколько проблем в их реализации (PUP-5983, PUP-5952 и PUP-5899), которые были моментально исправлены Puppet Labs.
- Поддерживаются несколько стратегий вытягивания значения из всех файлов по иерархии:
first
— выдаётся первое попавшиеся по приоритету значениеunique
— собирает все значения в одномерный массив и убирает дубликатыhash
— объединяет все найденные YAML Hash. Дубликаты ключей выбираются по приоритету.deep
— по сути рекурсивный вариант hash
- Прелесть в том, что стратегия выборки может задаваться как при вызове функции
lookup()
, т.к. и в любом файле иерархии через специальный ключ lookup_options, что активно используется в модуле cfnetwork.
- PuppetDB — по сути слой бизнес логики вокруг реляционной базы данных (PostgreSQL), который позволяет сохранять отчёты о фактах и проделанных развёртываниях и экспортировать ресурсы для последующего импорта в каталоги на других узлах или выборки через специальные функции. Ещё есть и веб-морда в виде Puppet Dashboard.
- X.509 PKI — уже упомянутая инфраструктура сертификатов, которую крайне удобно использовать для других сервисов без необходимости управления отдельной инфраструктурой.
- MCollective — вроде бы полезная вещь для событийного запуска задач на серверной ферме, но у автора есть определённое недоверие к безопасности конкретного решения.
- Puppet Forge — открытая площадка для публикации и скачивания Модулей.
- некоторые другие фичи в виде управления внешними устройствами типа оборудования Cisco и развёртывания на голом железе, но это уже отдельная история
Заметки по безопасности и доступности
Требуется понимать, что Puppet Server становится уязвимым местом всей IT инфраструктуры, т.к. определяет конечную конфигурацию всех систем. В особых случаях имеет смысл делать разделение — отдельный сервер для критичных элементов инфраструктуры с крайне ограниченным доступов и ручным обновлением и второй для всего остального.
Доступность Puppet Server определяет возможность управления всей инфраструктурой. Имеет смысл размещать Puppet Server на виртуалке в более надёжном и быстро восстанавливаемом стороннем облаке, чем собственные возможности. Или же следует устанавливать несколько серверов.
В любом случае, не следует устанавливать иные сервисы на системе, где будет развёрнут Puppet Server с прибамбасами. Виртуализация и контейнеризация вам в помощь.
Мульти-мастер (несколько обособленных Puppet Server)
- В данном случае только один сервер выступает в роли CA (Certificate Authority) — его недоступность означает невозможность добавить новые узлы.
- Puppet допускает использовать стороннюю инфраструктуру X.509, если встроенная не устраивает.
- Вся конфигурация (Среда) должна храниться в системе контроля версий и развёртываться на каждом сервере одновременно.
- Единственное общее — это база PostgreSQL, организация высокой доступности которой выходит за рамки данной статьи.
- Модуль cfpuppetserver поддерживает установки в качестве основного (с CA) и в качестве второстепенного сервера.
Что значимое изменилось с более старых версий
Полное описание есть у производителя.
- Все сервисы переехали на JVM, JRuby и Jetty. За очевидными плюсами интегрированности есть и минусы по расходу памяти.
- Добавились лямбда-функции для обработки коллекций — теперь нет необходимости пилить костыли на Ruby или извращаться через create_resources()
- Появился инструмент обработки шаблонов EPP — по сути тот же ERB, но с Puppet DSL вместо Ruby,
- Сильно поменялось структура каталогов конфигурационных файлов по умолчанию
- Появилась поддержка Data Providers для Сред и Модулей (больше не требуются хаки).
- Принижение роли глобальной Hiera. Новая связанная с этим команда
puppet lookup
.
Установка
Данный процесс достаточно примитивен, но требует соблюдения определённой последовательности шагов. Поскольку делать это вручную неблагодарное занятие, автор научит плохому, а именно скачивать непонятные скрипты с интернета и запускать под root'ом на своей системе.
Три основных компонента сервера — это сам Puppet Server, PuppetDB и PostgreSQL. Их всех можно запихнуть на один узел или же разбить на две-три системы. Puppet Server и Puppet DB могут быть запущены множество раз, а вот PostgeSQL является единым узлом отказа. Есть разнообразные подход к репликации и кластеризации PostgeSQL.Удобный подход в случае основного и второстепенного серверов будет Master + Read-Only Slave, что поддерживается в самом PuppetDB как основной и read-only узел базы данных, но автоматизация такой настройки требует времени и поэтому пока не входит в модуль
cfpuppetserver
.Непосредственно конфигурацию можно просто хранить хоть на файловой системе вместе с Puppet Server, но это как писать скрипты на боевом веб-сервере. Самое подходящее решение — это git репозиторий. Утилита r10k умеет вытягивать все ветки репозитория и развёртывать их на Puppet Server как отдельные Среды. У
r10k
достаточно плохо с вытягиванием зависимостей, поэтому поверх используется librarian-puppet. Стоит сразу заметить, что основной канонической Средой Puppet является "production". Поэтому в репозитории конфигурации следует использовать ветку с названием "production", а не "master".Системные требования
По железу описано самим производителем. Модуль
cfpuppetserver
пока поддерживает только Debian Jessie+ и Ubuntu Trusty+.Конфигурация в Git
Для самого r10k размещение репозитория не имеет большого значения — главное его доступность. Например, в целях тестирования репозиторий можно разместить на той же системе с доступом через
file://
. Хорошим стартом будет пример конфигурации codingfuture/puppet-exampleenv.- Клонируем репозиторий:
git clone https://github.com/codingfuture/puppet-exampleenv my-puppet-conf && cd my-puppet-conf
- Устанавливаем общие настройки админского доступа, используя подсказки в комментариях:
$EDITOR data/common.yaml
- Создадим конфигурацию узлов:
$MY_DOMAIN
— корневое доменной имя (например, example.org)$HOST_NAME
— имя клиентского узла без доменаmkdir data/$MY_DOMAIN
cp data/example.com/puppet.yaml data/${MY_DOMAIN}/puppet.yaml
$EDITOR nano -w data/${MY_DOMAIN}/puppet.yaml
— настройка узла с Puppet Server по подсказкам в комментарияхcp data/example.com/host.yaml data/${MY_DOMAIN}/${HOST_NAME}.yaml
$EDITOR nano -w data/${MY_DOMAIN}/${HOST_NAME}.yaml
— настройка произвольного узла по подсказкам в комментариях
- Пушаем на собственный Git сервер или же делаем доступным локально на узле с Puppet Server через rsync или scp. Локальный репозиторий удобен как промежуточный шаг пока Git сервер не развёрнут с самого же Puppet. В каком-то смысле это напоминает сборку компилятора в несколько этапов.
Ставим с нуля на чистой системе
Модуль
cfpuppetserver
позволяет установить всё средствами самого же Puppet, но для изначального установки базовые операции продублированы скриптом Bash.На целевой системе:
- Скачиваем скрипт установки:
wget https://raw.githubusercontent.com/codingfuture/puppet-cfpuppetserver/master/setup_puppetserver.sh
- Просматриваем скрипт и хмурим бровки:
less setup_puppetserver.sh
- Запускаем:
bash setup_puppetserver.sh <repo_uri> puppet.${MY_DOMAIN}
.
- Пример с удалённым репозиторием:
bash setup_puppetserver.sh ssh://git@git.example.com/puppet-conf
- Пример с локальным:
bash setup_puppetserver.sh file:///root/puppetconf/
- Пример с удалённым репозиторием:
- Смотрим как пыжится система и не очень быстро всё устанавливает.
- Если удалённый репозиторий:
- Создаём SSH ключ у root'а:
ssh-keygen -t rsa -b 2048
- Прописываем публичный ключ
/root/.ssh/id_rsa.pub
на удалённом сервере Git... - … и там же настриваем Git hook с вызовом следующей команды:
/usr/bin/ssh -T deploypuppet@puppet.${MY_DOMAIN} ./puppetdeploy.sh
- Создаём SSH ключ у root'а:
- Запускаем развёртывание конфигурации вручную:
/etc/puppetlabs/deploy.sh
- Пробуем как работает на самом сервере:
/opt/puppetlabs/bin/puppet agent --test
- Проверьте настройки сети, сетевого фильтра и SSH-доступа
Добавляем управляемые узлы
- Полное имя Puppet Server должно быть доступно через DNS на управляемом узле или же следует "зашить" в /etc/hosts.
- Пример:
echo "128.1.1.1 puppet.example.com" >> /etc/hosts
- Пример:
- На узле с Puppet Server запускаем следующий скрипт
/root/genclientinit.sh ${HOST_NAME}.${MY_DOMAIN}
. - Результат копируем целиком и вставляем в командную строку на целевой системе.
- Ждём окнчания выполнения и запускаем
/opt/puppetlabs/bin/puppet agent --test
. При первом запуске будет сгенерирован запрос на подпись сертификата. - Идём на узел Puppet Server чтобы подписать сертификат.
puppet cert list
— сверяем сигнатуру сертификата для пущей параноидальности.puppet cert sign ${HOST_NAME}.${MY_DOMAIN}
— собственно, подписываем сертификат.
- Возвращаемся на управляемый узел и запускаем: /opt/puppetlabs/bin/puppet agent --test` ещё раз. Это принудительно запустит процедуру развёртывания.
- Ждём пока закончится выполнение развёртывания через Puppet Agent.
- Всё, у вас готова минимальная инфраструктура Puppet!
Пример вывода /root/genclientinit.sh
bash <<EOT
#!/bin/bash
http_proxy=
if test "\$(id -un)" != 'root'; then
echo 'This script must run as root'
exit 1
fi
if test ! -z ""; then
echo -n >/etc/cflocation
fi
if test ! -z ""; then
echo -n >/etc/cflocationpool
fi
if test ! -z "\$http_proxy"; then
export http_proxy
export https_proxy="\$http_proxy"
export HTTP_PROXY="\$http_proxy"
export HTTPS_PROXY="\$http_proxy"
fi
echo host.example.com > /etc/hostname
hostname host.example.com
if ! which lsb-release | read; then
apt-get install lsb-release
fi
codename=\$(lsb_release -cs)
if test -z "\$codename"; then
echo "Failed to detect correct codename"
exit 1
fi
wget https://apt.puppetlabs.com/puppetlabs-release-pc1-\${codename}.deb
dpkg -i puppetlabs-release-pc1-\${codename}.deb
mkdir -p /etc/puppetlabs/puppet
cat > /etc/puppetlabs/puppet/puppet.conf <<EOF
[main]
certname = host.example.com
server = puppet.example.com
ca_server = puppet.example.com
environment = production
EOF
apt-get update &&
apt-get install puppet-agent
while ! /opt/puppetlabs/bin/puppet agent --test --wairforcert 120; do
echo "Please go to puppetserver and exec the following command when we wait for key"
echo "> puppet cert sign host.example.com"
echo "Use CTRL+C to stop cycle, if fails due to different reasons"
sleep 5
done
EOT
Описание модуля
Полный список параметров Bash скрипта изначальной установки
~# ./setup_puppetserver.sh
Usage: ./setup_puppetserver.sh <r10k_repo_url> [<certname=hostname> [<cflocation> [<cflocationpool> [<http_proxy>] ] ] ]
r10k_repo_url
— URI Git репозиторияcertname
— полное доменное имя узлаcflocation
— инициализация факта cf_locationcflocationpool
— инициализация факта cf_location_poolhttp_proxy
— прокси сервер для HTTP и HTTPS запросов
Полный список параметров Bash скрипта инициализации клиента Puppet
~# /root/genclientinit.sh
Usage: ./genclientinit.sh <certname> [<cflocation> [<cflocationpool> [<http_proxy>]]]
Значение параметров такое же, как у предыдущего скрипта.
класс cfpuppetserver
deployuser = 'deploypuppet'
— имя пользователя для автоматического развёртывания обновлений конфигурацииdeployuser_auth_keys = undef
— список ключей для $deployuserrepo_url = undef
— URI репозитория (пример: ssh://user@host/repo или file:///some/path)puppetserver = true
— устанавливать ли компонент Puppet Server на данный узелpuppetdb = true
— устанавливать ли компонент PuppetDB на данный узелpuppetdb_port = 8081
— порт для PuppetDBsetup_postgresql = true
— устанавливать ли компонент PostgreSQL на данный узел (только если включена установка PuppetDB)service_face = 'any'
— название ресурсаcfnetwork::iface
для принятия входящих соединенийpuppetserver_mem = auto
— оперативная память под Puppet Server в мегайбатах (минимум 192MB)puppetdb_mem = auto
— оперативная память под PuppetDB в мегайбатах (минимум 192MB)postgresql_mem = auto
— оперативная память под PostgreSQL в мегайбатах (минимум 128MB)
класс cfpuppetserver::puppetdb
postgresql_host = 'localhost'
— адрес базы данныхpostgresql_listen = $postgresql_host
— значение идёт прямо в директивуlisten_addresses
PostgreSQLpostgresql_port = 5432
— порт базы данныхpostgresql_user = 'puppetdb'
— пользователь PuppetDB в базе данныхpostgresql_pass = 'puppetdb'
— пароль пользователя PuppetDB в базе данныхpostgresql_ssl = false
— включение шифрования соединения на основе сертификатов Puppet PKI
класс cfpuppetserver::puppetserver
autosign = false
— НЕ СТОИТ использовать в боевой среде, разве что в DMZ. Существует исключительно для автоматизации тестирования.global_hiera_config = 'cfpuppetserver/hiera.yaml'
— путь к файлу конфигурации Hiera по умолчанию по канонам Puppet (первый компонент — название модуля, остальное — путь под папкойfiles/
в модуле)
tgz
puppet ужасен.
andvgal
Соглашусь, в терминологии он крайне "необычен". Если проследить историю того, как эта терминология формировалась, то становится более-менее понятно.
Сама же концепция очень даже неплоха и видны крайне активные подвижки к улучшениям, особенно при переходе от третьей версии к четвёртой.
Что меня больше всего поразило, так это скорость исправления проблем в Puppet Labs. Они исправляют даже по субботам, учитывая, что я всего-лишь Open Source пользователь. В одном из случаев оказалось, что проблема вообще в стороннем модуле с Puppet Forge, так они сделали Pull Request'ы в GitHub для этого стороннего модуля.
Зная как работают многие другие коммерческие компании, такой подход заслуживает как минимум уважения.