Порядок передачи unicast трафика внутри фабрики Cisco ACI подробно описан в различных источниках, хотя внимательно изучить их и не свихнуться – та ещё задачка. Распространению BUM внутри overlay посвящены несколько секций, но вот то, что происходит в underlay, на мой взгляд, сознательно вынесено из основного документа. Тут вовлечена некая сущность – FTAG: один из spine становится корнем дерева, к которому присоединяются все leaf-коммутаторы. Общее количество активных FTAG – 12; очевидно, так сделали, чтобы обеспечить отказоусточивость и некую балансировку нагрузки.
Описание выше разумно, однако оно вызывает несколько вопросов:
Как происходит передача BUM трафика, когда uplink на leaf-коммутаторе выходит из строя? Если только один spine участвует в построении дерева, то трафик при таком сценарии некуда слать, значит, придётся отбросить – не очень-то отказоустойчиво получается.
Рассмотрим применение Remote Leaf: RL отправляют BUM трафик на anycast адрес spine-коммутаторов. Что произойдёт, если такой трафик попадёт на spine, который не участвует в нужном FTAG? Тот же самый вопрос справедлив и для Multi-Site архитектуры.
На сайте CiscoLive есть хороший семинар, который поможет нам ответить на эти вопросы. Схема лабы очень проста:
Всё, что нам нужно – это поднять связность между двумя endpoint в одном EPG.
Модуль Tenant:
resource "aci_tenant" "TestTenant" {
name = "TestTenant"
}
resource "aci_vrf" "TestVrf" {
tenant_dn = aci_tenant.TestTenant.id
name = "TestVrf"
}
resource "aci_bridge_domain" "TestBD1" {
tenant_dn = aci_tenant.TestTenant.id
name = "TestBD1"
relation_fv_rs_ctx = aci_vrf.TestVrf.id
}
resource "aci_subnet" "Subnet1" {
parent_dn = aci_bridge_domain.TestBD1.id
ip = "192.168.0.254/24"
scope = ["private", "shared"]
}
resource "aci_application_profile" "TestAP" {
tenant_dn = aci_tenant.TestTenant.id
name = "TestAP"
}
resource "aci_application_epg" "TestEPG1" {
application_profile_dn = aci_application_profile.TestAP.id
name = "TestEPG1"
relation_fv_rs_bd = aci_bridge_domain.TestBD1.id
}
resource "aci_epg_to_domain" "EPG1Domain" {
application_epg_dn = aci_application_epg.TestEPG1.id
tdn = aci_physical_domain.PhysicalDomain.id
}
resource "aci_bulk_epg_to_static_path" "TestEPG1StaticPath" {
application_epg_dn = aci_application_epg.TestEPG1.id
static_path {
interface_dn = "topology/pod-1/paths-101/pathep-[eth1/2]"
encap = "vlan-101"
}
static_path {
interface_dn = "topology/pod-1/paths-104/pathep-[eth1/2]"
encap = "vlan-101"
}
}
Модуль Access policies:
resource "aci_vlan_pool" "TestPool" {
name = "TestPool"
description = "From Terraform"
alloc_mode = "static"
}
resource "aci_ranges" "TestRange" {
vlan_pool_dn = aci_vlan_pool.TestPool.id
from = "vlan-1"
to = "vlan-1000"
alloc_mode = "static"
}
resource "aci_physical_domain" "PhysicalDomain" {
name = "PhysicalDomain"
relation_infra_rs_vlan_ns = aci_vlan_pool.TestPool.id
}
resource "aci_attachable_access_entity_profile" "TestAAEP" {
name = "TestAAEP"
}
resource "aci_aaep_to_domain" "PhysicalDomain-to-TestAAEP" {
attachable_access_entity_profile_dn = aci_attachable_access_entity_profile.TestAAEP.id
domain_dn = aci_physical_domain.PhysicalDomain.id
}
resource "aci_leaf_interface_profile" "TestInterfaceProfile" {
name = "TestInterfaceProfile"
}
resource "aci_access_port_block" "TestAccessBlockSelector" {
access_port_selector_dn = aci_access_port_selector.TestAccessPortSelector.id
name = "TestAccessBlockSelector"
from_card = "1"
from_port = "2"
to_card = "1"
to_port = "2"
}
resource "aci_access_port_selector" "TestAccessPortSelector" {
leaf_interface_profile_dn = aci_leaf_interface_profile.TestInterfaceProfile.id
name = "TestAccessPortSelector"
access_port_selector_type = "range"
relation_infra_rs_acc_base_grp = aci_leaf_access_port_policy_group.TestAccessInterfacePolicy.id
}
resource "aci_leaf_access_port_policy_group" "TestAccessInterfacePolicy" {
name = "TestAccessInterfaceProfile"
relation_infra_rs_att_ent_p = aci_attachable_access_entity_profile.TestAAEP.id
}
resource "aci_leaf_profile" "TestSwitchProfile" {
name = "TestSwitchProfile"
leaf_selector {
name = "LeafSelector"
switch_association_type = "range"
node_block {
name = "Leaf101"
from_ = "101"
to_ = "101"
}
node_block {
name = "Leaf104"
from_ = "104"
to_ = "104"
}
}
relation_infra_rs_acc_port_p = [aci_leaf_interface_profile.TestInterfaceProfile.id]
}
Если пробежаться по презентации, можно заметить, что схема FTAG немного отличается от таковой в документации: spine-коммутаторы, которые не являются корневыми, всё равно участвуют в FTAG, подключаясь к дереву через один-единственный leaf. Помните, что выбор корня FTAG происходит через расширение IS-IS? Заглянем в консоль:
Spine202# show isis internal mcast routes ftag
<output omitted>
FTAG ID: 0 [Root] [Enabled] Cost:( 2/ 13/ 0)
----------------------------------
Root port: Ethernet1/1.68
OIF List:
FTAG ID: 1 [Root] [Enabled] Cost:( 0/ 0/ 0)
----------------------------------
Root port: -
OIF List:
Ethernet1/1.68
Ethernet1/2.67
Ethernet1/3.70
Ethernet1/4.69
FTAG ID: 2 [Root] [Enabled] Cost:( 0/ 0/ 0)
----------------------------------
Root port: -
OIF List:
Ethernet1/1.68
Ethernet1/2.67
Ethernet1/3.70
Ethernet1/4.69
FTAG ID: 3 [Root] [Enabled] Cost:( 0/ 0/ 0)
----------------------------------
Root port: -
OIF List:
Ethernet1/1.68
Ethernet1/2.67
Ethernet1/3.70
Ethernet1/4.69
FTAG ID: 4 [Root] [Enabled] Cost:( 0/ 0/ 0)
----------------------------------
Root port: -
OIF List:
Ethernet1/1.68
Ethernet1/2.67
Ethernet1/3.70
Ethernet1/4.69
FTAG ID: 5 [Enabled] Cost:( 2/ 7/ 0)
----------------------------------
Root port: Ethernet1/3.70
OIF List:
FTAG ID: 6 [Enabled] Cost:( 2/ 8/ 0)
----------------------------------
Root port: Ethernet1/2.67
OIF List:
FTAG ID: 7 [Enabled] Cost:( 2/ 9/ 0)
----------------------------------
Root port: Ethernet1/2.67
OIF List:
FTAG ID: 8 [Enabled] Cost:( 2/ 8/ 0)
----------------------------------
Root port: Ethernet1/3.70
OIF List:
FTAG ID: 9 [Enabled] Cost:( 2/ 7/ 0)
----------------------------------
Root port: Ethernet1/4.69
OIF List:
FTAG ID: 10 [Enabled] Cost:( 2/ 12/ 0)
----------------------------------
Root port: Ethernet1/1.68
OIF List:
FTAG ID: 11 [Root] [Enabled] Cost:( 0/ 0/ 0)
----------------------------------
Root port: -
OIF List:
Ethernet1/1.68
Ethernet1/2.67
Ethernet1/3.70
Ethernet1/4.69
FTAG ID: 12 [Root] [Enabled] Cost:( 0/ 0/ 0)
----------------------------------
Root port: -
OIF List:
Ethernet1/1.68
Ethernet1/2.67
Ethernet1/3.70
Ethernet1/4.69
FTAG ID: 13 [Disabled]
FTAG ID: 14 [Disabled]
FTAG ID: 15 [Disabled]
Spine201# show isis internal mcast route ftag
<output omitted>
FTAG ID: 0 [Root] [Enabled] Cost:( 0/ 0/ 0)
----------------------------------
Root port: -
OIF List:
Ethernet1/1.68
Ethernet1/2.67
Ethernet1/3.69
Ethernet1/4.70
FTAG ID: 1 [Enabled] Cost:( 2/ 8/ 0)
----------------------------------
Root port: Ethernet1/2.67
OIF List:
FTAG ID: 2 [Enabled] Cost:( 2/ 9/ 0)
----------------------------------
Root port: Ethernet1/2.67
OIF List:
FTAG ID: 3 [Enabled] Cost:( 2/ 8/ 0)
----------------------------------
Root port: Ethernet1/3.69
OIF List:
FTAG ID: 4 [Enabled] Cost:( 2/ 8/ 0)
----------------------------------
Root port: Ethernet1/4.70
OIF List:
FTAG ID: 5 [Root] [Enabled] Cost:( 0/ 0/ 0)
----------------------------------
Root port: -
OIF List:
Ethernet1/1.68
Ethernet1/2.67
Ethernet1/3.69
Ethernet1/4.70
FTAG ID: 6 [Root] [Enabled] Cost:( 0/ 0/ 0)
----------------------------------
Root port: -
OIF List:
Ethernet1/1.68
Ethernet1/2.67
Ethernet1/3.69
Ethernet1/4.70
FTAG ID: 7 [Root] [Enabled] Cost:( 0/ 0/ 0)
----------------------------------
Root port: -
OIF List:
Ethernet1/1.68
Ethernet1/2.67
Ethernet1/3.69
Ethernet1/4.70
FTAG ID: 8 [Root] [Enabled] Cost:( 0/ 0/ 0)
----------------------------------
Root port: -
OIF List:
Ethernet1/1.68
Ethernet1/2.67
Ethernet1/3.69
Ethernet1/4.70
FTAG ID: 9 [Root] [Enabled] Cost:( 0/ 0/ 0)
----------------------------------
Root port: -
OIF List:
Ethernet1/1.68
Ethernet1/2.67
Ethernet1/3.69
Ethernet1/4.70
FTAG ID: 10 [Root] [Enabled] Cost:( 0/ 0/ 0)
----------------------------------
Root port: -
OIF List:
Ethernet1/1.68
Ethernet1/2.67
Ethernet1/3.69
Ethernet1/4.70
FTAG ID: 11 [Enabled] Cost:( 2/ 12/ 0)
----------------------------------
Root port: Ethernet1/1.68
OIF List:
FTAG ID: 12 [Enabled] Cost:( 2/ 13/ 0)
----------------------------------
Root port: Ethernet1/1.68
OIF List:
FTAG ID: 13 [Disabled]
FTAG ID: 14 [Disabled]
FTAG ID: 15 [Disabled]
Действительно, все spine-коммутаторы участвуют в FTAG: если spine не является корнем, то у него не пуст Root port вместо OIL. GIPo адрес группы, которая соответствует BD – 225.0.69.80, поэтому она должна попадать в FTAG 0:
Такая топология позволяет естественным образом ответить на первый вопрос: если Leaf104 потеряет uplink в сторону Spine201, то он сможет использовать путь через Spine202 для передачи трафика внутри FTAG 0:
Spine202# show isis internal mcast routes ftag
<output omitted>
FTAG ID: 0 [Root][DEFERED] [Enabled] Cost:( 2/ 13/ 0)
----------------------------------
Root port: Ethernet1/1.68
OIF List:
Ethernet1/4.69 <--------- link towards Leaf104
<output omitted>
Leaf104# show isis internal mcast routes ftag
<output omitted>
FTAG ID: 0 [Enabled] Cost:( 3/ 13/ 0)
----------------------------------
Root port: Ethernet1/50.12 <--------- link towards Spine202
OIF List:
<output omitted>
Ответ на второй вопрос тоже понятен: если трафик попадает на некорневой spine, тот передаёт полученные пакеты через один из leaf-коммутаторов в нужный FTAG.
Spine202# show isis internal mcast route gipo
<output omitted>
GIPo: 225.0.69.80 [LOCAL]
OIF List:
Ethernet1/1.68
Ethernet1/4.70
Tunnel4 <--------- Multi-Site tunnel
<output omitted>
Должен существовать некий механизм, предотвращающий отправку «внешнего» трафика обратно в сторону Remote Leaf или другого Site, но я не смог найти информацию о том, как именно он реализован (ставлю на некий бит в iVXLAN заголовке).
Имеет ли описанное выше какое-либо значение для эксплуатации ACI? Пожалуй, нет, в конце концов ACI – это решение «под ключ», которое прячет сложность внутри себя, в отличие от подхода «сделай сам». Впрочем, мне всё же кажется, было бы неплохо понимать, что именно происходит внутри подобной системы, а также иметь возможность в общем виде связать между собой подходы, присущие разным этапам эволюции продукта.
Спасибо за рецензию: Анастасии Куралёвой
Канал в Телеграме: https://t.me/networking_it_ru
fantas1st0
Ярослав, сам FTAG добавляется в iVxlan заголовок как 4 бита в Dest IP.
Root для каждого дерева назначается APICом и разносится через IS-IS используя GM-LSP + martian addresses 0.0.0.<ftag id>.
Что касается случая с RL:
RL получает BUM от сервера и реплицирует трафик
локальный POD - mcast HREP Spine
все другие RL на сайте (включая другие POD)
Spine - отправляет через IPN (mcast encap)
Каждый Spine на других PODфлудит в рамках FTAG
доп. ACL на Spine для предотвращения loop/duplicate - реализация split horizon. Чекнуть ACL - не самая простая задача, т.к. оно имплементировано посредством label match в TCAM.
L3 mcast вообще помечается DSCP 0x39 и дропается где не надо с помощью ACL
смотреть в сторону show platform internal hal tcam rw-tcam
Скрипт для просмотра деревьев: https://github.com/agccie/aci-ftag-viewer