Продолжаем наш забег по сетевой автоматизации.
Итак, сеть спроектирована, IPAM запущен. И вот-вот начнут съезжаться миллионы наших стоек. Будем готовиться к этому.

Мы всё дальше от фантазий и абстрактных разговоров и ближе к практике.

И всё же снова сделаем отступление. Большое дело начинается с большого перекура.

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

Однако рано или поздно всё равно любая задача декомпозируется до уровня отдельных сетевых коробок.

И если про сеть как единый организм мы уже поговорили в 0-й статье, то теперь пришло время разобраться отдельными органами.



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

Естественно, всё это интересует нас с точки зрения автоматизируемости. Поэтому ещё мы нарисуем архитектуру системы автоматизации.

Кстати, не так давно вышла просто восхихитительная обзорная статья Дмитрия Тесля о процессе и инструментах сетевой автоматизации. Он смог лаконично изложить то, вокруг чего я пляшу уже несколько выпусков АДСМ. Настоятельно рекомендую прочитать её перед тем, как приступать к этой.

Содержание


  • Жизненный цикл оборудования
    • DAY 0
    • DAY 1
    • DAY N

  • IaC
  • Система автоматизации
    • Характеристики системы
    • Сценарии
      • 0. Проверка сети
      • 1. Ввод нового оборудования
      • 2. Переконфигурация из-за изменений переменных в инвентарной системе
      • 3. Переконфигурация из-за изменения дизайна
      • 4. Сбор информации с устройств
      • 5. Снятие и возврат нагрузки
      • 6. Обновление ПО
      • 7. Удаление устройства
      • 8. Замена устройства


  • Полезные ссылки



Так, ну а мы про

Общий взгляд на жизненный цикл оборудования





Вот мы купили сетевую железку. Что теперь? Проследим её жизнь с первого и до последнего дня.

  1. Day 0 — железка только появилась в наших руках. Сейчас самое важное — базовая настройка:
    • Добавить IP-адрес управления и маршрут
    • Включить SSH
    • Создать пользователя с правами настройки

    Иными словами задача Day0 конфигурации — организовать доступ на устройство.
  2. Day 1 — Железка уже встала на позицию, определена его роль в сети и сервисы, которые она обслуживает.
    Теперь нужно настроить уже целевую конфигурацию, с которой устройство встанет в сеть под нагрузку.
  3. Day N — Изменения конфигурации в процессе эксплуатации.
    Бывает мы добавляем новый сервис, пересматриваем дизайн или на худой конец нужно ACL поправить.
    Такие изменения нужно тоже уметь довозить до устройства.
  4. Обслуживание — Помимо нормальной работы есть периоды, когда устройство нужно аккуратно вывести из под нагрузки, чтобы, например, поменять в нём плату, провести обновление ПО.
  5. Отслеживание изменений — со всей сети следует собирать информацию о том, где и во сколько применялась новая конфигурация. Это позволит как скоррелировать жалобы клиентов с изменениями, так и знать, когда применялась новая конфигурация в обход процедуры.
  6. Проверка соответствия эталонной конфигурации — В течение всей жизни устройства нужно проверять, что его конфигурация не разошлась с целевой из-за сбоев в автоматике, обновлений ПО или прямого вмешательства чьих-то рук.
  7. Бэкапы — Даже если мы в любой момент можем сгенерировать эталонную конфигурацию, чтобы применить её на устройство, бэкапы необходимы.
  8. The Last Day — снятие нагрузки, удаление из всех систем, ритуальное сжигание. Под сжиганием я понимаю безопасную затирку конфигурации, чтобы хэши паролей (или, упаси Лейбниц, клиртекст), префикс-листы и ACL'и не оказались достоянием общественности.

Я намеренно обхожу вниманием в этой статье вопрос мониторингов операционного состояния и реакции на них, поскольку её лейтмотив — это всё же конфигурация.



Далее обсудим Day0 — DayN более детально.

Day0


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

