AEGLOSLAB ROS 2 Jazzy 대전

제11부 도구·관측성

11.1 현장 디버깅 플레이북

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

이 장은 제11부의 결론이자 이 책 앞부분 전체의 실무적 소환이다. 100개 장에 걸쳐 각 계층의 함정을 다뤘는데, 현장에서는 그것들이 증상 하나로 뭉쳐서 나타난다. "로봇이 안 움직여요"라는 한 문장 뒤에 스무 가지 원인이 있다.

플레이북이 필요한 이유는 명확하다. 현장에서는 생각이 잘 안 된다. 시간 압박이 있고, 관계자가 지켜보고, 도구가 제한되고, 재현이 어렵다. 그런 상황에서는 미리 정해 둔 순서가 즉흥적 판단보다 낫다.

배우는 것:

  • 분류 원칙 — 계층을 먼저 좁힌다.
  • 최초 대응 체크리스트 — 5분 안에 하는 것.
  • 증상별 경로 — 이 책 전체로의 색인.
  • 현장 제약 — 원격·재현 불가·시간 압박.
  • 데이터 확보 — 못 고쳐도 남길 것.
  • 에스컬레이션 — 멈춰야 할 때.
  • 사후 분석 — 재발 방지.
  • 진단 가능한 시스템 — 사전 설계.
  • 플레이북 검증(11.1.14) — 이 절차를 실제 문제에 실행해 본 결과.
  • 층을 가르는 한 명령(11.1.15) — ros2 doctor hello의 두 표를 읽는 법.

선수 지식: 이 책 전반. 특히 2.1.8(계층 식별), 3.3(디스커버리), 1.6(QoS), 5.2(tf), 6.9(진단), 11.2~11.7(도구).

정직: 이 장은 절차와 판단 원칙이며, 모든 문제에 대한 해답 목록이 아니다. 실제 현장은 예상 밖의 조합을 내놓는다. 원칙은 "계층을 좁히고, 데이터를 남기고, 무리하지 않는다" 이다. 이 환경에서 검증하지 않았다(목차 3).

11.1.2 분류 원칙 — 계층 먼저

2.1.8에서 세운 원칙이 플레이북의 뼈대다.

증상 어느 계층? ① 하드웨어 · 전원 · 연결 ② OS · 네트워크 · 시계 ③ 미들웨어 · 디스커버리 · QoS ④ ROS 그래프 · 이름 · 실행 ⑤ 데이터 규약 · tf · 시간 ⑥ 알고리즘 · 파라미터 ⑦ 애플리케이션 로직

아래에서 위로 확인하는 것이 원칙이다.

이유설명
아래가 깨지면 위가 모두 비정상이 된다원인을 위에서 찾으면 무익한 작업
아래일수록 확인이 빠르다전원·케이블·ros2 node list
아래일수록 흔하다케이블·전원·QoS가 다수

견해: "알고리즘이 잘못됐나 봐요"로 시작하는 진단이 가장 오래 걸린다. 실제로는 케이블이 빠졌거나, USB 권한이 없거나, ROS_DOMAIN_ID가 다르거나, QoS가 안 맞는 경우가 훨씬 많다. 아래부터 5분만 확인하면 대부분 여기서 끝난다.

11.1.3 최초 대응 체크리스트

증상이 무엇이든 먼저 하는 것. 5분.

# --- ① 하드웨어·전원 ---
# 전원 LED · 케이블 · E-stop 상태 · 배터리 전압

# --- ② OS·네트워크 ---
uptime                          # 재부팅 흔적
df -h                           # ★ 디스크 가득? (11.3.7)
free -h                         # 메모리
dmesg | tail -50                # USB 재연결 · OOM · 커널 오류
ip a                            # 네트워크 인터페이스
timedatectl                     # ★ 시계 동기 (7.7.6)

# --- ③ 미들웨어 ---
echo $ROS_DOMAIN_ID             # ★ 다르면 아무것도 안 보임 (1.5.8)
echo $RMW_IMPLEMENTATION        # 노드 간 불일치 (3.5.6)
ros2 daemon status              # 실재(실행 확인) — 실행 중이 아니면 "The daemon is not running"

# --- ④ ROS 그래프 ---
ros2 node list                  # 기대한 노드가 다 있나
ros2 topic list                 # 기대한 토픽이 있나

