3. SONiC源码结构与构建系统

SONiC 的源码体系并不是一个单一仓库,而是由多个职责明确的 Git 仓库协同组成。理解这套结构,是后续做平台适配、修改内核驱动、增删平台插件、定制镜像的前提。如果只把 SONiC 当成一个普通项目去 git clone 然后 make,很容易在子模块、Docker 镜像和平台配置之间迷失方向。本节从仓库布局入手,逐步展开到构建系统的工作机制。

一、仓库布局:主仓库与子模块的分工

SONiC 的核心构建仓库是 sonic-buildimage,它本身并不包含全部源码,而是通过 Git 子模块(submodule)把各个功能组件聚合在一起。典型布局如下:

  • sonic-buildimage/:顶层构建仓库,包含 Makefile、平台配置、构建脚本和子模块引用。
  • src/:各子模块的挂载点,例如 src/sonic-swss、src/sonic-sairedis、src/sonic-utilities、src/sonic-linux-kernel 等。
  • platform/:平台适配目录,按厂商和芯片系列组织,例如 platform/broadcom/、platform/mellanox/、platform/nephos/。
  • device/:设备描述目录,存放具体交换机型号的端口映射、风扇、电源、SFP 等配置。
  • rules/:Makefile 规则片段,定义 Docker 镜像构建、包安装、文件拷贝等通用逻辑。
  • build/:构建输出目录,生成最终镜像和中间产物。

这种“主仓库 + 子模块”的设计,使得 SONiC 可以在不复制全部代码的情况下,精确锁定每个组件的版本。子模块的版本由主仓库中的 .gitmodules 和 gitlink 记录,执行 git submodule update --init --recursive 后,各组件会检出到指定 commit。

二、sonic-buildimage 的构建入口

sonic-buildimage 的顶层 Makefile 是构建的总入口。它通过 include 引入 rules/ 下的规则文件,并根据 PLATFORM、PLATFORM_ARCH、SONIC_IMAGE_TYPE 等变量决定构建路径。常见变量包括:

  • PLATFORM:目标平台,如 broadcom、mellanox、vs(虚拟交换机)。
  • PLATFORM_ARCH:平台架构,如 amd64、arm64、armhf。
  • SONIC_IMAGE_TYPE:镜像类型,如 docker、onie、aboot。
  • SONIC_CONFIG_BUILD_JOBS:并行构建任务数。

一个典型的构建命令如下:

make init
make configure PLATFORM=broadcom PLATFORM_ARCH=amd64
make target/sonic-broadcom.bin

make init 负责初始化子模块和基础环境;make configure 根据平台生成配置;make target/sonic-broadcom.bin 触发完整镜像构建。构建过程中,Makefile 会依次完成:拉取子模块、构建各 Docker 镜像、生成根文件系统、打包最终镜像。

三、Docker 容器化构建:镜像即组件

SONiC 的构建系统大量使用 Docker 作为“构建容器”和“运行时容器”的双重角色。构建时,每个功能组件通常对应一个 Docker 镜像,例如:

  • docker-sonic-swss:SWSS 容器,负责交换机状态服务。
  • docker-sonic-sairedis:SAI Redis 容器,负责 SAI 与 Redis 的交互。
  • docker-sonic-frr:FRR 路由协议容器。
  • docker-sonic-platform:平台管理容器。

这些镜像的构建规则位于 rules/docker-*.mk 中。以 docker-sonic-swss 为例,构建过程大致为:先基于 sonic-slave-* 构建容器编译源码,再把编译产物安装到 sonic-* 运行容器中,最后生成可加载的 Docker 镜像 tar 包。最终镜像中,这些 tar 包会被放入根文件系统,系统启动时由 sonic-docker 服务加载并启动。

构建容器与运行容器分离,是 SONiC 镜像体积可控、组件可独立升级的关键。修改某个组件时,通常只需重新构建对应 Docker 镜像,而不必全量重编。

四、子模块管理:版本锁定与更新

子模块管理是 SONiC 构建中最容易出问题的环节。常见操作包括:

  • git submodule update --init --recursive:初始化并检出所有子模块到主仓库记录的 commit。
  • git submodule foreach 'git checkout master && git pull':批量更新子模块到各自远端最新(仅用于开发调试,不建议直接用于发布构建)。
  • git add src/sonic-swss && git commit:在主仓库中更新子模块指针,锁定新版本。

需要特别注意:子模块的 commit 是“指针”,不是分支。主仓库记录的是某个具体 commit,而不是“master 最新”。因此,构建可复现的镜像必须依赖主仓库的 commit 和子模块指针,而不是依赖远端分支的当前状态。

五、构建流程与关键路径

下面用一张图概括从源码到镜像的关键路径,重点体现主仓库、子模块、Docker 镜像和最终镜像之间的关系。

SONiC 源码结构与构建路径 sonic-buildimage(主仓库) Makefile / rules/ / platform/ device/ / .gitmodules 锁定子模块 commit Git 子模块(src/) sonic-swss / sonic-sairedis sonic-utilities / sonic-linux-kernel 按 commit 检出,非分支 Docker 镜像构建 docker-sonic-swss docker-sonic-sairedis 编译容器 → 运行容器 submodule 编译打包 构建中间产物 Docker 镜像 tar 包 · 根文件系统 · 内核与驱动模块 · 平台插件 最终 SONiC 镜像 sonic-broadcom.bin / sonic-vs.bin / sonic-mellanox.bin 打包 关键:主仓库 commit + 子模块指针共同决定构建输入;Docker 镜像按组件独立构建,最终统一打包进根文件系统。

六、平台适配中的实际关注点

做平台适配时,最常改动的位置是 platform/<vendor>/ 和 device/<vendor>/<model>/。前者通常包含平台驱动、SAI 适配层、风扇控制、温度传感器等;后者包含端口配置、SFP 映射、电源和风扇描述。构建系统会根据 PLATFORM 变量选择对应目录,并把平台相关文件安装到镜像中。

例如,为某款 Broadcom 芯片交换机新增平台支持时,通常需要:在 platform/broadcom/ 下新增平台目录,提供 platform.json、sai.profile、led_proc_init.sh 等文件;在 device/ 下新增设备目录,提供 port_config.ini、fancontrol、thermalcontrol 等配置;然后在 rules/ 或平台 Makefile 中注册这些文件,使构建时能正确拷贝到镜像。完成后,用 make configure PLATFORM=broadcom 重新生成配置,再构建镜像验证。

子模块未初始化或版本不匹配,是构建失败的高频原因。遇到 fatal: not a git repository 或头文件缺失时,先检查 git submodule status,确认所有子模块都处于主仓库记录的 commit,再重新执行 make init。