Pixel6のNativeカーネル上でdockerを動かす（Androidのカスタムカーネル）

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210612/a89166f8-906c-d57d-c83e-fdfd0897e77f.png">

## はじめに
Android上でdcokerが動けば便利じゃないかな〜と思いつつ、ズバリそれなページがあったのでチャレンジしてみました。

**Docker on Android(こっちがメイン)**
https://gist.github.com/FreddieOliveira/efe850df7ff3951cb62d74bd770dce27

**Docker-native-on-Termux-on-Android（上記ページの補足的な情報として）**

https://github.com/Morakhiyasaiyam/Docker-native-on-Termux-on-Android

セルラー通信(4G / 5G)ができるスマホ端末をホストに、docker絡みの開発やら動作環境が作れるので便利かな〜と。色々なペリフェラルデバイスが付いてるAndroid端末で動作できたら面白いですよね、Android端末自体余ってる人も多いでしょうし、
Android上で動く仮想ソリューションのUserLAndなんかもあるのですが、chrootを使ってのなんちゃってUbuntuが動作したりしますが、非root環境だったりするので、真のLinuxコンテナ動作や仮想化には少し遠いですね。

## よしやるぞ〜
このページ自体は結構前から知ってたのですが、手順がかなり面倒なのでやる気が起きませんでした。ざっくり手順を並べると、、

>(1) bootloader unlockする
(2) root化する(Magiskからboot.imgにrootパッチ当てて、ホストPC側に戻してfastbootで焼く)
(3) termuxを入れてroot権限でcheck-config.shを実行。
　※check-config.shはDocker動作に必要なカーネル設定で不足している機能を列挙してくれるスクリプト。
(4) repoで端末のカーネルと同じバージョンのソースコードを取得する
(5) とりあえず素の状態でビルドしてAndroidカーネル関連imgを作ってみる。それで動くか確認
　※Pixel6のUP1A.231105.003(Android14)でのカーネルブランチは「android-gs-raviole-5.10-android14」
(6) kernel configでDockerに必要な機能を追加してビルド、そのカーネルimgで動作するか確認
(7) 動作したらMagiskにて再度root化、check-config.shをもう一度実行してdocker対応しているか確認
(8) termuxでdocker関連のパッケージを入れたり、ビルドしたりする

、、、なかなか面倒くさぃ、dockerを動かすという情熱がないと挫けてしまいそうです。
今回はアドベントカレンダー2023向けにQiita記事を書く命題を受けましたので、チャレンジしてみようと思い立った訳です。

あと補足ですが、この記事自体が結構古い情報だったりして、
手順内にAndroidカーネルソースにパッチを当てろ、とか書いてあったりしますが、取得したソースを確認したところ、現状のカーネルソースには対応が不要な様に見えました。

手順を振り返ると、
今回の作業のキモとしては(5)Androidカーネルのビルドと、(6)カスタムカーネルが起動するか？にかかってると思います。

## ビルド環境とターゲットのAndroid端末
**ビルド環境**
>CPU AMD Ryzen 5 2400G
RAM 8GB DDR4
DISK 256GB NVMe disk & 2TB HDD
OS Lubuntu 22.04 x86_64

**ターゲット端末**
>Pixel6 (コードネームoriore)

AOSPの全ビルドに比べれば、ビルドマシンに求められるスペックは低いのですが、それでもなるべく高速なマシンが欲しいです。特にメモリは一番重い処理(GKIカーネルのLLVMリンク作業)で30GB近く使う様なので、32GBは欲しいです。
このビルド環境はそんなに無いので、NVME上にswapファイルを32GB作って更にzram-configのメモリ圧縮を導入しています。一応それでもビルドは環境できました。。。

## とりあえずキモになるカーネルビルドあたりの作業

```bash
$ mkdir android-kernel
$ cd android-kernel
$ repo init -u https://android.googlesource.com/kernel/manifest -b android-gs-raviole-5.10-android14
$ cp hoge~~/     # factoryイメージからvendorファイルを取得して配置、後述参照
$ BUILD_CONFIG=aosp/build.config.gki.aarch64 build/config.sh   # androidでのmenuconfig、GKIに対しての設定変更ができる
$ BUILD_AOSP_KERNEL=1 ./build_slider.sh   # androidでのmake、「BUILD_AOSP_KERNEL=1」はGKI部分もビルドするフラグ

#〜ビルド完了まで待つ〜
# out/mixed/dist/に必要なimg群ができる、
# Pixel6で使うイメージはboot.img, vendor_boot.img、dtbo.img、vendor_dlkm.img

$ fastboot flash boot boot.img
$ fastboot flash dtbo dtbo.img
$ fastboot flash vendor_boot vendor_boot.img
$ fastboot reboot fastboot
$ fastboot flash vendor_dlkm vendor_dlkm.img
#〜ここでPixelを再起動〜
```

