首页
/ container 构建指南:从零编译、测试到与 Containerization 联调的完整开发流程

container 构建指南:从零编译、测试到与 Containerization 联调的完整开发流程

2026-09-05 16:48:41作者:羿妍玫Ivan

本文以 BUILDING.md 为核心,系统讲解如何在 Apple silicon Mac 上从源码构建 container 项目、运行单元与集成测试、重新生成 gRPC 协议代码,以及如何通过 Swift Package Manager 将本地的 Containerization、container-builder-shim 仓库挂接进构建流程。读完本篇,你将掌握从 make allmake install 的完整产物布局、隔离测试数据目录的用法、本地依赖编辑与回滚的规范操作,以及基于 launchd 标签对 XPC 助手进程挂接调试器的技巧。

环境要求

根据 BUILDING.md 的明确说明,构建 container 需要满足以下条件:

  • Mac(Apple silicon 芯片);
  • macOS 15 起步,推荐 macOS 26;
  • Xcode 26,并将其设置为命令行工具所使用的 active developer directory。

这里有一个与源码一致的重要佐证:Package.swift 声明了 swift-tools-version: 6.2platforms: [.macOS("15")],即 Swift 工具链版本和 macOS 部署目标分别对应上述要求。从依赖清单看,项目锁定了对核心依赖的精确版本约束,例如:

.package(url: "https://github.com/apple/containerization.git", exact: Version(stringLiteral: scVersion)), // scVersion = "0.43.0"
.package(url: "https://github.com/grpc/grpc-swift-2.git", from: "2.3.0"),
.package(url: "https://github.com/apple/swift-protobuf.git", from: "1.36.0"),

Package.swift 第 25、26 行的 builderShimVersion = "0.13.1"scVersion = "0.43.0" 决定了 builder shim 与 Containerization 的配套版本。

macOS 26 上的已知限制(vmnet 框架 bug):如果 container 的助手应用位于你的 DocumentsDesktop 目录下,网络创建会失败。规避方式有两种——使用 make install 后直接运行 /usr/local 下的 container 二进制;或者如果你偏好直接使用 make all 在项目 binlibexec 目录中生成的二进制,就把项目放到其他位置(如 ~/projects/container),直到该问题修复。

编译、测试与安装

在隔离的数据目录中构建并测试

BUILDING.md 给出的标准开发循环是:

rm -rf test-data
make APP_ROOT=test-data all test integration

这行命令的意图是:构建 container 及其后台服务,并在一个隔离的应用数据目录(test-data)中运行基础测试与集成测试,避免污染你日常使用的 ~/Library/Application Support/com.apple.container 数据。结合 Makefile 可以看出每一步的底层行为:

  1. make all:默认目标,等价于 all: container init-block
    • container 目标先执行 swift build(见 Makefile 第 101–104 行),再以 SUDO=(不使用 sudo)和 DEST_DIR=<项目根目录> 调用 install,把产物安装到项目自己的 bin/libexec/ 下;
    • init-block 目标调用 scripts/install-init.sh:当 containerization 处于 Swift 包的 edit 模式(本地路径依赖)时,会自动构建并导入 init 镜像(详见后文「本地依赖联调」一节)。
  2. make test:先执行共享构建阶段 build-testsswift build --build-tests),再运行 swift test --skip-build,并显式跳过 TestCLIIntegrationTests——即只跑单元测试。注意默认构建还附带 -Xswiftc -warnings-as-errors -Xswiftc -enable-testing 参数(由 Makefile 第 16–18 行的 BUILD_CONFIGURATION/WARNINGS_AS_ERRORS 变量控制),也就是警告会被当作错误,测试编译选项默认开启。
  3. make integration:执行流程定义在 MakefileRUN_INTEGRATION 宏(第 281–313 行)中,依次完成:
    • 停止 apiserver(bin/container system stop + scripts/ensure-container-stopped.sh);
    • 清空 APP_ROOT 下的应用数据(若设置 PRESERVE_KERNELS=true 则保留 kernels 目录);
    • bin/container --debug system start --timeout 60 --enable-kernel-install --app-root "$(APP_ROOT)" 启动系统;
    • 分三阶段跑集成测试:ImageWarmup/ 预热、非 *Serial 命名的套件并发执行(并行宽度默认取物理核数)、*Serial 命名的套件串行执行。测试通过环境变量 CONTAINER_APP_ROOTCONTAINER_CLI_PATHCLITEST_SCRATCH_ROOT 等与隔离目录关联。

