AEGLOSLAB ROS 2 Jazzy 대전

제3부 미들웨어: DDS·RTPS·RMW

3.4 Discovery Server와 대규모 그래프

3.4.1 이 장에서 배우는 것 / 선수 지식

3.3에서 디스커버리 비용이 $O(N^2)$임을 봤다. Discovery Server는 그것을 $O(N)$으로 바꾼다. 방법은 단순하다 — 모두가 모두에게 알리는 대신, 중앙의 서버 하나가 정보를 모아 배포한다.

여기서 즉시 드는 의문이 있다. "그럼 ROS 1의 마스터로 돌아가는 것 아닌가?" 절반은 맞고 절반은 다르다. 이 장은 그 차이와, 중앙화가 되돌려주는 대가를 정직하게 다룬다.

배우는 것:

  • 구조 — Client·Server·Super-client의 역할.
  • 비용 구조의 변화 — $O(N^2)$이 왜 $O(N)$이 되는가.
  • 설정 — ROS_DISCOVERY_SERVER와 서버 실행.
  • 가용성 설계 — 서버가 종료되면 어떻게 되는가, 이중화·백업.
  • ROS 1 마스터와의 차이 — 무엇이 여전히 분산인가.
  • 판단 기준 — 언제 쓰고 언제 쓰지 않는가.
  • 대안과의 비교 — 도메인 분리·정적 디스커버리·zenoh.

선수 지식: 3.3(디스커버리 비용), 3.2(포트·GUID), 3.1(참가자), 0.4(환경변수).

정직: Discovery Server는 Fast DDS(eProsima)의 기능이며 DDS 표준이 아니다. 따라서 rmw_fastrtps_cpp를 쓸 때만 사용할 수 있다(Jazzy 기본 RMW이므로 대부분 해당 — BK-05). 다른 벤더는 각자의 방식을 갖거나 지원하지 않는다. 아래 환경변수·인자 형식은 eProsima 공개 문서 기준이며 이 환경에서 실행 검증하지 않았다(확인 필요: 인자 형식)(목차 3). 다만 CLI 이름은 실행 확인했다(88차 — Jazzy · ros:jazzy-ros-base): /opt/ros/jazzy/bin/에 fastdds·fast-discovery-server·ros-discovery 셋이 함께 설치된다. fastdds는 discovery·shm 같은 서브커맨드를 갖는 우산 명령이고, 나머지 둘은 같은 도구(eProsima Server-Client discovery auxiliary generator tool 1.0.1)를 직접 부르는 이름이다 — ros-discovery는 --help가 자신을 fastdds discovery로 소개하므로 사실상 별칭이다.

검증 상태(추가): 이 장의 서술 가운데 1곳은 이후 회차에서 Jazzy 실물과 대조해 확인했다 — 본문의 (실행 확인) 표기가 그것이다. 표기가 없는 서술은 위 고지가 그대로 적용된다(목차 3의 검증 범위 표).

3.4.2 구조 — 중앙 집중 디스커버리

단순 디스커버리 (기본) 참가자 1 참가자 2 참가자 3 참가자 4 Discovery Server Discovery Server 참가자 1 참가자 2 참가자 3 참가자 4
역할설명
Server디스커버리 정보를 모으고 배포. 데이터는 지나가지 않는다
Client서버에만 디스커버리 정보를 보내고 받는다
Super-client클라이언트이면서 그래프 전체 정보를 받는다 — 내성(introspection) 도구용

결정적으로 중요한 사실: 서버는 디스커버리만 중계한다. 실제 데이터는 여전히 피어 투 피어다.

① 디스커버리 정보 ② 상대 정보 ③ 실제 데이터 (직접) 발행 노드 Discovery Server 구독 노드

이것이 ROS 1 마스터와의 핵심 차이다. 마스터도 데이터를 중계하지는 않았지만, ROS 1은 마스터가 없으면 새 연결이 전혀 불가능했고 XML-RPC 왕복이 통신 경로에 있었다. Discovery Server는 이미 맺어진 연결이 서버와 무관하게 계속 동작한다.

Super-client가 필요한 이유: 일반 Client는 자기가 필요한 상대만 알면 된다. 그런데 ros2 topic list·rqt_graph 같은 도구는 그래프 전체를 알아야 한다. Super-client 모드가 그것을 제공한다.

함정: Discovery Server를 쓰면서 CLI를 일반 Client로 실행하면 그래프가 부분적으로만 보인다. "노드가 있는데 ros2 node list에 안 나온다"의 원인이 이것일 수 있다. 도구는 Super-client로 설정해야 한다(3.4.4).

3.4.3 비용 구조의 변화

단순 디스커버리: 참가자 $N$개가 서로 알아야 하므로

