Nov 1, 2025 · 7031 words · 34 min read
Примечание о переводе: Эта глава переведена с помощью ИИ на основе оригинала на английском языке. Мы стремимся к точности, однако некоторые нюансы могут отличаться от оригинала. Оригинал доступен на английском языке.

5. Исполнение#

Плейбук работал безупречно месяцами: десять коммутаторов доступа в лаборатории, два минуты от начала до конца, чистые результаты каждый раз. Когда команда решила развернуть его на полный инвентарь из 800 коммутаторов, никто не ожидал проблем. Первые 600 устройств обновились без единого сбоя. Затем задание замедлилось. Потом зависло. RADIUS-сервер, внезапно получивший 150 одновременных запросов SSH-аутентификации, начал отклонять подключения. Ansible завис на 150 устройствах прямо посреди выполнения. Инженер остановил задание.

Дальше началась более сложная проблема: никто не знал, какие из 600 устройств были обновлены, а какие 150 нет. Состояние выполнения нигде не записывалось. Плейбук запустили повторно в надежде, что идемпотентность спасёт ситуацию. В тот раз спасла. Но инцидент обнажил нечто важное: уровень исполнения был спроектирован для лаборатории, а не для реальной сети. Скорость, параллелизм, границы ошибок, отслеживание состояния - ничего из этого не было учтено. Автоматизация работала; архитектура исполнения - нет.

Большинство людей воспринимают сетевую автоматизацию так: “взять те же данные и, не подключаясь к CLI как человек, сделать что-то в сети.” Я готов поспорить, что именно так большинство начинающих приступают к сетевой автоматизации. И да, именно это делает блок Исполнения. Но, как вы видели на протяжении всей этой книги, сетевая автоматизация шире этого первого шага, а Исполнение - лишь один компонент внутри архитектуры.

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

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

5.1. Основы#

5.1.1. Контекст#

Исполнение определяет как выполнять действия. Что делать задаёт блок Намерения, а когда - Оркестрация.

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

5.1.2. Цели#

Что система исполнения должна реально делать? Важны пять вещей:

  1. Доставить нужные данные на место. Прежде чем что-либо выполнять, нужно знать что выполнять (намерение), где выполнять (на каких устройствах) и как получить к ним доступ (учётные данные, параметры подключения). Это означает получение инвентаря и целевого состояния из Источника истины (подробнее в Главе 4), а иногда и запрос данных наблюдаемости для понимания текущих условий. Без этой интеграции движок исполнения просто вслепую выполняет команды. Ради собственного душевного здоровья избегайте этого.

  2. Запускаться тогда, когда нужно: сейчас или позже. Иногда требуется немедленное выполнение: инженер нажимает «развернуть» и ожидает, что это произойдёт прямо сейчас. В других случаях выполнение должно ждать: развернуть в окно обслуживания, подождать, пока устройство выйдет в сеть, или среагировать на событие из наблюдаемости. Система должна поддерживать синхронные и асинхронные триггеры.

  3. Отслеживать состояние во времени. Общесетевые операции не мгновенны. Развёртывание на сотни устройств может занять часы. Какие устройства успешно обновились? Какие завершились с ошибкой? Какие ещё ожидают? Управление состоянием также обеспечивает идемпотентность (выполнить одну и ту же задачу дважды - получить тот же результат), откат (отменить только что сделанное) и возобновление (продолжить с места остановки после сбоя). Без отслеживания состояния каждое выполнение - это ставка в одну попытку.

  4. Надёжно выполнять при масштабировании. Скрипт, работающий на 5 устройствах, нередко ломается (или деградирует) на 500. Нужны параллельное выполнение, обработка ошибок, повторные попытки, ограничение частоты запросов и возможность пробного запуска. Система должна корректно обрабатывать частичные сбои, предоставлять чёткую обратную связь о том, что пошло не так, и никогда не оставлять сеть в неопределённом состоянии. Разные операции требуют разных стратегий: одни быстрые и параллельные, другие медленные и последовательные.

  5. Работать с любым сетевым устройством или платформой. В вашей сети, вероятно, есть Cisco, Arista, Juniper, облачные Application Programming Interface (API), Linux-серверы, межсетевые экраны, балансировщики нагрузки. Каждый из них говорит на разных протоколах и имеет разные операционные паттерны. Уровень исполнения нуждается в адаптерах для всех них с единым интерфейсом, чтобы остальная часть автоматизации не беспокоилась о различиях.

Имея эти цели в виду, какие архитектурные возможности действительно нужны?

5.1.3. Столпы#

Каждая цель транслируется в конкретные возможности:

  1. Уровень интеграции данных. Движок исполнения не существует изолированно. Ему нужен программный доступ к Источнику истины (инвентарь, учётные данные, намерение), к системам наблюдаемости (текущее состояние, метрики работоспособности) и потенциально к другим системам (системы заявок, управление изменениями, рабочие процессы согласования). Это означает реализацию Application Programming Interface (API)-клиентов, безопасную обработку аутентификации, подходящее кэширование данных и проверку наличия всего необходимого перед началом выполнения.

  2. Гибкие механизмы запуска. Важны несколько способов запуска выполнения. Синхронные триггеры включают Representational State Transfer (REST) Application Programming Interface (API)-вызовы (прямой вызов), webhooks (интеграция с внешними системами) и удалённые вызовы процедур. Асинхронные триггеры включают обработчики событий (реакция на оповещения наблюдаемости, изменения состояния устройств или внешние события), планировщики (периодическое выполнение по расписанию или разовые задачи по расписанию) и потребители очередей сообщений. Базовое связывание в цепочку тоже помогает: одно выполнение завершается и запускает следующее.

  3. Инфраструктура управления состоянием. Это выходит за рамки вопроса «есть ли у устройства эта конфигурация». Нужно отслеживать состояние выполнения (какие задачи выполняются, ожидают, завершены, завершились с ошибкой), желаемое состояние (что должно быть настроено) и фактическое состояние (что настроено). Для этого требуется постоянное хранилище, поддержка транзакций для атомарных операций, механизмы блокировки для предотвращения параллельных конфликтов и чёткая модель состояния. Инфраструктура должна справляться с распределёнными сценариями, где состояние разделяется между несколькими исполнительными воркерами.

  4. Надёжный движок исполнения. Это сердце системы. Он поддерживает как императивные рабочие процессы (выполнить команду 1, затем команду 2, затем команду 3), так и декларативные подходы (привести устройство к этому состоянию, разобраться со шагами самостоятельно). Движок управляет параллелизмом (выполнение на нескольких устройствах одновременно или пакетами), реализует логику повторных попыток с экспоненциальной задержкой, предоставляет возможности пробного запуска (предсказать, что произойдёт, не выполняя), фиксирует подробные журналы выполнения и корректно обрабатывает частичные сбои. Обработка ошибок критична: когда что-то идёт не так, следует ли прерывать всё, продолжать с другими устройствами или повторить попытку?

  5. Уровень абстракции протоколов. Сетевые устройства - разнородный беспорядок. Одни используют Secure Shell (SSH) и ожидают команды Command Line Interface (CLI). Другие используют NETCONF или RESTCONF. Современные устройства поддерживают gRPC Network Management Interface (gNMI) для потоковой телеметрии и конфигурации. Облачные платформы предоставляют Representational State Transfer (REST) API. Ваша система исполнения нуждается в адаптерах для всех них, представляя унифицированный интерфейс для вышестоящей логики. Этот уровень управляет пулингом соединений, управлением сессиями, аутентификацией, форматированием команд и разбором ответов. Хорошая абстракция означает, что вы можете добавить поддержку нового типа устройств, не переписывая всю автоматизацию.

