우분투 로봇 현장 디버깅: SSH 접속, 유선랜 직결, 로그·CPU·네트워크 상태 점검 명령

이 글의 핵심

로봇이 이상할 때 현장에서는 노트북을 유선랜으로 연결하고 SSH로 들어가 무엇이 문제인지 좁혀야 합니다. 접속, 로그, CPU·메모리·디스크, 네트워크, ROS 2 순서로 쓰는 명령을 정리하고, 한 번에 상태를 보는 스크립트를 만들어 실행한 결과를 함께 적었습니다. 컨테이너에서 확인한 부분과 실제 로봇이 필요한 부분을 구분했습니다.

로봇이 멈췄다는 연락을 받고 현장에 가면 하는 일이 거의 같다. 노트북을 로봇에 랜선으로 연결하고, SSH로 들어가고, 로그와 자원 상태를 보고, 네트워크가 정상인지 확인한다. 이 글은 그 순서와 각 단계에서 쓰는 명령을 정리한다. 명령은 우분투 24.04 컨테이너(aarch64)에서 직접 실행해 출력을 확인했고, ROS 2 명령은 ROS 2 Jazzy 컨테이너에서 확인했다. 실제 로봇, 실제 랜선, systemd는 이 환경에 없었으므로 그 부분은 “확인하지 못한 것”에 따로 적었다.

현장 점검 순서

  1. 유선랜으로 직결하고 연결을 확인한다 (ip, ping)
  2. SSH로 접속한다
  3. 상태를 한 번에 본다 (CPU, 메모리, 디스크, 네트워크, 포트)
  4. 로그를 본다 (journalctl, dmesg, 애플리케이션 로그)
  5. 로봇 소프트웨어 상태를 본다 (ROS 2 노드와 토픽)
  6. 원인을 좁히고 증거(로그, 명령 출력)를 저장한다

순서의 이유는 단순하다. 접속이 안 되면 아무것도 볼 수 없고, 자원 문제(CPU, 메모리, 디스크)는 몇 초면 확인되며, 로그는 그다음에 어디를 볼지 정하면 시간이 줄어든다.

1. 유선랜 직결

무선망이 불안정하거나 로봇의 IP를 모를 때는 랜선으로 직접 연결한다. 노트북의 유선 인터페이스에 로봇과 같은 대역의 고정 IP를 준다.

ip -br link                                   # 유선 인터페이스 이름 확인 (예: enp3s0)
sudo ip addr add 192.168.100.2/24 dev enp3s0  # 로봇이 192.168.100.x 대역일 때
sudo ip link set enp3s0 up
ip -br addr                                   # 주소가 붙었는지 확인
ping -c 3 192.168.100.10                      # 로봇 IP

컨테이너에서 가짜 인터페이스(dummy0)에 같은 방식으로 주소를 붙인 결과는 다음과 같았다.

lo               UNKNOWN        127.0.0.1/8 ::1/128
eth0@if30        UP             192.168.215.2/24
dummy0           UNKNOWN        192.168.100.2/24 fe80::d8f5:d0ff:fee0:44e1/64

ip route에는 192.168.100.0/24 dev dummy0 proto kernel scope link src 192.168.100.2가 생겼고, 이 주소로 ping은 2개 보내 2개 응답(0% loss)을 받았다. 이 ip addr add로 준 주소는 재부팅하면 사라지므로 현장 임시 용도다. 계속 쓰려면 NetworkManager나 netplan 설정이 필요한데, 이 글에서 그 설정은 실행해 보지 않았다.

로봇의 IP를 모를 때

  • 로봇 라벨, 설정 문서, 이전 담당자의 인수 문서를 먼저 본다.
  • 로봇에 모니터와 키보드를 연결할 수 있으면 ip -br addr로 확인한다.
  • 같은 대역에 있는 장비를 찾는 방법(ARP 테이블, 스캔 도구)이 있지만, 이 글에서 시험하지 않았다. 스캔은 현장 네트워크 정책에 따라 허용되지 않을 수 있으니 확인하고 쓴다.

2. SSH 접속

ssh robot@192.168.100.10
ssh -o ConnectTimeout=5 robot@192.168.100.10      # 응답이 없을 때 오래 기다리지 않기
scp robot@192.168.100.10:/var/log/myrobot/app.log .   # 로그 파일 가져오기

접속이 자주 필요하면 ~/.ssh/config에 별칭을 만든다.

Host robot1
  HostName 192.168.100.10
  User robot

