TinyPilot: Build a KVM Over IP for Under $100

Michael Lynch

TinyPilot: 100달러 이하로 만드는 KVM over IP

TinyPilot은 컴퓨터를 원격으로 제어할 수 있는 저렴한 오픈소스 장치입니다. 운영체제가 부팅되기 전부터 동작하기 때문에 베어메탈 홈랩 서버에 새 운영체제를 설치하거나 부팅 실패를 디버깅할 때 사용하고 있습니다.

이 글에서는 TinyPilot을 만들게 된 경험과 Raspberry Pi를 이용해 100달러 이하로 직접 만드는 방법을 소개합니다.

두 대의 컴퓨터를 연결한 TinyPilot 사진

Microsoft Surface의 Chrome에서 TinyPilot으로 Ubuntu 노트북을 제어하는 모습

인생 이야기는 됐고 만드는 방법이나 알려줘

TinyPilot을 개발하며 겪은 흥미진진한 성공과 좌절의 이야기를 건너뛰고 싶은 까칠한 분이라면 “내 TinyPilot 직접 만들기” 섹션으로 바로 이동하세요.

데모

왜 TinyPilot인가?

몇 년 전 소프트웨어 테스트용으로 홈 서버를 직접 만들었습니다. 가치 있는 투자였고 지금도 매일 사용하고 있습니다.

가상 머신을 호스팅하기 위해 2017년에 만든 홈랩 서버 사진

2017년에 가상 머신 호스팅용으로 만든 홈랩 서버

이 서버에는 키보드나 모니터가 연결되어 있지 않습니다. ssh나 웹 인터페이스로 접속하기 때문입니다. 편리한 구성이지만 작은 문제도 엄청난 골칫거리로 바뀌곤 합니다.

몇 달에 한 번씩 실수로 서버가 부팅되지 않거나 네트워크에 연결되지 못하게 만들어 사실상 접근이 차단되곤 합니다. 다시 정상화하려면 모든 연결을 뽑고 서버를 책상까지 끌고 와 케이블을 이리저리 옮겨가며 키보드와 모니터를 연결해야 합니다.

상용 솔루션

지인들은 iDRAC 경험을 극찬했습니다. iDRAC은 Dell 서버에 탑재된 칩으로 시스템 전원이 켜지는 순간부터 가상 콘솔을 제공합니다. 다음 홈 서버에 iDRAC을 도입할까 잠시 고민했지만 비싼 가격에 바로 포기했습니다. 라이선스만 대당 300달러에 비싼 전용 하드웨어도 필요합니다.

iDRAC 9 Enterprise 라이선스 300달러 가격 스크린샷

Dell iDRAC 기술 라이선스는 하드웨어 비용과 별도로 대당 300달러입니다

다음으로 상용 KVM over IP 솔루션을 알아봤습니다. Dell iDRAC과 비슷한 기능을 하지만 컴퓨터의 키보드·비디오·마우스 포트에 연결하는 외장 장치라는 점이 다릅니다(그래서 KVM이라는 이름이 붙었습니다). 안타깝게도 가격은 더 비싸 대당 500~1,000달러 수준입니다.

Raritan Dominion KVM over IP 구매 페이지 스크린샷

상용 KVM over IP 장비는 500~1,000달러에 이릅니다.

서버를 옮기는 게 귀찮다고는 해도 1년에 몇 번 케이블을 바꿔 끼우는 수고를 덜자고 500달러를 쓸 수는 없었습니다.

그래서 합리적인 프로그래머라면 누구나 할 법한 비합리적인 선택을 했습니다. 직접 KVM over IP를 만드는 데 수백 시간을 쏟아부은 것입니다.

Raspberry Pi로 KVM over IP 만들기

Raspberry Pi는 작고 저렴한 싱글보드 컴퓨터입니다. 풀 데스크톱 운영체제를 돌릴 만큼 성능이 충분하면서도 가격이 30~60달러에 불과해 취미 활동가와 프로그래머 사이에서 인기가 높습니다.

손바닥 위의 Raspberry Pi

Raspberry Pi는 단일 칩에 들어가는 완전한 컴퓨터이며 가격은 30~60달러에 불과합니다.

최근 버전의 Pi는 USB On-The-Go(USB OTG)를 지원해 키보드나 USB 메모리, 마이크 같은 USB 장치인 것처럼 동작할 수 있습니다.