5.1.4. Область применения#

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

В области применения:

  • Подключение к сетевым устройствам по любому поддерживаемому протоколу
  • Выполнение изменений конфигурации, операционных команд, передача файлов, перезагрузки
  • Управление состоянием выполнения и отслеживание прогресса
  • Обработка ошибок, повторных попыток и откатов
  • Предоставление обратной связи Оркестрации

За пределами области применения:

  • Принятие решений о том, что настраивать (это задача Намерения)
  • Оркестрация сложных многошаговых рабочих процессов и решение о том, когда запускать (это задача Оркестрации)
  • Долгосрочное хранение и анализ результатов выполнения (это задача Наблюдаемости)

Думайте об Исполнении как о двигателе автомобиля: он обеспечивает мощность и движение, но не решает, куда ехать или когда поворачивать. Эти решения принимает водитель (Оркестрация), следующий по карте (Намерение).

5.2. Функциональные возможности#

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

  1. Интеграция данных: получение инвентаря, учётных данных, намерения и данных наблюдаемости из вышестоящих систем
  2. Инициирование: запуск выполнения через синхронные или асинхронные механизмы
  3. Управление состоянием: отслеживание прогресса выполнения, обеспечение идемпотентности и отката
  4. Движок: основная логика, выполняющая задачи с надлежащим параллелизмом и обработкой ошибок
  5. Сетевой адаптер: протоколо-специфичные интерфейсы для взаимодействия с разнородными сетевыми устройствами

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

graph LR
    subgraph Goals
        G1[Get the right data to the right place]
        G2[Start when needed]
        G3[Track state over time]
        G4[Execute reliably at scale]
        G5[Work with any network device or platform]
    end

    subgraph Pillars
        P1[Data integration layer]
        P2[Flexible triggering mechanisms]
        P3[State management infrastructure]
        P4[Robust execution engine]
        P5[Protocol abstraction layer]
    end

    subgraph Functionalities
        F1[Data Integration]
        F2[Triggering]
        F3[State Management]
        F4[Engine]
        F5[Network Adapter]
    end

    G1 --> P1 --> F1
    G2 --> P2 --> F2
    G3 --> P3 --> F3
    G4 --> P4 --> F4
    G5 --> P5 --> F5

Внутренняя архитектура выглядит следующим образом:

graph TD
    A[Data Integration] --> C[Engine]
    B[Triggering] --> C[Engine]
    C --> E[State Management]
    E --> C
    C --> D[Network Adapter]
    classDef component fill:#e1f5ff,stroke:#4a90e2,stroke-width:2px;
    class A,B,C,D,E component;

5.2.1. Интеграция данных#

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

5.2.1.1. Инвентарь#

Данные инвентаря определяют целевые устройства. Мы рассмотрели это в Главе 4, но минимально необходимо следующее:

  • Цель: IP-адрес или FQDN для доступа к устройству
  • Платформа/ОС: тип устройства, производитель, версия ОС (определяет, какой протокол и команды использовать)
  • Учётные данные: имя пользователя/пароль, ключи Secure Shell (SSH), токены Application Programming Interface (API), пути к сертификатам
  • Параметры подключения: порт Secure Shell (SSH), значения тайм-аутов, конечные точки Application Programming Interface (API)
  • Метаданные: местоположение площадки, роль (spine/leaf/edge), среда (prod/staging)

Данные инвентаря должны поступать из Источника истины. Использование файлов хорошо работает только в очень небольших средах. Движок исполнения запрашивает Application Programming Interface (API) Источника истины во время выполнения (или получает данные через Оркестрацию при инициировании). Некоторые системы кэшируют инвентарь для производительности с настраиваемыми интервалами обновления или используют событийно-управляемый инвентарь, совмещающий триггер со связанной информацией.

Никогда не храните учётные данные в файлах инвентаря или журналах. Используйте систему управления секретами (HashiCorp Vault, AWS Secrets Manager, CyberArk) и вводите учётные данные во время выполнения. Источник истины должен предоставлять указатели на эти чувствительные данные и получать их во время выполнения. Ваш движок исполнения должен поддерживать несколько источников учётных данных и переопределения учётных данных для конкретных устройств.

5.2.1.2. Целевые данные#

Целевые данные - это то, что вы хотите настроить или изменить. Они поступают из блока Намерения/Источника истины и могут включать:

  • Артефакты конфигурации: готовые к развёртыванию артефакты, которые могут быть структурированными данными для использования непосредственно с API или набором CLI-команд, формирующих конфигурацию.
  • Команды: конкретные команды Command Line Interface (CLI) или вызовы Application Programming Interface (API) для выполнения операций на устройстве.
  • Файлы: образы программного обеспечения для обновлений, файлы конфигурации для импорта.

Должен ли движок исполнения сам получать данные намерения, или Оркестрация должна передавать их? Оба подхода работают. Самостоятельное получение намерения связывает Исполнение с вашим Источником истины, но гарантирует свежесть данных. Получение намерения в виде параметров делает Исполнение более универсальным, но требует от Оркестрации управления получением данных.

