지난 실습에서는 ROS 2 Talker/Listener 예제를 Snap으로 패키징하는 과정에서 LXD 빌드 환경의 네트워크 문제를 확인했었다.
이번에는 해당 문제를 이어서 확인하고, 최종적으로 ROS 2 Talker/Listener 애플리케이션을 Snap으로 패키징하고 설치 및 실행까지 진행했다.
지난 실습에서 Snapcraft의 LXD 빌드 환경이 외부 네트워크에 접근하지 못하는 문제가 있었다.
먼저 FORWARD 정책을 확인했다.
sudo iptables -L FORWARD -n -v --line-numbers
확인 결과:
Chain FORWARD (policy ACCEPT ...)
FORWARD 정책은 ACCEPT 상태였다.
이후 Snapcraft에서 사용하는 LXD 컨테이너의 네트워크를 확인했다.
lxc exec snap-test -- ip route
결과:
default via 10.66.137.1 dev eth0
10.66.137.0/24 dev eth0 ...
컨테이너의 기본 게이트웨이는 10.66.137.1이었다.
게이트웨이와의 통신도 확인했다.
lxc exec snap-test -- ping -c 3 10.66.137.1
결과:
3 packets transmitted, 3 received, 0% packet loss
따라서 컨테이너와 LXD 게이트웨이 사이의 통신은 정상이었다.
LXD의 네트워크 설정을 확인했다.
lxc network show lxdbr0
확인 결과:
ipv4.address: 10.66.137.1/24
ipv4.nat: "true"
즉 lxdbr0에는 IPv4 NAT가 활성화되어 있었다.
호스트의 IPv4 forwarding도 확인했다.
cat /proc/sys/net/ipv4/ip_forward
결과:
1
따라서 IPv4 forwarding도 활성화된 상태였다.
Ubuntu 환경에서는 iptables뿐만 아니라 nftables를 함께 확인할 필요가 있었다.
sudo nft list ruleset | grep -i -C 3 "10.66.137"
확인 결과 LXD가 다음과 같은 MASQUERADE 규칙을 사용하고 있었다.
ip saddr 10.66.137.0/24
ip daddr != 10.66.137.0/24
oifname != "lxdbr0"
masquerade
또한 forwarding 규칙도 확인했다.
sudo nft list table inet lxd
다음과 같이 lxdbr0에서 들어오고 나가는 IPv4 패킷을 허용하고 있었다.
ip version 4 oifname "lxdbr0" accept
ip version 4 iifname "lxdbr0" accept
따라서 LXD의 NAT 및 forwarding 설정 자체는 정상적으로 구성되어 있는 것을 확인했다.
호스트의 외부 인터페이스를 확인했다.
ip route get 8.8.8.8
결과:
8.8.8.8 via 203.237.142.1 dev enp7s0 src 203.237.142.127
따라서 외부 네트워크 인터페이스는 enp7s0였다.
tcpdump를 이용해서 실제 패킷이 외부 인터페이스로 나가는지도 확인했다.
sudo tcpdump -ni enp7s0 host 8.8.8.8
LXD 컨테이너에서 ping을 실행했을 때 다음과 같은 패킷이 확인되었다.
203.237.142.127 > 8.8.8.8: ICMP echo request
컨테이너의 주소는 10.66.137.49였기 때문에, 외부 인터페이스로 나갈 때 NAT가 적용되어 호스트의 IP인 203.237.142.127로 변환된 것을 확인할 수 있었다.
다만 8.8.8.8의 ping 응답은 확인되지 않았다.
ping은 ICMP를 사용하기 때문에 실제 인터넷 연결 상태를 판단하기 위해 HTTPS 연결도 확인했다.
호스트에서:
curl -I https://github.com
결과:
HTTP/2 200
호스트에서는 GitHub HTTPS 연결이 정상적으로 이루어졌다.
이어서 LXD 컨테이너에서도 확인했다.
lxc exec snap-test -- curl -I https://github.com
결과 역시:
HTTP/2 200
이었다.
따라서 LXD 컨테이너에서도 GitHub HTTPS 통신이 정상적으로 가능한 것을 확인했다.
결국 처음에 확인했던 8.8.8.8 ping 실패는 Snapcraft 패키징을 진행하는 데 필요한 HTTPS 통신 자체가 막힌 문제는 아니었다.
이제 실제 Snap 패키징을 진행했다.
프로젝트 디렉터리로 이동했다.
cd ~/ros2-humble-talker-listener-snap
프로젝트 구조를 확인했다.
ls
결과:
LICENSE
README.md
renovate.json
SECURITY.md
snap
그리고 snap 디렉터리 안에 snapcraft.yaml이 존재하는 것을 확인했다.
ls snap
프로젝트 디렉터리에서 다음 명령을 실행했다.
snapcraft pack
처음에는 LXD 빌드 환경에서 Snapcraft를 설치하는 과정이 600초 동안 timeout되면서 실패했다.
하지만 생성된 LXD 빌드 컨테이너를 직접 확인해 보니 컨테이너 자체는 정상적으로 실행되고 있었다.
lxc --project snapcraft list
빌드용 컨테이너가 RUNNING 상태인 것을 확인했다.
컨테이너 내부의 Snap 버전도 확인했다.
lxc --project snapcraft exec \
base-instance-snapcraft-buildd-base-v71--d799df4d4588b4abe861 \
-- snap version
결과:
snap 2.76.3+ubuntu22.04
snapd 2.76.3+ubuntu22.04
series 16
ubuntu 22.04
architecture amd64
또한 /tmp/snapcraft.snap 파일도 정상적으로 존재했다.
lxc --project snapcraft exec \
base-instance-snapcraft-buildd-base-v71--d799df4d4588b4abe861 \
-- ls -lh /tmp/snapcraft.snap
결과:
-rw-rw-r-- 1 root root 69M ... /tmp/snapcraft.snap
직접 설치를 테스트한 결과:
lxc --project snapcraft exec \
base-instance-snapcraft-buildd-base-v71--d799df4d4588b4abe861 \
-- snap install /tmp/snapcraft.snap --classic
정상적으로:
snapcraft 9.0.1 from Canonical✓ installed
가 출력되었다.
이후 다시 snapcraft pack을 실행했고, 최종적으로 Snap 패키징에 성공했다.
생성된 파일을 확인했다.
ls -lh *.snap
결과:
-rw-r--r-- 1 namseungjoo namseungjoo 64M Sep 21 15:38 \
ros2-talker-listener_0.1_amd64.snap
드디어 ROS 2 Talker/Listener 애플리케이션의 Snap 패키지가 정상적으로 생성되었다.
생성된 .snap 파일을 로컬에서 설치했다.
처음에는:
sudo snap install ./ros2-talker-listener_0.1_amd64.snap --dangerous
를 실행했지만 다음과 같은 메시지가 나왔다.
error: snap "./ros2-talker-listener_0.1_amd64.snap" requires devmode or
confinement override
snapcraft.yaml에서 다음과 같이 devmode를 사용하도록 설정되어 있기 때문이다.
confinement: devmode
따라서 --devmode 옵션을 추가해서 다시 설치했다.
sudo snap install ./ros2-talker-listener_0.1_amd64.snap --dangerous --devmode
결과:
ros2-talker-listener 0.1 installed
정상적으로 설치되었다.
설치된 Snap을 확인했다.
snap list ros2-talker-listener
결과:
Name Version Rev Tracking Publisher Notes
ros2-talker-listener 0.1 x1 - - devmode
따라서 Snap이 정상적으로 설치되어 있는 것을 확인할 수 있었다.
처음에는 서비스 형태로 실행되는지 확인하기 위해:
snap services ros2-talker-listener
를 실행했다.
하지만:
error: snap "ros2-talker-listener" has no services
라는 결과가 나왔다.
services로 실행하는 daemon 형태가 아니라 apps로 정의된 애플리케이션이기 때문에 snap run을 사용해야 했다.
현재 프로젝트의 snap/snapcraft.yaml을 확인했다.
name: ros2-talker-listener
version: '0.1'
summary: ROS 2 Talker/Listener Example
description: |
This example launches a ROS 2 talker and listener.
confinement: devmode
base: core22
parts:
ros-demos:
plugin: colcon
source: https://github.com/ros2/demos.git
source-branch: humble
source-subdir: demo_nodes_cpp
build-packages: [build-essential]
stage-packages: [ros-humble-ros2launch]
apps:
ros2-talker-listener:
command: opt/ros/humble/bin/ros2 launch demo_nodes_cpp talker_listener.launch.py
extensions: [ros2-humble]
여기서 apps에 다음과 같이 ros2-talker-listener가 정의되어 있었다.
apps:
ros2-talker-listener:
따라서 Snap 서비스가 아니라 다음과 같이 실행할 수 있다.
다음 명령을 실행했다.
snap run ros2-talker-listener
정상적으로 ROS 2 Launch가 실행되었고, Talker와 Listener 프로세스가 함께 시작되었다.
[INFO] [launch]: All log files can be found below ...
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [talker-1]: process started
[INFO] [listener-2]: process started
이후 Talker가 메시지를 발행하고 Listener가 해당 메시지를 수신하는 것을 확인했다.