그러면 ssh robot1로 접속한다. 오래 걸리는 확인 작업은 tmux를 먼저 열고 그 안에서 한다. 우분투 24.04 저장소의 tmux는 3.4였고, SSH가 끊겨도 tmux 안의 작업은 남는다(이 성질은 일반적으로 알려진 동작이며 이 글에서 연결 끊김을 재현해 보지는 않았다). SSH 자체는 SSH 원격 접속 가이드를 참고한다.

3. 상태를 한 번에 보는 스크립트

접속하자마자 같은 명령을 매번 치지 않도록 읽기만 하는 스크립트를 둔다.

#!/usr/bin/env bash
# 로봇(우분투)에 접속한 직후 한 번에 상태를 보는 스크립트. 읽기만 한다.
echo "== 시스템"; hostname; uptime; echo "코어 수: $(nproc)"
echo "== 메모리(MB)"; free -m | awk 'NR==1||NR==2||NR==3'
echo "== 디스크"; df -h / | tail -1
echo "== CPU 상위 3개"; ps -eo pid,pcpu,pmem,comm --sort=-pcpu | head -4
echo "== 네트워크 인터페이스"; ip -br addr
echo "== 기본 경로"; ip route | grep default || echo "(기본 경로 없음)"
echo "== 열려 있는 TCP 포트"; ss -tlnp 2>/dev/null | awk 'NR==1||NR>1' | head -8
echo "== 최근 커널 경고"; (dmesg --level=err,warn 2>/dev/null | tail -3) || true
[ -r /sys/class/thermal/thermal_zone0/temp ] && echo "온도: $(( $(cat /sys/class/thermal/thermal_zone0/temp)/1000 ))C" || echo "온도 센서 파일 없음(이 환경)"

컨테이너에서 CPU를 100% 쓰는 yes 프로세스 2개와 8080 포트로 파이썬 웹 서버를 띄운 상태에서 실행한 결과다.

== CPU 상위 3개
    PID %CPU %MEM COMMAND
   1997 99.6  0.0 yes
   1999 99.3  0.0 yes
   2001 33.3  0.0 bash
== 네트워크 인터페이스
lo               UNKNOWN        127.0.0.1/8 ::1/128
eth0@if33        UP             192.168.215.2/24
== 기본 경로
default via 192.168.215.1 dev eth0
== 열려 있는 TCP 포트
LISTEN 0      5            0.0.0.0:8080      0.0.0.0:*    users:(("python3",pid=1995,fd=3))

ps --sort=-%cpu로 범인이 바로 보이고, ss -tlnp에서는 포트를 어느 프로세스가 열었는지 나온다. 이 컨테이너에서는 dmesg 결과가 비어 있었고 온도 센서 파일이 없었다. 실제 로봇(특히 ARM 보드)에서는 둘 다 의미 있는 출력이 나올 수 있으나 이 글에서 확인하지 못했다.

4. CPU, 메모리, 디스크 읽는 법

명령볼 것
uptimeload average 세 값(1·5·15분). 코어 수(nproc)와 비교
top -bn1 | head -12%Cpu(s)의 us/sy/id/wa, 메모리 avail Mem
ps -eo pid,pcpu,pmem,comm --sort=-pcpu | headCPU를 많이 쓰는 프로세스
free -mavailable 열이 실제 여유. free 열만 보면 안 됨
df -h /사용률(Use%)과 Avail
vmstat 1 5b(대기), si/so(스왑), wa(I/O 대기)

몇 가지는 이 컨테이너 실험에서 직접 확인했다.

  • load average는 천천히 오른다. yes 프로세스 2개를 띄운 뒤 3초 뒤의 1분 load average가 0.56이었고, 같은 시점의 top에서는 %Cpu(s) 합이 약 26%(us 8.3, sy 17.9)였다. 문제가 막 시작된 직후에는 load average보다 top과 ps가 현재를 더 잘 보여 준다.
  • 사용률과 load는 다른 지표다. load average는 8코어(nproc 8) 기준으로 8 정도까지는 코어를 다 쓰는 수준으로 읽는다. 코어 수보다 크게 높으면 대기가 쌓이는 중이다.
  • free -m에서는 used 607MB인데 available은 5,384MB였다. 캐시 메모리는 필요할 때 회수되므로 available이 판단 기준이다.
  • vmstat의 id(유휴)가 100이고 wa가 0이면 CPU·I/O 병목이 없는 상태다. 위 컨테이너는 그랬다.

디스크가 가득 차면 로그 기록과 파일 저장이 실패하고 로봇 프로그램이 이상하게 죽는 일이 있다. df -h와 함께 큰 디렉터리를 찾는 du -h --max-depth=1 /var | sort -h | tail을 쓴다. 이 du 조합은 이 글에서 따로 실행하지 않았다.