$$\text{관계 수} = \binom{N}{2} = \frac{N(N-1)}{2} = O(N^2)$$

Discovery Server: 각 참가자가 서버 하나와만 관계를 맺으므로

$$\text{관계 수} = N = O(N)$$

참가자 수단순 디스커버리 관계Discovery Server 관계비율
1045104.5×
501,2255024.5×
1004,95010049.5×
20019,90020099.5×
500124,750500249.5×

추가 이득:

이득설명
멀티캐스트 불필요서버 주소를 알면 유니캐스트로 충분 — 무선·클라우드·컨테이너에서 결정적(3.9)
시작 시간 단축서버가 이미 정보를 갖고 있다
트래픽 예측 가능서버 중심의 단순한 패턴
서브넷 경계 통과라우팅만 되면 동작

"멀티캐스트 불필요"가 실무에서 가장 자주 채택 이유가 된다. 디스커버리 규모가 문제가 아니라 멀티캐스트가 막힌 환경이라서 Discovery Server를 쓰는 경우가 많다 — Docker bridge 네트워크, 일부 클라우드 VPC, Wi-Fi AP가 멀티캐스트를 억제하는 환경(3.9).

3.4.4 설정

서버 실행:

# 서버 ID 0, 포트 11811에서 대기
fastdds discovery --server-id 0 --port 11811
# Jazzy에는 fastdds · fast-discovery-server · ros-discovery 셋 다 설치된다 (88차 실행 확인)

클라이언트 설정:

export ROS_DISCOVERY_SERVER="192.168.1.100:11811"
ros2 run demo_nodes_cpp talker

여러 서버 지정(이중화):

# 세미콜론으로 구분 — 위치가 서버 ID에 대응한다
export ROS_DISCOVERY_SERVER="192.168.1.100:11811;192.168.1.101:11811"

빈 자리를 두어 특정 ID만 지정할 수도 있다:

export ROS_DISCOVERY_SERVER=";192.168.1.101:11811"   # 서버 ID 1만 사용

CLI를 Super-client로:

# XML 프로파일에서 super-client 모드를 지정하고
export FASTRTPS_DEFAULT_PROFILES_FILE=super_client.xml
export ROS_DISCOVERY_SERVER="192.168.1.100:11811"
ros2 topic list

Jazzy(Fast DDS 2.14.x)에서 프로파일 파일 변수는 FASTRTPS_DEFAULT_PROFILES_FILE이 맞다(실행 확인) — FASTDDS_ 접두사 이름은 이 버전의 라이브러리에 존재하지 않는다(3.7.3).

정직: Super-client 설정은 XML이 필요하며 환경변수만으로는 부족하다(확인 필요: 최신 버전에서 간소화됐을 수 있음). eProsima 문서의 현재 절차를 확인하기 바란다. 이 부분이 Discovery Server 도입 시 가장 자주 막히는 지점이다.

launch에서:

import os
from launch import LaunchDescription
from launch.actions import SetEnvironmentVariable
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        SetEnvironmentVariable('ROS_DISCOVERY_SERVER', '192.168.1.100:11811'),
        Node(package='my_pkg', executable='my_node'),
    ])

systemd로 서버 운영(14.8):

[Unit]
Description=Fast DDS Discovery Server
After=network-online.target

[Service]
ExecStart=/opt/ros/jazzy/bin/fastdds discovery --server-id 0 --port 11811
Restart=always
RestartSec=2

[Install]
WantedBy=multi-user.target

Restart=always가 중요하다 — 서버는 상시 가용해야 한다(3.4.5).

3.4.5 서버가 종료되면 어떻게 되는가

가장 중요한 질문이다. 중앙 요소를 도입했으니 그 실패를 설계해야 한다.

서버 사망 이미 맺어진 연결 ✅ 계속 동작 새 노드 참여 ❌ 불가 기존 노드가 새 엔티티 생성 ❌ 상대에게 안 알려짐 노드 재시작 ❌ 재연결 불가
상황서버 사망 시
이미 통신 중인 노드들정상 동작 유지
새 노드 시작아무도 못 찾음
노드 재시작재연결 실패
새 토픽·서비스 생성매칭 안 됨
CLI 조회실패 또는 부분 정보

즉 "실행되고 있는 시스템은 유지되지만, 변화는 멈춘다." 크래시 후 자동 재시작에 의존하는 시스템(14.8)에서는 이것이 치명적일 수 있다 — 노드가 재시작해도 다시 연결되지 못한다.

가용성 대책:

① 서버 이중화(다중 서버)

export ROS_DISCOVERY_SERVER="10.0.0.1:11811;10.0.0.2:11811"

클라이언트가 여러 서버에 연결하므로 하나가 종료돼도 계속된다. 서버끼리 정보를 공유하도록 구성할 수도 있다(확인 필요: 서버 간 연결 설정 방법).