Задачи Day 0 можно грубо разделить на две части:

  • Завести устройство в системах
  • Настроить базовый доступ

Говорить про них в отрыве друг от друга сложно, и делать мы так не будем.

Какие же есть способы?

  1. Бумер — вручную завести устройство в инвентарной системе и выделить свободный IP-адрес. Пусть это будет даже экселька.

    Подключить свитч к компьютеру и через консольный порт настроить IP-адрес, маршрут, включить SSH, создать пользователя.

    Отвезти свитч на позицию.

    + Просто, не требует почти никакой инфраструктуры.
    - Склонно к ошибками, масштабируется человеко-часами.
  2. Бумер+ — автоматизируем заведение устройства в DCIM/IPAM. Мы только нажимаем кнопочку, а в системе появляется железка на правильной локации со всеми нужными портами, ей выделяется автоматически имя и следующий свободный IP-адрес. В итоге генерируется базовый конфиг в виде текстового файлика.

    Администратор подключает свитч к компьютеру и через консольный порт копипастит содержимое этого файлика в терминал.

    + Ниже вероятность ошибок, значительно меньше ручной работы

    - Требуется уже какая-никакая инфраструктура: IPAM/DCIM с API, скрипт, всё ещё ручная работа, всё ещё настраивать на стенде и потом везти устройство на позицию.
  3. Миллениал — ZTP — Zero Touch Provisioning — подход, которому 100 лет в обед, но он почему-то всё ещё есть не везде. Идея в том, что устройство сразу же ставится на позицию и подключается в сеть управления, после чего по DHCP оно само получает свою конфигурацию.
    Для этого устройство должно быть уже заведено в IPAM/DCIM и предгенерирована конфигурация, которая и передаётся устройству.
    + Устройство можно сразу везти на позицию, минимум ручного труда
    - Нужна уже продуманная связная инфраструктура: IPAM/DCIM, DHCP, (T)FTP, автогенерация конфигов. Классическую вендорскую реализацию сложно применить для распределённых сетей, вроде ритейла.
  4. Зумеры — SD-WAN. Кстати, как раз подходит для ритейлов, хотя в свою очередь не очень для датацентров. Подход разделяет идею ZTP — мы устройство включаем, а оно само настраивается.
    + Меньше вероятность ошибок. На первый взгляд меньше работы
    - Однако SD-WAN — это преимущественно проприетарные решения вендоров, требующие мощной инфраструктуры, причём иногда только в облаке вендора. У нас, кстати, был целый подкаст про SD-WAN: telecom №91. SD-WAN.
  5. Пост-хипстеры — есть компании, где помимо Out of Band сети управления, есть ещё консольное соединение до абсолютно каждой железки. Для этого есть соответственно сеть консольных серверов внутри датацентров и точек присутствия.
    Каждое новое устройство после установки подключается в OOB-свитч по Ethernet и в консольный сервер консольным кабелем.
    Это позволяет реализовать схему, подобную описанной ниже:
    • Устройство добавляется в IPAM/DCIM
    • Устройство устанавливается и подключается по управлению
    • Инженер в ДЦ создаёт задачу на сервер наливки: настроить свитч, как Leaf, за консольными сервером №7, порт 3
    • Сервер наливки подключается на указанный порт, забирает серийный номер, с которым идёт в IPAM, генерирует базовый конфиг и обратно через тот же консольный порт применяет данную конфигурацию

    + Всегда есть консольный доступ на устройство, какие бы шторма ни гуляли в сети трафика и управления. Нет проблем с вендорскими особенностями — консольный протокол у всех реализован одинаково (с поправкой на параметры порта)
    - Совсем непросто и в абсолютных цифрах недёшево реализовывать ещё одну сеть управления. Не подходит для географически распределённых сетей. Требуется серьёзная инфраструктура даже в минимальном варианте без использования сервера наливки.

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

Кстати, вот классный доклад от фейсбука про их Вендинговые Машины по выдаче новых локаций:


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



Day 1


