SONiC (5) - SONiC 的架构

1. 参考文档

# Document Title Document Identifier & Link
1 SONiC official wiki https://github.com/sonic-net/SONiC/wiki
2 SONiC architecture https://github.com/sonic-net/SONiC/wiki/Architecture
3 SAI API https://github.com/opencomputeproject/SAI
4 Redis documentation https://redis.io/documentation
5 Click module http://click.pocoo.org/5/
6 JSON introduction https://www.json.org/
7 SONiC supported platforms https://github.com/sonic-net/SONiC/wiki/Supported-Devices-and-Platforms
8 SONiC Software Architecture 2018 Workshop Videos Slides

2. SONiC的系统架构

SONiC 系统的架构由多个模块组成,这些模块通过一个集中式且可扩展的基础设施相互作用。该基础设施依赖于使用 redis 数据库引擎:他是一个键值数据库,用于提供语言无关的接口、数据持久化方法、以及所有 SONiC 子系统之间的复制和多进程通信。

通过利用 redis-engine 基础设施所提供的发布者/订阅者消息传递模式,应用程序只需订阅其所需的数据视图即可,从而避免了与其实现细节无关的那些内容。

SONiC 将每个模块置于独立的 Docker 容器中,以保持具有语义关联的组件之间的高度内聚性,同时减少不相关组件之间的耦合性。这些组件均被设计为完全独立于与底层抽象进行交互所需的特定平台细节。

SONiC 将其主要功能组件划分为以下 Docker 容器:

  • Dhcp-relay

  • Pmon

  • Snmp

  • Lldp

  • Bgp

  • Teamd

  • Database

  • Swss

  • Syncd

以下图表展示了每个 Docker 容器所包含功能的概览,以及这些容器之间是如何相互作用的。请注意,并非所有的 SONiC 应用程序都会与其他 SONiC 组件进行交互,因为其中一些会从外部实体获取其状态。我们使用蓝色箭头来表示与集中式 Redis 引擎的交互,而使用黑色箭头表示其他所有交互(如 netlink、/sys 文件系统等)。

尽管 SONiC 的大部分主要组件都封装在 Docker 容器中,但仍有某些关键模块位于 Linux 主机系统内部。例如,SONiC 的配置模块(sonic-cfggen)和 SONiC 的命令行界面(CLI)就是这种情况。

关于所有可能的组件相互作用以及所转移的关联状态的更全面描述,将在本文件的后续部分中予以阐述。

3. SONiC 子系统描述

本节旨在对每个 Docker 容器内所包含的功能以及从 Linux 主机系统运行的关键 SONiC 组件进行描述。其目的是为读者提供一个高层次的介绍;在后续章节中,将采用更具图形化且更直观的方式进行阐述。

Teamd container:在 SONiC 设备中运行链路聚合功能(LAG)。"teamd" 是基于 Linux 的 LAG 协议的开源实现。"teamsyncd" 进程使得 "teamd" 与南向子系统之间能够进行交互。

  • teamd:基于 Linux 的开源 LAG 协议实现。
  • teamsyncd:负责 teamd 与底层子系统(south-bound subsystems)之间的交互,将 LAG 状态同步到 SONiC 的中央数据库(Redis 引擎)。

Pmon container: 负责运行"sensord"这一守护进程,该进程用于定期记录来自硬件组件的传感器读数,并在出现警报时发出警报。Pmon 容器还运行"fancontrol"进程,用于从相应的平台驱动程序中收集与风扇相关的状态信息。当然,随着发展,现在的pmon容器中承担着更多的功能和任务,主要还有xcvrd,psud等,对前端口的模块信息进行整体的监控,其他的container 根据模块的状态然后再进行相应的操作

  • sensord:周期性读取硬件传感器(如温度、电压)数据,并在触发告警时发出通知。
  • fancontrol:从平台驱动程序中收集风扇状态信息。

Snmp container: 主机支持 SNMP 功能。在这个容器内,有两组相关的内容:

Snmpd:实际的 SNMP 服务器,负责处理来自外部网络设备的 SNMP 查询请求。

