| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 | 31 |
- atmel
- raspberrypi
- Raspberry
- Linux
- Visual Studio Code
- Debug
- WPF
- STM32
- QT
- QEMU
- UART
- yocto project
- avr-gcc
- 라즈베리파이
- vscode
- 리눅스
- Visual Studio
- nucleo
- AArch64
- 디버깅
- MVVM
- bare metal
- esp32
- c#
- 아두이노
- buildroot
- yocto
- AVR
- C++
- Arduino
- Today
- Total
임베디드를 좋아하는 조금 특이한 개발자?
[ROS2] Docker를 통한 ROS2 개발 및 배포 본문
- 개발 환경
WSL2 (Ubuntu 24.04 LTS)
Docker : 29.6.2
ROS2 : Jazzy
- 소스 코드
https://github.com/MainForm/ros2_jazzy_tutorial_with_docker
GitHub - MainForm/ros2_jazzy_tutorial_with_docker
Contribute to MainForm/ros2_jazzy_tutorial_with_docker development by creating an account on GitHub.
github.com
1. 개요
ROS2를 Native 환경에서 개발하다 보면 너무 복잡한 의존성 때문에 프로젝트를 유지하거나 배포하는데 큰 어려움을 격는 경우가 많습니다. 이러한 문제를 해결하기 위해 Docker를 사용하여 필요한 패키지를 Dockerfile에 명시하여 자동적으로 개발 환경이 구성된 이미지를 빌드하고 Docker Compose를 통해 Docker Container를 목적에 맞추어 실행할 수 있습니다. 그래서 이번 포스트에서는 Docker를 통해 ROS2를 통해 어떻게 개발 할수 있고 어떻게 배포 할 수 있는지 소개해 드립니다.
2. Visual studio code Extension 설치
2.1. Remote Development
https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack
Remote Development - Visual Studio Marketplace
Extension for Visual Studio Code - An extension pack that lets you open any folder in a container, on a remote machine, or in WSL and take advantage of VS Code's full feature set.
marketplace.visualstudio.com
WSL 및 Docker Continer에 Visual Studio Code로 접속하여 편하게 개발하기 위함입니다.
2.2. Docker
https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker
Docker - Visual Studio Marketplace
Extension for Visual Studio Code - Makes it easy to create, manage, and debug containerized applications.
marketplace.visualstudio.com
3. 프로젝트 구성
프로젝트를 처음부터 직접 구성하는 것은 매우 힘든일이므로 Git repo를 Clone하고 분석하는 방법으로 하도록 하겠습니다.
# 개발 환경 Clone
git clone https://github.com/MainForm/ros2_jazzy_tutorial_with_docker.git ./ros2_dev
# 프로젝트 폴더로 이동
cd ros2_dev