Дальше на железку нужно накатить уже рабочую конфигурацию и пустить на неё нагрузку.
Тут уже заметно интереснее. Одно дело — сгенерировать простейший конфиг на 20 строчек, одинаковый для всех типов устройств, как было в Day 0, и совсем другое — целевой конфиг на пару тысяч строк, который может радикально отличаться от железки к железке в зависимости от её роли и необходимых сервисов. Например, конфигурации двух экземпляров одной и той же модели свитча, установленных в качестве лифа и спайна, будут различаться как минимум настройками даунлинк интерфейсов.

Основная идея здесь в том, что мы описываем дизайн сети в том или ином формальном виде и отдаём его генераторам. Генераторы берут этот дизайн, роль устройства, локацию, переменные из IPAM/DCIM, всё это перемешивают, а на выходе получается специфический для данной коробки конфиг.

То есть основных компонента здесь три:

  1. Формализованный дизайн
  2. Заполненные данные в IPAM/DCIM
  3. Набор генераторов

Здесь подробно останавливаться не будем — формализации дизайна я посвящу отдельный (и скорее всего не один) выпуск.

Итак, имеем конфиг Day1. Осталось всего ничего — применить его на железку.

И тут все средства хороши в разных комбинациях: консоль, SSH, netmiko, NETCONF, GNMI, REST API, SNMP (я сейчас не шучу — лично видел), FTP, SCP.

В целом на нерабочую пока железку применить конфиг действительно можно разными способами:

  • Ручной копипаст из файлика в терминал
  • Применение команд последовательно через SSH из кода, используя тот же netmiko
  • Копирование файла на флэшку устройства, установка его в качестве конфигурационного и ребут железки
  • А-ля config replace
  • Пульнуть через NETCONF весь конфиг в XML
  • gNMI

Об этом тоже ещё поговорим.

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

Замечу, что если есть процесс и инструменты Configuration Management и версионирования конфигурации, то Day1 — это лишь частный случай DayN.



Day N


И вот теперь — ежедневная эксплуатация и периодические реконфигурации.

А вот с этим дела обстоят туго чуть менее, чем у всех. Говоря это, я не шучу. Тут всё плохо.
Дело в том, что нагенерить конфигурацию — действительно несложно. Пусть это будет даже циклопический jinja-шаблон с циклами и каунтерами.

А вот применить этот конфиг на железку ещё и под продуктивной нагрузкой — цель для инженеров со стальными нервами.

Тут целый ком проблем, как очевидных, так и неявных.

Во-первых, интерфейс: CLI, NETCONF, GNMI, SCP/FTP.

Если CLI — то как быть с особенностями реализации каждого вендора? Режимы контекстов, интерактивные диалоги, порядок выполнения команд.

Если NETCONF или gNMI — то его не все вендоры поддерживают. А те, кто поддерживает, делают это сильно по-разному, и зачастую не в полной мере. А если в полной мере, то, конечно, же в своей схеме, а не в OpenConfig.

А если файлик подложить — то не все на лету умеют заменять, а значит с ребутом — только кому он нужен при добавлении BGP-пира?

Во-вторых, инструмент доставки: netmiko, ncclient, ansible (какой модуль), SaltStack?

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

В-четвёртых, даже если мы видим будущие изменения (кандидат-конфиг на самом устройстве, к примеру), то это не говорит о том, что мы ничего не разломаем по своей неосмотрительности. Тут уже напрашивается сетевой CI/CD.

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

Это область компромиссов.

Но давайте будем честны сами с собой: восьми компаниям из десяти не нужен выстроенный процесс версионирования конфигурации, конвейер CI/CD, автоматическая выкатка, а возможно, и вообще весь этот ваш DevOps в сети.

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

И большинство компаний именно так и делает.

Можно добавить GitLab, TeamCity, AWX, аппаратную лабораторию с набором специфических тестов (FIB, QoS). Это всё мощные улучшайзеры, которые сделают процесс выкатки новой конфигурации значительно безопаснее. Но они не переведут управление конфигурацией на принципиально новый уровень.



