Don't wanna be here? Send us removal request.
Text
Как правильно готовить SaltStack
SaltStack - очень мощный и гибкий инструмент управления конфигурациями. К большому сожалению из-за отсутствия договоренности ( политики / RFC , как это ни называй ) мало кто задумывается о "чистой конфигурации".
Под чистой конфигурацией я понимаю легкость чтения стейтов и их поддержку в будущем. Для себя я выбрал несколько принципов, которые позволят сделать ту самую "чистую конфигурацию".
В первую очередь подход заключается в том, чтобы готовить стейты как это принято в debain репозиториях и пакетах, плюс некоторые нюансы, которые взяты из моей практики.
Стейт, как deb-пакет - является самостоятельно и отчуждаемой сущностью
Каждый стейт должен находится в директории, сущности который он принадлежит.
Не стоит брезговать копипастой во избежании программирования на Jinja. Простые и примитивные вещи не ломаются.
Важно разделять стейты на слои. То что можно поставить на все хосты стоит унести в base. Все остальное - нужно уносить в другие environment ( production / stage / service ). Overwritting.
Pillars - это настройки ( конфиги ), Grains - это методанные ( информация о миньоне, например кол-во жестких дисков.
А теперь более детально по каждому пункту.
Стейт, как deb-пакет - является самостоятельной и отчуждаемой сущностью.
В первую очередь - это важно потому оно становится переносимым и самостоятельным. Как пример, можно смотреть на fat-binary в языке Go или на APP- приложений в MacOSX.
Идя другим путем можно наткнутся на типичную проблему с зависимостей.
Пример
Вы хотите принести какой-то экспортет на свой хост, чтобы собирать метрики или python-script написанный python3.6.
Разберем оба примера.
node_exporter - бинарь, а для его запуска нам нужно service-unit, возможно какой-то конфиг, если мы не запускаем его через аргументы командной строки.
Итого, наш стейт-пакет будет выглядеть так.
/path/to/node-exporter-service-unit: file.managed: - source: salt://node-exporter-service-unit packages: pkg.installed: - pkgs: - node_exporter node-exporter-service-unit: service.running: - enable: True - reload: True
/path/to/my-powerfull-script.py: # Приностим сам скрипт file.managed: - source: salt://my-powerfull-script.py packages: pkg.installed: - pkgs: - python3.6 # Да приносим python, даже если вы уверены, что он на хосте есть. - python3-pip - systemd python-click-pkg: pip.installed: - name: click - require: - pkg: python3-pip /path/to/my-powerfull-script.py: cron.present: - user: root - minute: random - hour: 2
Этот стейт приносит с собой необходимые зависимости. python, pip3 и даже systemd. Это на сто процентов гарантирует, что скрипт заработает.
Также он легко читаем, потому что все находится перед глазами в одном файле.
Его легко перенести куда угодно по дереву директорий или просто передать кому-то на переиспользование.
Каждый стейт должен находится в директории, сущности который он принадлежит.
Для упрощения понимания структуры / чтения. Стоит помещать каждый из стейтов в директорию, сущности, которой он принадлежит.
По аналогии с репозиториями deb-пакетов. Там это не так явно выражено, но есть метаданные пакета Group , которая указывает пакет какой категории вы устанавливаете.
Такой подходит позволит четко понимать какой стейт�� привязаны к миньону при просмотре state.show_top
Пример
Допустим, мы хотим настроить наш rsyslog для отправки логов в разные точки назначения - ELK и rsyslog-server.
А также хотим собирать метрики через node_exporter, а логи ротировать.
Дерево будет выглядеть так
. └── base ├── logs │ ├── logrotate.sls │ ├── rsyslog.sls │ └── rsyslog-elk.sls ├── monitoring │ ├── node_exporter.sls │ └── redis.sls └── system
Для elk нам понадобится отдельный модуль из репозитория + шаблон, для второго - только шаблон. И все они требуют установленный rsyslogd.
Как показано в пункте 1 каждый стейт должен приносить зависимости для себя.
Ответ: "ДА" - в каждом стейте мы дублируем rsyslog как зависимость.
P.S. Возможны случаи, когда не ясно как классифицировать тот или иной стейт. В таких случая я уношу их в system. Но всячески стараюсь этого избегать.
Плохой пример - называть директории backend / frontend - это не класс, а роль.
Не стоит брезговать копипастой.
Многие не согласны с таким высказыванием, но как я уже упоминал - это упросит отчуждаемость стейтов для переноса и переиспользования.
В примере выше нам нужно настроить rsyslog и они оба имеют одну зависимость - rsyslog пакет.
Казалось бы, можно вынести в отдельный стейт и назвать его packages.sls, но тем самым есть риск сломать стейт, кто-то другой решит о не надобности rsyslog, потому что он приезжает с базовой системой и по своему будет прав.
packages: pkg.installed: - pkgs: - rsyslog /etc/rsyslog.d/10-main.conf: file.managed: - source: salt://10-main.conf
packages: pkg.installed: - pkgs: - rsyslog - rsyslog-elasticsearch /etc/rsyslog.d/30-elasticsearch.conf: file.managed: - source: salt://30-elasticsearch.conf
Важно разделять стейты на слои. Environments: base / production / stage / service и overwritting.
Слои лучше рассматривать как: base - это то что одинаково для всех, например, список установленных пакетов. production / stage / servicename - это сервсис-зависимый слой.
В большинстве случаев ( в основном в небольших проектах ) нет необходимости в использование среды кроме base.
Как быть, если node_exporter нужен всем, а запускается он различными аргументами ( например, порт на котором он слушает или debug-флаг ) у каждого сервиса или в разной среде ( production / stage )?
Куда положить такой стейт в base или сервсисо-зависимый слой? Ответ: "в оба".
В случае когда есть единая команда ответственная за инфраструктуру ( нижний слой - предоставление виртуалок / железа и проч ) она должна предоставить готовое решение - железку/виртуалку с минимально рабочей конфигурацией - некоторыми дефолами.
А команды сервисов - сами вольны решать как именно им перенастроить дефолты - overwritting
Пример
. ├── base │ ├── logs │ │ ├── logrotate.sls │ │ ├── rsyslog │ │ └── rsyslog-elk.sls │ ├── monitoring │ │ ├── node_exporter.sls # Базовая настройка node_exoprter слушает порт 9117 │ │ └── redis.sls │ └── system └── service-name └── monitoring └── node_exporter.sls # Перезаписанная настройка node_exoprter слушает порт 8080 и запускается c debug-флагом
Pillars - это настройки, Grains - это методанные.
Если очень примитивно: Pillars - это настройки, Grains - это методанные. В принципе все что нужно о них знать.
К Pillars можно отнести: переменные для конфигов, секреты - пароли / сертификаты.
Grains - это то, что описывает миньон - метаданные: кол-во жестких дисков, модель процессора. У нас в компании - это еще и принадлежность хоста к группе ( они же hostgroups ).
Самая худшее что можно придумать с этиму сущностями - это делать на них таргетинг непосредствено в стейтах.
Плохой пример
base: '*': - node_exporter
packages: pkg.installed: - pkgs: - node_exporter run-exporter: cmd.run {% if grains['hostgroup'] != 'production-server' %} - 'node_exporter --arg1 foo' {% else %} - 'node_exporter --arg1 foo --arg2 bar' {% endif %}
Не стоит использовать их для таргетирования - это увеличивает энтропию. Для таргетирования - top.sls
Хороший пример
base: 'G:production-server': - path.to.node_exporter 'G:dev-server': - other.path.to.node_exporter
packages: pkg.installed: - pkgs: - node_exporter run-exporter: cmd.run - 'node_exporter --arg1 foo'
packages: pkg.installed: - pkgs: - node_exporter run-exporter: cmd.run - 'node_exporter --arg1 foo --arg2 bar'
0 notes
Photo

If @kellyslater likes my photo, this year will be amazing!!! :))
0 notes