3.1. Dockerfile
Dockerfile은 Docker Image을 어떻게 구성할 것인지를 명시한 파일입니다.
저는 ros에서 제공해주는 공식 이미지를 기반으로 이미지를 구성했습니다.
# ROS 2 Jazzy의 기본 ROS 환경을 기반 이미지로 사용
FROM ros:jazzy-ros-base
# 이후 RUN 명령을 Bash로 실행
SHELL ["/bin/bash", "-c"]
# colcon 등 ROS 2 C++ 패키지 빌드에 필요한 도구 설치
RUN apt-get update \
&& apt-get install -y \
ros-dev-tools
# 컨테이너 내부 ROS 2 workspace 경로
WORKDIR /workspace
# 기반 이미지에 포함된 UID/GID 1000의 ubuntu 사용자로 실행
# 일반적인 Linux 호스트 사용자와 파일 소유권이 일치함
USER ubuntu
3.2. docker-compose.yaml
# Talker와 Listener를 각각 별도 컨테이너로 실행
name: cpp-pubsub
# 노드 컨테이너가 공유하는 실행 환경
x-ros-common: &ros-common
# 동일한 Dockerfile로 만든 공통 이미지 사용
build: .
image: cpp-pubsub:local
# 소스와 빌드 결과를 모든 서비스가 동일하게 공유
volumes:
- ./ros2_ws:/workspace
# 두 노드가 동일한 ROS 2 DDS domain 사용
environment:
ROS_DOMAIN_ID: 2
RCUTILS_LOGGING_BUFFERED_STREAM: 1
HOME: /home/ubuntu
# 호스트 및 다른 host network ROS 2 노드와 통신
network_mode: host
# Fast DDS shared-memory transport를 컨테이너 사이에서 공유
ipc: host
services:
# 소스를 실시간으로 수정하고 빌드하는 개발 컨테이너
# dev profile을 지정했을 때만 생성되므로 기본 노드 실행에는 포함되지 않음
dev:
<<: *ros-common
profiles:
- dev
command: bash
stdin_open: true
tty: true
# Talker와 Listener 실행 전에 workspace를 한 번 빌드하는 일회성 서비스
workspace-build:
<<: *ros-common
command:
- bash
- -lc
- colcon build --symlink-install
# /topic에 std_msgs/msg/String 메시지 발행
talker:
<<: *ros-common
depends_on:
workspace-build:
condition: service_completed_successfully
command:
- bash
- -lc
- source install/setup.bash && exec ros2 run cpp_pubsub talker
# /topic의 std_msgs/msg/String 메시지 구독
listener:
<<: *ros-common
depends_on:
workspace-build:
condition: service_completed_successfully
command:
- bash
- -lc
- source install/setup.bash && exec ros2 run cpp_pubsub listener
위 docker compose는 내용이 많으므로 각 부분별로 설명 드리도록 하겠습니다.
3.2.1. x-ros-common 필드
# 노드 컨테이너가 공유하는 실행 환경
x-ros-common: &ros-common
# 동일한 Dockerfile로 만든 공통 이미지 사용
build: .
image: cpp-pubsub:local
# 소스와 빌드 결과를 모든 서비스가 동일하게 공유
volumes:
- ./ros2_ws:/workspace
# 두 노드가 동일한 ROS 2 DDS domain 사용
environment:
ROS_DOMAIN_ID: 2
RCUTILS_LOGGING_BUFFERED_STREAM: 1
HOME: /home/ubuntu
# 호스트 및 다른 host network ROS 2 노드와 통신
network_mode: host
# Fast DDS shared-memory transport를 컨테이너 사이에서 공유
ipc: host
x-ros-common은 4개의 서비스에 공통으로 적용할 설정을 모아 둔 확장 필드입니다. Docker Compose에서는 정의되지 않은 필드가 사용되면 오류가 발생할 수 있지만, 이름이 x-로 시작하는 필드는 확장 필드로 취급되어 Compose의 실행 대상에서 제외됩니다. 이 확장 필드에 공통 설정을 정의한 뒤 YAML의 anchor와 alias 문법을 사용하면, 여러 서비스에서 동일한 설정을 반복해서 작성하지 않고 재사용할 수 있습니다.
- 확장 필드 관련 내용
https://docs.docker.com/reference/compose-file/extension/
Extensions
Define and reuse custom fragments with extensions in Docker Compose
docs.docker.com
- volumes
Docker에서는 Host PC와 Container 간 파일을 공유 하기 위해서 volume를 사용합니다. 특히, Container의 경우 Container가 삭제 된다면 Container내 파일들도 동시에 삭제됩니다. 그러므로 Container와 상관없이 삭제되면 곤란한 파일들을 volume 설정으로 공유할 수 있습니다. 이 프로젝트의 경우 ROS2의 소스코드를 Container 공유하여 실제 ROS2의 빌드 및 노드 실행은 Container에서 하도록 하였습니다.