Pi를 KVM으로 활용한다는 아이디어의 개념 증명으로 Key Mime Pi라는 간단한 웹 앱을 만들었습니다.

Key Mime Pi 웹 인터페이스 스크린샷

키보드 전달만 지원하던 TinyPilot의 초기 전신인 Key Mime Pi

Key Mime Pi는 USB로 다른 컴퓨터에 연결되어 USB 키보드로 인식됩니다. 동시에 웹 페이지를 제공하고 JavaScript 키 이벤트를 수신합니다. 사용자가 입력하면 Key Mime Pi가 키 이벤트를 포착해 가짜 USB 키보드를 통해 키 입력으로 변환하고, 그 결과가 대상 컴퓨터에 나타납니다. 이 동작에 대해서는 이전 글에서 자세히 설명했습니다.

영상 캡처라는 난관

화면에 무엇이 나오는지 볼 수 없다면 키보드 전달만으로는 별 소용이 없습니다. 당연한 다음 단계는 서버의 화면 출력을 Pi에서 캡처해 브라우저에 표시하는 방법을 찾는 것이었습니다.

영상 캡처를 위해 처음 시도한 것은 Lenkeng LKV373A HDMI 익스텐더였습니다. Daniel Kučera(일명 danman)이 이 장치를 훌륭하게 리버스 엔지니어링해 두었습니다. eBay에서 중국 판매자들이 40달러 정도에 판매하고 있어 가장 현실적인 선택처럼 보였습니다.

Lenkeng LKV373A HDMI 익스텐더 사진

HDMI 영상 캡처를 위해 처음 시도한 Lenkeng LKV373A HDMI 익스텐더

영상 캡처가 까다로웠던 이유는 LKV373A 송신기가 영상 캡처 장치가 아니기 때문입니다. 본래 용도는 네트워크 스트림을 다시 HDMI 출력으로 변환하는 LKV373A 수신기와 짝을 이루는 것입니다. danman의 분석 덕분에 영상 스트림을 가로채 캡처하는 방법은 찾았지만, LKV373A는 RTP 프로토콜의 비표준 변형을 사용해 이를 이해하는 영상 도구가 거의 없었습니다.

다행히 danman이 ffmpeg에 패치를 기여해 LKV373A의 독특한 동작을 처리할 수 있게 되었고, 덕분에 ffmpeg의 비디오 플레이어로 스트림을 재생할 수 있었습니다.

ffplay -i udp://239.255.42.42:5004
ffplay로 LKV373A의 영상 스트림을 재생하는 스크린샷

ffplay로 LKV373A의 영상 스트림 재생하기

바로 이 지점에서 프로젝트 내내 따라다닌 문제, 즉 지연 시간을 처음으로 체감했습니다. 대상 컴퓨터와 제 데스크톱의 영상 재생 사이에 거의 1초의 지연이 있었습니다.

Lenkeng LKV373A HDMI 익스텐더 사진

LKV373A는 재인코딩 전 단계에서 이미 838밀리초의 지연을 만들었습니다.

스트림 속도를 높여보려고 ffplay의 수많은 명령줄 옵션을 이것저것 만져봤지만 800밀리초 이하로는 줄이지 못했습니다. 그것도 고성능 GPU와 CPU를 갖춘 데스크톱에서의 결과였으니, 작고 소중한 Raspberry Pi에서의 성능은 더욱 기대하기 어려웠습니다.

다행히 완전히 우연한 계기로 더 나은 해결책을 찾았습니다.

HDMI to USB 동글

트위터를 멍하니 스크롤하다가 Arsenio Dev의 트윗에서 저렴한 HDMI to USB 동글을 구입했다는 소식을 보게 되었습니다.

Arsenio Dev 트윗 스크린샷

Arsenio Dev의 트윗이 더 나은 영상 캡처 솔루션을 알려주었습니다.

1080p 해상도에 초당 30프레임으로 영상을 캡처한다는 말이 너무 좋아 보여 eBay에서 바로 주문했습니다. 배송비 포함 11달러에 불과했습니다. 이름도 제대로 없더군요. 브랜드가 없어 그냥 “HDMI 동글”이라고 부르겠습니다. 여러 변형이 있지만 모두 MacroSilicon MS2109 칩을 쓴 같은 제품의 하우징만 다른 것입니다.

