Часть 7. VxLAN. Multihoming
Цель
Настроить отказоустойчивое подключение клиентов с использованием EVPN Multihoming
Ожидаемый результат
Клиент подключен двумя линками к различным Leaf
Со стороны клиента настроен агрегированный канал (LACP)
Настроен multihoming для работы в Overlay сети. В нашем случае - ESI LAG.
Зафиксирован в документации - план работы, адресное пространство, схема сети, конфигурацию устройств.
Протестирована отказоустойчивость. Необходимо убедиться, что связнность не теряется при отключении одного из линков.
Достижение результата
Введение
В качестве технического долга имеем:
zero touch provisioning устройств
раскатывание конфигурации в автоматическом режиме
Вводятся новые понятия, а именно:
ES - ethernet segment. An Ethernet segment is associated with an access-facing interface of a VTEP and represents the connection with a host device. Each Ethernet segment is assigned a unique value known as Ethernet segment identifier (ESI). When a host device is connected to more than one VTEPs, then the ESI for these connections remains the same. Источник
ESI - Ethernet Segment Identifier. An EVPN LAG is identified using an Ethernet segment identifier (ESI). An ESI is a mandatory attribute that is required to enable EVPN LAG server multihoming. Источник
В EVPN-сетях — это ключ, по которому несколько PE-маршрутизаторов/коммутаторов идентифицируют общий Ethernet-сегмент (Multi-Homing). Он позволяет:
Обнаруживать и управлять мультихомингом (Active/Active или Active/Standby).
Избегать петель и дублирования кадров через split-horizon.
Корректно маршрутизировать/рассылать трафик в сценариях отказоустойчивости и балансировки нагрузки.
Без ESI протокол EVPN не мог бы грамотно различать отдельные физические сегменты и обеспечивать мультихоминг с сохранением изоляции и согласованности маршрутов/кадров.
Схема сети
За основу принимается всё та же топология из задания Часть 5. VxLAN. EVPN L2
В нее добавлена еще одна пара leaf'ов. Заодно проверим на надежность "конфигуратор" из Netbox и задокументируем процесс онбоардинга новых устройств xD
Для упрощения и экономии ресурсов будем работать внутри Rack 3 и Rack 4 согласно схемы
Внутри Rack 4 будет работать устройство на базе RHEL 8.4 с агрегированным (Bond0) интерфейсом, состоящим из двух подключений: первое - в сторону no-osl-dc1-f1-r03k04-lf01, второе - в сторону no-osl-dc1-f1-r03k04-lf02.
Это эмуляция подключения конечного сервера в двум ToR коммутаторам в пределах серверной стойки.


Тут мне попадается офигенная композиция в Яндекс.Музыке Valhalla Calling и еще один трек Ghost Riders in the Sky. Фак, чистый кайф!
VXLAN thunders in twilight’s dome,
ESI’s banner guides the roaming band.
Multihome stands as bonds of home,
Valhalla calls—EVPN at hand!
А. С. ChatGPT (c.)
Тут мы останавливаемся думать за генерацию ESI - он же Ethernet Segment Identifier. Самый распространённый формат — тип 0x00 (Manual), где 9 байт могут быть заданы вручную (часто встречается тип 00:aa:bb:cc:dd:...).
Предполагается, что ESI сегмент не распространяется за пределы серверной стойки. В таком случае мы можем привязаться к физической стойке (Rack)
В Netbox вводятся два новых поля в Customization -> Custom Fields
для документирования значений полей:
ESI ID - идентификатор Ethernet сегмента с проверкой по Regex
^[0-9A-Fa-f]{4}(:[0-9A-Fa-f]{4}){4}$
LACP System-id - идентификатор LACP для коммутаторов с проверкой по Regex
^[0-9A-Fa-f]{4}(.[0-9A-Fa-f]{4}){2}$