② 자동 재시작 — systemd Restart=always. 다만 재시작한 서버는 정보를 잃은 상태이므로, 클라이언트들이 다시 알려줄 때까지 그래프가 불완전하다.

③ 백업 서버 모드 — Fast DDS는 디스커버리 정보를 디스크에 저장했다 복구하는 백업 모드를 제공한다(확인 필요: Jazzy 버전에서의 사용법). 재시작 후 상태를 되찾을 수 있다.

④ 서버 배치 설계 — 서버를 로봇 안(온보드)에 둘지 외부에 둘지가 가용성을 좌우한다.

배치장점단점
로봇 온보드네트워크 단절에도 로봇 내부는 정상로봇마다 서버
중앙 서버플릿 전체가 한 그래프네트워크 단절 시 전부 마비
계층(로봇 내 + 중앙)균형설정 복잡

견해: 로봇에 온보드 서버를 두고, 필요하면 중앙 서버와 연결하는 계층 구조가 안전하다. 무선이 끊겨도 로봇 내부 그래프는 유지돼야 한다 — 이것은 성능이 아니라 안전 요건이다(14.10). 중앙 서버 하나에 모든 로봇을 물리는 구성은 무선 환경에서 위험하다.

3.4.6 ROS 1 마스터와의 차이

"마스터로 돌아간 것 아닌가"에 대한 정확한 답이다.

축ROS 1 마스터Discovery Server단순 디스커버리
데이터 경로피어 투 피어피어 투 피어피어 투 피어
디스커버리중앙중앙분산
마스터 사망 시 기존 연결유지유지—
마스터 사망 시 새 연결불가불가—
이중화불가(단일 마스터)가능(다중 서버)—
이름 등록마스터가 소유서버는 중계만각자
필수 여부필수선택기본

두 가지 실질적 차이:

  1. 선택 사항이다. 환경변수 하나로 켜고 끈다. 코드는 그대로다. ROS 1은 마스터 없이 동작할 방법이 없었다.
  2. 이중화가 가능하다. 단일 장애점을 설계로 제거할 수 있다.

견해: "중앙 요소는 불리하다"는 교조적 태도는 도움이 안 된다. ROS 2가 마스터를 없앤 것은 옳았지만, 그 결과 $O(N^2)$ 디스커버리라는 대가를 얻었다. Discovery Server는 필요한 곳에서 선택적으로 중앙화를 되돌리는 도구이며, 이중화로 원래 문제(단일 장애점)를 완화한다. 중요한 것은 선택권이 사용자에게 있다는 것이다.

3.4.7 언제 쓰고 언제 쓰지 않는가

쓸 만한 경우:

상황이유
멀티캐스트가 막힌 환경Docker bridge·클라우드·일부 Wi-Fi(3.9)
참가자 100개 이상$O(N^2)$ 부담
서브넷을 넘는 통신멀티캐스트가 라우팅 안 됨
디스커버리 트래픽이 측정으로 확인됨근거 있는 도입
시작 시간이 문제단축 효과

쓰지 않는 편이 나은 경우:

상황이유
노드 수십 개 이하 + 정상 LAN불필요한 복잡도
단일 로봇 개발 환경관리 부담이 이득 초과
서버 운영을 감당할 수 없는 조직새 장애 지점 추가
다른 RMW 벤더 사용Fast DDS 전용(3.4.1)
WAN·고지연 링크가 주된 문제zenoh가 더 적합(3.10)

견해: "규모가 커서" 도입하는 경우보다 "멀티캐스트가 안 돼서" 도입하는 경우가 실무에서 더 많다. 그리고 후자라면 ROS_STATIC_PEERS(3.3.7)로 먼저 시도해 볼 가치가 있다 — 노드가 적고 주소가 고정이면 서버 없이 해결된다. 가장 단순한 해법부터 시도하라.

3.4.8 도입 절차와 검증

도입 결정 1. 현재 디스커버리 비용 측정 3.3.6 2. 서버 배치 설계 온보드 · 중앙 · 계층 3. 서버 기동 + systemd 4. ROS_DISCOVERY_SERVER 배포 5. CLI를 Super-client로 6. 그래프 완전성 검증 7. 서버 장애 시나리오 시험 8. 개선 측정 · 비교

6번 검증이 중요하다. Discovery Server 도입 후 흔한 실패가 "일부 노드만 보인다" 이다.

# 도입 전후 비교
ros2 node list | sort > before.txt
# … Discovery Server 전환 …
ros2 node list | sort > after.txt
diff before.txt after.txt        # 차이가 없어야 한다

7번 장애 시험도 반드시 해야 한다.

