Docker 加持的安卓手机:随身携带的知识库(一)

这篇文章聊聊,如何借助 Docker ,尝试将一台五年前的手机,构建成一个随身携带的、本地化的知识库。

写在前面

本篇文章,我使用了一台去年从二手平台购入的五年前的手机,K20 Pro。

用于实验的设备:K20 Pro

为了让它能够稳定持续的运行,我还为它准备了一个“加强版”的散热壳。

加强的“散热装备”

设想中的平时使用场景也非常简单,不需要插卡、不需要联网,一根 Type-C 数据线把它和电脑连在一起,就可以通过“本地连接”来访问手机中运行的提供服务的软件。

因为手机本身自带“UPS”(电池),所以即使突然断电,也不会出现数据的丢失,除此之外,因为手机已经是五年前的规格了,即使大手笔的购入顶配中的至尊版,成本也并不高,但是却可以比较轻松的获得一台 8核心、12GB 内存、500GB 的硬盘设备。甚至还有一块可以用来调试或者展示内容的“全面屏”,没有任何摄像头打孔的完整玻璃屏幕。

至于性能,如果能够完全配置正常,应该是非常有性价比,但是这应该需要一些折腾,所以本篇文章是第一篇,先验证可行性。

本文中的许多资料来自开源项目 CGCL-codes/Android-Container,感谢作者的辛苦劳动。不过项目距今为止已有三年没有更新,我个人推荐只做验证使用,一如本文中的使用方案。

为什么选择这台 K20 Pro

翻阅之前的文章,我发现我差不多每年都会折腾一下小米的手机。

  • 今年早些时候,聊过使用搭载 8Gen3 的红米 K70 Pro 跑模型《使用搭载骁龙 8 Gen 3 的安卓手机运行 AI 大模型》
  • 2023 年的时候,折腾过闲置的小米 12 Pro《小米 12 PRO 刷 MIUI 14 海外版(Android 12)》
  • 2022 年的时候,折腾过红米 11T Pro 《红米 11T PRO 刷机 MIUI 13 海外版》
  • 2021 年的时候,折腾过 K30 Pro《小米 K30 PRO 刷机 MIUI 12.5 海外版》
  • 2019 年的时候,折腾过 K20 Pro《小米 K20Pro 体验 Android 10》

因为种种原因,目前的小米开发者生态没有了过去的开放,开发者难以获取 Bootloader 权限,各种有趣的功能要排队内测,甚至 GitHub 上都出现了一些离谱的项目,比如 “小米高考题库”,来解决解锁刷机权限必须度过的大量八股测试题;“小米解锁项目”,“小米解锁方案合集” 等等项目。

**虽然,看起来现在的小米手机型号已经不再适合折腾。**但是,过去的手机,有没有可能变成一台比较有趣的设备,做一些比较有趣的事情呢?

毕竟,过去的小米手机还是很顶的!“守门员”、“焊门员”、“小金刚”这些产品形容词不光包含了营销成分,也有来自粉丝们的赞许。

准备工作

这次的准备工作非常简单,首先是“适合折腾的手机”,我选择的是 K20 Pro,你可以选择你喜欢的设备。

然后,是一台协助手机重获新生的“电脑”,Windows 或者 macOS 桌面系统的设备会比较方便,我使用的是 macOS。

最后,需要一根 Type-C 口的数据线,能够将手机连到电脑上。

第一步:解锁手机 Bootloader 权限

你可以参考上文中,我分享的之前的相关手机折腾记录,来将手机解锁,以便我们能够刷入 TWRP,并在后续过程中刷入验证 Docker 使用的新的安卓系统镜像。

如果你使用的设备也是 K20 Pro(Mi 9T Pro),可以从这里下载该设备的 TWRP 引导镜像。

更新设备的 BootLoader 非常简单,只需要电脑侧执行下面两条命令即可:

fastboot flash recovery twrp.img
fastboot reboot