# --- ⑤ 데이터 ---
ros2 topic hz /scan --window 20 # 데이터가 흐르나 (창을 명시해야 최근 상태를 본다 — 0.7.12.2)
ros2 run tf2_tools view_frames  # tf 트리 (5.2.5)

# --- ⑥ 진단·로그 ---
ros2 topic echo /diagnostics_agg --once     # ★ 시스템이 스스로 말한다
ros2 topic echo /rosout | grep -i -E "error|warn"
확인자주 걸리는 것
df -h디스크 가득 → 온갖 이상 증상
dmesgUSB 재연결·OOM 킬
$ROS_DOMAIN_ID완전 무통신
ros2 node list종료된 노드
/diagnostics_agg시스템이 이미 답을 알고 있음

견해: /diagnostics_agg를 가장 먼저 보라(6.9.8). 진단을 제대로 구현했다면(6.9), 시스템이 무엇이 잘못됐는지 이미 말하고 있다. 그것을 안 보고 시행착오를 겪는 것이 흔한 낭비다.

견해: df -h와 dmesg가 의외로 자주 원인이다. 디스크가 가득 차면 로그·기록이 실패하고, 노드가 비정상적으로 종료되고, 원인 추적이 어려워진다(1.11.3·11.3.7). USB 장치 재연결은 센서가 조용히 사라지는 원인이다(6.1.8).

11.1.4 증상별 경로

이 책 전체로의 색인이다.

11.1.4.1 아무 데이터도 없다

예 예 아니오 아니오 아니오 예 데이터가 전혀 없다 ros2 node list에 보이나? 프로세스 종료 · DOMAIN_ ID · RMW 불일치 · 방화벽 topic list에 있나? 이름 · 네임스페이스 · 리매핑 topic hz에 나오나? 발행 안 함 · QoS 불일치 구독 측 문제
확인장
ROS_DOMAIN_ID·RMW·방화벽1.5.8·3.5.6·3.9
이름·네임스페이스·리매핑1.1.5·1.1.6
QoS 불일치1.5.4·1.6.3.1
멀티캐스트 차단3.3.5·3.9.4
여러 대 사이라면 먼저 ros2 doctor helloA.1.14.1 — 멀티캐스트와 DDS를 한 화면에서 가른다

11.1.4.2 데이터는 오는데 내용이 비정상이다

증상원인 후보장
값이 불규칙하게 변한다센서 노이즈·전원7.1
프레임이 이상tf·optical frame5.1.8·7.1.4
시각이 이상스탬프·시계7.7·1.10
단위가 이상규약 위반7.1.6
오래된 값큐·지연1.4.4·11.7.4
공분산이 이상0을 "모름"으로 발행7.1.12.2 — 소비자는 "절대 신뢰"로 읽는다
/compressed가 없음플러그인 미설치7.2.12 — ros-base에는 없다

11.1.4.3 가끔·간헐적

증상원인 후보장
가끔 끊김무선·큐·QoS3.9.3·1.6
주기가 튐실행기·부하2.5.8·11.6
재부팅 후 다름시작 순서·경합1.12.6·4.9
장시간 실행하면 이상누수·누적11.7.6
특정 위치에서환경·무선·측위3.9·8.4

견해: 간헐적 문제가 가장 어렵고, 여기서 기록이 결정적이다(11.3.3). 재현을 기다리지 말고 순환 버퍼 기록을 켜 두고 사건을 포착한다. 이것이 이 책이 기록을 강조한 실무적 이유다.

11.1.4.4 로봇이 안 움직인다

확인장
E-stop·전원·안전 상태6.1·8.7
/cmd_vel이 발행되나8.8.8
컨트롤러 활성 상태6.3.6
하드웨어 인터페이스 상태6.2
항법 상태·목표 거부8.7.6
코스트맵에 갇힘8.3.8
속도 제한·collision_monitor8.7.5

11.1.4.5 항법·조작이 실패한다

영역장
측위 흔들림8.4.8
코스트맵 이상8.3.8
경로 실패8.5.8
복구 반복8.7.6
계획 실패9.2.8
파지 실패9.5.8

11.1.4.6 빌드·실행 문제

증상장
빌드 실패4.5
옛 코드가 실행됨4.5.8(오버레이)
패키지를 못 찾음4.1.8
launch 오류14.3.8
파라미터 미적용1.9.7