Моя рекомендация - потреблять артефакты конфигурации напрямую. Блок Намерения должен отвечать за генерацию артефакта конфигурации с собственной логикой, рендеринг шаблонов конфигурации со структурированными данными, представляющими состояние (конфигурации VLAN, политики маршрутизации, ACL).

Артефакты конфигурации могут устаревать в промежутке между моментом их генерации и моментом потребления Исполнителем. Если данные SoT изменятся после кэширования предварительно сгенерированного артефакта, Исполнитель может применить устаревшую конфигурацию. Окно устаревания - это время между последним коммитом SoT и триггером выполнения. Для автоматизации, получающей артефакты в момент триггера (а не из кэша), это окно минимально. Для конвейеров, заранее рендерящих артефакты по расписанию и сохраняющих их для последующего использования, окно может вырасти до нескольких часов. Там, где важна свежесть данных, Оркестратор или триггер Исполнения всегда должен получать свежий артефакт из SoT, а не из кэшированного файла.

5.2.1.3. Наблюдаемые данные#

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

Вот несколько вариантов использования, где данные наблюдаемости используются вблизи потока выполнения:

  • Предварительная валидация: доступно ли устройство? Достаточно ли места на диске для обновления? Есть ли активные сессии, которые будут прерваны?
  • Корректная деградация: перед перезагрузкой коммутатора запросить наблюдаемость, чтобы проверить наличие резервного соединения. Если его нет - отложить или прервать.
  • Условная логика: применять конфигурацию только при выполнении определённых условий (загрузка CPU ниже порога, нет активных тревог, текущее время входит в окно)
  • Освобождение ёмкости: перед выполнением операции освобождения убедиться, что доступная ёмкость достаточна для поглощения изменения без нарушения SLO.

Это требует интеграции с вашими системами наблюдаемости. Движок исполнения может запрашивать Application Programming Interface (API) для получения текущих метрик, проверять состояние устройств или ожидать сигналов готовности. Оркестрация также может выполнять эту интеграцию, а затем контролировать выполнение на основе свежести данных и рисков.

Распространённый паттерн: такие инструменты, как модули «network health checks» Ansible или задачи сбора данных Nornir, запускают запросы наблюдаемости до и после выполнения, сравнивая результаты для обнаружения неожиданных изменений. Подход «снимок до и после» выявляет регрессии, которые в противном случае остались бы незамеченными.

5.2.2. Инициирование#

Выполнение не начинается само по себе. Что-то должно его запустить. Современные системы исполнения поддерживают несколько механизмов триггеров:

Синхронные (немедленные, блокирующие):

  • Вызовы Representational State Transfer (REST) Application Programming Interface (API): внешняя система или пользователь вызывает конечную точку Application Programming Interface (API), выполнение запускается, и ответ включает результаты. Просто и прямолинейно.
  • Webhooks: внешние системы (Term "git" not found, системы заявок, CI/CD) отправляют HTTP-запросы при наступлении событий. Распространённый паттерн: push в Term "git" not found запускает развёртывание конфигурации.
  • Команды Command Line Interface (CLI): инженеры выполняют команды, напрямую вызывающие исполнение (ansible-playbook, terraform apply, пользовательские скрипты). Эти CLI-команды являются частью облегчённого уровня представления, предлагаемого этими инструментами (подробнее в Главе 8).

Асинхронные (отложенные или событийно-управляемые):

  • Обработчики событий: реакция на события из наблюдаемости (устройство недоступно, превышен порог), очереди сообщений или внешние системы. Ansible Event-Driven Automation (EDA) реализовал этот паттерн явно: слушать события, сопоставлять правила, автоматически запускать плейбуки.
  • Очереди сообщений: задачи отправляются в очередь (RabbitMQ, Kafka, AWS SQS), воркеры забирают и выполняют их. Обеспечивает буферизацию, приоритетную очередь и ограничение частоты.

Правильный подход зависит от варианта использования. Немедленные изменения (устранение производственной проблемы) требуют синхронных триггеров. Реактивная автоматизация (реакция на оповещения) использует обработчики событий. Соображения масштаба тоже важны, как мы рассмотрим в Главе 11.

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

Одно важное соображение: что считается выполнением? Для меня выполнение - это больше, чем простая задача. Оно может включать несколько связанных в цепочку задач, не требующих сложного рабочего процесса (это задача Оркестрации). Граница размывается. Например, следующая последовательность всё ещё может быть одним потоком выполнения: обновить прошивку, подождать перезагрузки устройства, проверить работу, обновить инвентарь. Но если требуется валидация человеком или условное ветвление на основе более широкой проверки, это относится к блоку Оркестрации.

5.2.3. Управление состоянием#

Управление состоянием - это отслеживание где вы находитесь в процессе выполнения и как выглядит мир, чтобы принимать обоснованные решения. Существует две основные категории:

  • Состояние выполнения (отслеживание самой автоматизации):

    • Какие устройства были обработаны?
    • Какие задачи завершились успешно, с ошибкой или ожидают?
    • Если задача завершилась с временной ошибкой, можно ли повторить попытку?
    • Если выполнение было прервано, можно ли возобновить с места остановки?
  • Фактическое состояние инфраструктуры (отслеживание целевого состояния конфигурации):

    • Что сейчас настроено?

Существуют два подхода:

  • Без сохранения состояния/без агентов (например, Ansible). Каждое выполнение запускается независимо. Никакого постоянного состояния между запусками. Каждое выполнение начинается с нуля: собрать текущее состояние, вычислить diff, применить изменения. Проще в эксплуатации (нет базы данных состояния), но менее эффективно (каждый раз нужно заново обнаруживать всё) и нет встроенного отката.

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

Транзакционное выполнение - следующий шаг, когда нужны более сильные гарантии безопасности, чем «по возможности». Транзакция объединяет несколько изменений устройств в единое целое: либо всё применяется и валидируется, либо система откатывается к предыдущему состоянию. Для этого требуется три вещи:

  • Чёткая граница (какие операции входят в транзакцию)
  • Постоянная запись состояния до изменения (для отката)
  • Механизм блокировки или аренды для предотвращения нарушения атомарности параллельными изменениями