※ここらへんの詳細な手順は、Googleがご丁寧に説明してくれています。

https://source.android.com/docs/setup/build/building-pixel-kernels

あとカスタムカーネルを起動する場合は下記コマンドでカーネル検証を無効化する必要があるとの事です
```bash
$ fastboot oem disable-verification
```
fastbootコマンドなので、bootloader unlockのタイミングでサクッと叩いときましょう。

## ここまでの作業で共有できそうな設定やらテキスト
[・素のカーネルで取得したcheck-config.shの結果](https://drive.google.com/file/d/1KTQTbUDriLHJYZd60CIOJFKvcbgx7rA1/view?usp=sharing)

[・menuconfigで設定したファイル（aosp/arch/arm64/configs/gki_defconfigにコピって使えます）](https://drive.google.com/file/d/1Fb6sDlA98DmVqAgmUI1J99E8f32ewrTk/view?usp=sharing)

**〜余談〜**
kernelのmenuconfigで必要な項目を見つけてyに変えるのって闇雲に探すとなかなか大変なのですが、「/」を入力後に検索ワード(CONFIG_POSIX_MQUEUEとか)を指定すると、その項目に辿り着くまでの手順を教えてくれたりします。これ今回初めて知って目からウロコでした。

## カスタムカーネル導入後のcheck-configの結果
添付画像のとおりcheck-config的にはdocker動作が可能な状況が整いました。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210612/35e7ec93-2e23-a94a-ade3-45b0417f4cf1.png">

ここからdockerを動かしたりしたいのですが、
```bash:androidのtermux上の処理
$ pkg install root-repo && pkg install docker
$ pkg install tsu screen # screenの裏処理でsudo mount -t tmpfs cgroup_root /sys/fs/cgroup && sudo dockerd --iptables=false
$ sudo docker run hello-world
```
の流れで動くはずなんですが、エラーで動きませんね、
現状termuxパッケージの最新版dockerだとバグがあり動かないらしい。
まあカスタムカーネルで動くようになったので、とりあえず今回はここまでで良いか、、

## Androidカーネルの構成のおさらい

これまでに何回かGKIという単語が出てきましたが、Google/Generic? Kernel Imageの事です。
これまではACK(Android Common Kernel)と呼ばれてたらしいです。

![スクリーンショット 2023-11-26 15.22.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210612/f2b4d633-3460-e19c-a963-e08102621ffd.png)

Androidでは多様なベンダから提供される事を想定して、共通となるカーネルとデバドラ連携部分をGKIとKMIモジュールとして提供されています(図の下段緑色の部分)。通常はそれらのカーネル部分はboot.imgとして提供され、ベンダ依存のデバドラなどモジュール(図の下段青部分)はvendor_boot.imgやvendor_dlkmとして提供されてたりします。
今回のようなLinux由来の機能を追加してカスタムカーネルを作る場合には、GKIをリビルドする必要があるので「BUILD_AOSP_KERNEL=1」の指定する必要になります。

自分も今回の作業で分かった話なので、少し認識齟齬あるかもですが、大体そんな流れかと。。
詳しい話は下記をみて理解してください。

**Androidカーネルの概要**

https://source.android.com/docs/core/architecture/kernel?hl=ja

**Androidのパーティション**

https://scrapbox.io/onokatio/Android%E3%81%AE%E3%83%91%E3%83%BC%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%B3

**Guide compile kernel Raviole from sources !**

https://xdaforums.com/t/guide-compile-kernel-raviole-from-sources.4596285/

## まとめ
Linuxのカーネルビルドは結構やってましたが、Androidのカーネルビルドは初めてでした。
（Androidカーネルの構成(GKIやらvendor依存部の話)とかAndroid版menuconfigの手順も理解できてよかった。）
Linuxと違って起動ログが見えないから起動しなかったら原因解析がキツそうだな、と思いました。