견해: "고쳤는데 안 바뀐다"의 최다 원인은 오버레이·소싱 문제다(4.5.8). ros2 pkg prefix로 실제로 어느 경로가 쓰이는지 확인하는 것이 확실하다.

11.1.5 현장 제약

현장은 개발실이 아니다.

제약결과대응
GUI 불가RViz2·rqt 못 씀CLI 숙련(11.5.8)
SSH만 가능화면 없음tmux로 세션 유지
재현 어려움한 번뿐즉시 기록(11.3.3)
시간 압박조급함체크리스트
관계자 참관압박절차대로
재부팅 유혹증거 소실데이터 먼저
네트워크 제한원격 접속 불가로컬 수집
안전 요구함부로 못 움직임정지 상태 진단

견해: "일단 재부팅"이 현장에서 가장 아까운 행동이다. 문제가 사라지지만 원인도 사라진다. 재부팅 전에 최소한의 데이터를 확보하라 — 로그, dmesg, 프로세스 상태, 실행 중인 bag. 5분 투자로 다음 재발을 막을 수 있다.

재부팅 전 확보:

# 한 번에 수집하는 스크립트를 미리 만들어 두라
mkdir -p /tmp/incident_$(date +%s) && cd $_
ros2 node list > nodes.txt 2>&1
ros2 topic list -v > topics.txt 2>&1
ros2 param dump /some_node > params.yaml 2>&1
ros2 topic echo /diagnostics_agg --once > diag.txt 2>&1
dmesg > dmesg.txt
journalctl -n 5000 > journal.txt
cp -r ~/.ros/log/latest ros_log
df -h > disk.txt; free -h > mem.txt; ps auxf > ps.txt
ip a > net.txt; timedatectl > time.txt
tar czf ../incident.tgz .

견해: 이 수집 스크립트를 로봇에 미리 넣어 두라. 현장에서 명령을 하나씩 치는 것과 스크립트 하나를 실행하는 것은 다르다. 압박 상황에서 빠뜨리지 않는 것이 목적이다.

견해: tmux(또는 screen)를 습관화하라. SSH가 끊겨도 세션이 유지되고, 기록·모니터링을 여러 창에서 동시에 볼 수 있다. 무선이 불안정한 현장에서 특히 값을 한다.

11.1.6 데이터 확보

못 고쳐도 이것만은 남긴다.

우선순위데이터
1bag(가능하면 사건 전후)
2/rosout·노드 로그
3/diagnostics 이력
4dmesg·journalctl
5시스템 상태(디스크·메모리·프로세스)
6관찰 기록(사람이 본 것·시각)
7설정·커밋 해시

견해: "사람이 본 것"을 적는 것을 빠뜨리지 말라. "14:32경 로봇이 문 앞에서 좌우로 흔들리다 정지, 소리 없음" 같은 서술이 로그와 맞춰 볼 때 결정적 단서가 된다. 로그에는 없는 정보다.

견해: 시각을 정확히 적으라. 로그와 대조하려면 시각이 필요하다. 로봇 시계와 사람의 시계가 다를 수 있으니(7.7.6), 로봇에서 date를 한 번 출력해 두면 대조가 쉬워진다.

분석 순서:

사건 시각 특정 그 시각의 로그 · 진단 bag에서 해당 구간 재생 RViz2로 그 시점 상황 확인 PlotJuggler로 신호 분석 가설 수립 재현 시도 · 검증

11.1.7 에스컬레이션

멈춰야 할 때가 있다.

신호판단
안전이 걸림즉시 중단·전문가
30분 이상 진전 없음데이터 확보 후 후퇴
추측으로 설정을 바꾸고 있음위험 — 되돌리기
여러 변경을 동시에인과 불명
원인 모른 채 "고쳐짐"재발 가능
하드웨어 손상 위험중단

견해: "추측으로 설정을 계속 바꾸는 상태"를 자각하는 것이 중요하다. 이 상태에서는 문제를 더 복잡하게 만들 뿐이고, 우연히 고쳐져도 원인을 모른다. 변경을 되돌리고, 데이터를 모으고, 차분히 분석하는 것이 결국 빠르다.

견해: 한 번에 하나만 바꾸라. 현장 압박에서 세 가지를 동시에 바꾸면 무엇이 효과였는지 모르고, 부작용이 섞인다. 바꾼 것을 기록하면 되돌리기도 쉽다.

