Порядок передачи unicast трафика внутри фабрики Cisco ACI подробно описан в различных источниках, хотя внимательно изучить их и не свихнуться – та ещё задачка. Распространению BUM внутри overlay посвящены несколько секций, но вот то, что происходит в underlay, на мой взгляд, сознательно вынесено из основного документа. Тут вовлечена некая сущность – FTAG: один из spine становится корнем дерева, к которому присоединяются все leaf-коммутаторы. Общее количество активных FTAG – 12; очевидно, так сделали, чтобы обеспечить отказоусточивость и некую балансировку нагрузки.

Описание выше разумно, однако оно вызывает несколько вопросов:

  1. Как происходит передача BUM трафика, когда uplink на leaf-коммутаторе выходит из строя? Если только один spine участвует в построении дерева, то трафик при таком сценарии некуда слать, значит, придётся отбросить – не очень-то отказоустойчиво получается.

  2. Рассмотрим применение 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

Комментарии (1)


  1. fantas1st0
    00.00.0000 00:00
    +1

    Ярослав, сам 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