在安卓手機上原生執行docker

Python成长路發表於2024-07-18

前言

之前的文章(香橙派5plus上跑雲手機方案一 redroid(帶硬體加速))在Ubuntu的docker裡執行安卓,這裡說下怎麼在安卓手機下執行docker,測試也可以跑Ubuntu。

想在手機上執行docker想的不是一天兩天了,其實很久之前就有這個想法了,只是奈何安卓核心一直編譯不透過。現在覺得過了那麼久,技術應該有點提升了,覺得我又行了,所以又來試試。

測試用的手機是從閒魚淘的一個一加七Pro(12+256G),使用的系統是Lineageos19.1,後面放出我執行的刷機包和編譯的boot檔案,你直接刷進手機應該就能執行了。

檢查核心配置

首先檢查下當前的安卓核心確實哪些執行docker需要的核心配置

先下載https://raw.githubusercontent.com/moby/moby/master/contrib/check-config.sh推送到手機的/data/local/tmp下: adb push check-config.sh /data/local/tmp/check-config.sh

然後使用adb root切換到root(需要再開發者模式裡開啟允許root除錯),接著執行sh /data/local/tmp/check-config.sh檢視。缺少的其實挺多

編譯核心

要想在手機上原生執行docker,修改核心引數重新編譯核心這個是繞不過去的,所以先開始編譯核心。lineage的核心編譯比較簡單,如果下載系統原始碼來編譯的話基本沒什麼坑,這裡就不多說了。有興趣的可以看之前的文章:

  • 為一加七Pro(LineageOs17.1 4.14核心版本)編譯KernelSu
  • wsl2-ubuntu20編譯Lineage17(Android10)

執行docker也需要root許可權,所以會將kernelsu一起編譯進去,這裡也不贅述了。

修改核心引數

先說一下修改核心引數的流程:

cd kernel/oneplus/sm8150 進到對應的核心原始碼根目錄

export ARCH=arm64 指定核心編譯的平臺,不指定預設是x86

export SUBARCH=arm64

export CROSS_COMPILE=aarch64-linux-gnu-

make vendor/sm8150-perf_defconfig 透過這個檔案生成.config

make menuconfig 啟動核心編輯選單

make savedefconfig 儲存.config檔案為defconfig,至於這個和手動修改有什麼區別就沒細究了,反正都推薦用這個

mv defconfig arch/arm64/configs/vendor/sm8150-perf_defconfig 覆蓋原先的核心配置檔案,建議先備份一下原先的檔案

rm .config

make可能有一個錯誤提示/bin/sh: 1: aarch64-linux-gnu-gcc: not found,透過搜尋看到這個問答[1]說應該是缺少包:sudo apt-get install gcc-aarch64-linux-gnu

配置

這裡為了方便直接抄別人的核心配置放到.config檔案裡: https://yzddmr6.com/posts/android-run-docker/

CONFIG_NAMESPACES=y
CONFIG_NET_NS=y
CONFIG_PID_NS=y
CONFIG_IPC_NS=y
CONFIG_UTS_NS=y
CONFIG_CGROUPS=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_SCHED=y
CONFIG_CPUSETS=y
CONFIG_MEMCG=y
CONFIG_KEYS=y
CONFIG_VETH=y
CONFIG_BRIDGE=y
CONFIG_BRIDGE_NETFILTER=y
CONFIG_IP_NF_FILTER=y
CONFIG_IP_NF_TARGET_MASQUERADE=y
CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
CONFIG_NETFILTER_XT_MATCH_IPVS=y
CONFIG_NETFILTER_XT_MARK=y
CONFIG_IP_NF_NAT=y
CONFIG_NF_NAT=y
CONFIG_POSIX_MQUEUE=y
CONFIG_NF_NAT_IPV4=y
CONFIG_NF_NAT_NEEDED=y
CONFIG_CGROUP_BPF=y
CONFIG_USER_NS=y
CONFIG_SECCOMP=y
CONFIG_SECCOMP_FILTER=y
CONFIG_CGROUP_PIDS=y
CONFIG_MEMCG_SWAP=y
CONFIG_MEMCG_SWAP_ENABLED=y
CONFIG_IOSCHED_CFQ=y
CONFIG_CFQ_GROUP_IOSCHED=y
CONFIG_BLK_CGROUP=y
CONFIG_BLK_DEV_THROTTLING=y
CONFIG_CGROUP_PERF=y
CONFIG_CGROUP_HUGETLB=y
CONFIG_NET_CLS_CGROUP=y
CONFIG_CGROUP_NET_PRIO=y
CONFIG_CFS_BANDWIDTH=y
CONFIG_FAIR_GROUP_SCHED=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_IP_NF_TARGET_REDIRECT=y
CONFIG_IP_VS=y
CONFIG_IP_VS_NFCT=y
CONFIG_IP_VS_PROTO_TCP=y
CONFIG_IP_VS_PROTO_UDP=y
CONFIG_IP_VS_RR=y
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_APPARMOR=y
CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_POSIX_ACL=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_VXLAN=y 
CONFIG_BRIDGE_VLAN_FILTERING=y
CONFIG_CRYPTO=y 
CONFIG_CRYPTO_AEAD=y
CONFIG_CRYPTO_GCM=y
CONFIG_CRYPTO_SEQIV=y
CONFIG_CRYPTO_GHASH=y 
CONFIG_XFRM=y
CONFIG_XFRM_USER=y
CONFIG_XFRM_ALGO=y
CONFIG_INET_ESP=y
CONFIG_INET_XFRM_MODE_TRANSPORT=y
CONFIG_IPVLAN=y
CONFIG_MACVLAN=y
CONFIG_DUMMY=y
CONFIG_NF_NAT_FTP=y
CONFIG_NF_CONNTRACK_FTP=y
CONFIG_NF_NAT_TFTP=y
CONFIG_NF_CONNTRACK_TFTP=y
CONFIG_AUFS_FS=y
CONFIG_BTRFS_FS=y
CONFIG_BTRFS_FS_POSIX_ACL=y
CONFIG_BLK_DEV_DM=y
CONFIG_DM_THIN_PROVISIONING=y
CONFIG_OVERLAY_FS=y

