Annet (внутри Яндекса — «Аннушка») — это инструмент управления конфигурациями сетевого оборудования, разработанный командой инфраструктуры офисных сетей (Enterprise Network) и выложенный в open source. Продукт изначально создавался для сетевого инженера: генераторы конфигурации пишутся так, что инженер видит привычный синтаксис CLI и понимает, что именно произойдёт с конкретной железкой при применении настроек. Вторая ставка — мультивендорность. При замене сетевой коробки одного вендора на другую все данные подтягиваются из кода и источника истины: неважно, меняется Cisco на Huawei или наоборот. Достаточно налить конфигурацию — и сеть работает так же, как прежде. Доклад посвящён тому, как Annet работает с одним из самых специфичных CLI — MikroTik RouterOS.
Проблема: конфигурация, которая расходится с ожидаемой
Типичный цикл эксплуатации выглядит так: конфигурация наливается на устройство, сеть запускается и работает — до первого обращения. Дальше инженер подключается к железке, ищет проблему, быстро её фиксит. Так накапливаются legacy-настройки и выключенные правила (в случае MikroTik — особенно заметно). Чем дольше устройство работает, тем сложнее его диагностировать: конфигурация всё дальше уходит от ожидаемой.
Для большой и долго живущей инфраструктуры решением становится подход «инфраструктура как код». Прежде чем применять настройки на оборудовании, их можно протестировать локально и раскатать на канареечной группе. Когда группа расширяется, изменения фиксируются коммитом в систему контроля версий и раскатываются дальше. Если что-то пошло не так — коммит откатывается, и раскатывается предыдущая версия конфигов.
Что умеет Annet
Ядро.
Генерирует конфигурацию, забирая данные из источника истины, который одновременно является документацией на всю сеть, — например, NetBox. Для него есть встроенный адаптер, тоже выложенный в репозитории.
Патч.
На внутреннем сленге это называется «сварить конфиг» или «сварить патч». Патч — это уже вендор-читаемые команды, правильно отсортированные: одни настройки откатить, другие применить, всё в нужном синтаксисе. После применения патча diff должен стать пустым — это значит, что настройки приведены к ожидаемым и лишних висящих правил не осталось.
gnetcli.
Сервис на Go, который умеет подключаться к разным вендорам разными протоколами. Чаще всего это три протокола: SSH; Telnet (для legacy-оборудования или оборудования в защищённом контуре); консоль — у серьёзных сетевых железок есть обязательное подключение через консольный сервер, по нему тоже можно применять конфигурацию, например при первоначальной настройке коробки.
Адаптер gnetcli.
Набор правил о том, что именно нужно читать на железке. Читается прежде всего конфиг: на Cisco это show running-config, а на MikroTik нужны три команды — помимо самого экспорта, требуется прочитать файлы и пользователей, так как в базовый экспорт они не попадают.
annetbox.
Клиент для NetBox как наиболее популярной системы для ведения инвентаря. При наличии другого инвентаря Annet можно научить ходить и в его API, но у NetBox API развит хорошо.
Почему CLI, а не API
Логичный вопрос: почему для настройки железок используется CLI, а не API? API — это интерфейс для взаимодействия одной системы с другой, а CLI — интерфейс для человека. Раскаткой конфигурации занимается человек: он пишет генератор на понятном ему синтаксисе и сразу видит, что делает. Разбирать JSON — задача для роботов. С таким выбором можно спорить, но на практике он показывает себя хорошо.
Как начать
Порог входа невысокий. Есть подробный туториал; при наличии NetBox всё дальнейшее упрощается, а сам NetBox при необходимости разворачивается по инструкции, в том числе из Docker. Annet — набор Python-пакетов, устанавливается просто. Сервис gnetcli на Go также ставится без сложностей.
Самые востребованные команды:
1.
annet gen — сгенерировать конфигурацию и посмотреть её в текстовом виде;
2.
annet diff — увидеть расхождение с ожидаемым;
3.
annet patch — получить набор команд для приведения к ожидаемому;
4.
annet deploy — применить конфигурацию.
Есть и другие команды — например, генерация конфигурации с комментариями о том, в каком именно генераторе появилась та или иная настройка. Это позволяет быстро понять, в какой файл нужно вносить правку — скажем, при замене SNMP-хоста.
Специфика RouterOS
Вендоры сетевого оборудования — тоже по-своему художники и видят мир в своём свете. В RouterOS есть стандартная обёртка find, и, казалось бы, чтобы отменить команду, достаточно заменить add на remove, подставить find со скобками — и надеяться, что применится. На практике этот приём работает не всегда.
Отдельный случай: если на железку заходили руками и удалили интерфейсы, IP-адреса остаются висеть на интерфейсе *, и при чтении конфига это тоже приходится обрабатывать. Для таких ситуаций в Annet есть rulebook и механизм logic — набор правил, позволяющих кастомно обработать конкретные строки конфигурации. Всё это, как и генераторы, пишется на Python — языке, на котором можно почти тезисно записать желаемое и получить рабочий код. Правило описывается через logic, а сама «магия» выносится в функцию.
Примеры не оставлены за кадром: в репозитории Annet лежит развесистая конфигурация для RouterOS — с туннелями, IPsec, GRE поверх IPsec и резервированием провайдеров. Под такую инфраструктуру написан работающий набор правил. Например, функция обработки IP-адресов выцепляет адрес и интерфейс, при отсутствии маски подставляет дефолтную (/32 или /64 для IPv6), добавляет кавычки и формирует команду удаления либо по адресу, либо по паре «адрес + интерфейс», включая случай интерфейса *.
Склейка блочных команд.
Синтаксис RouterOS блочный: diff разбит по блокам, как при обычном экспорте. Но в патче команды приходится склеивать, потому что у блоков ip и ipv6 отличается только «макушка». Ядро Annet при виде двух одинаковых строк вырезало дубликат — а в firewall правило вида «разрешить established/related на forward и input» вполне закономерно встречается и для IPv4, и для IPv6. Решение: в diff блоковая структура сохраняется для удобства чтения человеком, а в патче строки склеиваются, как в export verbose.
Firewall.
Firewall — последовательный набор правил, аналог классического ACL, и порядок правил важен. Вырезать правило из середины и вставить в нужную позицию дорого: вычисление позиции — сложная задача. Поэтому подход простой: при появлении diff в firewall ruleset удаляется целиком и накатывается заново. Процедура выглядит опасной, но она безопасна при правильной архитектуре: если основное описано в фильтре, а специфика вынесена в address-list и interface-list, сам ruleset меняется редко. На практике при смене провайдера (а на роутере их два-три) меняются адреса, маршрутизация, route rules, IPsec — но не ruleset firewall, который остаётся близким к дефолтному MikroTik с небольшой спецификой. Связанность при этом не теряется: IPsec продолжает работать через второго оператора, и конфигурацию можно применить.
diff_logic.
Ещё более неочевидный, но полезный инструмент. Пример: провайдер подключён по DHCP, настройки приходят от оператора, а в DHCP-клиенте отрабатывает скрипт, динамически создающий правила. Все такие правила помечаются комментарием, а простая функция для diff_logic вырезает их из diff. Настройки самого скрипта при этом контролируются: изменения в скрипте применятся. Без этого Annet сравнил бы динамические адреса и шлюзы с источником истины, не нашёл бы их там и решил, что настройки нужно поменять, — заруинив связность.
order.
Порядок применения команд задаётся отдельным файлом сортировки. Инженер указывает, в какой последовательности безопасно применять команды на конкретном вендоре: например, из MikroTik-бриджа сначала удалить компоненты (VLAN'ы и порты), затем сам бридж, а затем собрать обратно. То же касается развесистой конфигурации IPsec с множеством компонентов. Патч не обязан повторять структуру экспорта — этим можно управлять.
Практика эксплуатации
Разграничение прав. Разные команды отвечают за разные домены: сети дата-центров, офисные сети, Wi-Fi. Разграничение переложено на политики системы контроля версий — раз инфраструктура описана кодом, всё лежит в VCS (git или, как в Яндексе, Arc). Отдельный файл политик описывает, какая команда отвечает за какой генератор. Роли прорастают из общей IDM-системы, а коммиты проходят через ревью. Есть и общие файлы — например, для свитча в дата-центре без динамической маршрутизации: изменения в такой файл уходят на ревью команде DC. Раскатка возможна только после коммита.
Безопасность раскатки. Тестирование идёт на проде, но на канареечных группах: сначала одно устройство, затем пара, затем десяток. При проблеме — откат на предыдущий коммит и раскатка предыдущей версии, так же по канареечным группам. Это возможно потому, что инфраструктура зарезервирована на других уровнях и падение одного устройства не ломает ничего глобально.
Как не потерять железку.
Команды сгруппированы в отдельные генераторы: один для маршрутизации, другой для IP-адресов, третий для правил firewall. Их можно применять по отдельности и в нужной последовательности — например, сначала раскатать IP-адреса, а затем менять шлюз. Дополнительно можно подложить соломки в самом инвентаре, заведя запасные адреса для возврата на устройство. Это зона ответственности инженера: думать о том, как не потерять железку, нужно и при ручном применении конфигурации.
Safe mode RouterOS сознательно не используется: он может навредить сильнее, чем помочь.
Порог внедрения. Ориентировочная оценка: примерно от сотни роутеров вложение в такую систему уже окупается — особенно если за ними стоят точки доступа. При этом сам Annet спокойно живёт на одной виртуалке. К этому моменту в компании обычно уже есть отдельный инвентарь и система мониторинга — одного inventory-файла из Ansible недостаточно.
Финал
Annet — это робот с человеческим лицом, точнее, с человеческими способностями. Работа идёт через CLI, а значит, нужно понять специфику CLI конкретного вендора и подстроиться под неё — инструменты для этого есть. Killer feature: конфигурация остаётся в порядке даже после того, как в неё залезали руками и что-то чинили. А если diff показывает, что что-то не так, это повод задуматься — возможно, стоит модифицировать генератор и привести систему к консистентному состоянию. logic и diff_logic — довольно мощные инструменты, а Annet можно научить работать с самыми разными интерфейсами, включая такой специфичный, как у RouterOS. Наработки по RouterOS перенесены из внутренних репозиториев в open source, и ставка на open-source-версию продукта усиливается: всё больше кастомизаций будет видно наружу, при этом останется механизм переопределения логики под собственные задачи.
Роботы выполняют рутину лучше, чем люди. Но их надо этому научить.