SNMP代理(sonic_ax_impl):这是 SONiC 对 AgentX SNMP 子代理的实现。该子代理会将从 SONiC 数据库中收集到的信息提供给主代理(snmpd),而该主代理位于集中式 Redis 引擎中。

  • snmpd:主 SNMP 代理,处理来自外部网元的 SNMP 查询请求。
  • snmp-agent(sonic_ax_impl) :SONiC 自研的 AgentX 子代理 ,从 Redis 数据库中提取信息(如接口状态、计数器等),并通过 AgentX 协议上报给 snmpd

Dhcp-relay container:"DHCP 中继代理"能够将来自没有 DHCP 服务器的子网的 DHCP 请求转发至其他子网中的一个或多个 DHCP 服务器。

大型网络中,客户端与 DHCP 服务器可能不在同一广播域。此容器实现 RFC 标准的 DHCP 中继功能,确保 IP 地址分配正常进行。

**Lldp container:**正如其名称所示,此容器具备 LLDP 功能。该容器中运行的相关进程如下:

  • lldpd:实际的 LLDP 守护进程,与对端设备交换系统能力、端口信息等。
  • lldp_syncd:将 LLDP 发现的邻居状态上传至 Redis 引擎(主要是 STATE_DB),供其他应用(如 SNMP)消费。
  • lldpmgr :通过订阅 STATE_DB 实现 LLDP 配置的增量更新(例如启用/禁用端口的 LLDP)。

Bgp container:运行支持的其中一种路由栈:Quagga 或 FRR。尽管该容器是以所使用的路由协议(如 BGP)来命名的,但实际上,这些路由栈还可以运行其他各种协议(例如 OSPF、ISIS、LDP 等)。

BGP 容器的功能划分如下:

  • bgpd :BGP 协议实现,通过 TCP/UDP 接收外部路由,再通过 zebra/fpmsyncd 下发到转发平面。
  • zebra :传统 IP 路由管理器,负责:
    • 更新 Linux 内核路由表;
    • 提供接口查询;
    • 在不同路由协议间重分发路由;
    • 通过 Netlink 向内核推送 FIB(转发信息库),并通过 FPM(Forwarding Plane Manager)接口向底层转发组件同步。
  • fpmsyncd :监听 zebra 生成的 FIB,将其写入 Redis 的 APPL_DB

BGP 容器是 SONiC 的"大脑"之一,负责动态路由计算。zebra 作为中心路由协调者,fpmsyncd 则是连接控制平面与数据平面的关键桥梁。

Database container :托管 Redis 数据库引擎,所有 SONiC 应用通过 Unix Socket 访问。

  • 主要数据库
    • APPL_DB :存放所有应用容器产生的运行时状态 (如路由、下一跳、邻居表)。这是应用层向系统其他部分传递信息的南向入口
    • CONFIG_DB :存储配置状态(如端口、VLAN、接口配置),由 CLI 或 REST API 写入。
    • STATE_DB :记录关键运行状态,用于解决模块间依赖(例如:LAG 成员端口是否存在?VLAN 成员是否就绪?)。
    • ASIC_DB :以 ASIC 友好格式存储硬件配置状态 ,供 syncd 驱动 ASIC。
    • COUNTERS_DB :存储各端口的性能计数器(如收发包数、错误包),用于 CLI 显示或遥测上报。

详解 :Redis 不仅是存储,更是 SONiC 的消息总线。各模块通过发布-订阅机制在 DB 间传递状态,实现松耦合架构。

