Saltstack: Создание Зависимых Или Ссылающихся Конфигураций Служб



О чем эта статья? Ознакомление с возможностями SaltStack по созданию конфигураций зависимых друг от друга сервисов; от сервисов, расположенных на других или всех других подчиненных системах и т. д. Проще говоря, рассмотрим, как каждая подчиненная система может получать данные от других подобных систем в момент создания и распространения своих конфигураций.

Это третья статья из серии о SaltStack, читайте первую Здесь , второй - здесь .



Задача: развернуть кластер какого-либо сервиса так, чтобы конфигурации узлов содержали ссылки на все остальные узлы этого кластера.

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

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

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

Рассмотрим самый простой вариант описанной задачи — записать в файл /etc/hosts на каждом узле адреса и имена всех остальных узлов кластера.

Как решить эту проблему с помощью SaltStack? Самое простое, что приходит в голову — записать имена и адреса всех узлов в файл столба и связать его со всеми состояниями всех узлов: столб/кластер-nodes.sls

  
  
  
  
  
  
  
  
  
   

cluster-nodes: node1: name: node1.domain.com ip: 10.0.0.1 node2: name: node2.domain.com ip: 10.0.0.2 node3: name: node3.domain.com ip: 10.0.0.3 .

nodeN: name: nodeN.domain.com ip: 10.0.0.N

столб/топ.

sls

base: '*': - cluster-nodes

Давайте создадим состояние для добавления этих данных в /etc/hosts. Для этого мы будем использовать соль.

states.host .

штаты/cluster-nodes.sls

{% for node in salt['pillar.get']('cluster-nodes', []) %} cluster-node-{{node['name']}}: host.present: - ip: {{node['ip']}} - names: - {{node['name']}} - {{node['name'].

split('.

')[0]}} {% endfor %}

После применения состояния мы получим на всех узлах что-то подобное: /etc/хосты :

127.0.0.1 localhost 10.0.0.1 node1.domain.com node1 10.0.0.2 node2.domain.com node2 10.0.0.3 node3.domain.com node3 .

10.0.0.N nodeN.domain.com nodeN

Казалось бы, цель достигнута — со всех хостов видны другие.

Такое решение хоть и не изящное, потому что.

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

и исправлено.

Что делать, если каждый узел получает свой адрес, например, по DHCP? Или созданием узлов занимается какой-нибудь поставщик IaaS, например Amazon EC2, GoGrid или Google Grid? Заранее не задаются адреса и имена узлов, а за фиксированные адреса также придется доплачивать.

(Лирическое отступление — в ближайшее время напишу статью о том, как создать свою инфраструктуру в EC2 с помощью SaltStack) .

В принципе, после установки миньона на узел информацию о его имени и адресе можно получить с помощью соль .

Например, вы можете получить эти данные о миньоне следующим образом:

#salt-call grains.item fqdn fqdn_ip4 local: ---------- fqdn: ip-10-6-0-150.ec2.internal fqdn_ip4: - 10.6.0.150

Или в описании состояния:

{% set host_name = salt['grains.get']('fqdn') %} {% set host_ip = salt['grains.get']('fqdn_ip4') %}

Все бы ничего, но есть одно существенное Но : все это доступно мастеру и недоступно отдельным миньонам.

То есть в момент генерации конфигурации на каждом из миньонов не видны данные самого миньона, что-то от мастера и абсолютно ничего о других миньонах.

Вот тут и возникает вопрос — как получить данные от других миньонов при генерации конфигурации для конкретного миньона? Ответ прост: для этого можно использовать соляная шахта .

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

Как он работает? Мастер кэширует определенный набор данных от каждого из миньонов через определенные промежутки времени и предоставляет доступ к этим кэшированным данным всем миньонам.

Как это реализовать? 1. Установить mine_functions — описание функций получения и кэширования отдельных данных от миньонов.

Их можно определить, используя прямое описание в /etc/salt/minion для каждого из миньонов или включив файл Pillar с описанием этих функций на мастере для каждого из миньонов.

2. Либо ждем некоторое время (mine_interval секунды - можно указать в конфигурации миньонов), либо принудительно обновляем вручную с помощью соль '*' mine.update 3. Используйте функцию mine.get для получения необходимых данных от мастера при настройке текущего миньона.

Давайте рассмотрим, как решить проблему с хостами, используя описанные шаги.

Так: 1. Создайте запись в файле Pillar и подключите ее ко всем миньонам: столб/minefuncs.sls

mine_functions: grains.item: [fqdn, fqdn_ip4]

столб/топ.

sls

base: '*': - minefuncs

2. Ускоряем сбор данных с миньонов.

3. Создайте государство для /etc/хосты :

{% for node, fqdn_data in salt['mine.get']('*', 'grains.item', expr_form='glob').

items() %} cluster-node-{{fqdn_data['fqdn']}}: host.present: - names: - {{fqdn_data['fqdn'].

split('.

')[0]}} - {{fqdn_data['fqdn']}} - ip: {{fqdn_data['fqdn_ip4'][0]}} {% endfor %}

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

Если есть необходимость изолировать определенный набор миньонов, к которым будет применено состояние (например, у одних нод в кластере одна роль, у других — другая, и вам нужно изолировать только ноды с определенными ролями), вы можете используйте следующие рекомендации: 1. Для всех миньонов определяем пользовательское зерно (это несложно и описано в стандартной документации), например: зернышки:роли:имя_узла и зерна:роли:data_node. 2. Сделайте образец в мой.

получить для указанных ролей.

Например вот так:

{% for node, fqdn_data in salt['mine.get']('roles:data_node', 'grains.item', expr_form='grain').

items() %} cluster-node-{{fqdn_data['fqdn']}}: host.present: - names: - {{fqdn_data['fqdn'].

split('.

')[0]}} - {{fqdn_data['fqdn']}} - ip: {{fqdn_data['fqdn_ip4'][0]}} {% endfor %} {% for node, fqdn_data in salt['mine.get']('* and not G@roles:data_node', 'grains.item', expr_form='compound').

items() %} cluster-node-{{fqdn_data['fqdn']}}: host.present: - names: - {{fqdn_data['fqdn'].

split('.

')[0]}} - {{fqdn_data['fqdn']}} - ip: {{fqdn_data['fqdn_ip4'][0]}} {% endfor %}

В этих случаях стоит обратить внимание на expr_form и описание выражения для образца.

Это, по сути, решение описанной выше проблемы — чтобы можно было получать данные конфигурации от миньонов.

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

Надеюсь, статья будет полезна всем, кто использует SaltStack в достаточно нетривиальных конфигурациях.

Спасибо за чтение.

Теги: #Salt #saltstack #jinja #pillar #python #salt-mine #python

Вместе с данным постом часто просматривают: