Appearance
持续构建、发布与部署
在知识图谱中的位置:模块四 · 04_工程实践 · 第 4 节 难度:⭐⭐⭐ | 前置知识:模块二·核心模块(colcon/package.xml/rosdep)、02_ROS2测试体系.md(CI/测试)、Linux 打包/Docker 基础
1. 概述
"代码能跑"到"机器人能量产部署"之间,隔着一整条工程流水线:本地构建 → 静态检查 → 自动测试(CI)→ 制品打包 → 目标机部署 → 版本管理。ROS 2 生态为每个环节都提供了成熟工具:colcon 构建、ament_lint + pre-commit 检查、action-ros-ci 自动 CI、bloom 打 deb 包、rosdep 管依赖、build.ros2.org 构建农场、Docker 多阶段裁剪。
本节串起这条流水线,并回答量产的版本策略问题:发行版冻结(distro freeze)还是滚动(rolling),以及应用侧如何锁定依赖。
2. 核心概念
2.1 从开发到上机的完整流水线
2.2 构建(colcon)与依赖(rosdep)
colcon 是 ROS 2 的元构建工具,按 package.xml 的依赖拓扑顺序调用底层 CMake/ament 构建每个包。rosdep 根据 package.xml 声明的依赖,自动从 apt 安装系统依赖(rosdep install --from-paths src --ignore-src -r -y),保证构建环境可复现。
2.3 制品打包(bloom)与构建农场(build farm)
- bloom(https://github.com/ros-infrastructure/bloom)是官方发布工具,把源码包转成 deb 二进制包 + 源码包 + rosdistro 元数据,是二进制包进入 apt 仓库的入口。
- 构建农场(https://build.ros2.org,Jenkins)负责在不同 Ubuntu/架构上真实构建+测试发布的包,产出放进 https://index.ros.org 供
apt install ros-jazzy-*安装。
2.4 版本策略:distro 冻结 vs rolling
ROS 2 同时维护两条线:
- 固定发行版(distro,如 Humble/Jazzy):API 冻结、只修 bug,适合量产——上机版本可长期稳定复现。
- rolling:持续滚动最新开发,适合尝鲜/上游开发,不适合量产。
2.5 Docker 化部署
用官方镜像(ros:jazzy-ros-base)做基底,多阶段构建把"编译环境"和"运行环境"分离,最终镜像只含运行时依赖,体积可控、可复现。
3. 技术原理/方法
3.1 本地构建与检查
bash
# 依赖安装
rosdep update
rosdep install --from-paths src --ignore-src -r -y
# 构建(开发期常用 symlink-install,改 Python/配置即时生效)
colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=RelWithDebInfo
# 生产/发布用 merge-install(单一 install 前缀,便于打包)
colcon build --merge-install
# 测试 + 结果
colcon test && colcon test-result --verbose3.2 pre-commit + ament_lint:把检查前置到 commit
pre-commit(https://pre-commit.com)在本地 git commit 前自动跑 lint/格式化,与 ament_lint 结合:
yaml
# .pre-commit-config.yaml(示例)
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks: [{id: trailing-whitespace}, {id: end-of-file-fixer}]
- repo: local
hooks:
- id: ament-lint
name: ament_lint
entry: colcon test --packages-select my_pkg --ctest-args -L linter
language: system
pass_filenames: false上例为示例配置,具体 hook 命令请按项目调整;ament_lint 机制见 02_ROS2测试体系.md。
3.3 CI:ros-tooling/action-ros-ci
社区标准 Action(https://github.com/ros-tooling/action-ros-ci),底层就是 colcon build + colcon test,支持指定 distro、自动 rosdep。完整工作流见 02_ROS2测试体系.md 第 3.6 节。官方 ROS 2 仓库自身用 https://github.com/ros2/ci 做大规模 CI。
3.4 制品打包:bloom
bash
# 一次性初始化 release 仓库(首次)
bloom-release --rosdistro jazzy --track jazzy my_pkg
# 常用分步流程(首次)
catkin_generate_changelog # 生成 changelog
catkin_prepare_release # 打 tag
bloom-generate rosdebian --ros-distro jazzy # 生成 debian/ 规则
fakeroot debian/rules binary # 本地打 deb 验证
bloom-generate rosdistro # 生成 rosdistro 提交官方流程文档:https://docs.ros.org/en/rolling/How-To-Guides/Releasing-a-ROS-2-package-with-bloom.html。发布成功后,构建农场自动在 build.ros2.org 上构建,二进制包进入 index.ros.org。
ament_release是 ROS 2 早期的发布辅助脚本,当前标准发布工具是 bloom,新项目统一用 bloom。
3.5 构建农场与 index.ros.org
- 构建农场(https://build.ros2.org):Jenkins 驱动的官方构建集群,对每个 distro 做 devel(源码最新)/source(tag)/binary(deb)三类作业,跑完整测试。
- index.ros.org(https://index.ros.org):ROS 二进制包仓库,
apt install ros-jazzy-my-pkg的来源,也是查"某个包在哪个 distro 可用"的入口。 - 个人包也可进官方索引(通过 bloom 发布后进入),测试见 https://docs.ros.org/en/rolling/Tutorials/Intermediate/Testing/BuildFarmTesting.html。
3.6 Docker 化部署:多阶段构建
dockerfile
# 示例 Dockerfile(多阶段)
FROM ros:jazzy-ros-base AS builder
RUN apt-get update && rosdep update \
&& apt-get install -y python3-colcon-common-extensions
WORKDIR /ws/src
COPY ./src ./src
RUN . /opt/ros/jazzy/setup.sh \
&& rosdep install --from-paths . --ignore-src -r -y \
&& colcon build --merge-install
FROM ros:jazzy-ros-base
RUN apt-get update && rosdep update \
&& rosdep install --from-paths /ws/src --ignore-src -r -y --simulate \
&& apt-get install -y --no-install-recommends <运行时依赖>
COPY --from=builder /ws/install /ws/install
CMD ["bash", "-c", ". /ws/install/setup.sh && ros2 launch my_pkg my_launch.py"]官方镜像定义见 https://github.com/osrf/docker_images(
ros:jazzy-ros-base是标准 tag)。上例为示例,需按实际依赖调整。
3.7 版本策略落地
- 量产基座锁 distro:整机统一一个 LTS distro(如 Jazzy),不追 rolling,避免 API 漂移。
- 应用侧锁依赖:
package.xml用version_gte/version_lt区间 +<build_depend>foo</build_depend>锁定接口,配合 rosdep 的版本约束;内部库用固定 tag/submodule 而非浮动分支。 - 制品不可变:目标机只装已发布的 deb(或固定 digest 的镜像),禁止现场
pip install漂移。
4. 实践指南
4.1 可执行命令(一键流水线骨架)
bash
# 本地一键:装依赖 → 构建 → 测试 → lint
rosdep install --from-paths src --ignore-src -r -y
colcon build --symlink-install
colcon test && colcon test-result --verbose
colcon test --packages-select my_pkg --ctest-args -L linter # lint
# 发布一个包到 Jazzy
bloom-release --rosdistro jazzy --track jazzy my_pkg
# 目标机部署(二进制)
sudo apt update && sudo apt install ros-jazzy-my-pkg
# 或 overlay 镜像部署
docker run --rm --network host my_pkg:jazzy-1.0.04.2 最佳实践
- CI 三层:push →
action-ros-ci(构建+单元+集成+lint);merge → 打 tag 触发 bloom 发布;发布后 → 构建农场自动跑 devel/source/binary。 - 制品版本语义化:tag 用
X.Y.Z,package.xml版本与 tag 一致,changelog 用catkin_generate_changelog自动生成。 - 可复现构建:锁定 base 镜像 digest、
rosdep依赖固定版本、CI 环境与目标机 distro 完全一致。 - 镜像瘦身:多阶段构建 +
--no-install-recommends,只装运行时依赖,镜像体积可缩小数倍。
4.3 陷阱
- 发布时测试跑挂:
BUILD_TESTING默认开启,构建农场会跑全部测试,测试里有硬编码路径/网络/GPU 依赖 → 发布失败。发布前本地colcon test全绿。 --symlink-install打不了干净包:发布/镜像务必用--merge-install,symlink 会把开发路径带进制品。- rosdep 依赖名拼错:
package.xml里<depend>写错 rosdep key,CI 装依赖阶段就挂——用rosdep resolve校验 key 合法性。 - apt 版本落后于源码:
apt install ros-jazzy-*装的是二进制农场版本,可能落后于你的 tag;量产后门用固定 deb 版本,别混用 apt 与源码 overlay。
4.4 调优
- 构建加速:
colcon build --parallel-workers N+ccache(-DCMAKE_CXX_COMPILER_LAUNCHER=ccache)大幅缩短重复构建。 - CI 缓存:缓存
build/和 rosdep 缓存,二次构建命中缓存从分钟级降到秒级。 - 镜像分层缓存:Dockerfile 把"装系统依赖"和"拷贝源码构建"分层,改源码只重建上层。
5. 方案对比
| 部署方案 | 可复现性 | 隔离性 | 更新方式 | 适用场景 |
|---|---|---|---|---|
| apt(index.ros.org) | 高(固定版本) | 低(系统级) | apt upgrade | 量产整机、单 distro |
| Docker overlay | 最高(镜像 digest) | 高(容器) | 换镜像 tag | 多版本并存、快速回滚 |
| 源码 overlay(工作区) | 中 | 低 | git pull 重建 | 开发/调试 |
| snap/AppImage | 高 | 高 | 应用级更新 | 分发到用户(非 ROS 原生) |
绝对不适用场景:当目标机是资源极度受限的嵌入式 MCU/无 MMU 设备(如 STM32 级,跑 micro-ROS)时,不要套用本节 apt/Docker 的完整流水线——容器和完整 deb 依赖体系在这类设备上是灾难,应使用 micro-ROS 的交叉编译 + 静态链接 + OTA 固件更新路径(参见 06_仿真与硬件生态.md 的硬件分层)。
6. 工具链
| 工具/包 | 用途 | 来源 |
|---|---|---|
colcon | 元构建/测试 | 官方 |
rosdep | 系统依赖安装 | 官方 |
ament_lint + pre-commit | 静态检查 + commit 钩子 | ament/ament_lint / pre-commit.com |
ros-tooling/action-ros-ci | GitHub Actions CI | ros-tooling |
bloom | 打 deb/发布 | ros-infrastructure/bloom |
build.ros2.org | 官方构建农场 | https://build.ros2.org |
index.ros.org | 二进制包仓库 | https://index.ros.org |
osrf/docker_images | 官方 Docker 镜像 | https://github.com/osrf/docker_images |
7. 参考资料
- ROS 2 官方文档(Jazzy):https://docs.ros.org/en/jazzy/ ✅
- 用 bloom 发布 ROS 2 包:https://docs.ros.org/en/rolling/How-To-Guides/Releasing-a-ROS-2-package-with-bloom.html ✅
- bloom 仓库:https://github.com/ros-infrastructure/bloom ✅
- 构建农场测试:https://docs.ros.org/en/rolling/Tutorials/Intermediate/Testing/BuildFarmTesting.html ✅
- 构建农场(Jenkins):https://build.ros2.org ✅
- 二进制包仓库:https://index.ros.org ✅
- ROS 2 CI 仓库:https://github.com/ros2/ci ✅;社区 Action:https://github.com/ros-tooling/action-ros-ci ✅
- 官方 Docker 镜像定义:https://github.com/osrf/docker_images ✅
- pre-commit:https://pre-commit.com ✅
8. 学习路径
- 入门:一个包跑通
rosdep install+colcon build+colcon test,理解依赖解析。 - 进阶:接入
action-ros-ci,让 push 自动构建+测试;加pre-commit钩子。 - 发布:用 bloom 把包发布到 Jazzy,在 index.ros.org 上看到
ros-jazzy-<pkg>。 - 部署:写多阶段 Dockerfile,把应用打成
ros:jazzy-ros-baseoverlay 镜像并部署。 - 串联:结合 02_ROS2测试体系.md 完善 CI 测试分层,结合 03_诊断监控与故障定位.md 做部署后监控,进入 05_导航与运动控制栈.md 理解三大栈如何打包部署。
9. 核心面试三问
Q1:为什么量产要锁发行版(distro freeze),而不是用 rolling?两者如何取舍?
答:rolling 的 API/依赖持续漂移,你今天构建通过的应用,下个月可能因上游接口变更而编译失败或行为改变,无法保证量产一致性。distro(如 Jazzy LTS)API 冻结、只修 bug,能保证"上机版本可长期稳定复现"。取舍:量产基座锁 LTS distro;需要新特性时通过内部 overlay 工作区或等下一个 distro 迁移,绝不把整机切成 rolling。
Q2:你的一个包已经推到 GitHub,怎么让它变成 apt install ros-jazzy-<pkg> 可装?描述完整链路。
答:① 代码 tag 版本号 +
catkin_generate_changelog生成 changelog;②bloom-release --rosdistro jazzy --track jazzy <pkg>生成 release 仓库(debian 规则 + rosdistro 元数据);③ 提交到 rosdistro 后,官方构建农场 build.ros2.org 自动在不同 Ubuntu 上真实构建+跑测试;④ 通过后二进制包进入 index.ros.org,用户即可apt install ros-jazzy-<pkg>。关键:发布前本地colcon test必须全绿,否则构建农场测试失败,包无法发布。
Q3:Docker 化部署里,多阶段构建解决什么问题?"编译镜像"和"运行镜像"为什么要分开?
答:多阶段构建把"需要完整编译工具链 + 全部头文件 + 构建产物"的编译阶段,与"只要运行时动态库 + 资源"的运行阶段分离,最终镜像只
COPY --from=builder运行时产物。好处:① 镜像体积大幅缩小(不带编译器、build 依赖、中间对象文件);② 攻击面减小(不含 gcc/头文件等);③ 分层缓存——改源码只重建上层,系统依赖层不变。量产机器人存储和启动时间都受益。
下一步:05_导航与运动控制栈.md · 模块导读:README.md