Шаблон для генерации конфигурации для leaf'ов претерпел значительные изменения и заставил потратить на себя кучу времени. Основные изменения коснулись секций для интерфейсов. Теперь по каждому типу интерфейсов собственный цикл обработки.
Ниже приведен пример для секции Port-Channel и кастомных полей:
{%- for interface in device.interfaces.all() %}
{%- if interface.name.startswith('Port-Channel') %}
interface {{ interface.name }}
{%- set ip_list = interface.ip_addresses.all() %}
{%- if ip_list|length > 0 %}
{%- for ip in ip_list %}
mtu 9214
no switchport
bfd interval 200 min-rx 200 multiplier 3
ip address {{ ip.address }}
{%- endfor %}
{%- else %}
{%- if interface.mode == 'access' and interface.untagged_vlan %}
switchport
switchport mode access
switchport access vlan {{ interface.untagged_vlan.vid }}
!
{%- elif interface.mode == 'tagged' and interface.tagged_vlans.all() %}
switchport
switchport mode trunk
{%- if interface.untagged_vlan %}
switchport trunk native vlan {{ interface.untagged_vlan.vid }}
{%- endif %}
switchport trunk allowed vlan {{ interface.tagged_vlans.all()|map(attribute='vid')|join(',') }}
!
{%- else %}
switchport
switchport mode access
switchport access vlan 1
{%- endif %}
{%- endif %}
{%- if interface.custom_field_data["esi_id"] %} # Настройка ESI
evpn ethernet-segment
identifier {{ interface.custom_field_data["esi_id"] }}
{%- endif %}
{%- if interface.custom_field_data["lacp_sid"] %} # Настройка LACP
lacp system-id {{ interface.custom_field_data["lacp_sid"] }}
{%- endif %}
{%- if not interface.enabled %}
shutdown
{%- else %}
no shutdown
{%- endif %}
{%- if interface.description %}
description {{ interface.description }}
{%- endif %}
!
{%- endif %}
{%- endfor %}
Пока не ясно как и откуда генерировать ESI и LACP System-ID. В лабораторных условиях принимаем за истину, что LACP System-ID - это mac-адрес mgmt порта первого устройства в паре. Для Rack 4 это будет 5001.000D.0000
Файлы конфигураций устройств
Сегодня будут приложены только файлы spin'ов и leaf'ов (Rack 3, Rack 4)
Вот тут показаны дополнительные поля:

Конфигурационные файлы устройств. Напоминаю, что это выгрузка из Netbox без ручных правок:
Тестирование на отказ
Сценарий 1. Как оказалось, он же "единственный"
Пробую отключить левое плечо у сервера и вижу, что трафик не бежит. Смотрю в табличку маршрутизации на лифе стойки 3 и вижу маршрут только через одну AS - 4200131337. Вот они бенефиты отдельных AS для лифов!
no-osl-dc1-f1-r03k03-lf02#sh bgp evpn route-type auto-discovery
BGP routing table information for VRF default
Router identifier 10.16.1.6, local AS number 4200131336
Route status codes: * - valid, > - active, S - Stale, E - ECMP head, e - ECMP
c - Contributing to ECMP, % - Pending BGP convergence
Origin codes: i - IGP, e - EGP, ? - incomplete
AS Path Attributes: Or-ID - Originator ID, C-LST - Cluster List, LL Nexthop - Link Local Nexthop
Network Next Hop Metric LocPref Weight Path
* >Ec RD: 10.16.1.7:10 auto-discovery 0 0000:5001:000d:0000:0000
10.16.4.7 - 100 0 4200131329 4200131337 i
* ec RD: 10.16.1.7:10 auto-discovery 0 0000:5001:000d:0000:0000
10.16.4.7 - 100 0 4200131329 4200131337 i
* >Ec RD: 10.16.4.7:1 auto-discovery 0000:5001:000d:0000:0000
10.16.4.7 - 100 0 4200131329 4200131337 i
* ec RD: 10.16.4.7:1 auto-discovery 0000:5001:000d:0000:0000
10.16.4.7 - 100 0 4200131329 4200131337 i
А проблема заключалась в том, что в Netbox для одного лифа Po1 был настроен в access vlan 10, а для второго - нет! После правки в Netbox и заливки нового конфига результат на лифе следующий:
Вывод команды sh bgp evpn route-type auto-discovery
no-osl-dc1-f1-r03k03-lf02#sh bgp evpn route-type auto-discovery
BGP routing table information for VRF default
Router identifier 10.16.1.6, local AS number 4200131336
Route status codes: * - valid, > - active, S - Stale, E - ECMP head, e - ECMP
c - Contributing to ECMP, % - Pending BGP convergence
Origin codes: i - IGP, e - EGP, ? - incomplete
AS Path Attributes: Or-ID - Originator ID, C-LST - Cluster List, LL Nexthop - Link Local Nexthop
Network Next Hop Metric LocPref Weight Path
* >Ec RD: 10.16.1.7:10 auto-discovery 0 0000:5001:000d:0000:0000
10.16.4.7 - 100 0 4200131329 4200131337 i
* ec RD: 10.16.1.7:10 auto-discovery 0 0000:5001:000d:0000:0000
10.16.4.7 - 100 0 4200131329 4200131337 i
* >Ec RD: 10.16.1.8:10 auto-discovery 0 0000:5001:000d:0000:0000
10.16.4.8 - 100 0 4200131329 4200131338 i
* ec RD: 10.16.1.8:10 auto-discovery 0 0000:5001:000d:0000:0000
10.16.4.8 - 100 0 4200131329 4200131338 i
* >Ec RD: 10.16.4.7:1 auto-discovery 0000:5001:000d:0000:0000
10.16.4.7 - 100 0 4200131329 4200131337 i
* ec RD: 10.16.4.7:1 auto-discovery 0000:5001:000d:0000:0000
10.16.4.7 - 100 0 4200131329 4200131337 i
* >Ec RD: 10.16.4.8:1 auto-discovery 0000:5001:000d:0000:0000
10.16.4.8 - 100 0 4200131329 4200131338 i
* ec RD: 10.16.4.8:1 auto-discovery 0000:5001:000d:0000:0000
10.16.4.8 - 100 0 4200131329 4200131338 i
Вывод команды show bgp evpn route-type mac-ip
no-osl-dc1-f1-r03k03-lf02#show bgp evpn route-type mac-ip
BGP routing table information for VRF default
Router identifier 10.16.1.6, local AS number 4200131336
Route status codes: * - valid, > - active, S - Stale, E - ECMP head, e - ECMP
c - Contributing to ECMP, % - Pending BGP convergence
Origin codes: i - IGP, e - EGP, ? - incomplete
AS Path Attributes: Or-ID - Originator ID, C-LST - Cluster List, LL Nexthop - Link Local Nexthop
Network Next Hop Metric LocPref Weight Path
* > RD: 10.16.1.6:10 mac-ip 5001.0008.0000
- - - 0 i
* >Ec RD: 10.16.1.7:10 mac-ip 5001.000f.0000
10.16.4.7 - 100 0 4200131329 4200131337 i
* ec RD: 10.16.1.7:10 mac-ip 5001.000f.0000
10.16.4.7 - 100 0 4200131329 4200131337 i
* >Ec RD: 10.16.1.8:10 mac-ip 5001.000f.0000
10.16.4.8 - 100 0 4200131329 4200131338 i
* ec RD: 10.16.1.8:10 mac-ip 5001.000f.0000
10.16.4.8 - 100 0 4200131329 4200131338 i
Как проходит трафик для пингования сервера

