그리고 Raspberry Pi Imager 프로그램에서 OS 선택 -> Use Custom (사용자 지정) -> 다운로드한 ext4 이미지 선택하고, 이미지를 디스크에 굽는다.
2. 부팅 확인
파이와 노트북을 랜선으로 연결한다. 이후 파이에 전원을 넣고 약 1분간 기다리고 노트북 브라우저에서 192.168.1.1 로 접속한다.
그럼 위와같은 화면이 뜰텐데, 기본 비밀번호는 없으니 그냥 로그인버튼을 누르면 된다.
3. 유선랜카드 드라이버 설치
파이와 연결은 확인했으니 유선랜카드 드라이버를 다운로드 받아야하는데, 지금 파이에는 인터넷이 없는 상태다. 근데 지금 이걸 공유기에 연결하게된다면, 공유기에서 할당해주는 50번 대역의 ip와, OpenWrt 자체가 갖고있는 1번 대역의 ip가 충돌할것으로 예상되어 그냥 내 노트북 와이파이 인터넷을 파이쪽에 공유해주려고 한다.
3-1. 인터넷 설정
제어판 > 네트워크 및 인터넷 > 네트워크 및 공유 센터 > 어댑터 설정 변경 으로 들어간다.
여기서 Wi-Fi를 우클릭해서 속성으로 들어간다.
그리고 위처럼 공유 탭에서 허용을 눌러주고, 홈 네트워킹 연결은 이더넷으로 설정해준다.
그리고 이번에는 이더넷을 우클릭해 속성을 들어간 뒤에, 네트워킹 탭에서 인터넷 프로토콜 버전 4를 눌러 속성으로 들어간다.
사진처럼 노트북의 ip를 192.168.1.2 로 지정해주면 된다.
3-2. 파이에서 게이트웨이 지정
192.168.1.1 로 접속하고 로그인하면 위와같은 화면이 뜰 것이다. Network > Interfaces 로 들어간 후, 아래 화면에서 lan 옆의 Edit을 누른다.
위 화면에서 IPv4 gateway 란에 192.168.1.2 를 입력하고, Advanced Settings 탭으로 들어가서 Custom DNS Server 를 8.8.8.8 로 입력하고 저장한다. 반드시 save 누른 후 나오는 화면에서 save & apply도 눌러야 한다. 그래야 적용되기 때문
3-3. 윈도우 방화벽 해제
윈도우 검색창에 '방화벽 상태 확인' 검색, 좌측의 'Windows Defender 방화벽 설정 또는 해제' 클릭해서 방화벽을 해제해준다. 드라이버 설치 이후 다시 활성화하면 된다.
3-4. 드라이버 설치
인터넷에 연결시킨 후, 192.168.1.1 로 접속하고 로그인하면 위와같은 화면이 뜰 것이다. 여기서 상단의 System > Software 로 들어가서, 아래의 update lists 버튼을 누른다.
Error가 뜨지 않았다면, 아래와 같이 Filter에 kmod-usb-net-rtl8152 를 입력하고 install 버튼을 눌러 설치한다.
설치하면 아래처럼 Network > Interfaces > Devices 에서 드디어 새로운 어댑터인 eth1 이 잡힌걸 확인할 수 있다!
유선랜카드와 랜선이 연결되어있지 않고 유선랜카드만 연결해도 떠야한다.
4. WAN 인터페이스 만들기
Network > Interfaces > Add new interface 로 들어간 후, 위처럼 wan (소문자), DHCP client, eth1를 지정한 후 create interface를 누른다.
create interface를 누르면 위와같은 창이 뜰텐데, Firewall Settings 탭으로 들어가 wan을 지정해준 후 save해준다. 이후 save & apply도 눌러야 적용된다.
아예 모니터를 연결하고 재부팅을 해주니 이젠 다시 정상적으로 네트워크도 연결이 되었고, 서비스들도 다시 돌아가기 시작했다.
일단 인터넷만의 문제는 아니었던게, 서버 접속이 끊긴 12시 30분부터 정확하게 prometheus의 기록이 끊겨있다.
또 보면 죽기 직전까지 급격하게 디스크 사용량이 계단식으로 증가하는걸 볼 수 있는데,
막상 집에 와서 접속해보니 디스크 상태는 너무 널널한 상태였다.
일단 crash라는 로그가 보이긴 하고,
디스크 사용량이 계단식으로 늘어났던게 좀 불안하긴 하지만, 원인을 찾을 수 없으니 할 수 있는 조치만 해보려고 한다.
조치
1. 커널 사망 -> 재부팅 설정
# 커널 패닉 발생 시 10초 후 자동 재부팅
echo "kernel.panic = 10" | sudo tee -a /etc/sysctl.conf
echo "kernel.panic_on_oops = 1" | sudo tee -a /etc/sysctl.conf
# 설정 즉시 적용
sudo sysctl -p
[ros2_control_node-1] [ERROR] [DynamixelSDKWrapper] [TxRxResult] There is no status packet! [ros2_control_node-1] *** stack smashing detected ***: terminated [ros2_control_node-1] #7 Object "libturtlebot3_manipulation_hardware.so", at ..., in DynamixelSDKWrapper::write(...)
2. 원인
처음에는 OpenCR의 펌웨어와, ros2 humble간의 패킷 사이즈가 버전 차이 이슈로 맞지 않나 싶어서 아두이노IDE로 OpenCR의 펌웨어를 다시 올려보기도 했고, ros2 패키지를 처음부터 다시 빌드하기도 했었으나,
라즈베리파이 클러스터에서 실시간 영상 데이터를 노드별로 분산 처리하면 처리량을 향상시킬 수 있는지를 검증하기 위해 진행한 프로젝트다. 수평확장성을 위해 마이크로 서비스 아키텍처로 설계했고, 각 노드는 독립적으로 프레임을 소비/처리하는 구조를 적용했다.
기술 스택
FastAPI, Redis, YOLO, K3S
최종 결과물
웹에 배포된 화면배포된 파드들
초기에는 라즈베리파이만으로 구성했지만, 마스터노드를 N100기반 미니pc로 교체하면서 파드 3개에서 안정적으로 consumer를 실행할 수 있었다. 이를 통해 전체 처리 성능이 약 16fps까지 증가했고, 노드 수 증가에 따라 5 -> 10 -> 16fps형태의 선형적 처리량 향상을 확인했다.
카메라가 장착된 라즈베리파이 클러스터
마이크로 서비스 구조
최종적으로 완성된 아키텍처
이 구조는 '최신 프레임만 소비하고, 오래된 프레임은 즉시 폐기하는 방식'을 통해 지연 누적 문제를 완전히 제거하고, 노드 수당 성능이 거의 선형적으로 증가하는 형태를 만들었다.
소회
프로젝트 초기에는 메시지큐 기반으로 프레임을 배포할 계획이었다. 심지어 프레임을 NFS에 저장한 뒤 메타데이터만 메시지큐로 전달하는 방식, 메시지큐와 Redis를 혼용하는 방식 등 다양한 구조를 시도했다. 요소들을 이해하고 있음에도, 메시지큐를 써야한다는 전제에서 출발하다 보니 구조가 계속 복잡해지고 스스로도 방향성을 잃게 됐다.
문제를 해결하기 위해 기술을 선택하는 것이 아니라, 이미 선택한 기술을 사용하기 위해 문제를 맞추려 했던 것이 가장 큰 원인이었다. 분산처리를 실험해보겠다는 초기 목적은 사라지고, 어느 시점부터는 메시지큐를 사용하는 것 자체가 목표로 변해 있었다.
하지만 필요성을 하나씩 다시 질문하다보니, 오히려 메시지큐가 필요 없다는 결론에 도달했다. 결국 가장 중요한 것은 실시간 처리, 그리고 프레임 누적 방지 였다. 최신 프레임만을 가져가고, 지나간 프레임은 버려도 무방한 구조라는 점을 다시 정의했을 때 Redis 하나로도 전체 파이프라인이 훨씬 단순하고 효과적으로 동작했다.
향후계획
이번 프로젝트를 통해 저성능 엣지 디바이스에서도 마이크로서비스를 이용한 수평 확장이 충분히 가능하다는 점을 검증했다. 하지만 현재 구조에서는 여전히 한 프레임을 처리하는 데 약 0.2초가 소요되며, 이는 어떤 방식의 수평 확장으로도 줄일 수 없는 지연이다. 이 지연을 개선하려면 모델 자체를 최적화하거나 경량화하는 방향이 필요하다.
따라서 다음 목표는 아래와 같다.
1. 모델 경량화 및 성능 개선 실험 - YOLO 계열 모델을 기준으로 모델 경량화를 경험해볼 예정이다. 2. 프레임 단위 분산처리 파이프라인을 경량 프레임워크로 발전 - 쿠버네티스 기반 엣지 분산 영상처리 파이프라인을 단순화해 쉽게 재사용 가능한 소형 프레임워크 형태로 배포해보고 싶다. 3. ROS2 + 쿠버네티스 실험 - ROS2의 컴포넌트 간 통신 구조가 마이크로 서비스와 매우 유사하다는 점에서, 로봇 시스템과 쿠버네티스 클러스터를 접목시키는 새로운 프로젝트를 시도해볼 계획이다.