TECH 으로 돌아가기
TECH HACKER NEWS 오늘 10분 읽기 29 READS

버린 폰이 서버가 되다: CMF Phone 1로 VPS를 대체한 실험

버린 폰이 서버가 되다: CMF Phone 1로 VPS를 대체한 실험
SOURCE IMAGE · HACKER NEWS

클라우드 VPS를 개인 인프라로 쓰다 보면 애매한 순간이 온다. 대단한 트래픽이 있는 것도 아닌데 매달 요금이 나가고, 무언가 무거운 워크로드가 끼면 저가 공유 인스턴스로는 버겁다. 한 개발자는 헤츠너(Hetzner)의 소형 VPS에서 웹 앱 몇 개와 원격 브라우저 서비스 'Surf', Caddy 등을 돌리다가 이 딜레마에 부딪혔다. 특히 Surf가 관리하는 크롬 인스턴스는 공유 CPU에서 성능이 굶주렸고, 전용 CPU 요금제는 '개인용 브라우저'에 지출하기엔 부담스러웠다. 그렇다고 새 미니 PC를 사자니 최근 DRAM 값이 치솟아 넉넉한 메모리를 갖춘 서버를 꾸리기엔 시점이 나빴다.

대안은 뜻밖에도 서랍 속에 있던 CMF Phone 1이었다. 8개의 ARM 코어, 8GB 램, 128GB 플래시, Wi-Fi 6, 5G 모뎀, 그리고 내장 배터리라는 사실상의 무정전 전원까지. 이미 값을 치른 하드웨어이고, 서랍에 방치하기엔 아까운 사양이었다. 오늘날 이 폰은 Surf와 그 크롬 인스턴스, 개인 가계부, 화면 공유 서비스, 그리고 소소한 웹 앱들을 돌리며 재부팅에도 살아남고 Git에서 배포되며 네트워크를 옮겨도 접근 가능한 상태로, 실제로 VPS를 대체했다.

정석을 버리고 안드로이드를 남기다

가장 '깔끔해 보이는' 접근은 폰에 일반 리눅스 배포판을 올리는 것이었다. CMF Phone 1은 postmarketOS 포팅이 있고 부팅도 됐다. 문제는 기기 지원 페이지에서 깨진 것으로 표시된 항목들 — Wi-Fi, 블루투스, 하드웨어 가속 등 서버로 쓸 때 정작 필요한 대부분이었다. 결국 스플래시 화면과 검은 디스플레이만 남았고, 서버도 폰도 아닌 상태가 됐다. 순정 Nothing OS 복구도 또 다른 고행이었다. 플래싱 도구가 윈도우를 요구해 QEMU에 윈도우를 설치하고 USB 패스스루와 미디어텍 드라이버와 씨름하다가, 결국 실제 윈도우 머신에서야 공장 이미지를 복원했다. 중간에 검은 화면만 나오는 소프트 벽돌 상태를 겪으며 멀쩡한 기기를 문진으로 만들었다고 확신한 순간도 있었다.

여기서 얻은 교훈은 명확하다. 안드로이드에는 Wi-Fi, 전원 관리, 배터리, GPU, 모뎀 등 이 하드웨어의 모든 조각에 대한 동작하는 드라이버가 이미 존재한다. 더 '정통' 리눅스 유저스페이스를 얻자고 그것을 통째로 버리는 것은 나쁜 거래였다. 필요한 것은 폰을 평범한 리눅스 머신으로 만드는 게 아니라, 안드로이드가 하드웨어 관련 일을 계속 잘 처리하는 동안 리눅스 애플리케이션을 안정적으로 돌리는 것이었다.

두 겹으로 나눈 구조

두 번째 시도는 순정 안드로이드를 유지하고 Termux를 호스트 환경으로 삼았다. Termux는 OpenSSH, runit, Caddy, Cloudflared, 패키지 관리와 웬만한 유닉스 도구를 제공한다. Termux:Boot가 재부팅 후 슈퍼바이저와 SSH를 띄우고, Tailscale이 안정적인 사설 주소를 부여해 tailnet의 어느 머신에서든 접속할 수 있게 한다. Termux는 가상머신이 아니라 안드로이드 리눅스 커널 위에서 실행되지만, Bionic 기반 유저스페이스가 데비안과 달라 기존 리눅스 이미지를 그대로 넣을 수는 없다. 그래서 Termux는 작은 제어 평면으로 두고, 각 애플리케이션이 자신이 기대하는 리눅스 파일시스템을 따로 들고 오는 방식을 택했다.

실제 서비스는 runit이 관리한다. 안드로이드의 배터리 관리는 폰에겐 훌륭하지만 서버 흉내를 내는 기기엔 독이라, Ansible 빌드가 안드로이드 호스트 프로파일을 적용한다. 상시 웨이크락 설치, 유휴 모드 비활성화, Termux·Termux:Boot·Tailscale의 백그라운드 제한 면제, 자식 프로세스 제한 해제, Wi-Fi 절전 차단, Tailscale 상시 VPN 설정이 그것이다. 개별 설정보다 중요한 것은 복구 사슬이다. 안드로이드 부팅 → 상시 VPN이 Tailscale 복구 → Termux:Boot가 runit 시작 → runit이 모든 상주 서비스 기동 → 헬스체크가 로컬·공개 경로 검증. 사람이 알아차리기를 기다리지 않고 폰이 스스로 재부팅에서 복귀한다.

