TinyPilot: 100달러 이하로 만드는 KVM over IP
원문은 Michael Lynch님이 에 게재했습니다. 이 블로그 구독하기
TinyPilot은 컴퓨터를 원격으로 제어하기 위해 만든 저렴한 오픈소스 장비다. 운영체제가 부팅되기 전부터 동작하기 때문에, 나는 베어메탈 홈랩 서버에 새로운 OS를 설치하거나 부팅 실패를 디버깅할 때 TinyPilot을 사용한다.
이 글에서는 TinyPilot을 만들게 된 경험을 자세히 풀고, 라즈베리 파이를 이용해 100달러 이하로 직접 만드는 방법을 소개한다.

Microsoft Surface의 Chrome에서 TinyPilot으로 Ubuntu 노트북을 제어하는 모습
인생 이야기는 됐고 만드는 방법만 알려줘
TinyPilot을 개발하며 겪은 흥미진진한 성공과 좌절의 이야기를 건너뛰고 싶은 심술쟁이라면, “직접 TinyPilot 만들기” 섹션으로 바로 넘어가면 된다.
데모
왜 TinyPilot인가?
몇 년 전, 소프트웨어 테스트용으로 홈 서버를 직접 만들었다. 꽤 값진 투자였고, 지금도 매일 사용하고 있다.

2017년에 가상 머신을 운영하기 위해 만든 홈랩 서버
이 서버에는 키보드나 모니터가 연결되어 있지 않다. ssh나 웹 인터페이스로 접속하기 때문이다. 대체로 편리한 구성이지만, 사소한 문제가 생기면 엄청난 골칫거리가 된다.
몇 달에 한 번씩은 꼭 뭔가를 잘못 건드려 서버가 부팅되지 않거나 네트워크에 연결되지 않아 아예 접근이 차단된다. 다시 살리려면 이것저것 다 뽑고 서버를 책상까지 끌고 와서, 데스크톱의 키보드와 모니터에 연결하려고 케이블을 이리저리 바꿔 끼워야 한다.
상용 솔루션
지인들은 iDRAC 사용 경험을 극찬했다. Dell 서버에 탑재된 칩으로, 시스템 전원이 켜지는 순간부터 가상 콘솔을 제공한다. 다음 홈 서버에 iDRAC을 도입할까 잠깐 고민했지만, 가격을 보고 바로 포기했다. 라이선스만 300달러에, 비싼 전용 하드웨어도 따로 필요했다.

Dell iDRAC 기술 라이선스는 머신당 300달러이며, 하드웨어 비용은 별도다
다음으로 상용 KVM over IP 솔루션을 알아봤다. 기능은 Dell의 iDRAC과 비슷하지만, 컴퓨터의 키보드·비디오·마우스 포트에 연결하는 외장 장치다(그래서 이름이 KVM이다). 안타깝게도 가격은 더 비싸서 대당 500~1,000달러에 이른다.

상용 KVM over IP 장비는 대당 500~1,000달러다.
서버를 끌고 다니기 귀찮은 성격이지만, 1년에 몇 번 케이블을 바꿔 끼우는 수고를 덜자고 500달러를 쓰는 건 도저히 합리화할 수 없었다.
그래서 지극히 비합리적인 프로그래머라면 할 법한 일을 했다. 수백 시간을 들여 KVM over IP를 직접 만든 것이다.
라즈베리 파이로 KVM over IP 만들기
라즈베리 파이는 작고 저렴한 싱글보드 컴퓨터다. 풀 데스크톱 운영체제를 돌릴 만큼 성능이 충분하면서도 가격이 30~60달러에 불과해 취미 활동가와 프로그래머들에게 인기 있는 도구다.