Вместо скриншотов видео на Youtube
Что-то вместо выводов
Конфигуратор в Netbox работает очень даже сносно :-) Что прям радует.
Отдельная AS для leaf'ов удобна в плане восприятия информации о маршрутах и в траблшутинге
Документирование через Netbox не всегда можно решить "в лоб". Я попался на Port-Channel, ESI и LACP SID. А также, нужно думать между разными вендорами и платформами.
ESI LAG кажется удобнее, чем MC-LAG. Коллеги добились использования >2 устройств для подключения серверов, нет необходимости сопровождать peerlink и синхронизировать какие-то части конфигурации, можно использовать мультивендорную среду.
Bonus
Доставка конфигурации на устройства из Netbox
Так получилось, что в процессе чтения и рефлексии набрел на плагин Netbox netbox-config-diff , который все время обходил стороной. Но это же то, что мне было нужно все это время!
Что умеет плагин:
Config Diff - сравнение конфигурации устройства из Render Config и реальным состоянием с помощью подключения через драйвер
Config Managemenet - доставка конфигурации на устройства и приведение его в эталонное состояние
Настройка плагина хорошо описана в документации.
После установки необходимо настроить платформу на вкладке Config Diff Plugin -> Compliance -> Platform Settings

Список поддерживаемых платформ:
arista_eos
cisco_iosxe
cisco_iosxr
cisco_nxos
juniper_junos
После этого можно запустить скрипт для проверки состояния устройств из вкладки Customization -> Scripts -> ConfigDiffScript

Результат работы скрипта:

После того, как скрипт отработает, можно провалиться в устройство и посмотреть Config Diff на вкладке Config Compliance

И самое интересное! Доставка конфигурации на устройства
На вкладке Config Diff Plugin -> Config Managemenet -> Configuration Requests
создается новый запрос на изменение конфигурации и следующий порядок действий:
Collect Diffs - сбор информации о разности конфигураций.
Approve - разрешение на применение конфигурации
Schedule - планирование конфигурации к применению

В джобах можно посмотреть историю:

И там же посмотреть, что получилось:

Лог выполнения джоба в JSON
Last updated