然後使用make menuconfig在介面上一個一個檢視配置的開啟情況,發現CONFIG_BRIDGE_VLAN_FILTERING沒有開啟,檢視了下是因為依賴項VLAN_8021Q沒有開啟

CONFIG_IPVLAN則是依賴項NET_L3_MASTER_DEV沒開啟

所以在配置檔案裡再加上這兩個配置, 再執行make menuconfig儲存

CONFIG_VLAN_8021Q=y
CONFIG_BRIDGE_VLAN_FILTERING=y
CONFIG_NET_L3_MASTER_DEV=y
CONFIG_IPVLAN=y

CONFIG_CGROUP_HUGETLB依賴於HUGETLB_PAGE,但是將這個配置直接加到.config不生效,介面上也不顯示具體的開啟路徑

問了下gpt說是在File systems -> Pseudo filesystems -> HugeTLB file system support,在介面上啟用,再在配置檔案里加上CONFIG_CGROUP_HUGETLB,再開啟介面看到就是啟用狀態。

準備工作都做完了,然後按照上面的命令儲存為defconfig在覆蓋原先的核心配置就可以開始編譯了

開始編譯

這裡需要到lineage原始碼根目錄,而不是在核心原始碼根目錄

source build/envsetup.sh
breakfast guacamole
make bootimage

接著就會出現一個折磨我兩週的錯誤:

aarch64-linux-gnu-ld is not allowed to be used. See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.

雖然他給了一個連結讓我們從裡面看更多資訊,連結裡也給瞭解決方法:

但是試了也是沒用,

用谷歌搜尋也搜不出什麼有效的方法,這兩天突然想能不能修改它提到的build/soong/ui/build/paths/config.go檔案,問了下gpt,它說在config.go裡的Configuration里加一行"aarch64-linux-gnu-ld": Allowed,就可以

試了下確實是可以,有時候gpt就很好用。我開始還以為是將ld改成Allowed,因為前面明顯是字首。然後等待幾分鐘就編譯完成了

將生成的boot.img在fastboot模式下用fastboot flash boot boot.img輸入手機後啟動手機。

檢測結果

編譯完的核心還有三個地方是紅色(missing)

第一處經測試不是核心引數的問題,需要執行一次這行命令:sudo mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup,下面兩個無法解決,因為是非必須項,先不管它了。

補丁

有的提到還需要對net/netfilter/xt_qtaguid.c做補丁,但我這個核心程式碼裡沒有這個檔案就跳過了。

補充

為了避免核心引數有遺漏,我們把https://github.com/lateautumn233/android_kernel_docker裡的提到的核心引數也加到.config裡,再重新編譯一次。

執行docker

目前看到的執行docker有兩種方式:

  • termux
  • 直接執行二進位制檔案

這裡我選擇第一種,第一種更成熟一點,很多東西都已經幫我們做好了。

termux

# The main termux repository, with cloudflare cache
deb https://packages-cf.termux.dev/apt/termux-main/ stable main
# The main termux repository, without cloudflare cache
# deb https://packages.termux.dev/apt/termux-main/ stable main