А мы ведь всё же хотим

  • Полную автоматизацию
  • Универсальное решение
  • Минимизацию рутины
  • Безопасные выкатки конфигурации
  • Формализованный дизайн
  • Версионирование
  • Транзакционность, а если быть точнее, то соответствие требованиям ACID

Поэтому давайте составим схему системы автоматизации, которая позволит нам решить все задачи.

Но прежде расширим понятие «Инфраструктура как код» на сетевую инфраструктуру.



IaC


Если вы не слышали о IaC — Infrastructure as Code, у меня для вас плохие новости. Очень плохие.

Ладно, не напрягайтесь, сейчас всё расскажу.

Как было раньше: выдали вам пачку физических машин — на каждой из них вы запустили KVM/VMWare/WTF, подняли на них флот виртуалочек, настроили сеть, выкатили своё приложение. Нужна дев-среда? Давайте всё в той же последовательности теми же руками. И во второй раз может получиться чуть-чуть не то же самое.

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

Далее эти текстовые файлы обрабатываются неким инструментарием, который настраивает инфраструктуру на основе этой информации. Это может быть Terraform, Ansible, SaltStack.
Как результат — вы всегда быстро и с минимальным участием человека получаете предсказуемый результат.

Например, вы декларативно записываете в yml-, txt-, tf-, wtf-файле, что на таких-то хостах нужно установить KVM, Open vSwitch, настроить IP-адреса и туннели. Далее поднять набор ВМ с убунтой, выдать им адреса, на них установить nginx, загрузить ваш сайт в указанный каталог и настроить nginx. И поставить всё это дело за свежезапущенный балансер.

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

Данные в самом файле могут быть в форматах yml, json, это может быть набор объектов вашего любимого ЯП или что угодно иное, что может быть принято инструментом.

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

На схеме ниже изображена упрощённая процедура того, как изменения появляются на сети в парадигме IaC.


dteslya.engineer/network_automaiton_101

То есть настройка инфраструктуры выглядит аналогично релизу новой версии приложения в DevOps — те же гиты, апрувы, CI/CD и прочая.

Что же до сетей?

Прежде в хорошей ситуации у нас для них был HLD (High Level Design), который руками превращался в LLD (Low Level Design) для каждой железки, готовился конфиг (скорее всего, руками) и заливался на железо (надо полагать, тоже руками). В плохой — инженер сразу на железке настраивал сервисы так, как ему казалось правильным. Я знаю — сам прошёл все эти стадии.

Я же предлагаю вам качественно новую жизнь!
Систему автоматизации сети мы будем рассматривать также в разрезе IaC. Давайте уже прекратим строить из себя особенных, потому лишь что у наших хостов проприетарная ОС. Сеть — такая же часть инфраструктуры, как физические машины, виртуалки, контейнеры. Ну да, прихлопнуть свитч с нерабочим чипом и пересоздать новый нельзя — но это просто добавляет красок в нашу работу.



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

В общем это именно то, о чём мы тут толкуем с самого 0-го выпуска. Дизайн описан в формализованном HLD, а конкретные данные берутся из нашего SoT — Netbox. Из них генерится конфигурация и складывается в репозиторий, где прогоняются авто-тесты (в аппаратной или виртуальной лабе или что-то а-ля Batfish), кто-то смотрит глазами и подтверждает изменения, далее они по всем правилам CD выезжают в прод.

