ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Redroid 镜像本地编译与定制化实战:从环境准备到多实例部署

Redroid 镜像本地编译与定制化实战:从环境准备到多实例部署 1. 为什么要在本地编译 Redroid 镜像Redroid 这个项目全称是 Remote Android本质上是一套跑在 Linux 容器里的 Android 系统。它跟传统模拟器最大的区别在于没有硬件虚拟化那一层直接复用宿主机的内核靠容器技术把 Android 的用户空间跑起来。带来的好处很直接——启动快、资源占用低、一台物理机可以同时跑几十甚至上百个实例。做云手机、App 自动化测试、群控、云游戏这些场景的人基本都会接触到它。但真正上手之后你会发现官方提供的镜像往往不够用。原因有几个一是官方镜像为了通用性预装的组件非常基础很多业务需要的服务、权限、系统属性都没有二是不同宿主机的内核版本、显卡、网络环境差异很大官方镜像不一定能直接跑起来三是做产品的时候你总得往系统里塞自己的 App、改默认配置、调性能参数这些都需要重新打包镜像。所以“编译与定制”这件事不是可选项而是必经之路。这篇文章我会把整个流程拆开讲清楚从环境准备、源码拉取、编译参数、镜像结构到定制化修改、常见报错排查全部按我实际操作的顺序来。适合已经了解 Docker 基本用法、想在云手机方向做深度定制的读者。如果你只是想让 Redroid 跑起来看看效果官方镜像就够了但如果你想把它变成能交付的产品那这篇内容应该能帮你少走不少弯路。2. 编译前的环境准备与依赖梳理2.1 宿主机系统的选择与内核要求Redroid 对宿主机最核心的要求是内核。它依赖几个关键的内核特性binder、ashmem或者 memfd、以及 cgroup 的相关支持。binder 是 Android 进程间通信的基础ashmem 是 Android 的共享内存机制。这两个东西在标准发行版内核里默认是不开启的所以你得自己确认或者重新编译内核。我实测下来Ubuntu 20.04 和 22.04 是比较省心的选择社区文档也最全。内核版本建议 5.4 以上5.10 和 5.15 这两个 LTS 版本兼容性最好。你可以用下面这组命令快速检查当前内核是否满足条件# 检查 binder 模块 ls /dev/binder* 2/dev/null # 检查 ashmem ls /dev/ashmem 2/dev/null # 检查内核配置 zcat /proc/config.gz | grep -E CONFIG_ANDROID_BINDER|CONFIG_ASHMEM|CONFIG_MEMFD如果/dev/binder不存在说明 binder 没加载。Ubuntu 上可以尝试modprobe binder_linux但很多发行版内核根本没编这个模块那就只能换内核或者用带 binder 的内核包。这一步是很多人卡住的第一个坑别急着往下走先把内核搞定。提示如果你用的是云服务器很多厂商的默认内核是裁剪过的binder 和 ashmem 都可能缺失。下单前最好先确认或者选支持自定义内核的机型。2.2 编译工具链与依赖包安装Redroid 的镜像编译本质上是在构建一个 Android 系统的 rootfs所以工具链比较重。核心依赖包括Docker用于构建和运行、Git、Python3、以及一堆构建工具。我一般会一次性把依赖装齐sudo apt update sudo apt install -y git curl python3 python3-pip \ build-essential zip unzip tar gzip \ libssl-dev libncurses-dev flex bison \ android-sdk-libsparse-utils这里重点说两个容易被忽略的包。一个是android-sdk-libsparse-utils它提供了img2simg和simg2img工具Redroid 的镜像最终是 sparse 格式的没有这个工具你没法处理镜像文件。另一个是libssl-dev编译过程中有些组件需要链接 OpenSSL缺了会报链接错误。Docker 的版本建议 20.10 以上因为 Redroid 容器需要用到一些较新的 namespace 和 cgroup 特性。装完之后记得把当前用户加入 docker 组不然每次都要 sudosudo usermod -aG docker $USER newgrp docker2.3 磁盘空间与内存的规划这一点我必须单独拎出来讲因为它是隐形成本。Redroid 的源码加上编译中间产物轻松吃掉 50GB 以上。如果你还要保留多个版本的镜像100GB 是起步。内存方面编译过程建议至少 8GB16GB 比较舒服。如果内存不够编译到一半被 OOM Killer 干掉是常有的事。我一般会单独挂一块盘给编译目录用df -h确认空间然后设置好工作目录。另外编译过程会产生大量小文件文件系统的 inode 数量也要留意ext4 默认的 inode 数量一般够用但如果你用的是某些精简文件系统可能会遇到 inode 耗尽的问题。3. Redroid 镜像的编译流程拆解3.1 源码获取与目录结构说明Redroid 的源码托管在 GitLab 上主仓库是redroid-doc里面包含了编译脚本和文档。但真正编译镜像用的是redroid-script这个仓库它封装了一套基于 Docker 的构建流程。我通常这样拉取git clone https://github.com/remote-android/redroid-doc.git cd redroid-doc目录结构大致是这样的android-builder目录下是构建脚本redroid目录下是运行时的配置。核心的构建脚本是build.sh它会调用 Docker 去拉取一个构建镜像然后在里面执行 Android 的编译流程。这里有个设计上的取舍值得说Redroid 没有让你从 AOSP 源码从头编译而是基于已有的 Android 系统镜像做二次加工。这样做的好处是编译时间从几小时缩短到几十分钟坏处是你能改的东西受限于基础镜像。对于大多数定制需求来说这个取舍是合理的因为真正需要改内核或者 framework 层的场景并不多。3.2 编译参数的选择与含义构建脚本支持一系列参数最关键的几个是ANDROID_VERSION、ARCH、TARGET。ANDROID_VERSION决定了基础镜像的 Android 版本常见的有 11、12、13。版本越高对内核的要求也越高比如 Android 13 需要内核支持更多的 binder 特性。ARCH一般是arm64或者x86_64。这里有个实际经验如果你在 x86 服务器上跑选x86_64性能最好因为不需要指令翻译但如果你要跑 ARM 架构的 App就得选arm64这时候宿主机如果是 x86就需要 QEMU 做指令翻译性能会打折扣。我实测下来x86_64 镜像跑 ARM App 的兼容性一般能跑但别指望流畅。TARGET参数用来指定输出镜像的用途比如userdebug会带 root 和调试能力user则是生产版本。做定制开发阶段我建议用userdebug方便进去改东西。一个典型的编译命令长这样./build.sh \ --android-version 13 \ --arch x86_64 \ --target userdebug \ --output ./output3.3 编译过程的监控与中断处理编译一旦开始会经历几个阶段拉取基础镜像、解包、修改、重新打包。整个过程视网络和机器性能大概 20 到 60 分钟。我一般会开着docker stats看资源占用同时用tail -f跟构建日志。如果中途失败了别急着重跑。先看日志最后几十行通常是某个依赖下载失败或者磁盘满了。构建脚本会在工作目录留下中间产物你可以进去手动修复后继续而不是从头再来。这一点在反复调试定制内容的时候特别有用。注意编译过程中不要手动去动工作目录里的文件脚本有状态标记乱动会导致下次构建行为异常。要改东西等构建结束或者明确知道自己在干什么。4. 镜像定制化的核心手段4.1 往镜像里预装 App 和系统服务最基础的定制就是预装 App。Redroid 的构建脚本支持一个--preinstall参数指向一个目录里面放 APK 文件构建时会自动把它们塞进/system/priv-app或者/data/app。但这里有个细节放到priv-app的 App 需要签名匹配否则系统不认。我一般会把 App 放到/data/app下用脚本在首次启动时安装。具体做法是在构建脚本的定制阶段往system/etc/init下加一个 rc 文件让系统启动时执行一段脚本去安装 APK。这个思路比直接塞进系统分区灵活因为系统分区是只读的塞进去的 App 升级麻烦。# 示例在 init.rc 中添加启动服务 service install_apps /system/bin/sh /system/etc/install_apps.sh class main oneshot对应的install_apps.sh里用pm install逐个安装。注意这个脚本要在zygote启动之后执行否则pm命令不可用。4.2 修改系统属性和默认配置系统属性是定制里最常改的东西。比如你要改默认语言、时区、屏幕密度、是否允许 root都是通过build.prop或者system.prop来控制的。Redroid 的构建脚本允许你提供一个prop文件构建时合并进去。我整理了一份常用属性对照表方便你按需修改属性名作用常用值ro.product.locale默认语言地区zh-CNpersist.sys.timezone默认时区Asia/Shanghairo.sf.lcd_density屏幕密度160 / 240 / 320ro.debuggable是否可调试1ro.secure是否安全模式0persist.sys.usb.configUSB 配置mtp,adb改这些属性的时候要注意有些属性是只读的改了不生效得在构建阶段就写进去。运行阶段用setprop只能改非 ro 开头的属性。4.3 调整内核参数与容器运行时配置镜像本身定制完之后运行时的配置同样重要。Redroid 容器启动时需要挂载 binder 和 ashmem 设备还要设置一些 cgroup 参数。这些在docker run命令里体现docker run -itd --privileged \ --name redroid \ -p 5555:5555 \ -v /dev/binder:/dev/binder \ -v /dev/ashmem:/dev/ashmem \ redroid/redroid:13.0.0_amd64 \ androidboot.redroid_width1080 \ androidboot.redroid_height1920 \ androidboot.redroid_dpi320--privileged是必须的因为容器需要访问宿主机的设备节点。androidboot.redroid_*这些参数是 Redroid 特有的用来控制虚拟屏幕的分辨率和密度。如果你要跑多个实例每个实例的端口和这些参数都要不一样。提示生产环境不建议用--privileged权限太大。可以用--device单独挂载需要的设备节点配合--cap-add精细化控制权限。5. 实操过程中踩过的坑与排查方法5.1 容器启动失败binder 设备找不到这是最高频的问题。现象是容器启动后立刻退出日志里报Failed to open /dev/binder。原因就是宿主机没有 binder 设备节点。解决办法分两种情况如果内核支持 binder 但没加载modprobe binder_linux即可如果内核根本不支持那就得换内核。我遇到过一种隐蔽情况内核支持 binder但设备节点名字不是/dev/binder而是/dev/binderfs/binder。这时候要么做个软链接要么在 docker run 时挂载正确的路径。排查的时候用find /dev -name *binder*先确认节点位置。5.2 编译报错磁盘空间不足与 inode 耗尽编译到一半报No space left on device但df -h看空间还有剩这时候大概率是 inode 用完了。用df -i确认。解决办法是清理编译中间产物或者换一个 inode 更多的文件系统。我一般会在编译前把工作目录清空避免旧产物占着 inode。另一个空间陷阱是 Docker 的镜像层。每次编译都会产生新的镜像层时间长了能占几十 GB。定期docker system prune清理一下但注意别把正在用的镜像删了。5.3 网络与 ADB 连接问题Redroid 默认监听 5555 端口用adb connect localhost:5555就能连上。但有时候连不上常见原因有三个一是端口没映射对检查docker ps的端口映射二是容器内的 adbd 没启动进容器ps -ef | grep adbd看看三是防火墙拦了这个在云服务器上很常见安全组要放行对应端口。如果 adb 连上了但设备显示 offline通常是 adb 版本和容器内 adbd 版本不匹配。升级本地的 adb 到最新版一般能解决。5.4 常见问题速查表现象可能原因排查方向容器启动即退出binder/ashmem 缺失检查 /dev 下设备节点编译中断报空间不足磁盘或 inode 耗尽df -h 和 df -iadb 连不上端口/防火墙/adbd逐层检查网络和进程App 安装失败签名或权限问题检查 priv-app 签名画面黑屏分辨率参数错误核对 androidboot 参数性能卡顿架构不匹配确认镜像架构与宿主机一致6. 定制化进阶从能跑到好用6.1 多实例管理与资源隔离一台机器跑多个 Redroid 实例是云手机的典型需求。每个实例要独立的端口、独立的数据目录、独立的屏幕参数。我一般用脚本批量生成 docker run 命令端口从 5555 开始递增数据目录按实例编号命名。资源隔离方面用--cpus和--memory限制每个实例的 CPU 和内存。不限制的话一个实例跑满会把其他实例拖死。我实测下来一个 1080p 的实例2 核 2GB 是比较舒服的配置跑轻量 App 够用。6.2 镜像瘦身与启动加速官方镜像动辄几个 GB传输和启动都慢。瘦身的手段有几个删掉不用的系统 App、压缩资源文件、去掉调试符号。但要注意删系统 App 可能影响依赖得一个个确认。我一般会保留核心框架把能删的预装 App 都删掉能省下几百 MB。启动加速方面可以把不必要开机启动的服务禁掉减少 init 阶段的工作量。另外用docker commit把定制好的容器保存成新镜像下次直接启动省去初始化时间。6.3 版本管理与回滚策略定制镜像一定要做版本管理。我的做法是每次编译出来的镜像都打上日期和版本号标签比如redroid-custom:20250101-v1。同时保留一份构建脚本和定制文件的快照这样出问题能快速回滚。提示不要把定制内容直接改在官方镜像上而是维护一份定制脚本每次基于官方镜像重新构建。这样官方更新时你能快速跟进而不是被自己的修改绑死。7. 一些个人体会做 Redroid 定制这段时间最大的感受是难点不在编译本身而在环境适配。同样的脚本在不同机器上跑出来的结果可能完全不一样内核版本、Docker 版本、甚至文件系统类型都会影响结果。所以我的建议是先把环境摸清楚把内核和依赖确认到位再动手编译。磨刀不误砍柴工这句话在这个场景里特别贴切。另外定制的时候要克制。很多人一上来就想大改结果改出一堆问题。我的做法是每次只改一个点改完验证通过了再改下一个。这样出问题的时候排查范围小定位快。最后再分享一个小技巧把常用的定制操作写成脚本参数化这样换一台机器或者换一个 Android 版本改几个参数就能复用效率提升非常明显。
返回列表