견해: 안전 관련 이상은 다르게 다뤄야 한다(8.7.1·9.6.1의 반복). 로봇이 예상 밖으로 움직이거나 안전 기능이 의심되면, 디버깅보다 정지와 격리가 먼저다. 이 책은 안전 인증에 관한 조언을 제공하지 않으며, 해당 판단은 안전 담당자·표준·전문가를 따라야 한다.

11.1.8 사후 분석

같은 문제를 두 번 겪지 않기 위한 절차다.

사건 사실 정리 시각 · 증상 · 조치 원인 분석 왜 발생했나 왜 못 막았나 감지 · 예방 조치 수정 · 감지 · 문서 회귀 데이터셋 추가 11.3.8
항목질문
무슨 일이사실만
직접 원인기술적 원인
왜 못 막았나시험·감지의 공백
왜 못 찾았나진단의 공백
조치수정·감지 추가
재발 방지회귀 시험

견해: "왜 못 막았나"와 "왜 못 찾았나"가 가장 가치 있는 질문이다. 원인을 고치는 것은 그 사건만 해결하지만, 감지 공백을 메우면 유사 문제 전체를 다음번엔 빨리 찾는다. 진단 항목 하나 추가(6.9)가 수 시간의 디버깅을 절약한다.

견해: 사람을 탓하는 사후 분석은 정보를 소실시킨다. 실수가 가능했던 구조를 묻는 것이 유용하다 — 왜 잘못된 설정이 배포될 수 있었나, 왜 시험에서 안 걸렸나. 이것이 CI(4.10)·배포 절차(4.7)를 개선하는 입력이 된다.

산출물:

산출예
코드·설정 수정직접 원인
진단 항목 추가다음엔 즉시 감지
회귀 시험 추가재발 차단
bag 데이터셋 편입11.3.8
문서·체크리스트 갱신절차
함정 목록 추가팀 지식

11.1.13 프로세스를 찾는 명령 — -f가 자기 자신을 매칭한다

이 책의 진단 예시는 $(pgrep -f <이름>) 형태를 자주 쓴다(1.13·2.2·2.4·2.6·2.7·3.2·3.5·3.6·6.6·7.8). 대개 잘 동작하지만 실패 방식이 까다로워서 한 번 짚어 둔다.

-f는 프로세스 이름이 아니라 명령줄 전체를 매칭한다. 그래서 다음이 함께 매칭된다:

함께 매칭되는 것결과
그 명령을 실행 중인 셸 자신명령줄에 그 문자열이 들어 있다
같은 이름을 연 편집기·less·grep잘못된 PID
ros2 run 래퍼와 실제 실행 파일PID가 둘 이상 나와 -p가 실패한다

pgrep이면 잘못된 결과에 그치지만, pkill -f면 자기 셸을 강제 종료한다. 스크립트가 그 줄에서 즉시 종료되며, 원인을 찾기 어렵다(종료 코드만 137/143으로 남는다).

안전한 형태 셋:

# ① 프로세스 이름이 정확하면 -x (권장)
PID=$(pgrep -x talker)

# ② 패턴이 꼭 필요하면 대괄호로 자기 매칭을 피한다
ps -eo pid,cmd | grep "[t]alker" | awk '{print $1}'

# ③ 내가 실행한 프로세스면 PID를 변수에 담아 둔다 — 검색 자체를 없앤다
/opt/ros/jazzy/lib/demo_nodes_cpp/talker & PID=$!

견해: ③이 가장 확실하다. 진단 스크립트를 쓸 때 "찾아서 강제 종료한다"는 구조 자체가 위험하다. 실행한 쪽이 PID를 기억하면 검색이 필요 없고, systemd로 관리한다면 systemctl kill이 프로세스 그룹을 정확히 다룬다(14.8).

함정: 여러 PID가 나오는 것도 흔한 실패다. ros2 run pkg node는 파이썬 런처와 실제 노드가 각각 프로세스라 pgrep -f node가 둘을 준다. ps -p $(pgrep -f node)는 인자 하나를 기대하므로 그대로 깨진다. pgrep -x로 실행 파일 이름을 정확히 지목하거나, pgrep -n(가장 최근)·head -1로 하나만 고른다.

11.1.9 진단 가능한 시스템

