みつきんのメモ

組み込みエンジニアです。Interface誌で「Yocto Projectではじめる 組み込みLinux開発入門」連載中

YoctoProject bitbake-hashservを使ってみる(unix domain socket編)

はじめに

bitbakeではShared State Cache(sstate-cache)という仕組みを持っていて、今回ビルドしたいものが前回ビルドしたものと変更がなければ、成果物をそのまま再利用するようになっている。

手っ取り早い方法は複数のビルドディレクトリのlocal.confで設定されるSSTATE_DIRを同じディレクトリに設定すること。

この仕組みをリモート間でも共有できるようにbitbake-hashservというツールが提供されている。

今回はbitbake-hashservをunix domain socketで使用してみる。

Shared State Cacheの仕組み

ざっくりとsstate-cacheの仕組みを説明すると下記のようになる。

初回実行時

  1. bitbakeで実行されるタスクのインプットデータについてハッシュを取っておく
  2. そのハッシュとタスクのアウトプットデータのマッピング情報を保存しておく
  3. アウトプットデータの情報は捨てずに取っておく

再実行時

  1. 今回実行されるタスクのインプットデータのハッシュとsstate-cache内のハッシュが一致するか確認する
  2. 一致した場合そのタスクは処理しない(一致しない場合はタスクを実行して3のプロセスはスキップ)
  3. 処理しないタスクのアウトプットデータをクライアントに供給する

bitbake-hashservについて

unix domain socketでの接続

bitbake-hashservはunix domain socketでも通信できるようになっていて、ローカルのPC内でもShared Stateを共有できるようになっている。 単にSSTATE_DIRを共有することとの違いは後から参照するbitbakeによってもともとあるsstate-cacheが破壊されづらいという利点がある。 bitbake-hashservは起動時にリードオンリーモードを指定できるため、更に安全に運用することができる。

短所としてはデータがコピーされるため同一PC内ではsstate-cacheに使用されるストレージ容量が増加してしまうところ。

BB_SIGNATURE_HANDLERに「OEEquivHash」を設定してbitbakeを実行するとcache/hashserv.dbが作成される。 これはSQLiteのデータベースとなっており、タスクのインプットデータのハッシュとアウトプットデータのマッピング情報を持っている。

また、OEEquivHashの「Equiv」はEquivalenceの略で、インプットデータ中の空白など実体に関係ない差分は「同一とみなす」という処理を行っている。これによりsstate-cacheの適用可能性が高くなるようになっている。

bitbake-hashservの実行

サーバ側の設定

サーバとなる初回ビルドを行う環境についてはlocal.confにこのような設定があれば良い。

BB_HASHSERVE = "auto"
BB_SIGNATURE_HANDLER = "OEEquivHash"

このままbitbakeを実行して「hashserv.db」と「sstate-cache」が生成されれば必要なデータは揃う。 もちろん初回実行時なので数時間はかかる。

サーバ側環境の構築

実際にbitbake-hashservを動かす環境を作成してみる。

$ mkdir -p ~/yocto/rpi-kirkstone
$ cd ~/yocto/rpi-kirkstone
$ git clone git://git.yoctoproject.org/poky.git -b kirkstone
$ source poky/oe-init-build-env build-hashserv
$ bitbake-layers layerindex-fetch meta-raspberrypi

local.confに下記を追加する。

MACHINE = "raspberrypi4-64"

# setup for hashserv database
BB_SIGNATURE_HANDLER = "OEEquivHash"

# enable uart
ENABLE_UART = "1"

# systemd
INIT_MANAGER = "systemd"

試しにcore-image-baseをビルドする。 今回はtimeコマンドで時間を計測する。

$ time bitbake core-image-base
... (snip) ...
real    84m43.468s
user    0m29.526s
sys     0m9.020s

まっさらな状態でビルドした結果、約85分かかった。

bitbake-hashservの起動