构建成功后,bin/libexec/ 中的产物布局可从 Makefile 的 staging 目录规则(第 127–158 行)得到印证:CLI 与 API 服务位于 bin/,各插件位于 libexec/container/plugins/<plugin>/bin/ 与各自的 config.toml 资源文件,插件包括:

二进制 插件目录 职责(从目录结构推断)
container bin/ 命令行入口
container-apiserver bin/ 常驻 API 服务
container-runtime-linux plugins/container-runtime-linux/ Linux 容器运行时
container-network-vmnet plugins/container-network-vmnet/ 基于 vmnet 的网络
container-core-images plugins/container-core-images/ 核心镜像服务
machine-apiserver plugins/machine-apiserver/ 轻量虚拟机 API
k8s plugins/k8s/ Kubernetes 支持

这与 Package.swift 中定义的 7 个可执行 target(containercontainer-apiservercontainer-core-imagescontainer-network-vmnetcontainer-runtime-linuxmachine-apiserverk8s)一一对应。

安装到 /usr/local

make install

该目标需要管理员密码。从 Makefile 第 115–125 行可见其内部流程:先通过 installer-pkg 目标把产物暂存到 bin/<configuration>/staging/,对每个二进制做 ad-hoc 签名(codesign --force --sign -,其中 container-runtime-linuxcontainer-network-vmnet 分别附带 signing/container-runtime-linux.entitlementssigning/container-network-vmnet.entitlements 权限),再用 pkgbuild 生成 unsigned pkg(container-installer-unsigned.pkg),最后以 sudo installer -pkg ... -target / 安装。因此最终的二进制会落到 /usr/local/bin/usr/local/libexec

发布版(release)构建

BUILDING.md 说明 release 构建比 debug 构建有更好的性能,用法是:

BUILD_CONFIGURATION=release make all test integration
BUILD_CONFIGURATION=release make install

BUILD_CONFIGURATION 变量会同时传给 swift build -c <配置>swift test -c <配置>,并决定产物输出目录 bin/<configuration>/

重新生成 gRPC 协议代码

container 通过 gRPC 与负责从 Dockerfile 创建镜像的 builder 虚拟机通信,并且依赖特定版本的 grpc-swiftswift-protobuf。如果你修改了 container-builder-shim 项目中的 gRPC API,需要安装工具并重新生成本项目中的 gRPC 代码:

make protos

Protobuf.Makefile 揭示了 make protos 的完整步骤:

  1. 从 protobuf 官方 release 下载并解压 protoc 26.1(universal 二进制)到 .local/bin/protoc@26.1/
  2. 构建本项目内的两个 protoc 插件:protoc-gen-swiftprotoc-gen-grpc-swift-2
  3. .local/container-builder-shim 尚不存在,则以 --depth 1 克隆该仓库的 BUILDER_SHIM_VERSION 标签——该版本直接解析自 Package.swift 第 25 行的 let builderShimVersion = "0.13.1"
  4. pkg/api/Builder.proto 为输入,把生成物写入 Sources/ContainerBuild 目录(即 Builder.pb.swiftBuilder.grpc.swift 的来源):
$(PROTOC) $(LOCAL_DIR)/container-builder-shim/pkg/api/Builder.proto \
    --plugin=protoc-gen-grpc-swift=$(BUILD_BIN_DIR)/protoc-gen-grpc-swift-2 \
    --plugin=protoc-gen-swift=$(BUILD_BIN_DIR)/protoc-gen-swift \
    --proto_path=$(LOCAL_DIR)/container-builder-shim/pkg/api \
    --grpc-swift_out="Sources/ContainerBuild" \
    --grpc-swift_opt=Visibility=Public \
    --swift_out="Sources/ContainerBuild" \
    --swift_opt=Visibility=Public \
    -I.

生成后还会自动调用 make update-licenses 补齐许可证头。对应的构建端实现位于 Sources/ContainerBuild/Builder.swift 及生成的 Builder.grpc.swift