第二步:按顺序刷入设备使用的系统镜像

在项目子目录文档的底部,我们能够找到用于验证的安卓镜像。

我们需要先刷入来自小米官方的 miui_RAPHAEL_V12.0.5.0.QFKCNXM_d03168fb55_10.0.zip 镜像,这个镜像发布自 2020 年 10 月 18 日,基于 Android 10 构建。如果项目页面下载比较慢,你可以从这里下载官方镜像。

在刷入官方镜像后,我们就可以刷入项目作者构建好的镜像 PixelExperience_raphael-10.0-20201204-0354-UNOFFICIAL-48bit-docker-criu.zip,来进行功能验证了。

在这个文档的上面,作者简单解释了如何进行镜像构建,而在另外一个目录中文档,则包含了另外一些比较重要的信息,包括如何给镜像内核做调整,来让 Docker 能够运行起来。

当我们完成镜像的更新后,就需要手动来完善运行环境,让 Docker 运行起来啦。

第三步:修复 Docker 运行环境

首先在一个命令行终端中执行:

adb shell

在进入一个交互式终端后,输入下面的命令,让我们能够修改系统中的只读目录:

mount -o rw,remount /

接着,我们将项目 Docker 目录和项目 files 目录的 files/dockerd.sh 使用 adb push 命令,传送到手机的 /system/bin/ 目录下,一个基本的 Docker 运行环境就就绪啦。

# adb push ./dockerd.sh /system/bin/dockerd.sh

./dockerd.sh: 1 file pushed, 0 skipped. 7.7 MB/s (4016 bytes in 0.000s)

...

为了我们操作 Docker 程序更方便,我们可以手动修复 docker-compose 命令,在 Compose 项目的发布页面,我们找到最新版本的 docker-compose-linux-aarch64 文件,下载,并使用上面的方式传送到手机环境中,并设置正确的执行权限。

# adb push ./docker-compose-linux-aarch64 /system/bin/docker-compose

./docker-compose-linux-aarch64: 1 file pushed, 0 skipped. 32.2 MB/s (61276577 bytes in 1.815s)

adb shell su -c "chmod +x /system/bin/docker-compose"

改进的 dockerd.sh

我对原始项目的 dockerd.sh 做了一些简单调整:

#!/system/bin/sh

mount -o rw,remount /

# 创建 Docker 相关的目录
root_dirs=("/var" "/run" "/tmp" "/opt" "/usr" "/system/etc/docker")
for dir in "${root_dirs[@]}"; do
    if [ ! -d "$dir" ]; then
        mkdir "$dir"
    fi
done


# 创建 /data 目录相关的目录
data_dirs=("/data/var:/var" "/data/run:/run" "/data/tmp:/tmp" "/data/opt:/opt" "/data/etc/docker:/etc/docker")
for dir in "${data_dirs[@]}"; do
    # 分割目录路径和挂载点
    paths=$(echo "$dir" | tr ':' ' ')
    data_dir=$(echo "$paths" | cut -d ' ' -f 1)
    mount_dir=$(echo "$paths" | cut -d ' ' -f 2)
    # 如果 /data/var 目录存在,清理目录中的 ./run 目录
    if [ "$data_dir" = "/data/var" ] && [ -d "$data_dir" ]; then
        rm -rf /data/var/run
    fi
    # 尝试创建并挂载目录
        mkdir -p "$data_dir"
        mount --bind "$data_dir" "$mount_dir"
        echo "mount $data_dir to $mount_dir"
done


# 创建 /dev 目录相关的目录,并挂载 cgroup
cgroup_dirs=("cpu" "cpuacct" "devices" "freezer" "hugetlb" "net_cls" "net_prio" "perf_event" "pids" "rdma")
for dir in "${cgroup_dirs[@]}"; do
    if [ ! -d "/dev/$dir" ]; then
        mkdir -p "/dev/$dir"
        case "$dir" in
            "cpu"|"cpuacct")
            ;;
        *)
            echo "mount -t cgroup -o $dir none /dev/$dir"
            mount -t cgroup -o $dir none /dev/$dir
            ;;
        esac
    fi