На практике большинство сетевых автоматизаций использует «транзакционно-подобное» поведение, а не строгую ACID-семантику, поскольку не все устройства поддерживают нативный коммит/откат. Тем не менее можно аппроксимировать транзакции, делая снимки, используя кандидатные конфигурации (там, где они поддерживаются), применяя исключительные блокировки и делая пути отката первоклассной частью потока выполнения.

Проблема? Синхронизация и совместное использование состояния. Если несколько людей или систем изменяют сетевые устройства, состояние устаревает. Terraform решает это с помощью удалённого хранилища состояния и блокировки. Ansible избегает проблемы, работая без состояния, но жертвует эффективностью. Промежуточный вариант: временно кэшировать текущее состояние, проверять перед каждой операцией и выстраивать паттерны «конечной согласованности», где кратковременные расхождения допустимы.

5.2.3.1. Идемпотентность#

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

Как инструменты достигают идемпотентности:

  • Встроенные модули (например, Ansible): большинство модулей Ansible идемпотентны по умолчанию. Модуль ios_vlan проверяет наличие VLAN перед его созданием. Если он уже существует с правильной конфигурацией, Ansible сообщает «ok» (нет изменений). Это требует от автора модуля реализации логики проверки.

  • Сравнение состояний (например, Terraform): Terraform сравнивает желаемое состояние с текущим, вычисляет diff и применяет только различия. Если запустить terraform apply дважды без изменений в конфигурации, второй запуск ничего не делает.

  • Декларативные API (например, NETCONF/YANG): некоторые протоколы обрабатывают идемпотентность нативно. Операция <edit-config> NETCONF с операцией merge по своей природе идемпотентна: она объединяет вашу конфигурацию с существующей, создавая или обновляя по необходимости.

  • Ручная проверка (например, чистые скрипты): если вы пишете скрипты на Python или Go, идемпотентность реализуется вручную: запросить текущее состояние, сравнить с желаемым, вносить изменения только при наличии diff.

Идемпотентность сложнее, чем кажется. Что если VLAN существует, но с неправильными настройками? Следует ли обновить его (возможно, нарушив трафик) или сообщить о конфликте? Что насчёт временных сбоев (устройство временно недоступно)? Логика повторных попыток должна различать «операция уже выполнена» (идемпотентный успех) и «операция завершилась с ошибкой» (реальная ошибка).

Идеальная идемпотентность добавляет накладные расходы. Каждая операция сначала требует запроса текущего состояния. При масштабных развёртываниях это замедляет работу. Некоторые команды мирятся с «в основном идемпотентным» (работает 99% времени) вместо «идеально идемпотентного» (работает всегда, но медленно).

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

5.2.4. Движок#

Движок исполнения - это основная логика, принимающая задачу («настроить этот VLAN на этих 50 коммутаторах») и выполняющая её безопасно и эффективно. Это не Оркестрация. Блок Оркестрации координирует несколько задач исполнения во времени и с учётом зависимостей. Движок просто хорошо выполняет одну задачу (или простую цепочку задач).

Что он делает:

  • Принимает определение задачи (что делать, на каких устройствах)
  • Разбивает её на атомарные операции (действия на каждом устройстве)
  • Выполняет операции с надлежащим параллелизмом (последовательным, параллельным, пакетным)
  • Обрабатывает ошибки, повторные попытки и откаты
  • Сообщает о прогрессе и результатах

Движок исполнения может параллельно настраивать 100 маршрутизаторов, но он не решает когда их настраивать, какие конфигурации применять на основе бизнес-логики или что делать дальше на основе результатов. Это задачи Оркестрации. Исполнение - рабочая лошадка; Оркестрация - прораб.

Тем не менее движки исполнения нередко поддерживают простое связывание в цепочку: «выполнить задачу A, затем задачу B на тех же устройствах». Это базовое последовательное выполнение, а не полноценная Оркестрация. Когда нужны сложные рабочие процессы (ожидание внешнего утверждения, ветвление на основе результатов, координация между несколькими системами), нужен настоящий уровень Оркестрации (подробнее в Главе 7).

5.2.4.1. Языки#

Как определяется логика выполнения? Разные стили языков имеют разные компромиссы:

СтильПримерыСильные стороныКомпромиссы
Предметно-специфические языки (DSL)Ansible (YAML), Terraform (HCL)Низкий порог входа, самодокументирующее намерение, встроенная семантика выполненияОграниченная гибкость, более сложная отладка в сложных сценариях, условная логика может стать неудобной
Языки программирования общего назначенияNornir (Python), пользовательские скрипты Python/GoПолная гибкость, мощные инструменты отладки, удобное повторное использование библиотекБолее высокие требования к навыкам, больше кода для поддержки, меньше стандартизации между командами

Многие команды используют DSL (Ansible) для общих паттернов и переходят к пользовательскому коду (модули Python, плагины) для сложных граничных случаев. Это балансирует доступность с мощью. Человеческий фактор обычно решает, какой подход побеждает, подробнее об этом в Главе 13.

5.2.4.2. Императивный подход против декларативного#

Императивный: вы указываете как что-то делать, шаг за шагом.

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

Если коротко: предпочитайте декларативный подход там, где он подходит.

Хороший способ увидеть разницу - посмотреть на Ansible, который поддерживает оба:

  • Императивный Ansible:

    - name: Create VLAN 100
      cisco.ios.ios_command:
        commands:
          - vlan 100
          - name Engineering

    Вы буквально указываете Ansible, какие команды выполнять. Если VLAN существует, это всё равно выполнится (хотя модуль может быть идемпотентным).

  • Декларативный Ansible:

    - name: Ensure VLAN 100 exists
      cisco.ios.ios_vlans:
        config:
          - vlan_id: 100
            name: Engineering
        state: merged

    Вы описываете желаемое состояние. Ansible разбирается, какие команды выполнять. Если VLAN 100 уже существует с правильным именем, Ansible ничего не делает.

ПодходСильные стороныКомпромиссы
ДекларативныйИдемпотентен по своей природе; намерение легче читать; меньше ошибок оператора, поскольку инструмент обрабатывает граничные случаиСильно зависит от качества модуля/провайдера; меньший контроль над путём выполнения; отладка может быть сложнее при абстракции внутренностей
ИмперативныйПолный контроль над каждым шагом; поток выполнения явный; работает почти в любой среде с доступом к CLIБольше кода и усилий на тестирование; идемпотентность на вас; долгосрочная поддержка быстро растёт по мере накопления граничных случаев

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

