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 构建中最容易出问题的环节。常见操作包括:
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 镜像和最终镜像之间的关系。
六、平台适配中的实际关注点
做平台适配时,最常改动的位置是 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。