みつきんのメモ

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

yoctoでコンテナイメージを取り込む(その1)

yoctoでコンテナイメージを取り込む(その1)

はじめに

前回作成したイメージをbitbakeで作成するイメージに取り込む方法を考える。

理想は、bitbakeした時点でイメージがdockerやpodmanにインポートされた状態になり、 初回起動時のdocker imagesやpodman imagesで認識された状態になること。

これは簡単そうに見えるが実はかなり難しい。

まず、検討しなければならない項目を下記に挙げる。

  1. 使用するコンテナシステム
    1. docker
    2. podman
  2. インポートするタイミング
  3. ビルド時に組み込む
  4. 初回起動時に組み込む

使用するコンテナシステム

まずは使用するコンテナシステムについて考えてみる。dockerとpodmanはどちらもmeta-virtualizationで提供されているためどちらかを組み込んで使用することができる。 これらコンフリクトするため同時にインストールすることはできない。

dockerとpodmanの大きな違いは、実行時にデーモン(daemon)が必要か否か?である。 dockerはコマンドラインで操作するためあまり意識しないが、実際のイメージやコンテナへの操作はdockerdというデーモンが行っている。すなわちdockerコマンドを実行するにはdockerdが起動済みである必要がある。

一方でpodmanはdockerdのようなデーモンが不要となる。

bitbakeの途中でコンテナの操作をしようと検討する際にdockerの使用を検討する場合は、dockerdをどうするか?を最初に解決する必要がある。(結論から言うと筆者の知識では実現できなかった)

docker vs podmanの検討としては、まずpodmanで実現を試みるほうがハードルは低いと判断する。

インポートするタイミング

次にイメージをインポートするタイミングを考えてみる。

最初に思いつくのは、bitbakeのビルドプロセスの途中で何らかの方法でイメージをコンテナシステムにインポートする処理を実行する方法。具体的にはROOTFS_POSTPROCESS_COMMANDSやpkg_postinstなどのタイミングで実行する。 これはホスト側で実行するため実現するためにはdocker-nativepodman-nativeなどのパッケージが必要となる。 さらにsysrootで分離されているとはいえ、インポートされたデータがどこに格納されるかきちんと把握し、それらが制御可能かどうかを検討する必要がある。

次に思いつくのが、イメージを書き込んだあとでターゲット側でインポート処理を実行する方法。 具体的には、pkg_postinst_ontargetで処理を仕込んでおきターゲットの初回起動時に実行させる方法。 もしくはsystemdやsysvinitなどの初期化プロセスに仕掛けを組み込む方法。 これらはビルドプロセスでインポートする際の課題は発生しない。

ビルド時 vs 初回起動時の検討としては初回起動時に実現を試みるほうがハードルは低いと考える。

レシピ化

どのような方針を取るにせよ、まずはレシピを作成する必要がある。

作業用レイヤ

下記のコマンドで作業用のレイヤを作成する。

$ bitbake-layers create-layer -a ../poky/meta-container-helper
$ mkdir -p ../poky/meta-container-helper/recipes-demo/containers
$ rm -rf ../poky/meta-container-helper/recipes-example

レシピ

ここでは最初のステップとして、前回のイメージの青果物をターゲットにインストールするだけのレシピを作成する。

実はこれもいろいろとテクニックが必要となる。

 1 LICENSE = "MIT"
 2 LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
 3 
 4 DEPENDS = "app-container"
 5 
 6 do_fetch[noexec] = "1"
 7 do_unpack[noexec] = "1"
 8 do_patch[noexec] = "1"
 9 do_configure[noexec] = "1"
10 do_compile[noexec] = "1"
11 
12 CONTAINER_IMAGE ?= "app-container-${MACHINE}.rootfs.tar.bz2"
13 
14 do_install() {
15     install -d ${D}/opt/data
16     install -m 644 ${DEPLOY_DIR_IMAGE}/${CONTAINER_IMAGE} ${D}/opt/data/${CONTAINER_IMAGE}
17 }
18 
19 FILES:${PN} = "/opt/data/*"

1〜2行目のライセンスは参照するコンテナのものと同じライセンスを設定している。 4行目でビルド時依存関係を設定することで、コンテナイメージのビルドが先行して実行されるようになる。

このレシピの処理対象はapp-containerレシピによって生成されたもととなるため、 do_fetch〜do_compileまでのタスクの実行は不要となる。 そのため、6〜10行目で無効化している。

12行目以降は一般的なインストール処理となる。

レシピ名はcontainer-app.bbとする。(いい名前が思いつかなかった。)

meta-container-helper/recipes-demo/containers/container-app.bbとして保存する。

組み込み確認

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

MACHINE ?= "qemuarm64"
INIT_MANAGER ?= "systemd"
DL_DIR ?= "${TOPDIR}/../downloads"

QB_OPT_APPEND:append = " -echr 0x14"


EXTRA_IMAGE_FEATURES += "ssh-server-openssh"
IMAGE_INSTALL:append = " packagegroup-docker"
DISTRO_FEATURES:append = " virtualization"

IMAGE_ROOTFS_EXTRA_SPACE = "5242880"

IMAGE_INSTALL:append = " container-app"

下記でビルドし、QEMUを実行する。

$ bitbake core-image-base
$ runqemu nographic

QEMU側で下記を確認する。

# ls /opt/data/
app-container-qemuarm64.rootfs.tar.bz2
# docker images
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

下記のことがわかる。

  • イメージファイルがターゲットに配置されている
  • コンテナシステムにインポートはされていない

まとめ

bitbakeで作成したコンテナイメージをターゲットに組み込むことを検討した。 今回は下準備として、イメージファイルをそのままターゲットにインストールするレシピを作成した。

次回以降でターゲットのコンテナシステムにインポートする方法を検討する。