done


# 为 Docker 添加路由表
ip_rule1=$(ip rule | grep "from all lookup main" | wc -l)
if [ "$ip_rule1" -ne 1 ]; then
    ip rule add pref 1 from all lookup main
fi
ip_rule2=$(ip rule | grep "from all lookup default" | wc -l)
if [ "$ip_rule2" -ne 1 ]; then
    ip rule add pref 2 from all lookup default
fi


# 设置 Docker 镜像加速
echo "{\"registry-mirrors\":[\"https://mirror.baidubce.com\"],\"experimental\":true}" > /etc/docker/daemon.json
# 关闭默认防火墙,允许外部访问 Docker 容器端口
setenforce 0
# 启动 Docker
export DOCKER_RAMDISK=true
dockerd --add-runtime crun=/bin/crun -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock > /dev/null 2>&1 &

搭配注释,做了什么应该都非常清楚啦,这里就不展开赘述了。等到重新构建镜像的时候,我们再进行进一步的优化(内置 & 精简)。

第四步:启动 Docker 后台进程

当我们将所有文件都传输到手机上之后,在 adb shell 环境中把 docker 进行拉起来,Docker 运行环境就准备就绪啦。

# dockerd.sh                                                                                                                                                                       

mount /data/var to /var
mount /data/run to /run
mount /data/tmp to /tmp
mount /data/opt to /opt
mount /data/etc/docker to /etc/docker
mount -t cgroup -o devices none /dev/devices
mount -t cgroup -o freezer none /dev/freezer
mount -t cgroup -o hugetlb none /dev/hugetlb
mount -t cgroup -o net_cls none /dev/net_cls
mount -t cgroup -o net_prio none /dev/net_prio
mount -t cgroup -o perf_event none /dev/perf_event
mount -t cgroup -o pids none /dev/pids
mount -t cgroup -o rdma none /dev/rdma

这个时候,执行 docker info 就能够看到 docker 的基础运行环境状况啦:

# docker info

Client:
 Debug Mode: false

Server:
 Containers: 1
  Running: 1
  Paused: 0
  Stopped: 0
 Images: 7
 Server Version: 19.03.13
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Native Overlay Diff: false
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
 Swarm: inactive
 Runtimes: runc crun
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 8fba4e9a7d01810a393d5d25a3621dc101981175
 runc version: dc9208a3303feef5b3839f4323d9beb36df0a9dd
 init version: fec3683
 Security Options:
  seccomp
   Profile: default
 Kernel Version: 4.14.180-F1xy-0.19-pe-ten/71771c10be
 OSType: linux
 Architecture: aarch64
 CPUs: 8
 Total Memory: 11.25GiB
 Name: localhost
 ID: LLQY:OOXR:BKTQ:M2ZP:MGUJ:Q3NY:RPZL:EV7D:YGQY:H45D:BKUY:RXMA
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Registry: https://index.docker.io/v1/
 Labels:
 Experimental: true
 Insecure Registries:
  127.0.0.0/8
 Registry Mirrors:
  https://mirror.baidubce.com/
 Live Restore Enabled: false
 Product License: Community Engine

WARNING: API is accessible on http://0.0.0.0:2375 without encryption.
         Access to the remote API is equivalent to root access on the host. Refer
         to the 'Docker daemon attack surface' section in the documentation for
         more information: https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface

第五步:创建一个 Docker 应用

Docker 环境就绪后,我们来测试 Docker 应用能否运行。先创建一个目录,用于后续存放 Docker 应用数据:

mkdir -p kb
cd kb

随后,在目录中创建一个内容如下的 docker-compose.yml 配置文件:

version: '3'