IaC — и ничегошеньки не настраиваем руками.





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


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

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

  1. Нужен интерфейс, через который можно создавать задачи. Он абстрагирует работу с сетевыми устройствами.
    Графический — для инженеров, API — для внешних сервисов.
  2. Ввод новых устройств
  3. Актуализация данных в инвентарной системе (LLDP, список интерфейсов, IP-адресов, версия ПО)
  4. Генерация целевых конфигураций
  5. Применение целевых конфигураций и временных патчей (тшут, костыль)
  6. Сличение целевых и реальных конфигов
  7. Снятие и возврат нагрузки
  8. Обновление ПО
  9. Сбор бэкапов, коммитов,
  10. Диспетчеризация задач, выполняющихся на железе.

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



  1. IPAM/DCIM — система, являющаяся Source of Truth для всей системы автоматизации. В нашем случае — Netbox.
  2. NetAPI — служба одного окна. Что бы ни вздумалось сделать с сетью — идём в него.
    Например, захотелось добавить новый свитч — идём в ручку NetAPI с нужным набором параметров (серийник, имя, локация) и создаём задачу на добавление свитча. А-ля: netapi.linkmeup.ru/api/adddevice.
    Захотелось собрать LLDP с устройств — идём в другую ручку со списком устройств. А-ля: 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). Мы всё это ещё потом в контейнеры сложим. И наступит полный микросервис.

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



Сценарии


В реальной жизни их, конечно, будет много. Я же опишу самые необходимые:

  • 0. Проверка сети
  • 1. Ввод нового оборудования
  • 2. Переконфигурация из-за изменений переменных в инвентарной системе
  • 3. Переконфигурация из-за изменения дизайна
  • 4. Сбор информации с устройств
  • 5. Снятие и возврат нагрузки
  • 6. Обновление ПО
  • 7. Удаление устройства
  • 8. Замена устройства

Распишем каждый из них детально.

0. Проверка сети


Наверно, совершенно оправданным будет нулевой приоритет отдать именно блоку ручек для проверки сети, поскольку к ним нужно будет обращаться почти в каждом последующем сценарии.

Часть из них будут реализовывать blackbox-проверки. Например, наличие e2e-связности, потерь, RTT.

Другая whitebox — существование тех или иных маршрутов в RIB, состояние FIB итд.

  1. Человек или сервис приходит в ручку NetAPI с запросом, в теле которого указаны параметры теста. Например, ICMP, устройство-источник, адрес назначения, VRF, число проб, размер пакета.
  2. NetAPI формирует запрос в NetGet, чтобы тот собрал данные с сети/устройства. И тот собирает.
  3. Результаты теста возвращаются клиенту.