證書錯誤

使用apt安裝包時出現下面的證書無效。

Certificate verification failed: The certificate is NOT trusted. The certificate chain uses not yet valid certificate.

發現是手機的系統時間不對,沒有在證書的有效時間內,修改下系統時間就好了。這個我是安裝了一個via瀏覽器去訪問百度,也跳出了證書不安全的提示,然後點選證書進去就看到證書有效時間是2023-12到2024-12的範圍,而手機的時間是2022,就猜測是時間不對。

換apt源

https://mirrors.tuna.tsinghua.edu.cn/help/termux/

將下面的內容先儲存為a.sh,然後用adb push a.sh /data/local/tmp/a.sh

sed -i 's@^\(deb.*stable main\)$@#\1\ndeb https://mirrors.tuna.tsinghua.edu.cn/termux/apt/termux-main stable main@' $PREFIX/etc/apt/sources.list
apt update && apt upgrade

接著在termux終端輸入sh /data/local/tmp/a.sh就好了。

遠端

apt install openssh

sshd 啟動ssh服務

whoami 檢視當前使用者

passwd 設定密碼

當然也可以透過公私鑰的方式配置免密登入,這裡我先用密碼了。安裝openssh的時候看提示應該就已經生成了公鑰,目錄是$PREFIX/etc/shh,然後就和linux免密登入一樣了。

$PREFIX這個變數是termux定義的,它類似於linux的根目錄,比如linux的/etc目錄在termux裡就是指$PREFIX/etc,這在後面的docker換源時會用到。

sshd啟動的預設埠是8022,我的使用者名稱是u0_a140,在cmd下使用ssh -p 8022 u0_a140@192.168.31.248就能連線了,也可以使用xshell這些工具連線。

切換pkg源

輸入termux-change-repo,選擇第二個按空格回車,會藍色畫面一會(應該還是網路原因,很慢),等待出現源選擇,然後空格勾選阿里的源

先掛載cgroup

需要安裝pkg install sudo,並且用kernelsu給termux root許可權,不清楚和下面的root-repo包有什麼區別

sudo mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup

這個每次重啟都需要執行一遍

執行docker

pkg install root-repo && pkg install docker 安裝root和docker

這時候需要先配置一下docker源,不然拉取不了映象

pkg install vim

mkdir -p /data/data/com.termux/files/usr/etc/docker

vim /data/data/com.termux/files/usr/etc/docker/daemon.json

將下面的內容加到這個檔案裡,記得前面要加個英文逗號

"registry-mirrors": [
        "https://hub.uuuadc.top",
        "https://docker.anyhub.us.kg",
        "https://dockerhub.jobcher.com",
        "https://dockerhub.icu",
        "https://docker.ckyl.me",
        "https://docker.awsl9527.cn"
    ]

sudo dockerd --iptables=false 執行docker服務

sudo docker run hello-world

這時候啟動docker容器會報錯,

docker: Error response from daemon: failed to create task for container: failed to start shim: start failed: io.containerd.runc.v2: create new shim socket: listen unix /data/data/com.termux/files/usr/var/run/containerd/s/fca432b16f1e32bdfce67923b7e94f3ab7f741783e5032a938bd6869d8b6d3af: bind: invalid argument: exit status 1: unknown.

這裡並不清楚什麼原因,但偶然記得之前在酷安上看到一個東西,過去翻了翻[2],說是要降級containerd包的版本。先檢視下containerd包的版本:

然後下載帖子裡這個包,用adb傳到手機上(xftp也可以),然後使用dpkg -i containerd_1.6.21-1_aarch64.deb安裝

接著檢視版本apt show containerd -a就看到已經安裝上了

然後重新啟動sudo dockerd --iptables=false,在跑hello-world容器就正常了

試了下跑python也可以,那說明可以在手機跑爬蟲了:

Ubuntu映象也是可以的:

刷機包和boot

https://github.com/kanadeblisst00/docker-in-guacamole

引用連結

  • [1] https://stackoverflow.com/questions/28565640/build-kernel-with-aarch64-linux-gnu-gcc
  • [2] https://www.coolapk.com/feed/51581431?shareKey=MmRlNTgxOTVmNjliNjY5M2QwMGU~&shareUid=4285440&shareFrom=com.coolapk.market_14.2.3

參考連結

  • https://gist.github.com/FreddieOliveira/efe850df7ff3951cb62d74bd770dce27
  • https://ivonblog.com/posts/run-docker-natively-on-android/
  • https://yzddmr6.com/posts/android-run-docker/

本文由部落格一文多發平臺 OpenWrite 釋出!

相關文章