Swss container: 提供 SONiC 模块间的通信与仲裁机制。如果说 database 容器是"仓库",swss 就是"物流调度中心"。

  • *核心 syncd 进程(状态生产者):

    • portsyncd:监听 Netlink 端口事件,解析硬件配置文件,将端口属性(速率、通道、MTU)写入 APPL_DB 和 STATE_DB。
    • intfsyncd:监听接口 IP 地址变更事件,同步到 APPL_DB。
    • neighsyncd:监听 ARP/NDP 产生的邻居事件(MAC、IP),构建 L2 转发表,写入 APPL_DB。
    • (注:teamsyncdfpmsyncdlldp_syncd 虽在各自容器中运行,但作用相同------都是 *syncd 类型的状态注入器)
  • 核心消费者进程

    • orchagentSwSS 最核心组件 。它:

      • 从 APPL_DB 等订阅状态;
      • 处理逻辑(如验证、转换);
      • 将结果写入 ASIC_DB,驱动硬件配置。

      既是消费者(读 APPL_DB),也是生产者(写 ASIC_DB)

    • IntfMgrd:根据 APPL_DB/CONFIG_DB/STATE_DB 中的状态,在 Linux 内核中配置接口(需确保依赖条件满足)。

    • VlanMgrd:类似地,配置 VLAN 接口,前提是成员端口已就绪。

SwSS 是 SONiC 控制平面的"中枢神经系统"。*syncd 进程采集状态,orchagent 做决策,Mgrd 进程执行配置,形成闭环。

Syncd container: 实现软件状态与硬件 ASIC 的同步,包括初始化、配置下发和状态采集。

  • 核心组件
    • syncd 进程
      • 编译时链接厂商提供的 ASIC SDK
      • 订阅 ASIC_DB 获取配置指令;
      • 调用 SDK API 配置 ASIC;
      • 同时将 ASIC 返回的状态(如端口状态、错误计数)发布回数据库。
    • SAI API (Switch Abstraction Interface):
      • 由 OCP(开放计算项目)定义的标准化交换芯片抽象接口
      • 使 SONiC 无需关心具体 ASIC 型号,实现硬件无关性。
    • ASIC SDK:芯片厂商提供的 SAI 兼容库(通常为 .so 动态库),由 syncd 加载调用。

详解:syncd 是 SONiC 与物理世界的"最后一公里"。SAI 屏蔽了硬件差异,使 SONiC 可在 Broadcom、Marvell、NVIDIA 等不同芯片上运行。

CLI / sonic-cfggen: SONiC 模块负责提供命令行界面功能和系统配置能力。

CLI 组件高度依赖于 Python 的 Click 库,以便为用户提供一种灵活且可定制的方式来构建命令行工具。

Sonic-cfggen 组件由 SONiC 的命令行界面调用,用于执行配置更改或任何需要与 SONiC 模块进行配置相关交互的操作

4. SONiC 系统组件间的交互

本节旨在让读者深入了解 SONiC 各组件之间发生的各种交互过程。为了使信息更易于理解,我们整合了所有能想到的系统交互,并针对每个主要功能所交换的特定状态进行了详细说明。

LLDP-state interactions

以下图表展示了在 LLDP 状态转换过程中所观察到的一系列交互情况。在这一具体示例中,我们正在依次执行在接收到携带状态变更的 LLDP 消息时所发生的步骤序列。

(1) 在 LLDP 容器启动时,lldpmgrd 进程会订阅 STATE_DB,以实时获取系统中物理端口的状态。该进程每 5 秒轮询一次 数据库。

  • 详解

    • STATE_DB 中包含如 PORT_TABLE 等关键表,记录每个端口的 oper_status(操作状态)、admin_status(管理状态)、速率、MTU 等。
    • lldpmgrd 的作用是 动态配置 LLDP 守护进程lldpd):
      • 如果某个端口被关闭(admin_status: down),则通知 lldpd 停止在该端口发送 LLDP 报文
      • 如果端口启用或属性变更(如描述信息修改),则更新 lldpd 的运行参数。
    • 这种设计实现了 LLDP 行为与端口生命周期的联动,避免在无效端口上浪费资源。

关键点lldpmgrd配置驱动者(Configuration Manager),它不处理外部 LLDP 报文,而是确保本地 LLDP 发送行为与系统配置一致。

