Система автоматизации с высоты птичьего полёта

Один из принципов, который нужно заложить в систему - это Zero Touch Prod, то есть свести к минимуму прямое хождение инженера на устройства. Любые изменения конфигурации только через платформу, только через интерфейс.

Итого, какой список задач решаем?

  1. Нужен интерфейс, через который можно создавать задачи. Он абстрагирует работу с сетевыми устройствами.
    Графический - для инженеров, API - для внешних сервисов.
  2. Ввод новых устройств

  3. Актуализация данных в инвентарной системе (LLDP, список интерфейсов, IP-адресов, версия ПО)

  4. Генерация целевых конфигураций

  5. Применение целевых конфигураций и временных патчей (тшут, костыль)

  6. Сличение целевых и реальных конфигов

  7. Снятие и возврат нагрузки

  8. Обновление ПО

  9. Сбор бэкапов, коммитов,

  10. Диспетчеризация задач, выполняющихся на железе.

Соответственно схематично я бы изобразил это так:

https://fs.linkmeup.ru/images/adsm/4/bird_scheme.svg
  1. IPAM/DCIM - система, являющаяся Source of Truth для всей системы автоматизации. В нашем случае - Netbox.

  2. NetAPI - служба одного окна. Что бы ни вздумалось сделать с сетью - идём в него.
    Например, захотелось добавить новый свитч - идём в ручку NetAPI с нужным набором параметров (серийник, имя, локация) и создаём задачу на добавление свитча. А-ля: https://netapi.linkmeup.ru/api/adddevice.
    Захотелось собрать LLDP с устройств - идём в другую ручку со списком устройств. А-ля: https://netapi.linkmeup.ru/api/lldp.
    Исключительно как вариант: это может быть приложение на Django, FastAPI, Flask, запущенное как systemd-сервис.
  3. Набор приложений, которые реализуют функционал ручек NetAPI. Например, клиент хочет получить список MAC-адресов со свитча - он идёт в ручку, а ручка дёргает модуль сбора MAC’ов, модуль идёт на свитч по SSH и собирает необходимую информацию (через CLI или NETCONF).
    Это может быть как интегральная часть NetAPI, так и отдельные сервисы, с которыми NetAPI взаимодействует по ещё одному API (REST, GRPC).
  4. Сервис NetGet, выполняющий регулярные и разовые задачи на сбор данных с сетевых устройств, таких как бэкапы, коммиты, версии ПО итд.
    Это может быть systemd-сервис или просто набор скриптов, запускающихся по cron’у или триггеру.
  5. ConfMan - Configuration Manager - это набор сервис и компонентов, выполняющий всю работу по управлению конфигурацией.
    Его составными частями являются:
    • HLD - формализованный дизайн сети (High Level Design). Это могут быть объекты того языка программирования, на котором написана система автоматизации, может быть набор YAML-файлов или что-то своё собственное.
    • Хранилище переменных, необходимых для конфигурации, которые по тем или иным причинам не получается хранить в IPAM/DCIM (например, префикс-листы или syslog-сервера).
    • Специфические компоненты, такие как система управления доступами - для ACL. Или система планирования нагрузки для генерирования конфигов QoS-очередей. Возможно, оркестраторы/контроллеры для инжиниринга трафика, тоже стоит рассматривать как часть ConfMan.
    • Набор генераторов конфигурации - то самое, что возьмёт HLD, обогатит его данными из IPAM/DCIM, хранилища, других систем и сформирует конечный вид конфигурации устройства.
    • Возможно, часть, которая вычисляет фактическую дельту конфига и формирует патч, то еcть список команд для достижения целевого состояния. Возможно - потому что вместо применения только изменений, можно целиком конфигурацию заменять.
    • Модуль, отвечающий за сличение целевого и реального конфига.
    Отдельные компоненты ConfMan взаимодействуют друг с другом через тот или иной API.
  6. Carrier - доставщик изменений на сеть. Например, ConfMan сгенерировал пачку конфигов и передал Carrier’у на применение.
    В зависимости от используемого интерфейса взаимодействия с сетевым устройством он выполняет разные функции.
    Так, для CLI он знает специфику взаимодействия с консолью конкретного вендора - интерактивные ответы, ошибки, информационные сообщения.
    Для NETCONF’а он умеет определять успешность или неуспешность применения конфигурации.

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

  7. Над всем этим царит Dispatcher - этакий диспетчер задач, бригадир, который распределяет работу.
    Он ведёт учёт всех поступивших задач, отслеживает их статусы, составляет расписание на исполнение.
    Например, если стоит задача обновить 300 свитчей, то он знает, что нельзя это делать одновременно, поэтому он составит расписание. Так же он не выведет из эксплуатации больше двух спайнов одновременно, и не проведёт работы на двух бордерах.
    Если на конкретную железку уже есть задача или на ней CPU под сотку, это значит, что применение изменений нужно отложить.
    В общем вот таким составлением расписания и занимается Dispatcher.
    Все задачи связанные с доступом на сетевое устройство, проходят через него.