라즈베리 파이는 칩 하나에 담긴 완전한 컴퓨터로 가격은 30~60달러에 불과하다.
최근 버전의 파이는 USB On-The-Go(USB OTG)를 지원해, 키보드나 USB 메모리, 마이크 같은 USB 장치인 척할 수 있다.
Pi를 KVM으로 쓰겠다는 아이디어의 개념 증명으로, Key Mime Pi라는 간단한 웹 앱을 만들었다.

Key Mime Pi, 키보드 전달만 지원하던 TinyPilot의 초기 전신
Key Mime Pi는 USB로 다른 컴퓨터에 연결되어 USB 키보드로 인식된다. 웹 페이지를 띄워 JavaScript 키 이벤트를 감지하고, 사용자가 타이핑하면 그 이벤트를 가짜 USB 키보드를 통해 키 입력으로 변환한다. 그 결과 대상 컴퓨터에 키 입력이 나타난다. 이 동작은 이전 글에서 자세히 설명했다.
영상 캡처라는 난관
화면에 무엇이 뜨는지 볼 수 없으면 키보드 전달만으로는 별 소용이 없다. 당연한 다음 단계는 서버의 화면 출력을 파이에서 캡처해 브라우저에 띄우는 방법을 찾는 것이었다.
영상 캡처를 위해 처음 시도한 건 Lenkeng LKV373A HDMI 익스텐더였다. Daniel Kučera(일명 danman)가 이 장치를 훌륭하게 리버스 엔지니어링해 두었다. eBay의 중국 판매자들에게서 40달러 정도에 구할 수 있어 최선의 선택처럼 보였다.

Lenkeng LKV373A HDMI 익스텐더는 내가 처음 시도한 HDMI 영상 캡처 장치였다.
영상 캡처가 까다로웠던 이유는 LKV373A 송신기가 본래 영상 캡처 장치가 아니기 때문이다. 원래 용도는 네트워크 스트림을 다시 HDMI 출력으로 변환하는 LKV373A 수신기와 짝을 이루는 것이다. danman의 분석을 통해 영상 스트림을 가로채 캡처하는 방법이 밝혀졌지만, LKV373A는 표준에서 벗어난 변형 RTP 프로토콜을 써서 이를 이해하는 영상 도구가 거의 없었다.
다행히 danman이 ffmpeg에 패치를 기여해 LKV373A의 엉뚱한 동작을 처리할 수 있게 되었고, 덕분에 ffmpeg의 비디오 플레이어로 스트림을 재생할 수 있었다:
ffplay -i udp://239.255.42.42:5004
ffplay로 LKV373A의 영상 스트림을 재생하는 모습
여기서 프로젝트 내내 나를 괴롭힌 문제, 바로 지연 시간을 처음으로 맛봤다. 대상 컴퓨터와 데스크톱의 영상 재생 사이에 거의 1초의 지연이 있었다.

LKV373A는 재인코딩 전부터 838밀리초의 지연을 만들었다.
스트림을 빠르게 해보려고 ffplay의 다양한 커맨드라인 옵션을 만져봤지만 800밀리초 이하로 줄이지 못했다. 그것도 고성능 GPU와 CPU를 갖춘 데스크톱에서의 결과였다. 비루한 라즈베리 파이에서의 성능을 생각하면 전망이 밝지 않았다.
다행히 완전히 우연한 계기로 더 나은 해결책을 찾았다.
HDMI to USB 동글
트위터를 멍하니 스크롤하다가 Arsenio Dev의 트윗을 우연히 보게 됐다. 그가 막 구매한 저렴한 HDMI to USB 동글에 대한 내용이었다:

더 나은 영상 캡처 솔루션을 알려준 Arsenio Dev의 트윗
1080p 해상도에 초당 30프레임으로 영상을 캡처한다는 건 너무 좋아 보여서, eBay에서 하나 주문했다. 배송비 포함 11달러에 불과했다. 정식 명칭도 모르겠다. 브랜드명조차 없어서 그냥 “HDMI 동글”이라고 부르겠다. 여러 변형이 있지만 모두 같은 MacroSilicon MS2109 칩에 다른 케이스를 씌운 것뿐이다.

