HN
Hacker News • 57일 전
루팅 없이 안드로이드에서 리눅스 컨테이너 구동
IMP 7/10
핵심 요약
Podroid는 안드로이드 폰에서 루팅(Root) 권한이나 복잡한 설정 없이 QEMU 기반의 Alpine Linux 가상 머신을 부팅하여 Podman 컨테이너 런타임을 사용할 수 있게 해주는 오픈소스 앱입니다. TUI 앱 지원 및 포트 포워딩 기능을 갖춘 완전한 xterm 터미널 에뮬레이터를 제공하며, 컨테이너 이미지와 설정이 재시작 후에도 유지되는 것이 특징입니다. 안드로이드 14 이상을 사용하는 arm64 기기에서 APK 설치만으로 즉시 네이티브에 가까운 리눅스 개발 환경을 구축할 수 있다는 점에서 모바일 개발자와 실무자들에게 유용합니다.
번역된 본문
Podroid 안드로이드에서 리눅스 컨테이너 실행 — 루팅 불필요
Podroid는 QEMU를 사용하여 스마트폰에서 가벼운 Alpine Linux 가상 머신(VM)을 구동시키며, 기본 내장된 시리얼 터미널과 함께 완벽하게 작동하는 Podman 컨테이너 런타임을 제공합니다.
주요 기능
- 컨테이너(Containers): 모든 OCI 이미지를 풀(Pull)하고 실행할 수 있습니다 —
podman run --rm -it alpine sh - 터미널(Terminal): Ctrl, Alt, F1-F12, 방향키 등을 완벽하게 지원하는 xterm 에뮬레이션
- 영속성(Persistence): 설치한 패키지, 설정 파일, 컨테이너 이미지가 재부팅 후에도 유지됩니다.
- 네트워킹(Networking): 즉시 사용 가능한 인터넷 접근 및 안드로이드 호스트로의 포트 포워딩 지원
- 자체 완결(Self-contained): 루팅, Termux, 호스트 바이너리가 필요 없습니다 — APK 설치만으로 됩니다.
시스템 요구 사항
- arm64 안드로이드 기기
- Android 14+ (API 34)
- 약 150 MB의 여유 저장 공간
빠른 시작
- 릴리스(Releases) 페이지에서 APK를 설치합니다.
- Podroid를 열고 Start Podman을 탭합니다.
- 부팅이 완료될 때까지 대기합니다(약 20초) — 진행 상황은 화면과 알림판에 표시됩니다.
- Open Terminal을 탭합니다.
- 컨테이너를 실행합니다:
podman run --rm alpine echo hellopodman run --rm -it alpine shpodman run -d -p 8080:80 nginx
터미널 터미널은 Termux의 TerminalView를 기반으로 하며, VM의 시리얼 콘솔에 직접 연결된 완전한 VT100/xterm 에뮬레이션을 제공합니다.
- 추가 키 바 (스크롤 가능): ESC, TAB, SYNC, CTRL, ALT, 화살표, HOME, END, PGUP, PGDN, F1–F12, -, /, |
- CTRL / ALT는 토글 방식입니다 — 한 번 탭한 후 문자 키를 누르면 적용됩니다.
- SYNC를 누르면 수동으로 터미널 크기를 VM에 동기화합니다.
- 키보드가 열리거나 닫힐 때 터미널 크기가 자동으로 동기화되므로 TUI 앱(vim, btop, htop)이 올바르게 렌더링됩니다.
- 벨(Bell) 문자는 햅틱 피드백(진동)을 발생시킵니다.
포트 포워딩 VM에서 안드로이드 기기로 포트를 포워딩합니다:
- 설정(Settings)으로 이동합니다.
- 규칙을 추가합니다 (예: TCP 8080 -> 80).
- 스마트폰의
localhost:8080에서 서비스에 접속합니다. 규칙은 재부팅 후에도 유지되며, VM이 실행 중인 동안에도 추가하거나 제거할 수 있습니다.
작동 원리
Android App
├── Foreground Service (VM을 활성 상태로 유지)
├── PodroidQemu
│ ├── libqemu-system-aarch64.so (QEMU TCG, KVM 미사용)
│ ├── Serial stdio ←→ TerminalEmulator
│ └── QMP socket (포트 포워딩, VM 제어)
└── Alpine Linux VM
├── initramfs (읽기 전용 베이스 레이어)
├── ext4 disk (영구적인 오버레이)
├── getty on ttyAMA0 (작업 제어)
└── Podman + crun + netavark + slirp4netns
- 부팅 시퀀스: QEMU가
vmlinuz-virt및initrd.img를 로드합니다. 2단계 init(init-podroid)은 영구적인 ext4 디스크를 initramfs 위의 overlayfs 상위 레이어로 마운트합니다. 설치한 패키지와 풀(Pull)한 컨테이너는 오버레이에 기록되며 재부팅 후에도 유지됩니다. - 터미널 연결: 앱은 호스트 프로세스를 포크(Fork)할 수 없으므로, TerminalSession은 리플렉션을 통해 QEMU의 시리얼 I/O에 연결됩니다. 키보드 입력은 QEMU의 stdin으로 전달되고, QEMU의 stdout은 터미널 에뮬레이터에 입력됩니다. 터미널 크기는
stty를 통해 VM과 동기화되어 TUI 앱이 올바른 크기를 인식합니다. - 네트워킹: QEMU 사용자 모드 네트워킹(SLIRP)을 통해 VM의 IP는 10.0.2.15로 설정됩니다. 포트 포워딩은 QEMU의 hostfwd를 사용하며, 시작 시에는 CLI 인수를 통해, 런타임에는 QMP를 통해 관리됩니다.
소스에서 빌드하기
- initramfs 빌드 (멀티 아키텍처를 지원하는 Docker 필요):
./docker-build-initramfs.sh - APK 빌드:
./gradlew assembleDebugadb install -r app/build/outputs/apk/debug/app-debug.apk
프로젝트 구조
Dockerfile# 다단계 initramfs 빌더 (Alpine aarch64)docker-build-initramfs.sh# 원커맨드 빌드 스크립트init-podroid# VM용 커스텀 /initapp/src/main/java/com/excp/podroid/engine/# QEMU 라이프사이클, QMP 클라이언트, VM 상태 머신service/# 부팅 단계 알림이 포함된 포그라운드 서비스data/repository/# 설정 + 포트 포워딩 영속성ui/screens/# 홈, 터미널, 설정 (Jetpack Compose)
jniLibs/arm64-v8a/# 사전 빌드된 QEMU + libslirpassets/# 커널 + initramfs (생성됨)
크레딧
- QEMU — 머신 에뮬레이션
- Alpine Linux — VM 베이스
- Podman — 컨테이너 런타임
- Termux — 터미널 에뮬레이터 라이브러리
- Limbo PC Emulator — 안드로이드에서 QEMU 사용의 선구자
라이선스 GNU General Public License v2.0
원문 보기 (영어)
Podroid Run Linux containers on Android — no root required. Podroid spins up a lightweight Alpine Linux VM on your phone using QEMU and gives you a fully working Podman container runtime with a built-in serial terminal. Highlights Containers Pull and run any OCI image — podman run --rm -it alpine sh Terminal Full xterm emulation with Ctrl, Alt, F1-F12, arrows, and more Persistence Packages, configs, and container images survive restarts Networking Internet access out of the box, port forwarding to Android host Self-contained No root, no Termux, no host binaries — just install the APK Requirements arm64 Android device Android 14+ (API 34) ~150 MB free storage Quick Start Install the APK from Releases Open Podroid and tap Start Podman Wait for boot (~20 s) — progress is shown on screen and in the notification Tap Open Terminal Run containers: podman run --rm alpine echo hello podman run --rm -it alpine sh podman run -d -p 8080:80 nginx Terminal The terminal is powered by Termux 's TerminalView with full VT100/xterm emulation wired directly to the VM's serial console. Extra keys bar (scrollable): ESC TAB SYNC CTRL ALT arrows HOME END PGUP PGDN F1–F12 - / | CTRL / ALT are sticky toggles — tap once, then press a letter SYNC manually pushes the terminal dimensions to the VM Terminal size auto-syncs on keyboard open/close so TUI apps (vim, btop, htop) render correctly Bell character triggers haptic feedback Port Forwarding Forward ports from the VM to your Android device: Go to Settings Add a rule (e.g. TCP 8080 -> 80) Access the service at localhost:8080 on your phone Rules persist across restarts and can be added or removed while the VM is running. How It Works Android App ├── Foreground Service (keeps VM alive) ├── PodroidQemu │ ├── libqemu-system-aarch64.so (QEMU TCG, no KVM) │ ├── Serial stdio ←→ TerminalEmulator │ └── QMP socket (port forwarding, VM control) └── Alpine Linux VM ├── initramfs (read-only base layer) ├── ext4 disk (persistent overlay) ├── getty on ttyAMA0 (job control) └── Podman + crun + netavark + slirp4netns Boot sequence: QEMU loads vmlinuz-virt + initrd.img . A two-phase init ( init-podroid ) mounts a persistent ext4 disk as an overlayfs upper layer over the initramfs. Packages you install and containers you pull are written to the overlay and survive reboots. Terminal wiring: The app cannot fork host processes, so TerminalSession is wired to QEMU's serial I/O via reflection — keyboard input goes to QEMU stdin, QEMU stdout feeds the terminal emulator. Terminal dimensions are synced to the VM via stty so TUI apps see the correct size. Networking: QEMU user-mode networking (SLIRP) puts the VM at 10.0.2.15 . Port forwarding uses QEMU's hostfwd , managed at startup via CLI args and at runtime via QMP. Building from Source 1. Build the initramfs Requires Docker with multi-arch support: ./docker-build-initramfs.sh 2. Build the APK ./gradlew assembleDebug adb install -r app/build/outputs/apk/debug/app-debug.apk Project Layout Dockerfile # Multi-stage initramfs builder (Alpine aarch64) docker-build-initramfs.sh # One-command build script init-podroid # Custom /init for the VM app/src/main/ ├── java/com/excp/podroid/ │ ├── engine/ # QEMU lifecycle, QMP client, VM state machine │ ├── service/ # Foreground service with boot-stage notifications │ ├── data/repository/ # Settings + port forward persistence │ └── ui/screens/ # Home, Terminal, Settings (Jetpack Compose) ├── jniLibs/arm64-v8a/ # Pre-built QEMU + libslirp └── assets/ # Kernel + initramfs (generated) Credits QEMU — machine emulation Alpine Linux — VM base Podman — container runtime Termux — terminal emulator libraries Limbo PC Emulator — pioneered QEMU on Android License GNU General Public License v2.0