services:

  wordpress:
    image: soulteary/sqlite-wordpress:6.5.2
    restart: always
    ports:
      - 8080:80
    volumes:
      - ./wordpress:/var/www/html

没错,这个配置来自项目 soulteary/docker-sqlite-wordpress,在之前的文章《WordPress 告别 MySQL:Docker SQLite WordPress》、《WordPress SQLite Docker 镜像封装细节》中,讲的比较详尽了,就不在此展开啦。

当我们准备好 docker-compose.yml 后,执行 docker-compose up -d

# docker-compose up -d
WARN[0000] /kb/docker-compose.yml: `version` is obsolete 
[+] Running 1/0
 ✔ Container kb-wordpress-1  Running 

WordPress 就很快的通过容器的方式启动起来啦。

启动应用后,我们使用下面的命令,可以简单验证服务是否正常运行:

docker exec kb-wordpress-1 curl -L http://0.0.0.0:80

不出意外,我们将得到类似下面的结果:

使用命令行验证手机上的 Docker 应用

上面的图片中,我们使用命令行来验证了 WordPress 中的程序渲染出了 HTML 页面,虽然执行时间非常长(需要排查或重新构建 Android 验证),但是证明了 Android 上运行 Docker 程序是可行的。

第六步:如何在电脑侧访问手机的应用

Android Debug Bridge(ADB)中有一个很有用的命令叫做 forward,我们可以通过这个命令,将电脑上的端口和手机上应用的端口进行打通。

 forward --list           list all forward socket connections

 forward [--no-rebind] LOCAL REMOTE
     forward socket connection using:
       tcp:<port> (<local> may be "tcp:0" to pick any open port)
       localabstract:<unix domain socket name>
       localreserved:<unix domain socket name>
       localfilesystem:<unix domain socket name>
       dev:<character device name>
       jdwp:<process pid> (remote only)
       vsock:<CID>:<port> (remote only)
       acceptfd:<fd> (listen only)

 forward --remove LOCAL   remove specific forward socket connection

 forward --remove-all     remove all forward socket connections

命令支持四种参数调用方法,分别是:

  • 以列表形式获取所有的转发映射
  • 进行本地和远程端口的转发映射
  • 撤销一个本地端口的转发映射
  • 撤销所有的转发映射

我们以实际的情况举例,上文中,我们在手机上将 WordPress 默认运行的 80 端口,映射到了手机的 8080 端口。如果我们将电脑的 8088 端口和手机上的 8080 端口打通,那么我们需要执行下面的命令:

adb forward tcp:8088 tcp:8080

当命令执行完毕后,我们就可以在浏览器中访问 http://localhost:8088 来访问手机上的应用啦。

使用电脑直接访问手机容器应用

当然,这里也会遇到和上面一样的问题,访问应用的时间会比较长。这是一个需要继续排查和解决的问题。毕竟除了 PHP 程序执行外的资源路径的响应都在 ms 级别。

当然,如果你希望这台设备上运行的 Docker 程序,能够在局域网共享,只需要让手机连上 Wi-Fi,然后在浏览器中访问手机在局域网中的 IP 地址,和对应的程序端口即可。

使用局域网访问手机上的容器应用

是不是十分简单,如果你想离线本地化使用,就不需要给手机网络,如果你想局域网共享,得到一台低成本、高性价比的 Linux 服务器,就让它连上 Wi-Fi。

最后

写到这里,本文提到的验证目标就结束啦。接下来相关的文章里,我们来聊聊本文遇到的性能问题,以及如何在 2024 年更简单的构建支持 Docker 运行的安卓内核和镜像。

我们下篇文章再见。

–EOF


我们有一个小小的折腾群,里面聚集了一些喜欢折腾、彼此坦诚相待的小伙伴。

我们在里面会一起聊聊软硬件、HomeLab、编程上、生活里以及职场中的一些问题,偶尔也在群里不定期的分享一些技术资料。