# 서버를 강제 종료하고 기존 통신이 유지되는지
sudo systemctl stop fastdds-discovery
ros2 topic hz /scan              # 계속 흘러야 정상
# 새 노드가 연결되지 못하는 것을 확인 (예상된 동작)
ros2 run demo_nodes_cpp listener # 안 붙어야 정상
# 서버 복구 후 회복 확인
sudo systemctl start fastdds-discovery

이 시험을 안 하면 현장에서 처음 겪게 된다. 서버 장애 시 시스템이 어떻게 행동하는지 미리 아는 것과 모르는 것의 차이는 크다.

8번 측정:

지표방법
디스커버리 트래픽패킷 캡처 비율(3.2.8)
시작 시간부팅부터 전체 노드 활성까지
CPU정상 상태에서의 사용률
메모리참가자당 캐시 감소

3.4.9 대안과의 비교

수단디스커버리 비용멀티캐스트 필요유연성단일 장애점벤더 종속
단순 디스커버리$O(N^2)$✅ 필요높음없음없음
ROS_STATIC_PEERS$O(N^2)$(피어 한정)❌ 불필요중간없음낮음
Discovery Server$O(N)$❌ 불필요높음있음(이중화 가능)Fast DDS
정적 디스커버리~0❌ 불필요없음없음높음
도메인 분리도메인별 $O(n^2)$✅ 필요중간없음없음
zenoh구조적으로 다름❌ 불필요높음라우터 모드면 있음rmw_zenoh

zenoh와의 관계가 특히 중요하다. zenoh는 Discovery Server처럼 라우터를 둘 수도 있고 완전 peer-to-peer로 쓸 수도 있으며, WAN·무선을 처음부터 염두에 두고 설계됐다. Discovery Server가 "DDS의 디스커버리 문제를 DDS 안에서 푸는" 접근이라면, zenoh는 "미들웨어를 바꾸는" 접근이다(3.10).

견해: 선택 순서는 대략 이렇다. ① 컴포지션·엔티티 정적화로 해결되는지 본다(3.3.9) → ② 멀티캐스트 문제면 ROS_STATIC_PEERS → ③ 규모·멀티캐스트 문제가 함께면 Discovery Server → ④ 무선·WAN·플릿이 본질이면 zenoh 검토. 각 단계가 다음 단계보다 비용이 낮다.

3.4.10 함정 카탈로그

#함정증상대처
1CLI를 Super-client로 설정 안 함그래프가 부분만 보임XML 프로파일 설정
2서버 장애 시나리오 미시험현장에서 처음 겪음도입 전 시험(3.4.8)
3중앙 서버 하나에 전 로봇무선 단절 시 전체 마비온보드 서버·계층 구조
4Restart 설정 누락서버 사망 후 복구 안 됨systemd Restart=always
5일부 노드만 환경변수 적용그래프 분열배포 일관성 확인
6다른 RMW로 전환Discovery Server 무효Fast DDS 전용임을 인지
7규모 문제가 아닌데 도입불필요한 복잡도더 값싼 수단 먼저
8서버 재시작 후 정보 손실 미고려일시적 그래프 불완전백업 모드·이중화
9도입 후 검증 생략노드 누락을 못 알아챔diff로 대조
10서버 포트 방화벽 미개방아무도 연결되지 못함11811 등 허용

3.4.11 체크포인트

  • Discovery Server의 세 가지 역할(Server·Client·Super-client)을 설명할 수 있는가. Super-client가 필요한 이유는?
  • "서버는 디스커버리만 중계하고 데이터는 피어 투 피어"라는 사실이 왜 결정적인가.
  • 참가자 100개일 때 단순 디스커버리와 Discovery Server의 관계 수를 각각 계산할 수 있는가.
  • 실무에서 Discovery Server 채택 이유로 규모보다 자주 등장하는 것은 무엇인가.
  • 서버가 종료됐을 때 유지되는 것과 불가능해지는 것을 각각 말할 수 있는가. 자동 재시작에 의존하는 시스템에서 왜 위험한가.
  • 가용성 대책 네 가지를 들 수 있는가. 서버 배치를 온보드로 두는 것이 왜 안전 요건인가.
  • ROS 1 마스터와 Discovery Server의 실질적 차이 두 가지는 무엇인가.
  • Discovery Server를 쓰지 않는 편이 나은 상황 다섯 가지를 들 수 있는가.
  • 도입 절차 여덟 단계 중 가장 자주 생략되고 가장 위험한 것은?
  • 완화 수단 선택 순서를 값싼 것부터 말할 수 있는가. zenoh는 어느 단계에서 검토하는가.

다음 장 3.5는 지금까지 "벤더마다 다르다"고 반복해 온 그 경계 — RMW 계층을 정면으로 다룬다. rmw API가 무엇을 약속하고, 구현 교체가 실제로 어떻게 일어나며, 어디까지가 이식 가능한가.