eBay에서 무료 배송으로 11.20달러에 판매 중인 HDMI 동글 스크린샷

eBay에서 무료 배송으로 11.20달러에 판매 중인 HDMI to USB 동글

며칠 뒤 제품이 도착했을 때 놀라지 않을 수 없었습니다. 별다른 설정 없이 Raspberry Pi에 꽂자마자 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 동글 크기 비교

Lenkeng LKV373A HDMI 익스텐더(왼쪽)는 HDMI 동글(오른쪽)보다 크고 연결도 더 많이 필요했습니다.

유일한 문제는 역시 지연 시간이었습니다. Pi에서 재전송된 영상 스트림은 원본 컴퓨터보다 7~10초나 느렸습니다.

Lenkeng LKV373A와 HDMI 동글 비교

ffmpeg로 Pi의 영상을 스트리밍했을 때 영상 지연이 최대 10초에 달했습니다.

이 지연이 동글 자체 때문인지, Pi의 ffmpeg 때문인지, 데스크톱의 ffplay 때문인지 알 수 없었습니다. Arsenio Dev는 지연이 20밀리초라고 했으니 ffmpeg의 난해하고 비밀스러운 명령줄 옵션을 파고들면 더 빠른 성능을 낼 수 있을 것 같았습니다.

다행히 또 한 번의 행운이 그 고된 작업을 덜어주었습니다.

비슷한 프로젝트에서 힌트 얻기

이전 블로그 글에서 Key Mime Pi를 소개했을 때 Max Devaev로부터 댓글을 받았습니다. 그의 프로젝트인 Pi-KVM을 한번 보라는 권유였습니다.

Max의 댓글: Hi:) Take a look at this project: https://github.com/pikvm/pikvm We have already done and debugged many things

Max Devaev가 기존 Pi-KVM 프로젝트를 알려주었습니다.

GPIO 핀 사진

브레드보드에 대한 저의 이전 경험은 실수로 녹여버린 것뿐이었습니다.

Pi-KVM을 잠깐 살펴본 적이 있었지만 브레드보드와 납땜이 필요하다는 요구사항에 겁을 먹고 물러났었습니다.

Max의 권유로 Pi-KVM을 다시 살펴보았고, 특히 영상 지연 문제를 어떻게 해결했는지에 주목했습니다. 그가 영상 캡처에 uStreamer라는 도구를 쓰고 있다는 것을 알게 되었습니다.

참고: Max와 더 이야기를 나눠보니 Pi-KVM은 납땜이나 브레드보드 없이도 구성할 수 있습니다.

uStreamer: 초고속 영상 스트리머

예상치 못한 문제까지 해결해 주는 훌륭한 도구를 만난 적이 있나요?

uStreamer는 아무 설정 없이 지연 시간을 8초에서 500~600밀리초로 줄여주었습니다. 거기에 더해 일련의 추가 작업까지 없애주었습니다.

uStreamer와 HDMI 동글 사용 시 500밀리초 지연

uStreamer 덕분에 지연 시간이 15분의 1로 줄었습니다.

uStreamer 이전에는 ffmpeg에서 영상을 어떻게 사용자 브라우저로 가져오는지 몰라 막막했습니다. ffmpeg에서 nginx로 HLS를 이용해 영상을 전달하는 대체로 정확한 튜토리얼을 테스트해 봤지만 지연이 더 늘어났습니다. HDMI 케이블 연결과 해제에 따라 스트리밍을 시작·종료하는 방법이나 영상을 브라우저 친화적인 형식으로 변환하는 문제도 여전히 남아 있었습니다.

uStreamer는 이 모든 것을 해결해 주었습니다. 브라우저가 네이티브로 재생하는 Motion JPEG 형식으로 영상을 제공하는 최소한의 HTTP 서버를 자체적으로 실행했습니다. HLS 스트림이나 ffmpeg와 nginx를 연동하는 데 신경 쓸 필요가 없었습니다.

기능이 워낙 풍부해서 Max가 더 성숙한 프로젝트를 포크한 줄 알았지만 오산이었습니다. 이 괴짜는 Pi 하드웨어에서 최대 성능을 짜내기 위해 C로 직접 영상 인코더를 작성한 것이었습니다. 저는 바로 Max에게 후원했고 그의 소프트웨어를 사용하는 모든 분께도 후원을 권합니다.