关于交友的标准,请参考下面的文章:

苏洋:致新朋友:为生活投票,不断寻找更好的朋友

当然,通过下面这篇文章添加好友时,请备注实名和公司或学校、注明来源和目的,珍惜彼此的时间 😄

苏洋:关于折腾群入群的那些事


本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 署名 4.0 国际 (CC BY 4.0)

本文作者: 苏洋

创建时间: 2024年05月03日
统计字数: 9721字
阅读时间: 20分钟阅读
本文链接: https://soulteary.com/2024/05/03/docker-powered-android-phone-knowledge-base-you-can-carry-with-you-1.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/591025.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch:对 Java 对象的 ES|QL 查询

作者&#xff1a;Laura Trotta ES|QL 是 Elasticsearch 引入的一种新的查询语言&#xff0c;它将简化的语法与管道操作符结合起来&#xff0c;使用户能够直观地推断和操作数据。官方 Java 客户端的新版本 8.13.0 引入了对 ES|QL 查询的支持&#xff0c;提供了一个新的 API&…

【简单介绍下Lisp的学习历程】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

【文献阅读】 The ITS Irregular Terrain Model(Longely-Rice模型)海上电波传播模型

前言 因为最近在做海上通信的一个项目&#xff0c;所以需要对海上的信道进行建模&#xff0c;所以才阅读到了这一篇文献&#xff0c;下面的内容大部分是我的个人理解&#xff0c;如有错误&#xff0c;请见谅。欢迎在评论区和我一起讨论。 Longely-Rice模型介绍 频率介于 20 …

深度学习:基于TensorFlow、Keras,使用长短期记忆神经网络模型(LSTM)对Microsoft股票进行预测分析

前言 系列专栏&#xff1a;机器学习&#xff1a;高级应用与实践【项目实战100】【2024】✨︎ 在本专栏中不仅包含一些适合初学者的最新机器学习项目&#xff0c;每个项目都处理一组不同的问题&#xff0c;包括监督和无监督学习、分类、回归和聚类&#xff0c;而且涉及创建深度学…

Python 植物大战僵尸

文章目录 效果图项目结构实现思路源代码 效果图 项目结构 实现思路 下面是代码的实现思路&#xff1a; 导入必要的库和模块&#xff1a;首先&#xff0c;我们导入了Python的os、time库以及pygame库&#xff0c;还有植物大战僵尸游戏中用到的各个植物和僵尸的类。 初始化游戏和…

基于Python的LSTM网络实现单特征预测回归任务(TensorFlow)

目录 一、数据集 二、任务目标 三、代码实现 1、从本地路径中读取数据文件 2、数据归一化 3、创建配置类&#xff0c;将LSTM的各个超参数声明为变量&#xff0c;便于后续使用 4、创建时间序列数据 5、划分数据集 6、定义LSTM网络 &#xff08;1&#xff09;创建顺序模…

【深度学习】第一门课 神经网络和深度学习 Week 4 深层神经网络

&#x1f680;Write In Front&#x1f680; &#x1f4dd;个人主页&#xff1a;令夏二十三 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;深度学习 &#x1f4ac;总结&#xff1a;希望你看完之后&#xff0c;能对…

G1 - 生成对抗网络(GAN)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目录 理论知识生成器判别器基本原理 环境步骤环境设置数据准备模型设计模型训练模型效果展示 总结与心得体会 理论知识 生成对抗网络&#xff08;Generative …

Jenkins流水线部署springboot项目

文章目录 Jenkins流水线任务介绍Jenkins流水线任务构建Jenkins流水线任务Groovy脚本Jenkinsfile实现 Jenkins流水线任务实现参数化构建拉取Git代码构建代码制作自定义镜像并发布 Jenkins流水线任务介绍 之前采用Jenkins的自由风格构建的项目&#xff0c;每个步骤流程都要通过不…

二维数组的鞍点(C语言)