Вот такая получается система. Не очень простая, но не очень и сложная.

Давайте сразу отметим несколько важных характеристик этой системы.

Характеристики системы

Единый интерфейс

Во-первых, отметим здесь центральную роль NetAPI. Он является точкой входа для большинства задач: ввести новое железо, переконфигурить старое, обновить свитч. Внутри задачи могут быть подзадачи, требующие обращение к NetAPI, например, обновление ПО своей подзадачей имеет снятие нагрузки, которое тоже может являться ручкой NetAPI, а снятие нагрузки в свою очередь требует проверки наличия трафика на портах, что тоже подразумевает поход в NetAPI. И так далее.

Асинхронность

Во-вторых, нам необходим асинхронный режим работы API. Некоторые из запросов (тот же ввод нового оборудования в работу) может длиться продолжительное время, то есть ответ клиенту не вернётся в обозримое время. Поэтому нужна возможность создать заявку, получить её ID и вернуться позже за уточнением её статуса.
Для этого каждому запросу в API выделяется ID, данные о нём вносятся в базу данных, статус обновляется по мере поступления новых данных.

Соответственно должна существовать отдельная ручка (-и), в которую (-ые) можно прийти и узнать статус запроса по ID.

ACID

В-третьих, применение конфигурации на сеть должно соответствовать принципам ACID.
Давайте рассматривать выкатку новой конфигурации на сеть как транзакцию.
  • A - Atomicity. Никакая конфигурация не должна примениться частично. Как в пределах устройства, так и в периметре сервиса - на наборе устройств. Применяется либо вся конфигурация, либо никакая. Соответственно, если на ряде устройств конфигурация применилась, она должна быть откачена. Либо средствами встроенного rollback-механизма, либо набором отменяющих изменения команд.
  • C - Consistency. Именно в том виде, как понятие консистентность применяется к БД, к сети, пожалуй, не применима, но мы будем иметь в виду, что все сетевые сервисы после применения новой конфигурации остаются работоспособными.
    Факт консистенстности проверяется набором тестов, запускающимся после выкатки конфигурации. В зависимости от типа изменений могут быть разные наборы тестов. Иногда достаточно проверить CPU на паре коробок, в другой раз запустить пинги и проверить статусы BGP-сессий, а в третьем - всесторонние тесты всего, что настроено на сети.
  • I - Isolation. Вполне понятный принцип применительно к сети - с того момента, как мы запланировали выкатку новой версии и до её применения, статус сети должен быть зафиксирован - никто не должен её менять. И уж тем более никто не должен настраивать что-то одновременно с запланированной выкаткой.
    Но это качество проще обозначить, чем обеспечить. Допустим, все таски внутри системы управляются Диспетчером, и он выстроит все задачи в правильном порядке. Однако как быть с тем, что кто-то может руками наадхочить на железке? Есть только один способ с этим справиться - люди не ходят на оборудование напрямую - Zero Touch Prod, помним. То есть на железе остаётся служебная учётка нашей системы автоматизации и аварийная для инженеров, которую используют только в ситуациях, когда система сложилась и надо срочно попасть на железо.
    Увы, это не отвечает на два вопроса: «А для тшута мы что делаем?» и «Что мешает инженеру пользоваться аварийной учёткой?». Вообще-то и на тот и на другой вопрос можно подобрать ответы, но не будем тут зацикливаться.
  • D - Durability. Ну тут всё просто - что бы ни случилось на сети, после восстановления конфигурация должна быть прежней. Решается это сохранением конфигурации при каждом коммите (или изменении конфиги, если коммита нет). Но есть нюанс - идентичная конфигурация не говорит об идентичном поведении - дело может быть в консистентности FIB. Но это тоже уже за рамками данной статьи.

Взаимодействие компонент через API

В-четвёртых, взаимодействие между элементами системы. Очевидно на схеме выше лишь упрощённая схема. Фактически она будет значительно больше, а количество связей и сообщений между элементами превысит все мыслимые и немыслимые значения, а Васюки станут центром десяти губерний!.
К чему это я? Взаимодействие между частями системы должно быть реализовано через API, каким бы он ни был - gRPC, HTTP REST, да хоть SOAP (нет, не хоть).
А кроме того, в какой-то момент нам может понадобиться очередь сообщений (Message Queue). Мы всё это ещё потом в контейнеры сложим. И наступит полный микросервис.

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