🇺🇸 English version
中華民國一百一十二年二月三日晨,解衣欲睡,夜色入戶,欣然起行,編譯核心。
Android核心跟GNU/Linux有差距,因此有Root也無法跑Docker。
在XDA終於有人移植新版LineageOS後,利用其核心原始碼為基礎,給Sony Xperia 5 II(pdx206)手機編譯自訂核心,使其能以原生效能跑Docker。

搞那麼大功夫就為了跑Docker?但少閒人如吾者耳。
將Xperia 5 II 刷成LineageOS 20,並將其Root。
安裝Termux並初始化。
下載Moby的指令稿用於檢查核心缺少的功能
1
2
3
4
5
| pkg install wget tsu
wget https://raw.githubusercontent.com/moby/moby/master/contrib/check-config.sh
chmod +x check-config.sh
sed -i '1s_.*_#!/data/data/com.termux/files/usr/bin/bash_' check-config.sh
sudo ./check-config.sh
|
- 將CONFIG顯示為紅字的項目記下來。

Generally Necessary
下的項目是一定要開啟的設定檔;Optional Features
為可開可不開。
保險起見還是盡量全開,但我可以告訴你的是,ZFS到現在(6.1.9)都還沒進入Linux主線核心,所以這設定檔是開不了的。
2021年曾經給紅米Note 5製作過能跑Docker的核心,但當時是使用獨立編譯核心的方式,失敗率很高。因此這次我就採用跟著原始碼樹一起編譯核心的作法。
參考此文下載50GB左右的LineageOS 20原始碼。
跳到該文的「編譯Linux核心」段落。
進入編譯環境。
1
2
| source build/envsetup.sh
breakfast pdx206
|
- 修改核心設定檔,切換至
~/android/lineage/kernel/sony/sm8250/
目錄,以pdx206_defconfig
產生.config
。
1
2
| export ARCH=arm64
make pdx206_defconfig
|
- 進入核心設定檔選單,開始修改核心設定檔
對照Moby指令稿顯示紅字缺少的CONFIG,將其一一開啟。用上下鍵盤移動,選中下方Exit返回上一頁(退出時記得選Save),Enter確認,空白鍵選取。
例如要找IP_VS
這個CONFIG,按/
,搜尋,接著它會告訴你具體位置。有些CONFIG需要先滿足Depends on
寫的CONFIG條件才會出現。

按照prompt去找,像CONFIG_IP_VS
位於Networking Support -> Networking options -> Network packet filtering framework (Netfilter) -> IP virtual server support