영상 지연 개선하기

uStreamer 덕분에 지연 시간이 10초에서 약 600밀리초로 줄었습니다. 큰 발전이었지만 여전히 눈에 띄는 지연이었습니다. Max에게 성능 개선이 가능하다면 uStreamer에 후원하고 싶다고 하자 대화를 이어가게 되었습니다.

Max는 제가 사용하는 HDMI 동글에 흥미를 보였습니다. 그 장치를 본 적이 없었기 때문입니다. 제 Pi에 원격으로 접속할 수 있도록 tmate로 공유 셸 세션을 만들어 달라고 요청했습니다.

Max가 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가 재인코딩을 건너뛰고 영상 스트림을 그대로 전달하도록 설정했습니다.

재인코딩 단계를 없앤 뒤 200밀리초 지연을 보여주는 사진

Pi에서 추가 재인코딩 단계를 생략하자 지연 시간이 600밀리초에서 200밀리초로 줄었습니다.

지연 시간은 600밀리초에서 200밀리초까지 떨어졌습니다. 즉각적이지는 않지만 몇 분만 사용해도 지연을 잊을 만큼 충분히 짧습니다.

TinyPilot 직접 써보기

이 글 맨 처음에 헤드리스 VM 서버에 부팅 전부터 접속하고 싶어 TinyPilot을 만든다고 했던 것 기억하시나요? 실제로 동작하고, 저는 잘 쓰고 있습니다!

Key Mime Pi를 개선해 영상 캡처 기능을 통합한 새로운 웹 인터페이스를 만들었습니다.

올해 새로운 헤드리스 VM 서버를 만들면서 TinyPilot으로 Proxmox를 설치했습니다. Proxmox는 VM 관리를 위한 오픈소스 하이퍼바이저이자 웹 인터페이스입니다.

TinyPilot 덕분에 전체 설치 과정을 브라우저에서 관리할 수 있었습니다. 컴퓨터를 끌고 다니고 케이블을 이리저리 바꿔 끼우던 예전 방식보다 훨씬 편했습니다.

내 TinyPilot 직접 만들기

준비물

Raspberry Pi OS Lite 설치

먼저 Raspberry Pi OS Lite(구 Raspbian)를 microSD 카드에 설치합니다.

Rufus 스크린샷

저는 Pi의 microSD 카드를 구울 때 Rufus를 사용하지만 어떤 디스크 이미징 도구든 상관없습니다.

microSD의 부트 파티션에 ssh라는 파일을 넣어 SSH 접속을 활성화합니다. 무선으로 연결할 경우 wpa_supplicant.conf 파일도 필요합니다.

microSD 카드 준비가 끝나면 Pi에 삽입합니다.

케이스 장착(선택 사항)

Raspberry Pi 4는 발열이 심하기로 유명합니다. 쿨링 없이도 동작은 하지만 시간이 지나면 안정성 문제가 생길 가능성이 큽니다.

저는 이 미니멀 케이스를 좋아합니다. 저렴하면서도 팬 없이 패시브 방식으로 Pi를 식혀주기 때문입니다.

Raspberry Pi용 미니멀 알루미늄 케이스

미니멀 알루미늄 케이스는 팬 없이도 Pi를 효과적으로 식혀줍니다.

USB로 기기에 연결

TinyPilot이 가상 키보드로 동작하도록 하려면 Pi의 USB-C 포트를 대상 기기의 USB-A 포트에 연결하세요.

Raspberry Pi에 연결된 USB 케이블대상 컴퓨터에 연결된 USB 케이블

USB-C to USB-A 케이블로 USB-C 쪽은 Pi의 USB-C 포트에, USB-A 쪽은 대상 컴퓨터에 연결하세요.

참고: Pi에 더 많은 전력을 공급할 수 있는 USB 3.0 포트를 사용하는 것이 좋습니다.

HDMI 캡처 동글 연결

물리적 조립을 마무리하려면 HDMI 동글을 Pi의 USB 포트 중 하나에 꽂습니다. 그런 다음 HDMI 케이블을 동글에 연결하고 반대쪽을 대상 컴퓨터의 영상 출력 포트에 꽂습니다.

