Skip to content

持续构建、发布与部署 ​

在知识图谱中的位置:模块四 · 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) ​

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 --verbose

3.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 ​

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.0

4.2 最佳实践 ​

  1. CI 三层:push → action-ros-ci(构建+单元+集成+lint);merge → 打 tag 触发 bloom 发布;发布后 → 构建农场自动跑 devel/source/binary。
  2. 制品版本语义化:tag 用 X.Y.Z,package.xml 版本与 tag 一致,changelog 用 catkin_generate_changelog 自动生成。
  3. 可复现构建:锁定 base 镜像 digest、rosdep 依赖固定版本、CI 环境与目标机 distro 完全一致。
  4. 镜像瘦身:多阶段构建 + --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-ciGitHub Actions CIros-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. 参考资料 ​


8. 学习路径 ​

  1. 入门:一个包跑通 rosdep install + colcon build + colcon test,理解依赖解析。
  2. 进阶:接入 action-ros-ci,让 push 自动构建+测试;加 pre-commit 钩子。
  3. 发布:用 bloom 把包发布到 Jazzy,在 index.ros.org 上看到 ros-jazzy-<pkg>。
  4. 部署:写多阶段 Dockerfile,把应用打成 ros:jazzy-ros-base overlay 镜像并部署。
  5. 串联:结合 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