5.2.4.3. Последовательное и параллельное выполнение#

Существуют два варианта выполнения задач:

  • Последовательное выполнение: обработка устройств по одному. Настроить устройство 1, дождаться завершения, настроить устройство 2 и т.д. Безопасно (сбои видны сразу, можно остановиться), но медленно (100 устройств = 100× времени на одно устройство).

  • Параллельное выполнение: обработка нескольких устройств одновременно. Настроить устройства 1-10 сразу, затем 11-20 и т.д.

flowchart TD
    subgraph Serial
        S1[Device 1] --> S2[Device 2] --> S3[Device 3]
    end
    subgraph Parallel
        P0[Start] --> P1[Device 1]
        P0 --> P2[Device 2]
        P0 --> P3[Device 3]
    end

Почему существует Nornir: Ansible изначально был последовательным. Для сетевой автоматизации в масштабе это было мучительно медленно. Ansible добавил strategy: free и позже forks для параллелизма, но всё ещё принципиально спроектирован для последовательного выполнения. Nornir был построен с нуля с учётом параллелизма: по умолчанию он использует потоки для параллельного выполнения на нескольких устройствах. Это делает его в 10-100 раз быстрее при большом количестве устройств.

Соображения для параллельного выполнения:

  • Влияние на плоскость управления: одновременное обращение к 1000 устройствам может перегрузить сети управления, источники данных, системы аутентификации или плоскости управления устройств.
  • Обработка зависимостей: если устройства зависят друг от друга (spine перед leaf), нужен порядок. Чистый параллелизм не работает.
  • Радиус поражения ошибки: если в вашей автоматизации есть ошибка, параллельное выполнение развернёт её на множество устройств, прежде чем вы это заметите. Последовательное выполнение завершается с ошибкой на устройстве 1, и вы останавливаетесь, прежде чем повредить устройства 2-100.

Моя рекомендация: использовать параллельное выполнение с пакетированием. Обрабатывайте устройства группами по 10-50 (я привык называть их «волнами»), проверяйте успешность каждого пакета перед продолжением. Это балансирует скорость с безопасностью. Подробнее о стратегиях развёртывания в Главе 10.

5.2.4.4. Пробный запуск (режим планирования)#

Пробный запуск - это фаза «планирования» движка исполнения: моделирование изменений без касания устройств с последующим представлением конкретного diff и рисков. Хороший пробный запуск получает текущее состояние, вычисляет точные операции на уровне устройств, которые будут выполнены, и проверяет предварительные условия (доступность, схема, порядок зависимостей). Он должен быть быстрым, детерминированным и воспроизводимым, чтобы рецензенты могли ему доверять.

Пример (план Terraform для AWS VPC с проверяемыми изменениями):