디버깅의 난이도는 대부분 사전 설계로 정해진다. 이 책이 여러 장에서 반복한 것들을 여기 모은다.

설계장효과
진단 발행(/diagnostics)6.9시스템이 스스로 말함
의미 있는 로그(구조화·레벨)1.11사후 추적
상시 기록(작은 토픽)11.3.2사건 전 상황
순환 버퍼·이벤트 기록11.3.3간헐 문제 포착
성능 지표 진단11.7.9열화 감지
명확한 이름·네임스페이스1.1그래프 이해
tf 트리 단순·단일 부모5.1프레임 문제 감소
규약 준수(REP·단위·프레임)7.1오해 감소
상태 기계 명시1.12상태 파악
파라미터 문서화·검증1.9설정 실수 감소
버전·커밋 노출4.7무엇이 실행되고 있나
수집 스크립트 배치11.1.5현장 대응

견해: "현장에서 SSH만으로 진단할 수 있는가"를 설계 기준으로 삼으라. 이 질문에 "아니오"라면, 필요한 정보가 시스템 밖으로 나오지 않는다는 뜻이다. 진단·로그·기록이 그 통로이며, 나중에 추가하기보다 처음부터 넣는 것이 훨씬 비용이 낮다.

견해: 버전 정보를 노출하는 것이 의외로 중요하다. 현장에서 "지금 어느 버전이 실행되고 있나"를 모르면 분석이 시작되지 않는다. 노드가 시작 시 커밋 해시·빌드 시각을 로그에 남기거나 진단에 포함하면(4.7), 이 혼란이 사라진다.

11.1.10 제11부를 마치며

관측성 기록 11.2 · 11.3 시각화 11.4 · 11.5 내부 관측 11.6 측정 11.7 현장 대응 11.1

제11부가 반복한 것은 셋이다.

  1. 기록하지 않은 것은 분석할 수 없다. 사건은 예고 없이 오고 재현되지 않는다. 상시 기록과 순환 버퍼가 그 대비다(11.2·11.3).
  2. 질문에서 도구로 가라. 도구가 많고 각각 강점이 다르다. "무엇을 알고 싶은가"에서 출발하면 도구는 정해지고, 반대로 하면 시간을 버린다(11.5.8).
  3. 측정 없이 판단하지 말라. 성능도, 회귀도, 병목도 추측으로는 틀린다. 그리고 평균이 아니라 꼬리를 보라(11.7.3).

그리고 이 부 전체를 관통하는 하나 — 진단 가능성은 사후에 얻어지지 않는다. 6.9에서 "진단으로 내보내라"고 한 것, 1.11에서 "의미 있게 로깅하라"고 한 것, 11.3에서 "상시 기록하라"고 한 것이 모두 현장에서 회수되는 투자다.

11.1.14 이 플레이북을 실제로 실행해 봤다 — 다섯 시나리오 실측

책이 "이렇게 진단하라"고 적어 두는 것과 그 절차가 실제로 답을 주는 것은 다르다. Jazzy 컨테이너에서 문제를 일부러 만들고 이 장의 절차대로 따라가 봤다(실행 확인).

11.1.14.1 시나리오 ① — "토픽은 있는데 데이터가 안 온다"

BEST_EFFORT로 발행하는 노드와 RELIABLE을 요구하는 구독자를 실행했다.

단계결과
증상구독자가 6초 동안 0건 수신 — 에러도 경고도 없다
11.1.4의 첫 확인: ros2 topic info /probe -vEndpoint type: PUBLISHER / Reliability: BEST_EFFORT 가 바로 보인다
판정한 번의 명령으로 원인 확정

이 장이 topic info -v를 첫 확인으로 둔 것이 맞다. 발행자·구독자의 QoS가 나란히 출력되므로, 둘을 비교하면 호환성 규칙(1.5.4)에 걸리는지 즉시 안다.

예외 하나 — system_default 엔드포인트에서는 이 판정이 성립하지 않는다. 내구성이 TRANSIENT_LOCAL로 표시되는데 실제 동작은 VOLATILE이고, 히스토리·깊이는 전혀 안 실린다(82차 실측, 1.6.12). 신뢰성은 위 시나리오처럼 믿을 수 있고, 내구성이 걸린 증상("늦게 참여하면 못 받는다")일 때만 늦은 구독자 시험으로 확인하라.