이처럼 Talker가 발행한 Hello World 메시지를 Listener가 정상적으로 수신하고 있었다.
기존 Snap에는 ROS 2 Talker/Listener 애플리케이션만 포함되어 있었다.
이번에는 Canonical Robotics의 Exercise를 따라 ros2topic을 추가하여 Snap 내부에서 ROS 2 토픽을 확인할 수 있도록 구성했다.
먼저 snapcraft.yaml의 stage-packages에 ros-humble-ros2topic을 추가했다.
ros2topic 실행 과정에서 NumPy가 BLAS 라이브러리를 찾지 못하는 문제가 발생하여 libblas3도 함께 추가했다.
stage-packages: [ros-humble-ros2launch, ros-humble-ros2topic, libblas3]
그리고 apps에 ros2topic 애플리케이션을 추가했다.
ros2topic:
command: opt/ros/humble/bin/ros2 topic
environment:
LD_LIBRARY_PATH: $SNAP/usr/lib/x86_64-linux-gnu/blas:$SNAP/usr/lib/x86_64-linux-gnu/lapack:$LD_LIBRARY_PATH
extensions: [ros2-humble]
이후 다시 Snap 패키지를 빌드했다.
snapcraft pack
정상적으로 다음과 같은 Snap 패키지가 생성되었다.
Created snap package ros2-talker-listener_0.1_amd64.snap
생성된 Snap을 다시 설치했다.
sudo snap install ./ros2-talker-listener_0.1_amd64.snap --dangerous --devmode
결과:
ros2-talker-listener 0.1 installed
Talker/Listener를 실행한 상태에서 새 터미널을 열고 다음 명령을 실행했다.
snap run ros2-talker-listener.ros2topic hz /chatter
처음 ros2topic을 실행했을 때 NumPy가 사용하는 BLAS/LAPACK 라이브러리를 찾지 못하는 문제가 발생했다.
libblas.so.3: cannot open shared object file
확인 결과 libblas.so.3와 liblapack.so.3는 Snap 내부에 존재했지만 각각 blas, lapack 하위 디렉터리에 위치하고 있었다.
따라서 LD_LIBRARY_PATH에 해당 경로를 추가하여 테스트했다.
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$SNAP/usr/lib/x86_64-linux-gnu/blas:$SNAP/usr/lib/x86_64-linux-gnu/lapack"
이후 다시 실행하자 정상적으로 /chatter 토픽의 발행 주기를 확인할 수 있었다.
ros2 topic hz /chatter
average rate: 1.000
min: 1.000s max: 1.000s
테스트 결과를 바탕으로 해당 환경변수 설정을 snapcraft.yaml의 ros2topic 앱에 추가했다.
이후 Snap을 다시 빌드하고 설치한 뒤에는 별도의 export 명령 없이도 다음 명령만으로 정상적으로 실행할 수 있었다.
snap run ros2-talker-listener.ros2topic hz /chatter
최종적으로 /chatter 토픽이 약 1Hz의 주기로 정상적으로 발행되고 있는 것을 확인했다.