1. Ввод нового оборудования


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

  1. Человек или часть системы, реализующей нечто а-ля ZTP, приходит в NetAPI для инициализации устройства.
    Устройство идентифицируется по своему серийнику или инвентарному номеру, и ему должна быть задана роль, чтобы было понятно, с какой конфигурацией его наливать.

    1. Ручка дёргает конкретное приложение, отвечающее за этот шаг
    2. Приложение создаёт устройство в NetBox и прописывает его
      • Имя
      • Серийник
      • Локацию
      • Вендор/модель
      • Роль в сети
      • Присущие ему свойства: список интерфейсов, консольных портов, комментарии.

    3. Приложение определяет и при необходимости создаёт MGMT-интерфейс
    4. Приложение выделяет MGMT IP.

    На данном шаге устройство в минимальном виде заведено в инвентарной системе, и заполнены необходимые для первичной настройки параметры.
  2. Далее другая часть процесса, а-ля ZTP, приходит в ручку NetAPI в поисках первоначального конфига

    1. Ручка дёргает конкретное приложение
    2. Приложение собирает данные из NetBox и, возможно, внешних систем
    3. Приложение рендерит конфиг, возвращает его клиенту и заодно складывает его в git-репозиторий.
    4. Клиент каким-то образом доставляет конфигурацию до устройства — это может быть ZTP или пропихивание конфига через консольный порт. Идентификатором устройства тут выступает серийник.

    После этого шага появляется удалённый SSH-доступ на устройство.

    Теперь по какому-то триггеру запускается конвейер ввода устройства в эксплуатацию.

    Триггером может быть:

    • Чьё-то ручное действие — например, нажатие кнопки в интерфейсе — и сигнал в NetAPI.
    • Обращение к ручке ввода в NetAPI от системы ZTP после завершения.
    • Факт появления доступа по SSH на устройство — например, кроняка пытается доступиться до железки, которая помечена как «для ввода».

  3. Заполняются данные в NetBox, которые в дальнейшем будут служить переменными для генерации конфигурации.

    1. Система посылает в NetGet запрос на сбор данных о LLDP с данного свитча.
      1. Информация о соседях вносится в NetBox, порты связываются друг с другом.
      2. При необходимости создаются сабинтерфейсы или интерфейсы добавляются в LAG.
      3. Вычисляются (или выделяются) P2P IP-адреса.

      Необходимые изменения выполняются и на соседнем устройстве.

      Этот шаг позволяет, во-первых, подготовить данные для настройки IP-адресов, во-вторых, визуализировать топологию при необходимости, в-третьих, собрать в будущем информацию о BGP-соседях, если на узле используется BGP.
    2. Система создаёт набор виртуальных интерфейсов и выделяет IP-адреса.
      Например, loopback'и и VLAN-интерфейсы.
    3. Заполняет другие необходимые данные. Например, ASN, IS-IS Network Entity, настройки l2-интерфейсов.

  4. Обновление данных в NetBox инициирует запрос в NetAPI на запуск конвейера для вычисления и деплоя новой конфигурации. Это может быть, например, Web-hook, отправленный самим Netbox'ом.

    Речь здесь идёт обо всех устройствах, конфигурация которых меняется в результате ввода новых устройств. Добавляется новый Leaf — поменяется конфигурация Spine.


  5. NetAPI через Диспетчера адресует задачу на ConfMan, который вычисляет вендор-агностик конфигурацию.
    Для этого система берёт формализованную модель конфигурации данных (питоновские объекты, yaml итд) и подставляет в неё данные из NetBox.
    Результатом может быть словарь, тот же yaml или питоновский объект.
  6. Система генерит конфиг для списка устройств. Результатом может быть текст, содержащий последовательность CLI-команд, NETCONF XML, набор объектов для YANG, Protobuf для gNMI.
  7. Выполняются лабораторные тесты CI/CD. Они могут быть в симуляторе, вроде Batfish, виртуальном стенде или всамделишной небольшой железной лабе, мимикрирующей под настоящую сеть.

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

    Проходят ручные проверки и подтверждения.
    Это немного сколькзий момент. С одной стороны я всё же не верю, что в обозримом будущем на сеть новый конфиг можно катить без человеческого подтверждения, как это давно происходит в мире WEB-приложений.
    С другой — когда изменения катятся на тысячу устройств, пойди глазами всё просмотри. Поэтому всё же CI/CD и канареечные деплои — это то, к чему мы будем стремиться.

    Опционально этот шаг может выполняться в git-репозитории. Хотя заставлять человека переходить во внешний относительно основной системы автоматизации сервис — негуманно. Впрочем как первые шаги разработки такой системы — вполне нормально.
  8. По факту сгенерированного конфига или полученных апрувов формируется задача в Dispatcher для Carrier'а на доставку и применение конфигурации на сеть.


  9. Диспетчер диспетчеризирует и следит за выполнением каждой конкретной задачи и всей транзакции целиком.
    Он несёт полную ответственность за то, когда выполняется задача и с каким статусом она завершается.
  10. В случае успешной транзакции Диспетчер обращается в ручки NetAPI, чтобы провести ряд тестов, проверяющих две вещи:
    • Новое устройство готово к обслуживанию трафика,
    • Сеть при этом не сломалась.




    Запускаются какие-то пинги. Проверяется маршрутная информация на сети — сравнивается с бейзлайном (например, состояние, как было до деплоя). Последнее предполагает, что мы либо собрали состояние перед обновлением, либо есть некая база данных с временными рядами (TSDB — Time Series Data Base), содержащая срезы исторических данных.

    Есть тесты, падение которых вызовет аварию, но операция будет считаться завершённой. А есть те, после которых произойдёт автоматический откат всей транзакции. Лучше не сделать ничего, чем сделать хорошо, но наполовину.
  11. В случае успешных тестов в NetBox и/или иных системах проставляются индикаторы успешного ввода, новое устройство заводится в мониторинги и другие системы.
  12. С результатами Диспетчер идёт в ручку NetAPI и сообщает, что ввод завершён успешно, либо нет.