使用本地 Containerization 副本开发

当你的改动需要同时修改 Containerization 项目(或反过来)时,按 BUILDING.md 的流程操作:

  1. 把 Containerization 仓库克隆到 container 克隆目录的同级位置,并按其 README 的「prepare to build package」指引准备好构建环境。

  2. 进入 container 项目目录:

    cd container
    
  3. container 服务已在运行,先停止:

    bin/container system stop
    
  4. 用 Swift Package Manager 把依赖切换到本地 containerization 路径并更新 Package.resolved

    /usr/bin/swift package edit --path ../containerization containerization
    /usr/bin/swift package update containerization
    

    使用 Xcode 的注意事项:不要运行 swift package edit,而是临时把 Package.swift 中的版本化依赖:

    .package(url: "https://github.com/apple/containerization.git", exact: Version(stringLiteral: scVersion)),
    

    替换为本地路径依赖:

    .package(path: "../containerization"),
    

    注意:如果你已经(无论有意还是意外)运行过 swift package edit,必须先按下一节的方法恢复正常依赖,否则修改后的 Package.swift 不生效,项目可能构建失败。

  5. 如果你希望 container 使用 Containerization 中 vminit 子项目的改动,在运行时配置文件 ~/.config/container/config.toml 中设置 init 镜像:

    [vminit]
    image = "vminit:latest"
    

    这个配置项有明确的源码对应:Sources/ContainerPersistence/ContainerSystemConfig.swift 第 147–165 行的 VminitConfig 定义了默认值——当 Containerization 版本为 latest 时默认镜像是 vminit:latest,否则是 ghcr.io/apple/containerization/vminit:<tag>。把它覆盖为本地构建的 vminit:latest 后,运行时才会消费你本地改出的 init 镜像。

  6. 构建:

    make clean all
    

    此处 make allinit-block 环节尤为关键:scripts/install-init.sh 会通过 swift package show-dependencies --format json 探测 containerization 依赖状态,一旦发现版本是 unspecified(即处于本地 edit 模式),就会在本地 Containerization 目录执行 make init 构建 init 镜像、用 cctl images save 导出为 vminit:latest,停止系统后重新加载该镜像(bin/container i load -i /tmp/init.tar)。也就是说,本地 Containerization 联调时的 init 镜像构建是 make all 自动完成的。

  7. 重启服务:

    bin/container system stop
    bin/container system start
    

回滚到 Package.swift 中的正式依赖

  1. 如果之前使用了本地 init 文件系统,从 ~/.config/container/config.toml 中移除 init 覆盖(若 [vminit] 段没有别的镜像设置则整段删除)。

  2. 用 Swift Package Manager 恢复正常依赖并更新 Package.resolved(Xcode 用户则改回 Package.swift,不要使用 swift package unedit):

    /usr/bin/swift package unedit containerization
    /usr/bin/swift package update containerization
    
  3. 重新构建:

    make clean all
    
  4. 重启服务:

    bin/container system stop
    bin/container system start
    

使用本地 container-builder-shim 副本开发

要测试需要改动 container-builder-shim 项目的变更:

  1. 克隆 container-builder-shim 仓库并进入其目录。

  2. 完成改动后,构建自定义 builder 镜像、将其设为 ~/.config/container/config.toml 中的活动 builder 镜像,并删除现有 buildkit 容器以确保新镜像生效:

    container build -t builder .
    container rm -f buildkit
    

    ~/.config/container/config.toml 中添加:

    [build]
    image = "builder:latest"
    

    这一配置项对应 Sources/ContainerPersistence/ContainerSystemConfig.swift 第 80–113 行的 BuildConfig:默认 imageghcr.io/apple/container-builder-shim/builder:<builderShimVersion>(当前为 0.13.1),另有 rosetta(默认 true)、cpus(默认 2)、memory(默认 2048MB)三个可调参数,覆盖 image 后构建流程就会使用你本地打标签的 builder:latest

  3. 照常运行构建命令:

    container build ...
    

注意:如果你的修改把 builder 镜像改坏了,务必先重新构建并正确打好标签,再去尝试构建 container-builder-shim,否则会陷入用坏镜像构建坏镜像的循环。