11.1.14.2 시나리오 ② — "아무것도 안 보인다"

ROS_DOMAIN_ID=7로 노드를 실행하고 기본 도메인에서 찾았다.

단계결과
증상ros2 node list가 0줄
11.1.3 ③의 확인: echo $ROS_DOMAIN_ID셸은 미설정(=0), 노드는 7
확인ROS_DOMAIN_ID=7 ros2 node list → /talker 가 보인다

도메인을 바꿔 한 번 더 조회하는 것이 결정적이다. "환경 변수를 확인하라"는 지시만으로는 "내 값이 맞는지"를 모르지만, 다른 값으로 조회해 보이면 원인이 확정된다.

견해: 최초 대응 체크리스트(11.1.3)에 "의심되는 도메인으로 다시 조회"를 넣어 두면 좋다. 값을 읽는 것과 값을 바꿔 시험하는 것은 다른 행동이고, 후자가 훨씬 빠르게 답을 준다.

11.1.14.3 시나리오 ③ — "파라미터가 적용되지 않는다"

YAML의 노드 이름을 일부러 틀리게 두고 노드를 실행했다(실행 확인).

YAML 최상위 키실제 노드결과
my_node:/real_node기본값 10.0 — 오류도 경고도 없다
/real_node:/real_node99.0 적용
/**:/real_node77.0 적용

틀린 이름은 조용히 무시된다. 파일을 읽었다는 로그도, 매칭 실패 경고도 없다 — "YAML을 줬는데 값이 그대로"가 유일한 증상이다.

진단 순서: ① ros2 param get <노드> <이름>으로 서버에 저장된 값을 본다 → 기본값이면 ② YAML 최상위 키와 ros2 node list의 이름을 글자 그대로 대조한다(앞 슬래시·네임스페이스 포함) → ③ 급하면 /**:로 바꿔 적용되는지 본다. 적용되면 이름 문제가 확정된다(1.9.7·14.3.12.2).

*①의 표현에 주의하라 — param get이 보여 주는 것은 저장값이지 노드가 쓰는 값이 아니다. 값이 제대로 보이는데도 동작이 안 바뀐다면 YAML 문제가 아니라 노드가 시작 때 읽고 캐시한 것이다(83차 실측, 0.7.11.1) — 그때는 노드를 재시작*하거나 add_on_set_parameters_callback을 넣어야 한다.

11.1.14.4 시나리오 ④ — "고쳤는데 안 바뀐다"

같은 패키지를 두 워크스페이스에 두고 AMENT_PREFIX_PATH 순서만 바꿨다(실행 확인).

경로 순서ros2 pkg prefix demo_pkg
B가 앞/tmp/ws_b/install/demo_pkg
A가 앞/tmp/ws_a/install/demo_pkg

ros2 pkg prefix가 실제로 쓰이는 경로를 그대로 알려 준다. 이 장이 첫 확인으로 이 명령을 둔 것이 맞다 — 내가 고친 워크스페이스가 아니라 다른 곳이 나오면 그 자리에서 원인이 확정된다.

함정: 패키지가 Package not found로 나오는 경우가 있다. 경로만 만들고 share/ament_index/resource_index/packages/<패키지> 마커가 없으면 ament가 못 찾는다(0.5.5). 빌드가 중간에 실패했을 때 이 상태가 되므로, "경로는 있는데 못 찾는다"면 마커를 확인하라.

11.1.14.5 시나리오 ⑤ — "컨테이너에서 미발견"

Docker 기본 브리지 네트워크에서 확인했다(실행 확인).

2: eth0@if4370: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 …
$ ros2 multicast receive        # 다른 셸에서
Waiting for UDP multicast datagram...
Received from 172.17.0.5:57330: 'Hello World!'

같은 컨테이너 안에서는 멀티캐스트가 정상 동작한다. eth0에 MULTICAST 플래그가 있고 ros2 multicast가 왕복한다.

즉 "컨테이너라서 디스커버리가 안 된다"는 말은 부정확하다. 진단은 안쪽부터 — 컨테이너 안에서 ros2 multicast send/receive가 되는지 먼저 보고, 되면 그 다음이 네트워크 모드 문제다.

정정: 처음에는 여기에 "막히는 것은 컨테이너 사이와 호스트↔컨테이너"라고 적었는데, 다음 회차에 컨테이너를 둘 기동해 측정해 보니 그것도 틀렸다. 같은 Docker 네트워크의 컨테이너끼리는 별도 설정 없이 통한다 — 실제 경계는 3.9.12에 실측으로 정리했다. 널리 퍼진 말을 검증 없이 옮기면 이렇게 된다는 사례로 남겨 둔다.

11.1.14.6 이 장의 조언이 스스로에게도 적용됐다

검증 중에 pkill -f로 자기 셸을 강제 종료했다(exit 143). 이 장이 11.1.13에 "-f는 자기 자신을 매칭한다"고 적어 둔 바로 그 함정이다.

책의 경고가 실제로 일어나는 것을 확인한 셈이라 남겨 둔다 — 진단 중에는 손이 빨라져 아는 함정도 밟는다. pgrep -x를 쓰는 습관이 그래서 필요하다.

11.1.15 ros2 doctor hello 두 표 읽기 — 실측

11.1.2의 원칙은 "계층 먼저"였다. ros2 doctor hello는 그 원칙을 명령 하나로 실행해 주는 도구인데, 출력의 표가 두 개라는 점을 모르면 그 값을 놓친다. 세 가지 상황을 만들어 측정해 봤다(실행 확인 — Jazzy · ros:jazzy-ros-base 컨테이너 둘 · rmw_fastrtps_cpp).

11.1.15.1 출력의 두 표

두 대(또는 두 컨테이너)에서 동시에 실행하면 상대가 표에 나타난다.

MULTIMACHINE COMMUNICATION SUMMARY
Topic: /canyouhearme, Published Msg Count: 10
Subscribed from:                          ← ① ROS 층 (토픽이 실제로 오가는가)
                Hostname             Msg Count /1.0s
                93f3fa8503b8         10
Multicast Group/Port: 225.0.0.1/49150, Sent Msg Count: 10
Received from:                            ← ② 네트워크 층 (생 멀티캐스트가 닿는가)
                Hostname             Msg Count /1.0s
                93f3fa8503b8         10

①은 ROS 2 토픽, ②는 생 UDP 멀티캐스트다. 두 층을 각각 재 주기 때문에 어느 층에서 끊겼는지가 한 화면에 나온다.

11.1.15.2 세 상황을 만들어 측정해 봤다

상황① Subscribed from② Received from읽는 법
같은 네트워크·같은 도메인채워짐채워짐정상
다른 Docker 네트워크비어 있음비어 있음네트워크 층 문제 — 방화벽·서브넷·멀티캐스트 차단(3.9)
도메인 불일치(0 ↔ 42)비어 있음채워짐네트워크는 정상, ROS 설정 문제 — 도메인·RMW·QoS(1.1·3.8)

셋째 줄이 이 도구의 존재 이유다. "패킷은 오는데 토픽은 안 온다" — 이 조합이 보이면 네트워크를 더 볼 필요가 없다. 방화벽 규칙을 며칠 뒤지는 대신 ROS_DOMAIN_ID·RMW_IMPLEMENTATION·QoS를 본다.

견해: 노드가 서로 안 보일 때 두 번째로 칠 명령이 이것이다. 첫 번째는 echo $ROS_DOMAIN_ID(11.1.14.2), 그다음이 ros2 doctor hello다. 순서를 이렇게 두는 이유는 도메인 확인이 1초면 끝나는 데 비해 doctor hello는 양쪽에서 동시에 실행해야 하기 때문이다 — 비용이 낮은 것부터.

네 번째 조합(① 채워짐 · ② 비어 있음)은 만들지 못했다. 멀티캐스트 없이 디스커버리가 이미 성립한 경우 — Discovery Server나 ROS_STATIC_PEERS(3.3.7) — 라면 나올 법하지만 측정해 보지 않았으므로 추측이다.

11.1.15.3 실행할 때의 함정 둘

① 양쪽에서 동시에 실행해야 한다. 한쪽만 실행하면 표가 비고, 그것을 "통신 실패"로 오독하기 쉽다. 자기 자신은 표에 나오지 않는다 — 표에 나타나는 것은 상대뿐이다.

② Ctrl-C가 아니면 잘 종료되지 않는다. 스크립트에서 timeout으로 감쌌더니 SIGTERM으로 종료되지 않아 명령이 계속 매달렸다.

# ❌ 안 끝난다
timeout 15 ros2 doctor hello

# ✅ 확실히 끝낸다
timeout -s KILL 15 ros2 doctor hello

멀티캐스트 수신 스레드가 recvfrom에서 블로킹된 채 남기 때문으로 보인다(확인 필요: 정확한 원인). 자동화 스크립트에 넣을 때는 -s KILL을 쓰라. 11.1.13의 교훈과 같은 계열이다 — 진단 명령 자체가 진단을 방해하는 경우가 있다.

11.1.11 함정 카탈로그

#함정증상대처
1위 계층부터 의심오래 걸림아래부터
2/diagnostics 미확인답을 두고 헤맴먼저 보기
3df·dmesg 미확인근본 원인 놓침체크리스트
4재부팅 먼저증거 소실데이터 먼저
5여러 변경 동시인과 불명하나씩
6추측 기반 설정 변경 반복악화되돌리고 분석
7원인 모른 채 종료재발사후 분석
8관찰 기록 없음단서 상실사람이 본 것 기록
9시각 미기록로그 대조 불가시각 명시
10GUI 의존현장에서 무력CLI 숙련
11tmux 미사용세션 끊김습관화
12안전 이상을 디버깅으로위험정지·전문가
13사람 탓 사후 분석정보 은폐구조를 물음
14진단 없는 시스템매번 어려움사전 설계
15버전 불명분석 시작 불가버전 노출
16pgrep -f 로 PID 찾기자기 셸까지 매칭됨 · PID 여러 개pgrep -x·대괄호 grep·PID 변수(11.1.13)
17환경 변수 값만 읽고 넘어감내 값이 맞는지 알 수 없다의심되는 값으로 다시 조회해 본다(11.1.14.2)
18doctor hello를 한쪽에서만 실행표가 비어 통신 실패로 오독양쪽 동시 실행(11.1.15.3)
19컨테이너면 디스커버리 실패로 단정--network host부터 켜게 됨경계를 먼저 좁힌다(3.9.12)

11.1.12 체크포인트

  • 계층을 아래부터 확인하는 이유 셋을 말할 수 있는가.
  • 최초 대응 체크리스트에서 자주 원인이 되는 다섯 항목은?
  • /diagnostics_agg를 가장 먼저 보라는 조언의 근거는?
  • "아무 데이터도 없다"의 진단 흐름과, 각 분기의 원인 후보를 말할 수 있는가.
  • 간헐적 문제에서 결정적인 것은 무엇인가.
  • "고쳤는데 안 바뀐다"의 최다 원인과 확인 방법은?
  • 현장 제약 여덟 가지와, 가장 아까운 행동은 무엇인가.
  • 재부팅 전에 확보할 데이터와, 스크립트를 미리 두는 이유는?
  • 데이터 확보 우선순위 일곱 가지 중, 로그에 없는 정보는?
  • 에스컬레이션 신호 여섯 가지와, 자각해야 할 상태는?
  • "한 번에 하나만 바꾸라"의 근거 둘은?
  • 사후 분석에서 가장 가치 있는 두 질문과 그 이유는?
  • 사람을 탓하지 않는 사후 분석이 실용적인 이유는?
  • 진단 가능한 시스템의 설계 요소 열두 가지를 들 수 있는가.
  • "현장에서 SSH만으로 진단할 수 있는가"라는 기준의 의미는?
  • 제11부가 반복한 세 원칙은?
  • QoS 불일치와 도메인 불일치를 각각 어느 명령 한 번으로 확정할 수 있는가(11.1.14).
  • ros2 doctor hello의 표 두 개는 각각 무엇을 측정하는가. 한쪽만 채워졌을 때 어디를 봐야 하는가(11.1.15).

여기까지가 제11부 도구·관측성이다. 기록(11.2·11.3), 시각화(11.4·11.5), 내부 관측(11.6), 측정(11.7), 그리고 현장(11.1).

11.7에서 성능을 측정하는 법을 다뤘다면, 다음 질문은 자연스럽다 — 어떻게 빠르게, 그리고 시간을 지키도록 만드는가. 실시간 제약이란 무엇이고, 리눅스에서 어디까지 가능하며, ROS 2의 어느 부분이 실시간을 방해하는가. 제12부 실시간·성능(12.1~12.6) 의 주제다.