대부분의 앱은 리눅스 ARM64 OCI 이미지로 제공되므로 proot-distro로 데비안 위에서 어렵지 않게 돌렸다. PRoot는 유저스페이스에서 파일시스템·프로세스 호출을 가로채 Termux 프로세스가 데비안 루트에 있다고 믿게 만든다. 컨테이너 경계는 아니고 커널·네트워크 네임스페이스·UID를 공유하지만, 루트나 특수 커널 없이 호환성 계층으로 쓰기에 유용하다. 다만 지연에 민감한 Surf 브라우저 워크로드가 예외였다. 프로세스 시작, 라이브러리 로딩, 경로 탐색, 브라우저 프로파일 읽기가 모두 PRoot의 번역 계층을 거치며 크롬이 CPU에 효율적으로 닿지 못했다. 그래서 폰을 루팅했다 — 안드로이드를 대체하려는 게 아니라, 같은 데비안 파일시스템을 제대로 마운트해 진짜 chroot로 진입하기 위해서였다. 네이티브 시스콜로 커널에 닿자 개선은 미묘하지 않았다. 이후 소규모 상주 서비스들도 같은 방식으로 통일됐다.

집을 떠나도 서버로 남는 폰

인그레스도 과제였다. 가정용 회선에는 VPS 같은 고정 서버 설정이 없고, SSH나 애플리케이션 포트를 라우터로 노출하고 싶지도 않았다. HTTP 앱은 Cloudflare Tunnel을 쓴다. Cloudflared가 폰에서 아웃바운드 연결 하나만 열면 클라우드플레어가 호스트명별 요청을 그 터널로 보내고 Caddy가 적절한 루프백 서비스로 라우팅한다. 인바운드 라우터 규칙이 없으니 폰을 다른 네트워크로 옮겨도 터널이 재연결되고 호스트명이 따라온다. 관리 경로는 Tailscale이 같은 역할을 한다. 반면 Surf 백엔드는 지연에 민감하고 자체 TLS를 종단하며 접속하는 구형 아이패드가 서버 신원을 핀 고정하기에, 집에서는 Cloudflare DDNS와 포트 포워딩으로 직결하고, 로밍 시에는 Surf의 전체 TLS 스트림을 평범한 웹소켓 안에 감싸 종단 간 암호화를 유지했다. 이 경우 대략 한 번의 네트워크 왕복만큼 지연이 늘어 초기 테스트에서 60ms 수준이었지만, 2012년 운영체제에서 Tailscale 설치 없이도 동작했다.

전체 호스트는 Ansible 관리 상태로 옮겨졌다. 버전, 서비스 정의, 라우트, 전원 설정, 시크릿, 헬스체크가 하나의 비공개 저장소에 산다. 릴리스는 다이제스트나 체크섬으로 고정돼 버전 디렉터리에 설치되고 원자적 current 심링크 뒤에 놓이며, 체크섬이나 헬스체크가 실패하면 배포가 멈추고 롤백은 핀을 되돌려 다시 적용하는 것으로 끝난다. 시크릿은 폰에 Git 체크아웃 자체가 없어 저장되지 않고, Ansible Vault 값은 저장소에 암호화돼 있으며 볼트 비밀번호는 1Password SSH 에이전트가 고정 챌린지에 서명해 파생하므로 개인 키는 폰에 닿지 않는다. 이 선언적 구성 덕분에 폰이 죽어도 루팅 가능한 다른 ARM64 폰을 같은 상태로 끌어올릴 수 있다.

실무자 입장에서 이 실험이 주는 시사점은 분명하되 한계도 함께 봐야 한다. systemd도, 정상적인 Docker 데몬도, 보안 경계도 없다. 상주 서비스들은 안드로이드 커널과 네트워크 스택을 공유하며 사설 마운트 네임스페이스는 주로 마운트와 정리를 예측 가능하게 할 뿐, 격리를 보장하지 않는다. Mali GPU를 VirGL·안드로이드 불칸으로 브리지하려던 시도는 화면 깨짐과 성능 저하만 남겨 결국 소프트웨어 렌더링이 더 나았다. 그럼에도 리눅스 커널 위에 유능한 유저랜드를 얹는 것만으로 충분했다는 점, 그리고 가계부의 SQLite처럼 진짜 아끼는 상태에는 자동 원격 백업과 검증된 복구 경로를 별도로 둔다는 점은, 남는 하드웨어와 오픈소스 도구만으로 어디까지 자기 인프라를 재구성할 수 있는지 보여주는 실용적 참고가 된다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://seg6.space/posts/phone-server/
SHARE
처리 중...