有些功能低版本核心找不到就是找不到,例如CONFIG_CGROUP_HUGETLB
要Linux 4.2以上核心才有,如果該設定檔沒有列在Generally Necessary
就不用太緊張。
我另外啟用了CONFIG_BINFMT_MISC
設定檔,方便跑跨架構的應用程式。
根據Frederico Oliveira的文章,需要修改核心原始碼目錄下的kernel/Makefile
為以下內容。可用patch程式套用,或手動改原始碼。
1
2
3
4
5
6
7
8
9
10
11
12
13
| diff --git a/kernel/Makefile b/kernel/Makefile
index d5c1115..2dea801 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -121,7 +121,7 @@ $(obj)/configs.o: $(obj)/config_data.h
# config_data.h contains the same information as ikconfig.h but gzipped.
# Info from config_data can be extracted from /proc/config*
targets += config_data.gz
-$(obj)/config_data.gz: arch/arm64/configs/lavender_stock-defconfig FORCE
+$(obj)/config_data.gz: $(KCONFIG_CONFIG) FORCE
$(call if_changed,gzip)
filechk_ikconfiggz = (echo "static const char kernel_config_data[] __used = MAGIC_START"; cat $< | scripts/basic/bin2c; echo "MAGIC_END;")
|
- 接著修改
net/netfilter/xt_qtaguid.c
為以下內容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| --- orig/net/netfilter/xt_qtaguid.c 2020-05-12 12:13:14.000000000 +0300
+++ my/net/netfilter/xt_qtaguid.c 2019-09-15 23:56:45.000000000 +0300
@@ -737,7 +737,7 @@
{
struct proc_iface_stat_fmt_info *p = m->private;
struct iface_stat *iface_entry;
- struct rtnl_link_stats64 dev_stats, *stats;
+ struct rtnl_link_stats64 *stats;
struct rtnl_link_stats64 no_dev_stats = {0};
@@ -745,13 +745,8 @@
current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
iface_entry = list_entry(v, struct iface_stat, list);
+ stats = &no_dev_stats;
- if (iface_entry->active) {
- stats = dev_get_stats(iface_entry->net_dev,
- &dev_stats);
- } else {
- stats = &no_dev_stats;
- }
/*
* If the meaning of the data changes, then update the fmtX
* string.
|
- 修改完成後,用
.config
取代原本的pdx206_defconfig
。下一步make clean後.config
會被刪除。
1
| cp .config arch/arm64/configs/pdx206_defconfig
|
- 編譯核心
1
2
| make clean
mka bootimage
|
輸出的boot.img
位於~/android/lineage/out/target/product/pdx206/
。如果需要多次重複編譯,可以只刪這裡的檔案。
Docker需要Root權限才能跑,所以要將boot.img
傳到手機,開啟Magisk選取修補boot.img
,再將修補後的magisk-boot.img
傳回電腦。
使用fastboot將修補好的magisk-boot.img
刷到手機。我另外將編譯好的成品放到了Github。
1
| fastboot flash boot magisk-boot.img
|
開機後顯示:「你的裝置發生內部問題」是正常現象。
- 開啟Termux,手動掛載cgroup
1
| sudo mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup
|
- 這次編譯的時候也順便開啟了binfmt設定檔,也許chroot的時候會用到,將其啟用:
1
2
3
| su
mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc
echo 1 > /proc/sys/fs/binfmt_misc/status
|
再次執行Moby指令稿:sudo ./check-config.sh
,查看CONFIG是否都變成綠字。

安裝Docker與Docker compose。目前Termux還沒收podman,也許可以用chroot或proot安裝?
1
2
| pkg install root-repo
pkg install docker docker-compose
|
- 啟動Docker daemon
1
| sudo dockerd --iptables=false
|
- 從螢幕左側滑進來,按New Session開啟新終端機。測試Hello World:
1
| sudo docker run hello-world
|
應該會看到如下輸出

如果映像檔需要使用--init
引數,那麼得編譯安裝tini
:
1
2
3
4
5
6
7
8
9
10
| cd $TMPDIR/docker-build
wget https://github.com/krallin/tini/archive/v0.19.0.tar.gz
tar xf v0.19.0.tar.gz
cd tini-0.19.0
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$PREFIX ..
make -j8
make install
ln -s $PREFIX/bin/tini-static $PREFIX/bin/docker-init
|
測試成功之後,就可以在手機跑Docker容器了!可以嘗試跑Linux容器、伺服器服務、轉檔工具等等,也可以跑圖形界面再裝Box86 + Wine。
如果映像檔只支援x86架構,在編譯binfmt後參考此篇解決。
目前Termux的Docker還有些缺陷,例如無法使用itptables、只能使用host網路(加上--net=host --dns=8.8.8.8
引數)、docker-compose無法使用等問題。
關閉Docker前務必在Docker daemon的終端機按CTRL+C取消執行。
Docker需要的cgroups同時也能用來跑Flatpak應用程式。Flatpak是跨發行版的打包套件格式,可以輕鬆解決依賴問題。
chroot環境只要做些修改就可以跑Flatpak APP。

Termux沒有收Flatpak套件,因此要先設定chroot Ubuntu環境。
修改登入Ubuntu的指令稿,在最前面加入掛載自身目錄的指令,防止出現Failed to make / slave: Invalid argument
的bug。
1
2
3
4
5
| # 加入至頁首
busybox mount --bind /data/local/tmp/chrootubuntu /data/local/tmp/chrootubuntu
# 加入至末尾
busybox umount /data/local/tmp/chrootubuntu
|
- 登入Ubuntu,安裝Flatpak,然後登出Ubuntu,手機重開機。
1
2
3
| sudo apt install flatpak
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
exit
|
- 重新登入chroot Ubuntu,便可安裝Flaptak的應用程式,例如ffmpeg:
1
| flatpak install org.freedesktop.Platform
|
- 執行Flatpak應用程式前要先啟動dbus:
1
2
| mkdir /run/dbus
dbus-daemon --system
|
- 執行Flatpak應用程式可加上
--devel
看除錯輸出。
1
| flatpak run --devel --command=ffmpeg org.freedesktop.Platform -version
|