5. 네트워크 상태

ip -br addr             # 인터페이스별 주소
ip route                # 기본 경로(default via ...)
ss -tlnp                # 대기 중인 TCP 포트와 프로세스
ping -c 3 192.168.100.1 # 게이트웨이나 다른 장비까지 도달 여부

로봇이 서버나 관제와 통신하지 못할 때는 이 순서로 좁힌다. 인터페이스가 UP인지, 기본 경로가 있는지, 상대 주소가 ping에 응답하는지, 로봇이 기다리는 포트가 열려 있는지를 차례로 본다. 포트가 열려 있지 않다면 ss -tlnp에 안 나오고, 이는 프로세스가 죽었거나 다른 주소에 바인딩된 경우일 수 있다. 8080 포트를 0.0.0.0에 연 위 예시처럼 Local Address 열이 127.0.0.1이면 외부에서 접속할 수 없다.

6. 로그 보기

journalctl -u myrobot.service -n 200 --no-pager   # 서비스 로그 마지막 200줄
journalctl -u myrobot.service -f                   # 실시간으로 따라가기
journalctl -p err -b                               # 이번 부팅의 오류 이상 로그
dmesg -T | tail -50                                # 커널 메시지(디스크, USB, 전원 관련)
tail -f /var/log/myrobot/app.log                   # 애플리케이션 로그 파일

journalctl의 옵션은 journalctl 가이드에 자세히 있다. 이 글의 컨테이너에는 systemd가 없어서 위 journalctl 명령은 실행해 보지 못했다. 위 명령들은 표준 사용법이고 서비스 이름(myrobot.service)과 로그 경로는 예시다.

7. ROS 2 상태 확인

ROS 2를 쓰는 로봇이라면 소프트웨어 상태는 노드와 토픽으로 본다. ROS 2 Jazzy 컨테이너에 데모 talker·listener를 띄우고 실행한 결과다.

source /opt/ros/jazzy/setup.bash
ros2 node list          # /listener, /talker
ros2 topic list         # /chatter, /parameter_events, /rosout
ros2 topic info /chatter

ros2 topic info /chatter는 Type: std_msgs/msg/String, Publisher count: 1, Subscription count: 1을 보여 줬다. 발행자와 구독자 수가 0이면 노드가 안 떠 있거나 서로 못 찾는 상태다. ros2 node info /talker는 그 노드의 구독, 발행, 서비스 목록을 보여 줬고, ros2 topic echo --once /chatter는 메시지를 하나만 출력했다(data: 'Hello World: 11').

주의할 점이 하나 있었다. 위 환경에서 ros2 topic hz /chatter를 노드를 띄운 직후 짧은 시간만 실행하자 WARNING: topic [/chatter] does not appear to be published yet가 나왔다. 토픽 탐색에는 시간이 걸릴 수 있으니 이 경고만 보고 발행이 없다고 판단하지 말고 다시 실행해 보라. 여러 장비가 같은 네트워크에 있을 때 ROS_DOMAIN_ID가 서로 다르면 노드가 보이지 않는다는 것은 일반적으로 알려져 있으나, 이 글에서 멀티 머신 구성은 시험하지 않았다. 통신 설정 점검은 ros2 doctor와 QoS 호환성 실험 글을 참고할 수 있다.

자주 쓰는 명령 모음

목적명령
내 주소와 인터페이스ip -br addr
경로ip route
연결 확인ping -c 3 <IP>
열린 포트와 프로세스ss -tlnp
부하uptime, top -bn1 | head, vmstat 1 5
CPU 점유 프로세스ps -eo pid,pcpu,pmem,comm --sort=-pcpu | head
메모리free -m (available 확인)
디스크df -h
서비스 로그journalctl -u <서비스> -n 200 --no-pager
커널 메시지dmesg -T | tail
ROS 2 노드·토픽ros2 node list, ros2 topic list, ros2 topic info <토픽>

확인하지 못한 것

  • 실제 로봇과 실제 랜선 직결(이 글의 ip 실험은 컨테이너의 가짜 인터페이스로 했다)
  • journalctl, dmesg, 온도 센서(컨테이너에는 systemd와 센서가 없다)
  • NetworkManager·netplan으로 고정 IP를 영구 설정하는 방법
  • 멀티 머신 ROS 2(DDS) 통신 문제, 배터리·모터 드라이버·CAN 같은 하드웨어 상태

로봇 자체의 기본 구조는 AMR 기본 가이드에서 다룬다.