SaltStack

Очередная рассказка на тему SCM на питонятине. Ничего принципиально нового ребята не придумали, просто сделали то, что требуется рядовому системному администратору для управления зоопарком в 50+ машин.

Структура

По структуре в большинстве случаев Salt разделяется на два больших куска. Pillars - это статические данные, которые определяют всякие индивидуальные настройки для нод1) и States - эдакие “модули” (по аналогии с puppet), которые отвечают за непосредственную настройку ПО. Так же на стороне клиентов есть такое понятие, как Gains - это набор статической информации, которая собирается с сервера перед началом обработки стейтов. По ним можно определить всякие вещи, как то версия ОС, наличие или отсутствие тех или иных интерфейсов, а так же (как и в любой другой SCM) можно написать свои grains, которые после синхронизации на клиентах будут подмешиваться в общую структуру данных.

Pillars

Если подходить к построению SCM с умом, то можно получить вполне себе логичную и удобную вещь, но у любой функции есть экстремумы, поэтому даже простой куб можно извратить до тэтраэдра, если очень постараться. Как я написал выше - я презираю использование import_yaml и вообще любой сложной динамики в пилларах, поскольку даже разработчиками заявлено, что это статическая информация. Кажется в версиях до 2016 были проблемы с вложением одних пилларов в другие (вроде нельзя было переопределять уже существующие поля), однако с версии 2017 это стало возможно, поэтому будучи как админом, так и программистом я нашёл для себя золотую середину в написании пилларов, а именно:

  - pillars
  | - top.sls
  | - base/
    | - pillars.sls
    | - common/
      | - common.sls
      | - sysctl.sls
      | - dns.sls
      | - ntp.sls
  | - area/
    | - in
      | - owlhost
        | - cloud
          | - msk2/
            | - base/
              | - pillars.sls
              | - common/
                | - common.sls
                | - dns.sls
                | - network.sls
            | - nodes/
              | - msk2-mgt1.cloud.owlhost.in.sls
              | - msk2-cmp1.cloud.owlhost.in.sls

Ну и конечно же чтобы не городить в top.sls кучу кода на jinja создаётся ещё маленький grains, который для каждого хоста добавляет нужные данные в grains

/srv/salt/_grains/extinfo.py
#!/usr/bin/env python
import subprocess
 
def extinfo():
    grains = {}
 
    minion_id = open("/etc/salt/minion_id").read().replace("\n", "") # msk2-cmp1.cloud.owlhost.in
 
    parts = minion_id.split(".")
    parts.reverse() # ['in','owlhost','cloud','msk2-cmp1']
 
    grains["nodename"] = parts.pop() # 'msk2-cmp1'
    grains["env"] = grains["nodename"].split("-")[0] # msk2
 
    parts.append(grains["env"]) # ['in','owlhost','cloud','msk2']
    grains["pillar_path"] = ".".join(parts) # in.owlhost.cloud.msk2
 
    return grains # {'nodename': 'msk2-cmp1', 'pillar_path': 'in.owlhost.cloud.msk2', 'env': 'msk2'}
 
if __name__ == "__main__":
    print(extinfo())

После чего синкаем grains на всех нодах

# salt \* saltutil.sync_grains

И можно писать top.sls

top.sls
base:
  '*'
    - base.pillars
    - area.{{ grains['pillar_path'] }}.base.pillars
    - area.{{ grains['pillar_path'] }}.nodes.{{ grains['id'] }}

Вот собственно и весь top.sls. Работает он так - сначала загружает базовую структуру данных, которая общая для всех площадок, после чего подгружает структуру данных, которая уникальна в рамках площадки а следом уже накладывает изменения, которые уникальны в рамках одного хоста. Естественно в pillars.sls и common.sls должны быть другие include'ы, но в целом дальше salt развивается как обычно. Ах да, ещё один момент - hostname или minion_id должен быть специально офрмлен, как то - <площадка>-<любое количество информативных данных>.<top host> (например msk2-cmp1.cloud.owlhost.in)

1)
люди, перестаньте использовать import_yaml, это страшная вещь, которая заставляет сальт тормозить