Terraform will perform the following actions:

  # aws_vpc.main will be created
  + resource "aws_vpc" "main" {
      + cidr_block           = "10.10.0.0/16"
      + enable_dns_support   = true
      + enable_dns_hostnames = true
      + tags = {
          + "Name" = "core-vpc"
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

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

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

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

5.2.4.5. Устойчивость#

Сетевое выполнение по своей природе ненадёжно (как и большинство распределённых систем): устройства перезагружаются, подключение прерывается, плоскости управления перегружаются. Другие зависимости, такие как блок Намерения, также могут иметь временные проблемы. Устойчивое выполнение обрабатывает их корректно:

Логика повторных попыток:

  • Временные сбои (тайм-аут соединения, временный пик CPU) должны вызывать повторные попытки с экспоненциальной задержкой
  • Различайте повторяемые ошибки (тайм-аут) от постоянных (ошибка аутентификации, синтаксическая ошибка)
  • Ограничивайте количество попыток, чтобы избежать бесконечных циклов

Стратегии тайм-аутов:

  • Тайм-аут соединения: как долго ждать ответа устройства перед отказом
  • Тайм-аут задачи: как долго может выполняться полная операция (предотвращает зависание на зависших устройствах)
  • Глобальный тайм-аут: максимальное время выполнения всего задания

Обработка ошибок:

  • Быстрый отказ: одно устройство даёт сбой - прерывать всё (безопасно, но неэффективно)
  • Продолжение: логировать сбой, продолжать с другими устройствами (эффективно, но может распространить ущерб)
  • На основе порога: если >10% устройств дают сбой - остановиться (сбалансированно)

Возможности отката:

  • Делать снимки конфигурации перед изменениями
  • При сбое выполнения автоматически восстанавливать снимки
  • Поддерживать режим пробного запуска: показывать, что изменится, без реального изменения

Прерыватели схемы:

  • Если устройство постоянно даёт сбой - отмечать его как нездоровое и временно пропускать
  • Предотвращает трату времени на повторные попытки подключения к недоступным устройствам

Контрольные точки:

  • Периодически сохранять прогресс
  • При сбое выполнения возобновлять с последней контрольной точки, а не начинать заново

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

Один полезный паттерн: рассматривать «режимы безопасности» как первоклассные состояния выполнения. Рендерить целевую конфигурацию, разбирать текущую конфигурацию, собирать факты, а затем применять (merged/replaced/overridden) только после прохождения ворот валидации. Это даёт детерминированные контрольные точки перед касанием продакшена.

5.2.5. Сетевой адаптер#

Сетевые устройства говорят на разных протоколах. Уровень сетевого адаптера абстрагирует эти различия, чтобы вышестоящим уровням было всё равно, взаимодействуют ли они с коммутатором Cisco через Secure Shell (SSH) или с коммутатором Arista через Representational State Transfer (REST) Application Programming Interface (API).

5.2.5.1. Интерфейсы#

Разные устройства поддерживают разные интерфейсы управления:

ИнтерфейсОписаниеПримеры библиотек/инструментовТипичное использование
Secure Shell (SSH) / Command Line Interface (CLI)Наиболее универсальный, наименее структурированный (текст на входе/выходе)Netmiko (Python), Paramiko (Python), scrapli (Python), scrapligo (Go)Унаследованные платформы, вендор-специфичные операционные команды
NETCONF / RESTCONFСтруктурированное управление на основе моделей (YANG-based)ncclient (Python), scrapli-netconf (Python), nemith/netconf (Go)Декларативная конфигурация и стандартизированные модели данных
gRPC Network Management Interface (gNMI) / gNOIgRPC-based интерфейсы для конфигурации/состояния и операционных RPCpygnmi (Python), gnmic (Go CLI), openconfig/gnmi (Go)Потоковая телеметрия и современные операционные рабочие процессы
Representational State Transfer (REST) APIsHTTP/JSON или XML API, часто платформо-специфичныеrequests/httpx (Python), net/http + OpenAPI-generated clients (Go)API контроллеров и облачных/сетевых платформ
JSON-RPC / вендорный gRPCСтруктурированные RPC-паттерны, используемые конкретными сетевыми ОСArista eAPI (JSON-RPC), вендорные gRPC SDKБыстрое удалённое выполнение процедур со структурированными полезными нагрузками

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

  • NAPALM (Network Automation and Programmability Abstraction Layer with Multivendor support): Python-библиотека, предоставляющая унифицированный API для разных вендоров.
    • Поддерживает Cisco, Arista, Juniper и других
    • Под капотом использует подходящий протокол (Secure Shell (SSH), Application Programming Interface (API), NETCONF)
    • Вариант использования: многовендорные среды, где нужен согласованный код автоматизации
  • Pybatfish: не библиотека транспорта устройств, но мощный компаньон для безопасности выполнения. Практический паттерн: «план/применение с NAPALM или Ansible, валидация сетевого поведения с pybatfish до и после изменений».

Зачем использовать уровень абстракции? Вместо написания разного кода для Cisco и Juniper NAPALM даёт один API. Вызовите get_facts() и NAPALM разберётся, как получить факты устройства, будь то устройство Cisco IOS (через Secure Shell (SSH)), Arista EOS (через eAPI) или Juniper Junos (через NETCONF). Компромисс: абстракция скрывает вендор-специфичные возможности. Для общих операций (получить конфигурацию, применить конфигурацию, получить факты) это отлично. Для более продвинутых операций или экзотических вендорных возможностей приходится возвращаться к нативным интерфейсам, поскольку они, скорее всего, не реализованы.

5.2.5.2. Операции#

Сетевое выполнение - это не только управление конфигурацией:

Тип операцииЧто охватываетПримечания
Изменения конфигурацииПрименение полных конфигураций/фрагментов/декларативного состояния; режимы merge/replace/deleteНаиболее распространённый тип операции, лучше всего поддерживаемый инструментами
Zero-Touch Provisioning (Zero Touch Provisioning (ZTP))Автоматизированное подключение при первой загрузкеТребует DHCP + bootstrap-серверов (TFTP/HTTP/HTTPS) и поддержки устройством
Передача файловЗагрузка образов, скачивание журналов/резервных копийОбщие протоколы: SCP, SFTP, TFTP, HTTP
Операции с устройствамиПерезагрузка/reload, операционные команды (ping/traceroute), резервное копированиеОбычны для day-2 операций и исправления
ОткатВозврат конфигурации к предыдущему известному исправному состояниюНативный откат на некоторых платформах; восстановление из резервной копии на других

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

Где принадлежат валидация и рендеринг: Источник истины владеет намерением и (опционально) предварительно отрендеренными конфигурациями. Исполнение владеет проверками безопасности на уровне устройств и операционной валидацией (доступность, предварительные/последующие проверки). Оркестрация решает когда валидировать и что делать с результатами (утверждать, приостанавливать, откатывать или эскалировать). Для простого правила: валидация, меняющая рабочий процесс, принадлежит Оркестрации; валидация, меняющая действия на устройстве, принадлежит Исполнению.

5.2.6. Решения#

Для сетевого исполнения существует множество инструментов, и их сравнение помогает понять компромиссы:

ИнструментМодель исполнения и сильные стороныЛучше всего подходитОсновные ограничения
AnsibleDSL (YAML), без агентов, большая экосистема модулей, силён в смешанной автоматизации серверов и сетиКоманды, которым нужны быстрые результаты и широкая поддержка платформНа большом масштабе требует настройки; сложная логика может стать трудно поддерживаемой в YAML
TerraformДекларативный IaC (HCL), мощный движок diff/plan, рабочие процессы с состоянием, отличная интеграция с облакомКоманды, уже стандартизирующие на Terraform для инфраструктурыЗрелость сетевых провайдеров варьируется; управление состоянием добавляет операционные накладные расходы; слабее для day-2 операций
SaltPython-based с агентом или без, событийно-управляемая архитектура, хорошие характеристики масштабированияСуществующие Salt-инсталляции или событийно-насыщенные операцииМеньшее сообщество по сетевой автоматизации и более крутое начальное обучение
NornirPython-first фреймворк, поточный параллелизм, высокая гибкость, простая отладка и быстротаКоманды с навыками Python, с пользовательскими или требовательными к производительности нуждамиМеньше готовых компонентов; требуется больше собственной инженерии
Пользовательский Python/GoМаксимальный контроль и свобода проектирования для специфичной доменной логикиГраничные случаи, внутренние платформы и высокоспециализированные рабочие процессыВы владеете всем: стандарты, паттерны надёжности, тестирование и поддержка жизненного цикла
Вендорные контроллерыКонтроллеры намерений и политик с вендор-нативными рабочими процессами (примеры: Cisco Catalyst Center, Aruba Central, Juniper Mist)Команды, стандартизирующие вокруг экосистемы одного вендора с мощными API контроллеровМенее переносимые паттерны в многовендорных средах

Многие команды используют несколько инструментов в зависимости от варианта использования и сетевой инфраструктуры.

5.2.7. Автоматическое развёртывание с нулевым вмешательством#

Zero Touch Provisioning (ZTP) - это особый паттерн исполнения, при котором устройство загружается впервые и автоматически получает и применяет полную конфигурацию без какого-либо вмешательства человека на устройстве. Поток данных структурно отличается от day-2 операций и заслуживает рассмотрения как именованный архитектурный паттерн.

Поток ZTP

flowchart LR
    A[Device boots\nno config] --> B[DHCP assigns\nmanagement IP\nand bootstrap URL]
    B --> C[Device fetches\nbootstrap config\nfrom server]
    C --> D[Device authenticates\nto SoT and management\nnetwork]
    D --> E[Orchestrator detects\nnew device, triggers\nfull provisioning job]
    E --> F[Executor applies\nfull intent from SoT]

Ключевое понимание состоит в том, что шаг начальной загрузки намеренно минимален: ровно столько конфигурации, чтобы устройство вышло в сеть управления и смогло аутентифицироваться. Полное развёртывание запускается после этого как стандартное задание Исполнителя, получающее полное намерение из Источника истины. Это означает, что ZTP повторно использует тот же конвейер исполнения, что и day-2 операции, а не требует отдельной системы развёртывания.

Распространённые паттерны ZTP

ПаттернКак работаетКомпромисс
DHCP + статический файлDHCP указывает на статический файл конфигурации для каждого устройства (сопоставление по MAC или серийному номеру)Просто реализовать; не получает данные из SoT; не масштабируется
DHCP + динамическая генерацияBootstrap-сервер запрашивает SoT и генерирует начальную конфигурацию для конкретного устройства в момент запросаУправляется SoT с первого дня; требует, чтобы устройство было зарегистрировано в SoT до загрузки
ОС + конфигурацияУстройство скачивает образ ОС и конфигурацию во время загрузки (необходимо для устройств, требующих развёртывания ОС)Обрабатывает bare-metal или сброшенные до заводских настроек устройства; усложняет bootstrap-сервер

ZTP вводит зависимость последовательности: устройство должно быть зарегистрировано в Источнике истины до физической загрузки, иначе у bootstrap-сервера нет данных для генерации конфигурации. В сценарии с кампусом это обрабатывается синхронизацией ServiceNow с Nautobot: когда коммутатор добавляется как актив в ServiceNow (до его прибытия на площадку), Nautobot автоматически создаёт запись устройства с местоположением, ролью и вендором. Когда коммутатор загружается, SoT уже готов.

5.2.8. Гибридное и облачное исполнение#

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

Проблема расхождения

ИзмерениеСетевые устройстваОблачные платформы
Стиль APISSH, NETCONF, gNMI (состояниеёмкие, долгоживущие соединения)REST/HTTP (без состояния, короткоживущие запросы)
Модель аутентификацииОбщие учётные данные (имя/пароль, SSH-ключи)Временные токены, сервисные аккаунты, роли экземпляров (AWS SigV4, Azure AD, GCP service accounts)
Завершение операцииОбычно синхронное (команда либо успешна, либо завершается с ошибкой до окончания сессии)Часто асинхронное (API возвращает «accepted», требуется опрос для получения итогового статуса)
Неопределённость сбояРазорванная NETCONF-сессия - явный сбойТайм-аут облачного API может или не может означать применение изменения; нужно запросить для выяснения
Подход к идемпотентностиИмперативный по умолчанию; декларативность требует тщательного проектирования модулейНативно декларативный на большинстве платформ (Terraform, CloudFormation, Pulumi)

Рекомендуемая архитектура

Держите Намерение (SoT) и Оркестрацию едиными. Тот же бизнес-запрос, создающий VLAN на кампусных коммутаторах, может также потребовать создания группы безопасности в AWS. Источник истины хранит и то, и другое. Оркестратор координирует и то, и другое. Только на уровне Сетевого адаптера (5.2.5) путь выполнения расходится: один адаптер обрабатывает NETCONF/Ansible для кампусных коммутаторов; другой обрабатывает Terraform или API облачного провайдера для облачной среды.

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

Стратегия менеджера секретов должна учитывать как долгоживущие учётные данные сетевых устройств, так и краткоживущие облачные токены. Для облачных платформ учётные данные должны генерироваться при запуске задания и никогда не сохраняться. Большинство облачных провайдеров предоставляют механизмы для этого (профили экземпляров AWS, управляемые идентификаторы Azure, рабочая идентичность GCP). Учётные данные сетевых устройств остаются в Vault и вводятся во время выполнения. Исполнитель никогда не должен хранить учётные данные между запусками заданий независимо от типа цели.

5.2.9. Соображения безопасности#

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

Управление учётными данными

Учётные данные для сетевых устройств никогда не должны появляться в плейбуках, шаблонах, определениях заданий или системах контроля версий. Паттерн прост: хранить секреты в выделенном менеджере секретов (HashiCorp Vault, AWS Secrets Manager, CyberArk) и вводить их во время выполнения в среду Исполнителя. Исполнитель получает учётные данные при запуске задания; он не хранит их постоянно.

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

Минимальные привилегии для каждой операции

Не каждое выполнение требует одинакового уровня доступа. Операции чтения (сбор фактов, проверки пробного запуска, предразвёртывательная валидация) должны использовать учётные данные только для чтения. Операции записи должны использовать учётные данные, ограниченные типом изменения: задание, развёртывающее изменения VLAN, не должно иметь учётных данных, позволяющих изменять конфигурацию BGP. Где сетевая платформа поддерживает управление доступом на основе ролей (роли Arista, уровни привилегий Cisco, управление доступом NETCONF), используйте его для ограничения радиуса поражения любой скомпрометированной сессии.

В кампусном примере это означает, что задание развёртывания VLAN на коммутаторе Arista использует роль, разрешающую настройку VLAN и интерфейсов, но не позволяющую касаться конфигурации протоколов маршрутизации или настроек плоскости управления.

Разделение обязанностей

Кто может запускать выполнение и кто может изменять логику автоматизации должны быть разными ролями с разными контролями доступа. Оператор, утверждающий и запускающий задание развёртывания VLAN, не нуждается и не должен иметь доступа к изменению ролей Ansible или шаблонов плейбука, реализующих это задание. В AWX и AAP это обеспечивается через назначение ролей: «Job Launcher» может запускать предварительно утверждённые шаблоны заданий; «Project Editor» может изменять логику автоматизации.

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

Требования к аудиторскому следу

Каждое событие выполнения должно логироваться с достаточными деталями для восстановления произошедшего: кто запустил задание, какой шаблон задания использовался, какие устройства были в цели, какие параметры были переданы, каков результат для каждого устройства и с каким временем. Журналы должны храниться в течение периода хранения, применимого к организации, обычно 12-24 месяца для управления изменениями в регулируемых отраслях.

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

Сетевая сегментация инфраструктуры автоматизации

Исполнитель и его плоскость управления (AWX, управляющие узлы Ansible) должны находиться в сегменте сети управления. Трафик выполнения к устройствам должен проходить через внеполосную сеть управления там, где это возможно, а не через ту же плоскость данных, которую Исполнитель может модифицировать. Входящий доступ для запуска заданий должен требовать аутентификации и не должен быть доступен из недоверенных сетей.

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

5.3. Пример реализации#

5.3.1. Вариант использования: автоматизированное развёртывание VLAN в гетерогенной сети кампуса#

Продолжаем с сетью кампуса из Главы 4. Определение сервиса VLAN (его ID, подсеть, вендор-специфичные шаблоны конфигурации и целевые группы коммутаторов) теперь хранится в Источнике истины. Эта глава сосредоточена на том, как блок Исполнения подхватывает это намерение и надёжно развёртывает его на все 800 коммутаторов.

Сценарий: бизнес-команда запрашивает новый VLAN для нового приложения. Запрос поступает через систему заявок с деталями: ID VLAN, имя, площадки кампуса и утверждение. Сетевые операции развёртывают этот VLAN на коммутаторах доступа и дистрибуции Arista, HPE и Cisco, проверяют связность и сообщают об успехе.

Требования:

  • Развернуть конфигурацию VLAN параллельно на несколько коммутаторов
  • Проверить предварительные условия (VLAN ещё не существует, коммутаторы доступны)
  • Выполнить пробный запуск для отображения того, что изменится
  • Выполнить фактическое развёртывание с возможностью отката при сбоях
  • Проверить после развёртывания (VLAN активен, нет ошибок)

5.3.2. Архитектура решения#

Прежде чем погружаться в детали выполнения, рассмотрим всю архитектуру:

  1. Источник истины: NetBox (хранит инвентарь, определения VLAN)
  2. Оркестрация/Инициирование: координация шаблона рабочего процесса AWX (запуск через API, webhook, расписание)
  3. Наблюдаемость: предоставляет данные в реальном времени для принятия решений
  4. Исполнение:
    • Движок исполнения: шаблоны/задачи заданий Ansible (предварительная проверка, пробный запуск, развёртывание, верификация, откат)
    • Интеграция данных: плагин инвентаря NetBox в Ansible/AWX
    • Сетевые адаптеры: модули Ansible для Cisco IOS XE, Arista EOS и HPE/Aruba
    • Управление состоянием: статус заданий и рабочего процесса AWX на фазу и на хост с задачей отката на путях сбоя

Это решение представлено в иллюстративных целях; это не универсальная рекомендация.

5.3.3. Поток реализации#

Это выполняется как рабочий процесс AWX с несколькими узлами задач:

  1. Изменение намерения VLAN в NetBox запускает AWX через webhook
  2. Рабочий процесс AWX стартует и выполняет синхронизацию инвентаря (NetBox)
  3. Задание предварительной проверки валидирует доступность, существующее состояние VLAN и ограничения площадки
  4. Задание пробного запуска рендерит вендор-специфичные задачи без применения изменений
  5. Узел утверждения (опционально) контролирует выполнение в продакшене
  6. Задание развёртывания применяет намерение VLAN параллельными пакетами по вендорам
  7. Задание верификации подтверждает операционное и целевое состояние
  8. При сбоях задание отката выполняется только для затронутых устройств
  9. Наблюдаемость собирает данные до/после для финальной валидации
  10. Запускается финальная валидация и публикуется сводка выполнения
flowchart TD
    A[NetBox VLAN Intent Change] --> B[Webhook to AWX]
    B --> C[AWX Workflow Start]
    C --> E[Inventory Sync from NetBox]
    E --> F[Precheck Job Task]
    F --> G[Dry-run Job Task]
    G --> H[Approval Node]
    H --> I[Deploy Job Task]
    I --> J[Verify Job Task]
    J --> N[Gather Network Data]
    N --> L[Final Validation]
    L --> M[Update Ticket and Report]

    F -->|failed| X[Stop and return validation errors]
    I -->|failed hosts| Y[Rollback Job Task for affected devices]
    Y --> J
    F --> N

    %% --- Styles ---
    classDef sot fill:#cfe8ff,stroke:#0b5fb3,stroke-width:2px;
    classDef orch fill:#ffe0cc,stroke:#c24b00,stroke-width:2px;
    classDef exec fill:#d7f4e1,stroke:#0f7a3b,stroke-width:2px;
    classDef obs fill:#ffd6d6,stroke:#b22222,stroke-width:2px;

    class A sot;
    class B,C,E,H,L,M orch;
    class F,G,I,J,Y,X exec;
    class N obs;

5.3.4. Краткое изложение решения#

Данная реализация демонстрирует все ключевые возможности исполнения:

  • Интеграция данных: получение инвентаря и намерения из NetBox через плагин динамического инвентаря
  • Инициирование: рабочий процесс AWX поддерживает API/webhook/расписание, плюс выполнение с контролем утверждения
  • Управление состоянием: статус рабочего процесса/задания AWX отслеживает прогресс и домены сбоев; путь отката явно определён
  • Движок: Ansible обрабатывает параллельное выполнение (настраивается через forks/пакетирование), обработку ошибок и пробный запуск
  • Сетевой адаптер: вендор-нативные модули ресурсов отображают одно намерение на разные платформы (cisco.ios.ios_vlans, arista.eos.eos_vlans и модули коллекции HPE/Aruba)
  • Наблюдаемость: сбор данных до/после поддерживает финальную валидацию и отчётность

Функции устойчивости:

  • Предварительные проверки предотвращают развёртывание на недоступные устройства или создание дублирующихся VLAN
  • Пробный запуск показывает изменения перед применением
  • Контрольные точки безопасности используют состояния модулей ресурсов (rendered, parsed, gathered) перед состояниями применения (merged, replaced, overridden)
  • Идемпотентные модули делают повторный запуск безопасным
  • Пост-верификация выявляет «тихие» сбои
  • Откат: специальный узел отката AWX ограничивает откат областью затронутых устройств

Соображения масштабирования:

  • Параллельное выполнение через forks: 50 обрабатывает 50 коммутаторов одновременно
  • Для более крупных развёртываний (500+ коммутаторов) разбивайте на группы и используйте Ansible Tower/AWX для управления рабочими процессами
  • Добавьте ограничение частоты, если плоскость управления или системы аутентификации не справляются с нагрузкой

5.4. Резюме#

Блок Исполнения - это то место, где сетевая автоматизация становится ощутимой: конфигурации применяются, устройства перезагружаются, изменения происходят. Но надёжное исполнение - это больше, чем отправка команд через Secure Shell (SSH).

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

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

Ссылки и дополнительная литература#

💬 Found something to improve? Send feedback for this chapter