eBay에서 무료 배송에 11.20달러에 판매 중인 HDMI to USB 동글
며칠 뒤 장치가 도착했을 때 정말 놀랐다. 라즈베리 파이에 꽂자마자 별다른 설정 없이 UVC 영상 캡처 장치로 인식됐다.
$ sudo v4l2-ctl --list-devices
bcm2835-codec-decode (platform:bcm2835-codec):
/dev/video10
/dev/video11
/dev/video12
UVC Camera (534d:2109): USB Vid (usb-0000:01:00.0-1.4): <<< HDMI capture dongle
/dev/video0
/dev/video1몇 분 만에 HDMI 영상을 캡처해 재스트리밍할 수 있었다:
# On the Pi
ffmpeg \
-re \
-f v4l2 \
-i /dev/video0 \
-vcodec libx264 \
-f mpegts udp://10.0.0.100:1234/stream
# On my Windows desktop
ffplay.exe -i udp://@10.0.0.100:1234/stream게다가 엄청나게 편리했다. LKV373A는 벽돌만 한 크기에 전원과 이더넷 케이블이 따로 필요했지만, HDMI 동글은 USB 메모리만 한 크기에 USB 포트 하나면 충분했다.

Lenkeng LKV373A HDMI 익스텐더(왼쪽)는 HDMI 동글(오른쪽)보다 크고 연결도 더 많이 필요했다.
유일한 문제는 또다시 지연 시간이었다. 파이에서 재송출한 영상 스트림은 원본 컴퓨터보다 7~10초나 뒤처졌다.

파이에서 ffmpeg로 영상을 스트리밍하자 최대 10초의 지연이 발생했다.
이 지연이 동글 자체 때문인지, 파이의 ffmpeg 때문인지, 데스크톱의 ffplay 때문인지 알 수 없었다. Arsenio Dev는 지연이 20ms라고 했으니, ffmpeg의 난해하고 신비로운 커맨드라인 옵션들을 파고들면 더 빠른 성능을 낼 수 있을 것 같았다.
또 한 번의 행운이 그 끔찍한 작업을 하지 않게 해줬다.
비슷한 프로젝트에서 힌트 얻기
이전 블로그 글을 올렸을 때 Max Devaev로부터 댓글을 받았다. 그의 프로젝트인 Pi-KVM을 한번 보라는 권유였다.

Max Devaev가 자신의 Pi-KVM 프로젝트를 알려줬다.

브레드보드에 대한 내 이전 경험은 실수로 녹여버린 것이 전부였다.
Pi-KVM을 잠깐 살펴본 적은 있었지만, 브레드보드와 납땜이 필요하다는 요구사항 때문에 겁을 먹고 물러났었다.
Max의 제안에 Pi-KVM을 다시 살펴봤고, 특히 그가 영상 지연 문제를 어떻게 해결했는지에 주목했다. 그가 uStreamer라는 도구로 영상을 캡처한다는 것을 알게 됐다.
참고: Max와 더 얘기를 나눠보니 Pi-KVM도 납땜이나 브레드보드 없이 만들 수 있는 구성을 지원한다.
uStreamer: 초고속 영상 스트리머
너무 훌륭해서 예상치도 못한 문제까지 해결해 주는 도구를 만난 적 있는가?
uStreamer는 설치하자마자 지연 시간을 8초에서 500~600밀리초로 줄여줬다. 게다가 뒤따를 수많은 추가 작업을 통째로 없애줬다.