调试 XPC 助手进程

container 的后台服务(runtime、network、apiserver 等)是 launchd 托管的 XPC 助手进程,可以用 launchd 服务标签挂接调试器。

  1. 查找 launchd 服务标签:

    % container system start
    % container run -d --name test debian:bookworm sleep infinity
    test
    % launchctl list | grep container
    27068   0       com.apple.container.container-network-vmnet.default
    27072   0       com.apple.container.container-core-images
    26980   0       com.apple.container.apiserver
    27331   0       com.apple.container.container-runtime-linux.test
    
  2. 设置环境变量 CONTAINER_DEBUG_LAUNCHD_LABEL 为要调试的服务标签后重启系统。标签以该值为前缀的服务将等待调试器:

    % export CONTAINER_DEBUG_LAUNCHD_LABEL=com.apple.container.container-runtime-linux.test
    % container system start # 只有 com.apple.container.container-runtime-linux.test 等待调试器
    
    % export CONTAINER_DEBUG_LAUNCHD_LABEL=com.apple.container.container-runtime-linux
    % container system start # 所有以 com.apple.container.container-runtime-linux 开头的服务都等待调试器
    

    前缀匹配的行为在源码中有直接实现:Sources/ContainerPlugin/LaunchPlist.swift 第 66–79 行的 getWaitForDebugger 会检查 label == debugTarget || label.starts(with: debugTarget + "."),命中则在生成的 launchd plist 中写入 WaitForDebugger = true(对应 waitForDebugger 字段与 WaitForDebugger plist 键)。

  3. 触发服务启动的命令(如 container run)会挂起,此时再附上调试器:

    % container run -it --name test debian:bookworm
    ⠧ [6/6] Starting container [0s] # 服务在等待调试器,命令挂起
    

安装 pre-commit 钩子

make pre-commit

该目标在 git commit 时保证改动具备正确的格式与许可证头。对照 Makefile 第 395–404 行,它会把 scripts/pre-commit.fmt 复制到 .git/hooks/,并在既有 pre-commit 钩子中追加一行对 hooks/pre-commit.fmt 的引用(支持 PRECOMMIT_NOFMT 环境变量),同时通过 scripts/ensure-hawkeye-exists.sh 确保 hawkeye 工具就绪。日常格式化与校验也可以直接用 make fmt(swift-format + 更新许可证头)和 make check(格式 lint + 许可证头检查)两个目标完成。

附:常用 make 目标速查

结合 MakefileProtobuf.Makefile,与日常开发相关的目标汇总如下(BUILDING.md 只覆盖了其中一部分):

目标 作用
all 构建全部产物并安装到项目 bin/libexec/,同时构建 init-block
build / cli 仅编译 / 仅构建 CLI 并拷贝到 bin/container
test 运行单元测试(跳过 TestCLIIntegrationTests
integration 在隔离数据目录中启动系统并运行集成测试(warmup → 并发 → 串行三阶段)
install 签名、打包 pkg 并通过 sudo installer 安装到 /usr/local
release 以 release 配置执行 all
protos 下载 protoc、构建插件、重新生成 gRPC 代码
pre-commit 安装格式与许可证头检查的 pre-commit 钩子
fmt / check 应用 / 校验 swift-format 格式与许可证头
install-kernel 停止系统后以 --enable-kernel-install 启动以安装内核
clean 清理 bin/libexec/swift package clean
coverage / coverage-unit / coverage-integration 代码覆盖率收集与报告(llvm-cov)

小结

container 的开发工作流可以归纳为四条主线:日常开发用 make APP_ROOT=test-data all test integration 在隔离目录完成构建与验证;交付安装用 make install 走签名与 pkg 安装链路;协议变更用 make protos 对齐 container-builder-shim 版本(0.13.1);跨仓库联调则依赖 Swift 包的 edit/unedit 机制,并配合 ~/.config/container/config.toml 中的 [vminit][build] 镜像覆盖、scripts/install-init.sh 的自动 init 镜像构建,以及 CONTAINER_DEBUG_LAUNCHD_LABEL 提供的 XPC 进程断点能力。所有命令与配置项均可在 BUILDING.mdMakefileProtobuf.MakefilePackage.swift 中逐项查证。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384