Конвейер завершён.

Это весьма упрощённый конвейер, конечно. Здесь опущены шаги, которые могут быть фактически необходимы в реальной жизни: подавления аварийных сообщений, отписывание комментариев в тикеты, возможные проверки и подтверждения целевой конфигурации живыми людьми, всевозможные валидации на каждом шаге.

2. Переконфигурация из-за изменений переменных в инвентарной системе


Допустим по какой-то причине данные в нашем SoT поменялись — человек руками дескрипшон на порту изменил или автоматика пересчитала LLDP-соседства или ещё что-то.

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

Триггером может быть Web-hook от SoT или опять же кроняка, которая следит за изменениями в этом SoT.

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

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

Не забываем про версионирование — изменения переменных в SoT фактически ведёт к изменению версии конфигурации сети. Мажорное, минорное или патч — это предмет жарких дискуссий, судьёй которому будет semver.

3. Переконфигурация из-за изменения дизайна


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

Так же вопрос без ответа, в каком виде дизайн должен храниться — питоновские объекты, словарь, yaml, json? Хотел бы знать.

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

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

4. Сбор информации с устройств


В целом сбором информации занимается NetGet. Как периодическим, так и разовым по запросу.
Поэтому, когда нужно собрать, например, MAC'и с конкретного устройства, клиент идёт в ручку NetAPI, а тот в свою очередь дёргает NetGet.

NetGet формирует задачу для Диспетчера, чтобы Carrier сходил на устройство и собрал необходимую информацию.

Учитывая, что для таких запросов клиент ожидает синхронный режим, Диспетчер должен по возможности прогнать его с высоким приоритетом и быстро вернуть ответ NetGet'у.

Из любопытных идей для оптимизации: NetGet видится очень активноиспользуемым компонентом — вплоть до того, что мониторинг будет ходить в него, чтобы собрать счётчики и состояние сети — и, возможно, ему стоило бы держать открытыми и прогретыми сессии со всем флотом сетевых устройств. С использованием asyncio данные будут собираться просто в мгновение ока. А шардирование сетевых элементов по разным worker'ам позволит не упираться в лимиты.

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


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

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

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

Это может быть реализовано как две ручки: для снятия нагрузки и для возврата — так и как одна: выполняющая полный цикл.

  1. Клиент приходит в ручку NetAPI. А тот запускает конвейер увода нагрузки
  2. Приложение определяет список сервисов, которые нужно погасить (L2/L3VPN, базовая маршрутизация, MPLS итд)
  3. Приложение формирует список действий, которые нужно совершить.
    Например:
    1. Плавно увести трафик с помощью BGP gshut community или ISIS overload bit (или ещё чего-то)
    2. Убедиться в отсутствии трафика на интерфейсах
    3. Выключить BGP-сессии в нужном порядке (сначала сервисные, потом транспортные)
    4. Выключить интерфейсы
    5. Убедиться в отсутствии активных аварий по сервисам.

  4. Зафиксировать статус задачи.

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

По завершении клиент дёргает ту же ручку для возврата нагрузки — и тогда в обратном порядке выполняются предыдущие действия.

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

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


Обновление может быть двух видов — требующее прерывания сервисов, и нет.

Соответственно конвейеры для них будут разные.

Рассмотрим для сложного случая

  1. Клиент приходит в ручку NetAPI.
  2. Запускается конвейер снятия нагрузки
  3. Запускается конвейер обновления ПО:
    1. Залить файлы ПО
    2. Проверить контрольную сумму
    3. Обновить прошивку, указать загрузочные файлы, перезагрузить устройство и провести иные мероприятия
    4. После обновления проверить версию ПО

  4. Запустить конвейер возврата нагрузки.