(2)某一时刻,一个新的 LLDP 报文到达内核空间中为 LLDP 预留的套接字(socket)。内核网络协议栈随后将该报文的有效载荷传递给用户空间的 lldp 进程。

  • 详解

    • LLDP 使用 以太网类型 0x88CC 的帧,在数据链路层直接传输。
    • Linux 内核通过 PF_PACKET 套接字 或专用的 lldp socket 接收此类帧。
    • 报文被传递给运行在 LLDP 容器中的 lldpd 守护进程(即开源项目 lldpd)。
    • 此时,lldpd 解析报文内容,包括:
      • 对端系统名称(System Name)
      • 端口 ID(Port ID)
      • 系统能力(如是否支持桥接、路由)
      • 管理地址、VLAN 信息等 TLV(Type-Length-Value)字段。

关键点 :此阶段完全由标准 lldpd 实现,SONiC 未修改其核心逻辑,仅通过后续同步机制集成。

(3)lldp 解析并消化这一新状态,随后在 lldp_syncd 执行 lldpctl CLI 命令时被采集------该命令通常每 10 秒执行一次

  • lldp_syncd 是 SONiC 自研的同步守护进程,运行在 LLDP 容器内

  • 它通过调用 lldpctl -f json 命令(lldpd 提供的 CLI 工具)以 JSON 格式导出当前已学习的邻居信息

  • 示例输出片段:

    复制代码
    {
      "lldp": {
        "interface": {
          "Ethernet0": {
            "chassis": { "name": "switch-peer-01" },
            "port": { "id": "Ethernet48" }
          }
        }
      }
    }
  • lldp_syncd 解析此 JSON,提取结构化数据,准备写入中央数据库。

⚠️ 注意 :由于依赖 CLI 调用,LLDP 状态同步存在最大 10 秒延迟(非实时)。这是 SONiC 当前架构的一个权衡(简单性 vs 实时性)。

(4)lldp_syncd 将此新状态推送到 APPL_DB,具体写入 LLDP_ENTRY_TABLE 表。

详解

  • APPL_DB 是应用层状态的"发布总线"。

  • LLDP_ENTRY_TABLE 的键值格式通常为:

    复制代码
    LLDP_ENTRY_TABLE|Ethernet0 = {
      "lldp_rem_sys_name": "switch-peer-01",
      "lldp_rem_port_id": "Ethernet48",
      "lldp_rem_sys_cap_supported": "78",
      "lldp_rem_mgmt_addr": "192.168.10.10"
    }
  • 此操作通过 Redis 的 HSET 命令完成,触发数据库的 Pub/Sub 机制

