제4부 빌드·패키징·배포
4.9 릴리스 — bloom·rosdistro·빌드팜
4.9.1 이 장에서 배우는 것 / 선수 지식
sudo apt install ros-jazzy-nav2-bringup 한 줄이 동작하기까지, 누군가는 그 패키지를 릴리스했다. 이 장은 그 파이프라인을 연다.
두 부류의 독자를 위한 장이다. ① 공개 패키지를 릴리스하려는 사람 — 오픈소스 패키지를 ROS 생태계에 등록하는 절차. ② 사내 배포 체계를 세우려는 사람 — 공개 빌드팜을 쓰지 않고 자체 apt 저장소를 운영하는 방법. 후자가 실무에서 더 흔하다.
배우는 것:
- 릴리스 파이프라인 전체 — 소스에서 apt까지의 경로.
- 버전과 CHANGELOG —
catkin_prepare_release워크플로. - bloom — 릴리스 저장소와 debian 패키징 생성.
- rosdistro — 배포판 인덱스에 등록하기.
- 빌드팜 — 어떤 작업들이 실행되는가.
- 사내 릴리스 — 자체 빌드팜·apt 저장소 구축 선택지.
- 릴리스 정책 — 버전 규칙·지원 기간.
선수 지식: 4.4(package.xml·버전), 4.6(rosdep 키), 4.7(소스·재현성), 4.5(colcon).
정직: bloom·빌드팜 절차는 도구 버전과 rosdistro 정책에 따라 변한다. 아래 명령·워크플로는 일반적으로 통용되는 형태이며, 공식 릴리스를 실제로 수행할 때는 ROS 2 문서와 rosdistro 기여 가이드의 현재 내용을 따라야 한다
(확인 필요). 이 환경에서 실행 검증하지 않았다(목차 3).
검증 상태(추가): 130번째 회차(151차 갭 점검)까지 이 장은 실행 검증이 전혀 없었다. 130번째 회차가 GitHub 인증·rosdistro PR이 필요 없는 부분 —
bloom-generate rosdebian으로 debian 패키징을 생성하고 실제로.deb를 빌드·설치·실행까지 전 과정을 실제로 실행해 본문에 (실행 확인) 표기를 처음 추가했다 — 4.9.4·4.9.7을 보라. rosdistro PR·빌드팜(4.9.5·4.9.6, 실제 GitHub 권한과 공개 인프라가 필요)은 이번 회차도 다루지 못해 여전히 미검증이다.
4.9.2 릴리스 파이프라인 전체
| 단계 | 주체 | 산출물 |
|---|---|---|
| 버전·CHANGELOG | 개발자 | 태그·CHANGELOG.rst |
| bloom | 개발자 | 릴리스 저장소의 debian 브랜치 |
| rosdistro PR | 개발자 → 검토자 | distribution.yaml 항목 |
| 빌드팜 | 자동 | .deb 패키지 |
| apt 저장소 | 자동 | 공개 배포 |
두 개의 저장소가 등장한다는 점이 혼동을 낳는다:
| 저장소 | 내용 | 누가 편집 |
|---|---|---|
개발 저장소 (my_pkg) | 소스 코드 | 개발자 |
릴리스 저장소 (my_pkg-release) | debian 패키징 메타데이터 | bloom이 자동 생성 |
릴리스 저장소는 수동으로 편집하지 않는다. bloom이 관리하며, 개발자는 개발 저장소만 다룬다.
4.9.3 버전과 CHANGELOG
CHANGELOG.rst가 릴리스의 기록이다.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Changelog for package my_robot_driver
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1.2.0 (2026-08-13)
------------------
* 시리얼 재연결 로직 추가
* CAN 드라이버의 타임아웃 처리 수정 (#42)
* Contributors: Alice, Bob
1.1.0 (2026-06-01)
------------------
* 초기 릴리스
* Contributors: Alice
도구가 커밋 로그에서 자동 생성한다:
# ① 커밋 로그에서 CHANGELOG 초안 생성
catkin_generate_changelog
# ② 초안을 사람이 다듬는다 (중요)
$EDITOR CHANGELOG.rst
# ③ 버전 갱신하고 태그 생성
catkin_prepare_release
# ④ 푸시
git push --follow-tags
catkin_prepare_release가 하는 일:
| 작업 | 설명 |
|---|---|
package.xml의 <version> 증가 | 대화형으로 major/minor/patch 선택 |
CHANGELOG.rst의 forthcoming을 버전·날짜로 확정 | |
| 커밋 생성 | |
| git 태그 생성 | 1.2.0 형태 |
함정:
setup.py의version은 자동으로 갱신되지 않는다(4.3.6). Python 패키지에서는 수동으로 맞춰야 한다. 릴리스 체크리스트에 반드시 넣는다.
함정: 워크스페이스에 여러 패키지가 있으면 버전이 함께 갱신된다.
catkin_prepare_release는 저장소 안의 모든 패키지를 같은 버전으로 맞추는 것이 기본이다(확인 필요: 옵션으로 조정 가능). 패키지마다 독립적인 버전을 원하면 저장소를 나누거나 옵션을 확인해야 한다.
CHANGELOG를 다듬는 것이 중요하다. 자동 생성된 것은 커밋 메시지 나열이라 사용자에게 유용하지 않다. "무엇이 바뀌었고 사용자가 무엇을 해야 하는가" 를 적어야 한다 — 특히 호환을 깨는 변경(1.2.8).
2.0.0 (2026-09-01)
------------------
* **호환성 깨짐**: ``MotorState`` 메시지에 ``temperature`` 필드 추가.
이 패키지를 쓰는 모든 노드를 재빌드해야 한다 (1.3.5 type hash 변경).
* 마이그레이션: ...
4.9.4 bloom
sudo apt install python3-bloom
# 릴리스 실행
bloom-release --rosdistro jazzy --track jazzy my_robot_driver
bloom이 하는 일:
최초 실행 시 대화형으로 설정을 묻는다:
| 항목 | 내용 |
|---|---|
| 릴리스 저장소 URL | github.com/ros2-gbp/my_pkg-release 형태 |
| upstream 저장소 URL | 개발 저장소 |
| upstream 브랜치·태그 | 대개 태그에서 |
| 버전 | :{version}(package.xml에서 자동) |
이 설정은 릴리스 저장소의 tracks.yaml에 저장되어 다음 릴리스부터 재사용된다.
생성되는 debian 메타데이터:
debian/control— 패키지 이름(ros-jazzy-my-robot-driver), 의존(rosdep으로 변환된 것 — 4.6).debian/rules— 빌드 절차.debian/changelog—CHANGELOG.rst에서 변환.
함정: 패키지 이름 변환 규칙에 주의하라.
my_robot_driver→ros-jazzy-my-robot-driver. 밑줄이 하이픈이 된다. rosdep 키나 문서에서 이름을 쓸 때 혼동하기 쉽다.
정직: bloom은 GitHub 인증·릴리스 저장소 권한을 요구하며, 최초 설정이 까다롭다. 공개 릴리스는 대개
ros2-gbp조직에 릴리스 저장소를 만드는 절차를 거친다(확인 필요: 현재 정책). 상세는 ROS 2 문서의 릴리스 가이드를 따라야 한다.
실행 확인(151차, Jazzy) —
bloom-generate rosdebian이 생성하는 debian 메타데이터를 실제 패키지로 직접 대조했다. 이 절의 예시 이름 그대로(my_robot_driver,rclcpp·std_msgs의존,ament_cmake) 최소 패키지를 만들고bloom-generate rosdebian --ros-distro jazzy를 실행했다(GitHub 인증·릴리스 저장소 없이, rosdistro 인덱스 읽기만 필요 — 4.9.7이 말하는 "bloom-generate만 쓰는 방법"이 바로 이 경로다). 생성된debian/control을 그대로 옮기면:Source: ros-jazzy-my-robot-driver Build-Depends: debhelper (>= 9.0.0), ros-jazzy-ament-cmake, ros-jazzy-rclcpp, ros-jazzy-std-msgs Package: ros-jazzy-my-robot-driver Depends: ${shlibs:Depends}, ${misc:Depends}, ros-jazzy-rclcpp, ros-jazzy-std-msgs패키지 이름 변환(
my_robot_driver→ros-jazzy-my-robot-driver)과 rosdep 의존 변환(rclcpp→ros-jazzy-rclcpp,std_msgs→ros-jazzy-std-msgs, 빌드 의존ament_cmake→ros-jazzy-ament-cmake) 모두 위 함정·본문 서술과 정확히 일치했다.debian/changelog도CHANGELOG.rst의 항목("Test release for gap151...")을 그대로 옮겨 실었다. 원시 데이터는tools/gap_harness/gap151/data/debian_control·debian_changelog·debian_rules. 4.9.7을 이어 실제 빌드까지 확인했다 — 그쪽을 보라. 목차 3.
4.9.5 rosdistro
distribution.yaml이 배포판의 패키지 인덱스다.
# rosdistro/jazzy/distribution.yaml 의 한 항목
my_robot_driver:
doc:
type: git
url: https://github.com/myorg/my_robot_driver.git
version: jazzy
release:
tags:
release: release/jazzy/{package}/{version}
url: https://github.com/ros2-gbp/my_robot_driver-release.git
version: 1.2.0-1
source:
type: git
url: https://github.com/myorg/my_robot_driver.git
version: jazzy
status: developed
bloom이 이 항목을 갱신하는 PR을 자동으로 만들어 준다. 검토자가 병합하면 빌드팜이 인식한다.
| 필드 | 의미 |
|---|---|
release.version | 릴리스된 버전(-1은 debian 리비전) |
source.version | 개발 브랜치 |
status | developed·maintained·end-of-life |
rosdistro는 다른 것도 담는다:
| 파일 | 내용 |
|---|---|
distribution.yaml | 패키지 인덱스 |
rosdep/base.yaml·python.yaml | rosdep 규칙(4.6.2) |
index-v4.yaml | 배포판 목록 |
rosdep 규칙 기여도 여기로 한다 — 공개 라이브러리의 rosdep 키가 없다면 여기에 PR을 보낸다(4.6.7·15.5).
4.9.6 빌드팜
rosdistro 병합 후 자동으로 실행되는 작업들이다.
| 작업 | 하는 일 | 산출물 |
|---|---|---|
| sourcedeb | 릴리스 저장소에서 소스 deb 생성 | .dsc·.tar.gz |
| binarydeb | 아키텍처별 컴파일 | .deb |
| devel | 개발 브랜치를 빌드·테스트(회귀 감시) | 테스트 결과 |
| pull request | PR을 빌드·테스트 | 검증 결과 |
| doc | API 문서 생성 | 문서 사이트 |
빌드팜이 실패하는 흔한 원인:
| 원인 | 설명 |
|---|---|
| 의존 선언 누락 | 개발 머신엔 있는데 클린 환경엔 없음(4.4.3) |
| rosdep 키 없음 | 새 시스템 의존을 등록 안 함(4.6.7) |
| 테스트 실패 | 로컬과 다른 환경 |
| 아키텍처 의존 버그 | arm64 빌드만 실패(4.8.7) |
| 빌드 시간 초과 | 규모가 큰 패키지 |
| 의존 패키지가 아직 릴리스 안 됨 | 순서 문제 |
견해: 빌드팜 실패의 대부분은 로컬에서 예방할 수 있다. 클린 컨테이너에서
rosdep install+colcon build+colcon test를 실행하면(4.4.9) 의존 누락과 테스트 실패를 미리 검출한다. 릴리스 전에 이것을 CI로 자동화하라(4.10).
상태 확인:
# 빌드 상태 페이지(build.ros.org)에서 확인
# 패키지별 빌드 결과·로그를 볼 수 있다
릴리스 후 반영까지 시간이 걸린다 — 빌드·동기화 주기에 따라 수 시간~며칠(확인 필요: 현재 동기화 정책). apt install이 바로 안 되는 것이 정상이다.
4.9.7 사내 릴리스 — 자체 배포 체계
실무에서 더 흔한 경우다. 사내 패키지를 공개 빌드팜에 등록할 수는 없다.
선택지 셋:
① 컨테이너 이미지만 (가장 단순)
docker buildx build --platform linux/arm64 -t registry.internal/my_robot:1.2.0 --push .
- 장점: 별도 인프라 최소. 4.8.6의 배포와 통합.
- 단점: 패키지 단위 업데이트 불가(전체 이미지 교체).
- 적합: 팀이 작고 로봇 구성이 단일할 때.
② 자체 apt 저장소
# deb 생성 (bloom 없이 직접)
cd my_robot_driver
bloom-generate rosdebian --ros-distro jazzy
fakeroot debian/rules binary
# → ../ros-jazzy-my-robot-driver_1.2.0-1_arm64.deb
# 저장소 구축 (aptly · reprepro 등)
aptly repo create -distribution=noble internal-ros
aptly repo add internal-ros ../*.deb
aptly publish repo internal-ros
# 로봇에서
echo "deb [trusted=yes] http://apt.internal/ noble main" | \
sudo tee /etc/apt/sources.list.d/internal-ros.list
sudo apt update && sudo apt install ros-jazzy-my-robot-driver
- 장점: 패키지 단위 업데이트·롤백. 기존 OS 관리와 일관.
- 단점: 저장소 운영·서명·의존 관리 부담.
- 적합: 로봇 종류가 여럿이고 구성이 다양할 때.
③ 자체 빌드팜
ROS 빌드팜 소프트웨어를 사내에 구축한다.
- 장점: 공개 빌드팜과 동일한 워크플로.
- 단점: 구축·운영 비용이 매우 크다.
- 적합: 패키지 수십~수백 개, 전담 인력이 있는 조직.
견해: 대부분의 팀에게 ①로 시작하고, 필요해지면 ②로 가는 것이 맞다. ③은 규모가 확실히 커진 뒤에 검토할 일이며, 그 전에는 투자 대비 효과가 낮다. 로봇이 여러 종류이거나 부분 업데이트가 필요해지는 시점이 ①→② 전환의 신호다.
bloom-generate만 쓰는 방법이 유용하다 — 릴리스 저장소·rosdistro 없이 deb만 만든다.
bloom-generate rosdebian --ros-distro jazzy
fakeroot debian/rules binary
이것만으로 사내 배포에 필요한 deb를 얻을 수 있고, bloom의 전체 워크플로를 감당할 필요가 없다.
실행 확인(151차, Jazzy) —
bloom-generate+fakeroot debian/rules binary+apt install까지 전 과정을 실제로 실행해 실제 설치·실행 가능한 패키지가 나오는지 확인했다. 4.9.4에서 생성한debian/으로fakeroot debian/rules binary를 실행하니ros-jazzy-my-robot-driver_1.2.0-0noble_amd64.deb(10.6KB)가 정상적으로 만들어졌다(dh_shlibdeps가 ROS 공유 라이브러리 이름을 못 알아보는 경고 몇 줄을 빼면 별다른 문제 없이 완료).dpkg -I로 확인한 실제Depends:줄은libc6 (>= 2.34), libgcc-s1 (>= 3.3.1), libstdc++6 (>= 13.1), ros-jazzy-rclcpp, ros-jazzy-std-msgs로 — 시스템 공유 라이브러리 의존과 ROS 의존이 자동으로 함께 채워졌다.dpkg -c로 본 설치 경로는/opt/ros/jazzy/lib/my_robot_driver/driver_node·/opt/ros/jazzy/share/my_robot_driver/...등 —apt install ros-jazzy-*로 설치한 여느 공식 패키지와 완전히 동일한 레이아웃이었다.
apt-get install ./ros-jazzy-my-robot-driver_1.2.0-0noble_amd64.deb로 실제 설치했고,ros2 pkg list에my_robot_driver가 나타났으며,ros2 run my_robot_driver driver_node가 실제로 기동해[INFO] [driver_node]: driver_node up로그를 냈다. GitHub 인증도 릴리스 저장소도 rosdistro PR도 전혀 필요 없었다 — 4.9.7의 "사내 배포에 필요한 deb를 얻을 수 있고, bloom의 전체 워크플로를 감당할 필요가 없다"는 서술이 시작부터 끝까지(패키징 생성 → 빌드 → 설치 → 실행) 실측으로 확인됐다. 원시 로그는tools/gap_harness/gap151/logs/fakeroot_debian_rules_binary.log·apt_install_deb.log, 원시 데이터는tools/gap_harness/gap151/data/deb_inspect.txt. 목차 3.
4.9.8 릴리스 정책
팀이 정해 두어야 할 것들이다.
버전 규칙:
| 변경 | 버전 |
|---|---|
| 호환 깨짐(메시지 변경·API 제거) | major |
| 기능 추가(호환 유지) | minor |
| 버그 수정 | patch |
메시지 변경은 거의 항상 major다(1.2.8·1.3.5) — type hash가 바뀌어 통신이 끊기므로.
릴리스 체크리스트:
- [ ] 클린 환경에서 빌드·테스트 통과(4.4.9)
- [ ] 대상 아키텍처에서 빌드 확인(4.8.7)
- [ ]
CHANGELOG.rst사람이 읽을 수 있게 다듬음 - [ ] 호환 깨짐이 있으면 명시 + 마이그레이션 안내
- [ ]
package.xml·setup.py버전 일치(4.3.6) - [ ] 라이선스·유지보수자 정보 최신(4.4.8)
- [ ] 의존 선언 정확(
rosdep check) - [ ] 소스 잠금 파일 보관(
vcs export --exact— 4.7.8) - [ ] 태그 푸시
지원 정책:
| 항목 | 결정할 것 |
|---|---|
| 지원 배포판 | Jazzy만? Humble도? |
| 지원 기간 | 배포판 EOL까지? |
| 브랜치 전략 | 배포판별 브랜치? 단일 브랜치 + condition(4.4.4)? |
| 백포트 정책 | 버그 수정을 이전 배포판에도? |
견해: 배포판별 브랜치가 명확하다. 단일 브랜치로 여러 배포판을 지원하려면
condition과 조건부 코드가 늘어나고, 어느 배포판에서 무엇이 되는지 추적이 어려워진다. 브랜치를 나누면 백포트 비용이 들지만 각 브랜치가 단순해진다. 패키지가 작고 배포판 간 차이가 적으면 단일 브랜치도 가능하다(15.1).
4.9.9 함정 카탈로그
| # | 함정 | 증상 | 대처 |
|---|---|---|---|
| 1 | 클린 환경 검증 생략 | 빌드팜에서 실패 | 컨테이너 검증(4.4.9) |
| 2 | setup.py 버전 미갱신 | 버전 불일치 | 체크리스트 |
| 3 | CHANGELOG를 자동 생성 그대로 | 사용자에게 무용 | 사람이 다듬기 |
| 4 | 호환 깨짐을 minor로 릴리스 | 사용자 시스템 파손 | major + 명시 |
| 5 | 새 rosdep 키 미등록 | 빌드팜 의존 해석 실패 | rosdistro PR(4.6.7) |
| 6 | 릴리스 저장소를 수동으로 편집 | bloom과 충돌 | bloom에 맡김 |
| 7 | arm64 검증 없이 릴리스 | 아키텍처별 실패 | 크로스 검증(4.8) |
| 8 | 패키지 이름 변환 혼동 | 문서·스크립트 오류 | 밑줄→하이픈 인지 |
| 9 | 사내에서 성급히 빌드팜 구축 | 운영 부담 | 이미지·apt부터 |
| 10 | 소스 잠금 미보관 | 과거 릴리스 재현 불가 | vcs export --exact |
| 11 | 릴리스 후 즉시 apt 기대 | 아직 반영 안 됨 | 동기화 주기 인지 |
| 12 | 지원 정책 미정의 | 백포트 요청에 혼란 | 명문화 |
4.9.10 체크포인트
- 릴리스 파이프라인 다섯 단계와 각 단계의 산출물을 말할 수 있는가.
- 개발 저장소와 릴리스 저장소의 차이는 무엇이며, 개발자가 편집하는 것은 어느 쪽인가.
catkin_prepare_release가 하는 네 가지 작업과, 자동으로 되지 않는 것 하나는 무엇인가.- CHANGELOG를 사람이 다듬어야 하는 이유는? 호환 깨짐이 있을 때 무엇을 적어야 하는가.
- bloom이 생성하는 debian 메타데이터 세 가지는? 패키지 이름 변환 규칙은?
distribution.yaml의 주요 필드 세 가지를 말할 수 있는가. rosdistro가 담는 다른 것은?- 빌드팜의 작업 종류 다섯 가지와, 실패의 흔한 원인 여섯 가지를 들 수 있는가. 대부분을 로컬에서 예방하는 방법은?
- 사내 배포 선택지 세 가지의 장단을 비교하고, 전환 신호가 무엇인지 말할 수 있는가.
bloom-generate만 쓰는 방식의 이점은?- 메시지 정의 변경이 왜 거의 항상 major 버전인가.
- 배포판별 브랜치와 단일 브랜치의 트레이드오프를 설명할 수 있는가.
다음 장 4.10은 제4부의 마지막 — CI와 품질 게이트다. 지금까지 다룬 빌드·의존·릴리스 검증을 자동화하고, 어떤 게이트를 세울 것인가.