Raspberry Pi에 연결된 HDMI 입력대상 컴퓨터에서 나온 HDMI 출력

대상 컴퓨터의 영상 출력을 HDMI 동글에 연결한 뒤 Pi의 USB 포트에 꽂으세요.

참고: 연결하려는 컴퓨터에 HDMI 출력이 없다면 DisplayPort to HDMI 케이블이나 DVI to HDMI 케이블을 사용해도 됩니다. 다만 저는 직접 테스트해 보지는 않았습니다.

이더넷 케이블 연결

유선 LAN으로 Pi에 연결한다면 네트워크 케이블을 Pi의 이더넷 포트에 연결하세요.

Pi에 연결된 이더넷 케이블 사진

Pi에 이더넷 케이블을 연결하세요.

참고: 위에서 wpa_supplicant.conf 파일을 추가해 무선 접속을 설정했다면 이 단계는 건너뛰어도 됩니다.

TinyPilot 소프트웨어 설치

Pi에 SSH로 접속한 뒤( Raspberry Pi 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: Pi가 USB 장치를 에뮬레이트할 수 있게 하는 “USB 가젯 모드”를 활성화하는 스크립트
  • tinypilot: 제가 만든 TinyPilot용 웹 인터페이스

TinyPilot 사용하기

설치 스크립트를 실행한 뒤에는 다음 주소에서 TinyPilot을 이용할 수 있습니다.

TinyPilot 웹 인터페이스 스크린샷

설정이 완료되면 로컬 네트워크에서 http://raspberrypi/ 주소로 TinyPilot 웹 인터페이스에 접속할 수 있습니다.

전원 문제

이 구성의 가장 큰 제약은 전원입니다. 대상 컴퓨터에서 전원을 끌어오면 대상이 종료될 때 Pi도 예기치 않게 전원이 끊깁니다.

게다가 Pi 4는 안정적인 동작을 위해 3암페어가 필요하지만 더 낮은 전력에서도 동작은 합니다. 컴퓨터의 USB 3.0 포트는 0.9암페어, USB 2.0 포트는 0.5암페어만 공급하므로 Pi 시스템 로그에서 다음과 같은 경고가 보일 수 있습니다.

 $ sudo journalctl -xe | grep "Under-voltage"
Jun 28 06:23:15 tinypilot kernel: Under-voltage detected! (0x00050005)

이 문제를 해결하기 위해 엔지니어링 업체와 함께 Pi의 USB-C 포트를 두 개로 나누는 맞춤형 회로 기판을 만들었습니다. 한 포트는 USB 전원 입력용으로 Pi에 3암페어를 온전히 공급하고, 다른 포트는 USB 데이터 출력용으로 Pi가 여전히 USB 키보드인 척할 수 있게 합니다.

전원 커넥터 클로즈업Raspberry Pi와 microUSB 케이블에 연결된 전원 커넥터

TinyPilot Power Connector는 USB OTG 기능을 유지하면서 Pi의 USB-C 포트를 통해 3암페어 전원을 공급받을 수 있게 합니다.

중요한 점은 전원 커넥터의 데이터 포트가 USB 전원선을 제외한다는 것입니다. 이를 통해 컴퓨터 전원과 Pi 전원 사이의 전압 차이로 인한 원치 않는 역전류를 방지합니다.

참고: 적절한 커넥터 없이 컴퓨터에 연결된 상태에서 외부 전원으로 Pi에 전원을 공급하면 하드웨어가 손상될 위험이 있습니다. 자세한 내용은 TinyPilot 위키를 참고하세요.

소스 코드

TinyPilot 소프트웨어는 관대한 MIT 라이선스로 공개된 오픈소스입니다.

완제품 TinyPilot 장치

완제품 TinyPilot 장치는 TinyPilot 웹사이트에서 구매할 수 있습니다. 제가 TinyPilot 회사의 원래 설립자이지만 2024년 4월에 사업을 매각했으며 현재는 열성적인 사용자로만 회사와 관계를 맺고 있습니다.


놀라운 uStreamer 작업과 TinyPilot에 기여해 주신 Max Devaev 님께 특별히 감사드립니다.

원문은 Michael Lynch님이 에 게재했습니다.

이 글은 muse-spark-1.2-contributor 모델을 사용해 번역했습니다.