uStreamer 덕분에 지연 시간이 15분의 1로 줄었다.
uStreamer 이전에는 ffmpeg의 영상을 어떻게 사용자 브라우저로 가져와야 할지 막막했지만, 어떻게든 가능하리라는 건 알고 있었다. ffmpeg에서 nginx로 HLS를 이용해 영상을 전달하는, 대체로 정확한 이 튜토리얼을 시험해 봤지만 지연만 더 늘어났다. HDMI 케이블을 꽂고 뽑을 때 스트리밍을 시작하고 멈추는 방법이나, 영상을 브라우저 친화적인 형식으로 변환하는 방법 같은 문제는 여전히 남아 있었다.
uStreamer는 이 모든 걸 해결했다. 브라우저가 네이티브로 재생하는 형식인 Motion JPEG로 영상을 제공하는 최소한의 HTTP 서버를 자체적으로 구동했다. HLS 스트림이나 ffmpeg와 nginx를 연동하느라 애쓸 필요가 없었다.
너무 기능이 완벽해서 Max가 더 성숙한 프로젝트를 포크한 줄 알았는데, 착각이었다. 이 괴짜는 파이 하드웨어에서 최대 성능을 짜내기 위해 C로 영상 인코더를 직접 작성한 것이었다. 나는 바로 Max에게 후원했고, 그의 소프트웨어를 사용하는 모든 분에게도 후원을 권한다.
영상 지연 개선하기
uStreamer 덕분에 지연 시간이 10초에서 약 600밀리초로 줄었다. 엄청난 발전이었지만 여전히 체감되는 지연이었다. Max에게 성능을 더 개선할 방법을 찾는다면 uStreamer 개발을 후원하고 싶다고 전했고, 그렇게 대화를 나누게 됐다.
Max는 내가 쓰는 HDMI 동글에 흥미를 보였다. 그 장치를 본 적이 없다고 했다. 그는 tmate를 이용해 공유 셸 세션을 만들면 원격으로 내 파이에 접속해 보겠다고 제안했다.

Max는 지연 개선을 도와주거나, 나를 연방 범죄로 뒤집어씌우겠다고 제안했다. 다행히 전자를 실행해 줬다.
내 하드웨어에서 uStreamer가 어떻게 동작하는지 몇 분간 테스트한 뒤, Max는 v4l2-ctl 유틸리티를 실행했고, 자신을 매료시켰지만 나는 전혀 이해하지 못한 한 줄을 발견했다:
$ sudo v4l2-ctl --all
Driver Info:
Driver name : uvcvideo
Card type : UVC Camera (534d:2109): USB Vid
...
Format Video Capture:
Width/Height : 1280/720
Pixel Format : 'MJPG' (Motion-JPEG)
...
Streaming Parameters Video Capture:
Capabilities : timeperframe
Frames per second: 30.000 (30/1)HDMI 동글이 영상 스트림을 Motion JPEG 형식으로 전달하고 있었던 것이다! uStreamer의 하드웨어 가속 인코딩은 빨랐지만, 최신 브라우저가 Motion JPEG를 네이티브로 재생하므로 완전히 불필요한 과정이었다.
우리는 uStreamer가 재인코딩을 건너뛰고 영상 스트림을 그대로 전달하도록 설정했다.

파이에서 추가 재인코딩 단계를 건너뛰자 지연이 600ms에서 200ms로 줄었다.
지연 시간은 600밀리초에서 200밀리초까지 떨어졌다. 즉각적이라고 할 순 없지만, 몇 분만 쓰면 지연을 잊을 정도로 낮아졌다.
TinyPilot 실제로 써보기
글 초반에 TinyPilot이 필요한 이유로 헤드리스 VM 서버에 부팅 전부터 접속하고 싶다고 했던 걸 기억하는가? 실제로 잘 동작하고, 나는 그렇게 쓰고 있다!
Key Mime Pi를 개선해 영상 캡처 기능을 통합한 새로운 웹 인터페이스를 만들었다:

올해 새로운 헤드리스 VM 서버를 만들면서 TinyPilot으로 Proxmox를 설치했다. Proxmox는 VM을 관리하기 위한 오픈소스 하이퍼바이저이자 웹 인터페이스다.
TinyPilot 덕분에 설치 전 과정을 브라우저에서 관리할 수 있었다. 컴퓨터를 끌고 다니며 케이블을 바꿔 끼우던 예전 방식보다 훨씬 쾌적했다.
직접 TinyPilot 만들기
준비물 목록
- Raspberry Pi 4 (모든 모델 사용 가능)
- USB-C to USB-A 케이블 (Male/Male)
- HDMI to USB 캡처 동글
- 이상하게도 이 제품들에는 브랜드명이 없지만, 생김새로 구분할 수 있다.
- 보통 eBay에서 11~15달러에 구할 수 있다.
- microSD 카드 (Class 10, 8GB 이상)
- HDMI to HDMI 케이블
- 또는 대상 머신의 영상 출력 방식에 따라 다른 규격 to HDMI 케이블을 써도 된다.
- (선택 사항) USB-C OTG 분배 커넥터
- 추가로 USB-A to microUSB 케이블 2개와 3A 전원 어댑터가 필요하다.
- (선택 사항) 쿨링 케이스, 히트싱크 또는 팬
- 파이의 GPIO 핀에 접근할 수 있는 케이스를 선택한다.
- 나는 이 미니멀한 패시브 쿨링 케이스를 사용한다.
Raspberry Pi OS Lite 설치하기
먼저 microSD 카드에 Raspberry Pi OS Lite(구 Raspbian)를 설치한다.

나는 파이의 microSD 카드를 구울 때 Rufus를 쓰지만, 어떤 디스크 이미징 툴을 써도 된다.
microSD의 boot 파티션에 ssh라는 파일을 넣어 SSH 접속을 활성화한다. 무선으로 연결할 경우 wpa_supplicant.conf 파일도 필요하다.
microSD 카드 준비가 끝나면 파이 장치에 꽂는다.
케이스 장착하기 (선택 사항)
라즈베리 파이 4는 발열이 심하기로 유명하다. 쿨링 없이도 동작은 하지만 시간이 지나면 안정성 문제가 생길 가능성이 높다.
나는 이 미니멀한 케이스를 좋아한다. 저렴하면서도 전원 팬의 복잡함 없이 패시브 방식으로 파이를 식혀주기 때문이다:

이 미니멀한 알루미늄 케이스는 팬의 복잡함 없이 파이를 효과적으로 식혀준다.
USB로 대상 머신에 연결하기
TinyPilot이 가상 키보드로 동작하도록, 파이의 USB-C 포트를 대상 머신의 USB-A 포트에 연결한다:


USB-C to USB-A 케이블로 USB-C 쪽은 파이의 USB-C 포트에, USB-A 쪽은 대상 컴퓨터에 연결한다.
참고: 파이에 더 많은 전력을 공급하므로 가능하면 USB 3.0 포트를 사용한다.
HDMI 캡처 동글 연결하기
물리적 조립을 마무리하려면 HDMI 동글을 파이의 USB 포트 중 하나에 꽂는다. 그런 다음 HDMI 케이블을 동글에 연결하고, 반대쪽 끝을 대상 컴퓨터의 디스플레이 출력에 꽂는다.


대상 컴퓨터의 디스플레이 출력을 HDMI 동글에 연결해 파이의 USB 포트에 꽂는다.
참고: 연결하려는 컴퓨터에 HDMI 출력이 없다면 DisplayPort to HDMI 케이블이나 DVI to HDMI 케이블을 사용하면 될 것이다. 다만 직접 테스트해 보지는 않았다.
이더넷 케이블 연결하기
유선 LAN으로 파이에 연결한다면 네트워크 케이블을 파이의 이더넷 포트에 연결한다:

이더넷 케이블을 파이에 연결한다.
참고: 위에서 wpa_supplicant.conf 파일을 추가해 무선 접속을 설정했다면 이 단계는 건너뛰어도 된다.
TinyPilot 소프트웨어 설치하기
파이 장치에 SSH로 접속한 뒤(라즈베리 파이 OS의 기본 계정은 pi / raspberry다) 다음 명령어를 실행한다:
curl -sS https://raw.githubusercontent.com/tiny-pilot/tinypilot/master/quick-install \
| bash -
sudo reboot웹상의 임의 스크립트를 셸로 파이프해 실행하는 것이 의심스럽다면 당연한 일이다. 소스를 직접 확인해 보길 권한다.
이 스크립트는 내 TinyPilot Ansible 롤과 함께 독립적인 Ansible 환경을 부트스트랩한다. 매 부팅 시 실행되는 네 가지 서비스를 설치한다:
- nginx: 널리 쓰이는 오픈소스 웹 서버
- ustreamer: 가벼운 HTTP 영상 스트리밍 서버
- usb-gadget: 파이가 USB 장치를 흉내 낼 수 있게 하는 “USB 가젯 모드”를 활성화하는 스크립트
- tinypilot: 내가 만든 TinyPilot용 웹 인터페이스
TinyPilot 사용하기
설치 스크립트를 실행하고 나면 TinyPilot은 다음 주소에서 이용할 수 있다:

설정이 완료되면 로컬 네트워크에서 http://raspberrypi/ 주소로 TinyPilot 웹 인터페이스에 접속할 수 있다.
전원 문제
이 구성의 가장 큰 제약은 전원이다. 대상 컴퓨터에서 전원을 끌어오면, 대상이 꺼질 때 파이에 예기치 않은 전원 차단이 발생한다.
게다가 파이 4는 안정적인 동작을 위해 3A가 필요하지만, 더 낮은 전력에서도 동작은 한다. 컴퓨터의 USB 3.0 포트는 0.9A, USB 2.0은 0.5A만 제공하므로, 파이 시스템 로그에서 다음과 같은 경고가 보일 수 있다:
$ sudo journalctl -xe | grep "Under-voltage"
Jun 28 06:23:15 tinypilot kernel: Under-voltage detected! (0x00050005)이 문제를 해결하기 위해 엔지니어링 업체와 협력해 파이의 USB-C 포트를 둘로 나누는 커스텀 회로 기판을 만들었다. 첫 번째 포트는 USB 전원을 받아 파이에 3A를 온전히 공급하고, 두 번째 포트는 USB 데이터를 출력해 파이가 여전히 USB 키보드인 척할 수 있게 한다.


TinyPilot Power Connector를 사용하면 USB OTG 기능을 유지하면서 파이의 USB-C 포트를 통해 3A 전원을 공급받을 수 있다.
중요한 점은 이 전원 커넥터의 데이터 포트가 USB 전원선을 제외한다는 것이다. 덕분에 컴퓨터 전원과 파이 전원 사이의 전압 차이로 인한 원치 않는 역전류를 방지할 수 있다.
참고: 적절한 커넥터 없이 파이를 외부 전원에 연결한 채 컴퓨터에 연결하면 하드웨어가 손상될 위험이 있다. 자세한 내용은 TinyPilot 위키를 참고한다.
소스 코드
TinyPilot 소프트웨어는 관대한 MIT 라이선스 하에 오픈소스로 공개되어 있다:
- tinypilot: TinyPilot 웹 인터페이스와 백엔드.
- ansible-role-tinypilot: TinyPilot과 의존성을 systemd 서비스로 설치하는 Ansible 롤.
완제품 TinyPilot 장치
완제품 TinyPilot 장치는 TinyPilot 웹사이트에서 구매할 수 있다. 나는 TinyPilot이라는 회사의 원래 창업자이지만, 2024년 4월에 사업을 매각했으며 현재는 열성적인 사용자로서 외에는 회사와 아무 관계가 없다.
uStreamer에 대한 믿기 힘든 작업과 TinyPilot에 대한 기여를 해준 Max Devaev에게 특별히 감사드린다.
글을 무작위로 읽기
댓글
로그인하고 댓글 남기기