(5)从此刻起,所有订阅了该表的实体都将收到这份新状态的副本(目前,SNMP 是唯一的订阅者)。

  • 在 SONiC 中,模块通过 Redis 的键空间通知 (Keyspace Notification)或 Selectable/Consumer 机制(SwSS 框架)监听特定表。
  • SNMP 容器中的 sonic_ax_impl 子代理
    • 订阅 LLDP_ENTRY_TABLE
    • 当有新条目写入时,将其映射为标准 LLDP MIB(如 lldpLocPortTable, lldpRemTable);
    • 外部 NMS(网络管理系统)通过 SNMP GET 请求即可查询邻居拓扑。
  • 虽然目前只有 SNMP 使用此数据,但未来可扩展至:
    • Telemetry(遥测上报)
    • CLI 显示(如 show lldp neighbors
    • 自动化运维平台(如 Ansible、NetBox 同步)

SNMP-state interactions

正如之前所述,SNMP 容器中同时运行着一个 SNMP 主代理(snmpd)以及一个 SONiC 特定的 AgentX 子代理进程(snmp_subagent)。该子代理负责与多个 Redis 数据库/表进行交互,这些数据库/表提供了用于构建 MIB(管理信息库)状态所需的信息。

具体而言,snmp_subagent 会订阅以下数据库和表:

  • APPL_DB(应用数据库):

    • PORT_TABLE(端口表)
    • LAG_TABLE(链路聚合组表)
    • LAG_MEMBER_TABLE(LAG 成员表)
    • LLDP_ENTRY_TABLE(LLDP 条目表)
  • STATE_DB(状态数据库):

    • 订阅全部内容(用 * 表示)
  • COUNTERS_DB(计数器数据库):

    • 订阅全部内容(用 * 表示)
  • ASIC_DB(ASIC 数据库):

    • ASIC_STATE:SAI_OBJECT_TYPE_FDB*(FDB 表项,即转发数据库条目,以 SAI 对象类型表示)

以下图表描绘了系统处理传入的 SNMP 查询期间,SONiC 各组件之间典型的交互情况。

(0)在 snmp-subagent 进程中支持的各种 MIB 子组件初始化期间,该进程会与上述提到的各个数据库建立连接。从这一刻起,从所有这些数据库获取的状态会在 snmp-subagent 内部进行本地缓存。此信息每几秒(<60 秒)刷新一次,以确保数据库与 snmp-subagent 完全同步。

(1)一个 SNMP 查询到达内核空间中的 SNMP 套接字。内核的网络协议栈将数据包传递给 snmpd 进程。

(2)SNMP 消息被解析,相关请求被发送到 SONiC 的 AgentX 子代理(即 sonic_ax_impl)。

(3)snmp-subagent 从其本地数据结构中缓存的状态中处理查询,并将信息发送回 snmpd 进程。

(4)snmpd 最终通过常规的套接字接口将回复发送回请求发起方。

Routing-state interactions.

在本节中,我们将详细介绍 SONiC 处理从 eBGP 邻居接收新路由时的步骤序列。我们假设 eBGP 会话已建立,且新路由使用直连邻居作为下一跳。

下图展示了此过程中涉及的组件。请注意,为聚焦 SONiC 架构核心,此处省略了无关细节。

(0)在 BGP 容器初始化期间,zebra 通过常规 TCP 套接字连接到 fpmsyncd。在稳定/非瞬态条件下,zebra、Linux 内核、APPL_DB 和 ASIC_DB 中的路由表应完全一致/等效。

(1)一个新的 TCP 数据包到达内核空间中 BGP 的套接字。内核网络协议栈最终将相关载荷传递给 bgpd 进程。

(2)bgpd 解析新数据包,处理 BGP UPDATE 消息,并将新前缀及其关联的协议下一跳通知给 zebra。

(3)zebra 判定该前缀的可行性/可达性(例如,存在转发下一跳)后,生成 route-netlink 消息以将新状态注入内核。

(4)zebra 使用 FPM 接口将此 netlink-route 消息传递给 fpmsyncd。

(5)fpmsyncd 处理 netlink 消息,并将此状态推送至 APPL_DB。

(6)orchagentd 作为 APPL_DB 的订阅者,将接收之前推送到 APPL_DB 的信息内容。

(7)处理接收到的信息后,orchagentd 调用 sairedis API 将路由信息注入 ASIC_DB。

(8)syncd 作为 ASIC_DB 的订阅者,将接收 orchagentd 生成的新状态。

(9)syncd 处理信息并调用 SAI API 将此状态注入相应的 ASIC 驱动。

(10)新路由最终被推送至硬件。

Port-state interactions.

本节描述端口相关信息在 SONiC 系统中传递时的组件交互流程。考虑到 portsyncd(端口同步守护进程) 的核心作用及其对其他子系统的依赖,我们首先从 portsyncd 的初始化过程开始介绍。

本节目标有二:

  1. 揭示系统中产生或消费端口信息的多个组件;
  2. 通过实例说明 STATE_DB 的使用方式,以及不同应用如何依赖其信息完成内部操作。

(0)初始化期间,portsyncd 与 redis-engine 中的主要数据库建立通信通道。portsyncd 声明其作为 APPL_DB 和 STATE_DB 的发布者 ,以及 CONFIG_DB 的订阅者 。同时,portsyncd 还订阅系统中负责传递端口/链路状态信息的 netlink 通道

(1)portsyncd 开始解析与系统硬件配置(硬件型号/SKU)关联的 port_config.ini 文件(详见配置章节)。端口相关信息(如 lanes 通道、接口名、别名、速率等)通过此通道传输至 APPL_DB。

(2)orchagent 感知到 APPL_DB 中的新状态,但会延迟操作,直至 portsyncd 通知其已完全解析 port_config.ini。一旦收到通知,orchagent 会开始在硬件/内核中初始化对应端口接口,并调用 sairedis API,通过 ASIC_DB 接口将请求传递给 syncd。

(3)syncd 通过 ASIC_DB 接收新请求,并准备调用满足 orchagent 请求所需的 SAI API。

(4)syncd 使用 SAI API 和 ASIC SDK 创建与待初始化物理端口关联的内核主机接口(host-interfaces)

(5)步骤 (4) 会生成 netlink 消息并被 portsyncd 接收。当 portsyncd 收到所有从 port_config.ini 解析的端口(步骤1)对应的 netlink 消息后,会宣布"初始化过程完成"。

(6)作为步骤 (5) 的一部分,portsyncd 为每个成功初始化的端口向 STATE_DB 写入一条记录

(7)从此刻起,之前订阅 STATE_DB 内容的应用会收到通知,允许它们开始使用所依赖的端口。换言之,若 STATE_DB 中某端口无有效记录,任何应用都无法使用该端口。

:目前主动监听 STATE_DB 变更的应用包括:teamsyncd(Teaming 同步)、intfmgrd(接口管理)、vlanmgrd(VLAN 管理)和 lldpmgr(LLDP 管理)。后续章节将详细介绍这些组件(lldpmgr 已在前面章节提及)

现在,让我们依次回顾一下物理端口出现故障时所经历的一系列步骤:

(0)如概述部分所述,syncd 在 ASIC_DB 中同时扮演 发布者(publisher)订阅者(subscriber) 角色。

  • 订阅者模式:syncd 需要接收北向应用(如 orchagent)下发的状态(如前文所有模块交互所示);
  • 发布者模式 :syncd 需向高层组件通知硬件触发的事件(如端口 down、链路故障)。

(1)当对应 ASIC 的光模块检测到 信号丢失(loss-of-carrier) 时,会向关联的驱动发送通知,驱动再将此信息传递给 syncd。

(2)syncd 调用相应的通知处理程序,并将 port-down 事件发送至 ASIC_DB。

(3)orchagent 通过其专用通知线程 从 ASIC_DB 收集新状态,并执行"port-state-change"处理程序,完成以下操作:

a. 更新 APPL_DB,以通知依赖此状态的应用(如 CLI 命令 show interface status);

b. 调用 sairedis API,通知 syncd 需要更新与故障端口关联的内核主机接口状态。orchagent 通过 ASIC_DB 接口将此请求传递给 syncd。

(4)syncd 通过 ASIC_DB 接收 orchagent 的新请求,并准备调用满足该请求所需的 SAI API。

(5)syncd 使用 SAI API 和 ASIC SDK,将受影响主机接口的最新操作状态(DOWN)更新至内核。

(6)步骤 (5) 会生成 netlink 消息并被 portsyncd 接收,但由于此时 SONiC 所有组件均已通过其他路径感知到 port-down 事件,该消息被静默丢弃

相关推荐
ljp11125 小时前
UNRaid安装chfs
docker·免费·文件共享
昵称为空C5 小时前
Spring Boot 项目docker分层镜像构建案例
spring boot·ci/cd·docker
kali-Myon5 小时前
快速解决 Docker 环境中无法打开 gdb 调试窗口以及 tmux 中无法滚动页面内容和无法选中复制的问题
运维·安全·docker·容器·gdb·pwn·tmux
管理大亨5 小时前
ELK + Redis Docker 企业级部署落地方案
大数据·运维·elk·elasticsearch·docker·jenkins
pblh1236 小时前
基于Docker部署测试PySpark
运维·docker·容器
yBmZlQzJ7 小时前
财运到内网穿透-群晖NAS安装(docker版本)
运维·经验分享·网络协议·docker·容器
白学还是没白学?7 小时前
exec db docker from A to B
数据库·docker·容器
WGS.7 小时前
docker run 报错:ImportError: PyCapsule_Import could not import module “datetime“
docker
萨文 摩尔杰7 小时前
Docker
docker