7. Удаление устройства


Это весьма частый сценарий. Особенно если рассматривать переезд старого устройства в новую роль или локацию, как удаление и создание нового.

  1. Клиент приходит в NetAPI. Тот дёргает приложение, отвечающее за удаление устройства.
  2. Приложение проверяет, что нагрузка на устройстве ниже определённого порога.
  3. Приложение обращается в NetAPI в ручку снятия нагрузки.
  4. Приложение ищет все зависящие от этого устройства объекты в SoT. Как пример:
    • Интерфейсы
    • IP-адреса
    • Подсети
    • Интерфейсы соседних устройств
    • P2P-адреса соседних устройств
    • Итд.

  5. Приложение удаляет их все.
  6. Изменения в SoT триггерят запуск уже известного нам конвейера. Как вы видите он весьма и весьма универсален.
    Как результат — настройки соседних устройств, относящиеся к удаляемому, так же удаляются в процессе деплоя новой конфигурации.

    Само же устройство затирается к заводским настройкам. Кроме того оно удаляется из всех мониторингов и других систем.
  7. Устройство удаляется из БД или помечается каким-то образом, если нужно сохранить о нём информацию.

8. Замена устройства


Случается, что свитч ломается. Или нужно железку проапгрейдить на новую модель. В общем надо её снять, а новую поставить.

Теоретически это выглядит как два шага:

  • Удаление текущего устройства
  • Добавление нового

Но нам важны несколько вещей:

  • Имя нового устройства должно быть таким же, как и у прежнего
  • Сохранить MGMT IP
  • Сохранить и другие атрибуты: лупбэки, вланы, ASN, итд
  • Скорее всего, и конфигурацию

Не факт, что это всё необходимо, но, скорее всего, так.

Самым простым выглядит в существующей записи поменять минимум вещей — инвентарник, серийник, модель. Но это лишает гибкости и добавляет несколько щекотливых моментов при выводе старой железки.

Кроме того, мне импонирует мысль, что девайс в БД собой олицетворяет не место и роль, а вполне конкретное устройство. И при добавлении в сеть нового свитча или роутера, в DCIM появляется новая запись.

Поэтому я бы всё же рассматривал замену устройства на сети как

  • Удаление старого устройства
  • Добавление нового с определённым набором атрибутов, значение которых хотим зафиксировать, и которые в противном случае определялись/выделялись бы автоматически.

При этом процедура удаления, определённая шагом выше, берётся как есть: с удалением артефактов на других устройствах (пересоздадим на втором шаге) и вычисткой конфига с устройства (чтобы, например, оно случайно на новом месте неожиданно не запустилось со старыми адресами и не начало всасывать и блэкхолить трафик).



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

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

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

Сами конвейеры при этом декомпозируются на ещё более простые и универсальные атомы.



Полезные ссылки





Заключение


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



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

И ещё хуже то, что в рамках этой серии крайне маловероятно, что я сделаю что-то практическое, хотя бы отдалённое напоминающее описанную схему.
Хотя зарекаться не буду.

В следующих статьях мы поразбираемся с ZTP и какими-то базовыми скриптами автоматизации. А ещё рано или поздно углубимся в интерфейсы взаимодействия с сетевыми коробками: NETCONF/YANG, gNMI, GRPC.



Спасибы


  • Роману Горге — бывшему ведущему подкаста linkmeup, а ныне эксперту в области облачных платформ — за комментарии про подход IaC и концепцию ACID применительно к сети.
  • Михаилу Арефьеву — руководителю проектов по сетевой автоматизации в Яндексе — за анализ и критику архитектуры решения и сценариев.
  • Дмитрию Фиголю — Network Automation Architect at Cisco Global Demo Engineering — за острые замечания и дискуссию.
  • Никите Асташенко — моему другу и классному разработчику — за поездку на Алтай и долгие разговоры у костра, без которых эта идея не вызрела бы.