이번 실습에서는 ROS 2의 Talker/Listener 애플리케이션을 Snap으로 패키징하고, LXD 빌드 환경의 네트워크 상태를 확인한 뒤 실제 Ubuntu 환경에 설치하여 실행해 보았다.
또한 기존 Snap에 ros2topic 애플리케이션을 추가하고, /chatter 토픽의 발행 주기를 직접 확인했다. 이 과정에서 Snap 내부의 BLAS/LAPACK 라이브러리 경로 문제를 확인하고 LD_LIBRARY_PATH를 설정하여 해결했다.
전체 과정은 다음과 같다.
ROS 2 Talker/Listener
↓
snapcraft.yaml 작성
↓
LXD 빌드 환경 및 네트워크 확인
↓
snapcraft pack
↓
ros2-talker-listener_0.1_amd64.snap 생성
↓
Snap 설치
↓
snap run ros2-talker-listener
↓
Talker → "Hello World"
↓
Listener → 메시지 수신
↓
ros2topic 추가
↓
ros2 topic hz /chatter
↓
약 1 Hz 확인
최종적으로 ROS 2 Talker/Listener 애플리케이션을 Snap으로 패키징하고 설치 및 실행하는 과정을 완료했으며, ros2topic을 이용하여 /chatter 토픽의 통신 상태까지 확인할 수 있었다.