https://docs.docker.com/engine/storage/bind-mounts/
Bind mounts
Using bind mounts
docs.docker.com
- environment
- ROS_DOMAIN_ID: 2
https://docs.ros.org/en/jazzy/Concepts/Intermediate/About-Domain-ID.html
ROS_DOMAIN_ID는 다른 같은 물리적 네트워크(같은 공유기)에 연결된 장치에서 논리적 네트워크를 구성하기 위해 같은 ROS_DOMAIN_ID를 같게 설정하여 자동적으로 같은 DDS 도메인을 찾도록 설정합니다. - RCUTILS_LOGGING_BUFFERED_STREAM: 1
https://docs.ros.org/en/jazzy/Concepts/Intermediate/About-Logging.html
Log 출력 관련 설정입니다. 0인 경우 버퍼가 없이 바로 출력하지만 1로 설정하여 퍼버를 두어 개행(\n)이 되었을 때 Log를 출력하도록 합니다.
- network_mode : host
컨테이너가 호스트의 네트워크 환경을 그대로 사용하도록 설정합니다. 이를 통해 컨테이너 내부의 ROS 2 노드가 호스트 또는 같은 물리적 네트워크에 있는 다른 ROS 2 노드를 쉽게 발견하고 통신할 수 있습니다. 단, ROS_DOMAIN_ID가 같아야 합니다.
- ipc: host
컨테이너가 호스트의 IPC 공간을 공유하도록 설정합니다. 이를 통해 같은 호스트에서 실행되는 여러 컨테이너의 Fast DDS가 동일한 공유 메모리 영역을 사용하여 데이터를 더 효율적으로 전달할 수 있습니다.
3.2.2. Service 필드
3.2.2.1. dev 서비스
# 소스 코드를 실시간으로 수정하고 빌드하기 위한 개발용 컨테이너
dev:
# ROS 2 컨테이너의 공통 설정 적용
<<: *ros-common
# 기본 docker compose up 실행에서는 제외하고,
# dev 프로필을 지정한 경우에만 실행
profiles:
- dev
# 컨테이너 시작 시 ROS 2 노드 대신 Bash 실행
command: bash
# 대화형 Bash 환경을 유지하여 컨테이너가 종료되지 않게 하고,
# Visual Studio Code에서 실행 중인 컨테이너에 접근할 수 있도록 설정
stdin_open: true
tty: true
해당 dev 서비스는 Visual Studo Code로 Container에 접속하여 개발하기 위한 서비스입니다.
3.2.2.2. build 서비스
# Talker와 Listener 실행 전에 ROS 2 workspace를 빌드하는 일회성 서비스
workspace-build:
# ROS 2 컨테이너의 공통 설정 적용
<<: *ros-common
# Bash 로그인 셸에서 colcon build 명령 실행
command:
- bash
- -lc
- colcon build --symlink-install
해당 build 서비스는 단순하게 일회성으로 talker와 listener 노드가 실행되기 위해 ros2를 빌드하기 위한 서비스 입니다.
3.2.2.3. talker 서비스 와 listenr 서비스
# /topic에 std_msgs/msg/String 메시지를 발행하는 Talker 서비스
talker:
# ROS 2 컨테이너의 공통 설정 적용
<<: *ros-common
# workspace-build 서비스가 성공적으로 완료된 후 실행
depends_on:
workspace-build:
condition: service_completed_successfully
# 빌드된 workspace 환경을 적용한 후 Talker 노드 실행
# exec를 사용하여 ros2 프로세스가 컨테이너의 메인 프로세스가 되도록 설정
command:
- bash
- -lc
- source install/setup.bash && exec ros2 run cpp_pubsub talker
# /topic의 std_msgs/msg/String 메시지를 구독하는 Listener 서비스
listener:
# ROS 2 컨테이너의 공통 설정 적용
<<: *ros-common
# workspace-build 서비스가 성공적으로 완료된 후 실행
depends_on:
workspace-build:
condition: service_completed_successfully
# 빌드된 workspace 환경을 적용한 후 Listener 노드 실행
# exec를 사용하여 ros2 프로세스가 컨테이너의 메인 프로세스가 되도록 설정
command:
- bash
- -lc
- source install/setup.bash && exec ros2 run cpp_pubsub listener
3.3. ros2_ws 폴더
ROS2를 실행하기 위한 예제가 작성되었습니다. 해당 예제는 ROS2에서 제공하는 기본 예제를 사용하였습니다.
https://docs.ros.org/en/jazzy/Tutorials.html
4. ROS2 개발 방법
# dev service 실행
docker compose up -d dev


위 과정까지 따라 왔다면, 새로운 Visual Studio Code가 열리고 dev container에 자동으로 접속 될 것입니다.


이제 해당 Visual Studo Code에서 평소대로 개발하듯이 개발이 가능합니다.
source /opt/ros/jazzy/setup.bash
colcon build
source ./install/setup.bash
ros2 run cpp_pubsub talker

※주의)
새로운 패키지를 설치할때 Container 내 apt 패키지 매니져로 설치한다면 Container 재 실행시 해당 패키지을 재설치해야할 수 있습니다. 그러므로 새로운 패키지를 설치해야한다면 Dockerfile 내에서 추가해주어야 합니다.
5. ROS2 노드 배포
이제 개발이 완료 되었다면 이제 해당 노드를 자동적으로 실행하도록 할 수있습니다.
2개의 터미널을 실행하여 각 2개의 명령어를 별개로 싱행하면 ros2 node가 정상적으로 실행되는 것을 확인할 수 있습니다.
docker compose up talker
docker compose up listener

※주의)
해당 두 노드는 서로 다른 컨테이너에서 실행되고 있음을 인지하고 있어야합니다.