この状態でcacheディレクトリを覗くと下記のようにhashserv.dbが生成されていることがわかる。

~/yocto/rpi-kirkstone/build-hashserv$ ls ./cache/
bb_codeparser.dat        bb_unihashes.dat  local_file_checksum_cache.dat
bb_persist_data.sqlite3  hashserv.db       sanity_info

これを使用してbitbake-hashserveを起動する。接続状況を把握しやすいようにログレベルをdebugに指定している。

$ bitbake-hashserv -r -l debug -d ./cache/hashserv.db -b "unix://./hashserv.sock"

今回は「unix://./hashserv.sock」で待ち受けしているため、${HOME}/yocto/rpi-kirkstone/build-hashserv/hashserv.sockに接続することでサーバと通信できる。

bitbake-hashservへの接続

クライアント側の設定

実行中のbitbake-hashservに接続するにはlocal.confに次のような設定を行う。

BB_SIGNATURE_HANDLER = "OEEquivHash"
BB_HASHSERVE = "unix://${HOME}/yocto/rpi-kirkstone/build-hashserv/hashserv.sock"
SSTATE_MIRRORS = "file://.* file://${HOME}/yocto/rpi-kirkstone/build-hashserv/sstate-cache/PATH"

BB_HASHSERVEにはbitbake-hashserv実行時に作成されたunix domain socketのファイルを指定する。

SSTATE_MIRRORSにはサーバ側の初回ビルド時に作成されたsstate-cacheの保存場所を指定する。 つまり、サーバが提供するsstate-cacheの在処となる。 PATHと指定することでsstate-cacheディレクトリ以下の構造をbitbakeが展開できるようになる。

クライアント側環境の構築

実際にbitbake-hashservに接続する環境を作成してみる。

サーバ側とは別の端末を開き下記を実行する。

$ cd ~/yocto/rpi-kirkstone
$ source poky/oe-init-build-env build-client
$ bitbake-layers add-layer ../poky/meta-raspberrypi

local.confに下記を記載する。

MACHINE = "raspberrypi4-64"

# setup for connecting to bitbake-hashserv
BB_SIGNATURE_HANDLER = "OEEquivHash"
BB_HASHSERVE = "unix://${HOME}/yocto/rpi-kirkstone/build-hashserv/hashserv.sock"
SSTATE_MIRRORS = "file://.* file://${HOME}/yocto/rpi-kirkstone/build-hashserv/sstate-cache/PATH"

# enable uart
ENABLE_UART = "1"

# systemd
INIT_MANAGER = "systemd"

bitbake-hashservへの接続

接続のための設定をlocal.confに行ったため、bitbakeを実行してみる。

$ time bitbake core-image-base
... (snip) ...
real    1m43.522s
user    0m1.438s
sys     0m0.472s

bitbake-hashservの提供するsstate-cacheを参照することでビルド時間が劇的に短くなることがわかる。

サーバ側のログ

この時のサーバ側のログの表示は下記のようになっており、外部から接続されデータを供給したことが伺える。

$ bitbake-hashserv -r -l debug -d ./cache/hashserv.db -b "unix://./hashserv.sock"
Listening on './hashserv.sock'
Client '' connected
Handling get-stream
Client disconnected

まとめ

bitbakeではもともと実装されていたsstate-cacheを他のビルド環境から参照することでビルド時間が劇的に改善することがわかった。 もちろん別のMACHINE向けのビルドだったり、参照するレシピが更新されていたりして保存されているsstate-cacheと入出力が異なる場合はタスクが実行されるためこの効果は薄いもしくは無い。

bitbake-hashservを使用することで直接SSTATE_DIRを共有しなくても、既存のsstate-cacheを参照できることがわかった。 今回はunix domain socketで同一ホストのデータを参照したが、リモートで提供されているsstate-cacheを参照できそうなこともわかった。

機会があれば実際にリモートのsstate-cacheを提供/参照する方法も試してみたい。