一、鞍点解释&#xff1b; 鞍点就是该位置上的元素在该行上最大、在该列上最小&#xff1b; 二、N-S流程图&#xff1b; 三、运行结果&#xff1b; 四、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff…

Java_JVM_JVMs

JVM 官方文档说明文档目录 官方文档 JVM Specification 说明 以Java SE 17为标准 文档目录 2&#xff1a;JVM 结构 class文件数据类型 基本数据类型引用数据类型 运行时数据区 栈帧 其他内容 对象的表示浮点数运算特殊方法 初始化方法【实例、类】多态方法 3&#xff…

AI代理架构的发展:从单一到多代理系统的演进及其影响分析

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Python中无法pip的解决办法和pip的介绍

什么是pip&#xff1f; PIP是通用的Python包管理工具&#xff0c;提供了对 Python 包的查找、下载、安装、卸载、更新等功能。安装诸如Pygame、Pymysql、requests、Django等Python包时&#xff0c;都要用到pip。 注意&#xff1a;在Python3.4&#xff08;一说是3.6&#xff09…

自动化滇医通

###我已经将数据爬取出来### 现在开源集合大家的思路一起研究 &#xff08;请更换ip 以及 暂停时间 不然会提示违规操作&#xff09; 脚本读取预约信息后开始随机抢一家的&#xff0c;qiang方法里面请自行修改抓包数据参数&#xff01;&#xff01; 现在开源大家一起讨论 pyt…

富文本编辑器 iOS

https://gitee.com/klkxxy/WGEditor-mobile#wgeditor-mobile 采用iOS系统浏览器做的一款富文本编辑器工具。 原理就是使用WKWebView加载一个本地的一个html文件&#xff0c;从而达到编辑器功能的效果&#xff01; 由于浏览器的一些特性等&#xff0c;富文本编辑器手机端很难做…

【开源物联网平台】window环境下搭建调试监控设备环境

&#x1f308; 个人主页&#xff1a;帐篷Li &#x1f525; 系列专栏&#xff1a;FastBee物联网开源项目 &#x1f4aa;&#x1f3fb; 专注于简单&#xff0c;易用&#xff0c;可拓展&#xff0c;低成本商业化的AIOT物联网解决方案 目录 一、使用docker脚本部署zlmediakit 1.1 …

WebDriver使用带用户名密码验证的IP代理解决方案

背景&#xff0c;使用python3 selenium 先定义一个方法&#xff0c;这里主要用到了chrome插件的功能&#xff0c;利用这个插件来放进代理内容。 def create_proxy_auth_extension(proxy_host, proxy_port,proxy_username, proxy_password, schemehttp):manifest_json "…

【HAL库 STM32】输入捕获并实现超声波测距

文章目录 HC-SR04 超声波模块简介HC-SR04 工作原理如何使用HC-SR04模块程序效果 一、工程配置代码如果您发现文章有错误请与我留言&#xff0c;感谢 HC-SR04 超声波模块简介 HC-SR04 工作原理 模块有2个超声波换能器&#xff08;如图所示&#xff09;&#xff0c;一个发出声波…

Spark Stream

一、Spark Streaming是什么 Spark Streaming 用于流式数据的处理。Spark Streaming 支持的数据输入源很多&#xff0c;例如&#xff1a;Kafka、Flume、Twitter、ZeroMQ 和简单的 TCP 套接字等等。数据输入后可以用 Spark 的高度抽象原语如&#xff1a;map、reduce、join、wind…

基于SSM SpringBoot vue教务排课系统

基于SSM SpringBoot vue教务排课系统 系统功能 登录 个人中心 学生信息管理 教师信息管理 课室信息管理 班级信息管理 系别信息管理 专业信息管理 课程信息管理 选课信息管理 课表信息管理 开发环境和技术 开发语言&#xff1a;Java 使用框架: SSM(Spring SpringMVC Myba…