mmdebstrapはdebootstrapライクなインターフェースを持つ、ルートファイルシステムを作るツールです。今回はこのツールを用いて、より小さなUbuntuルートファイルシステムを作ってみましょう。
debootstrapとmmdebstrap
Linux環境を構築する上で「ルートファイルシステム」は非常に重要なコンポーネントです。ルートファイルシステムの品質によって、ユーザーの使い勝手が大きく変わります。Ubuntuをはじめとする「Linuxディストリビューション」は「より良いルートファイルシステムを構築する」ためのプロジェクトとも言えるのです。
世界に遍く存在する多種多様なソフトウェアをすべてひとつのルートファイルシステムに含めることは事実上不可能であり、ムダも多いため、Linuxディストリビューションは「パッケージ管理システム」を構築する方向に進化しました。つまりユーザーが必要なソフトウェアを、必要に応じて、より簡単にインストール・活用できるようにしたのです。
よって大半のLinuxディストリビューションは、パッケージ管理システムを動かすために必要なソフトウェアに加えて、そのLinuxディストリビューションがターゲットにしている大半のユーザーがおそらく使うであろうソフトウェア一式をまとめて提供しています。このため、現在ではそのルートファイルシステムは数百MBから数GBぐらいのサイズになります。
しかしながらコンテナ仮想化の普及によって、ルートファイルシステムに対して別の要求が生まれます。曰く「Linuxシステムと必要なソフトウェアをひとつのイメージにまとめて配布する」ために「ルートファイルシステムはできるだけ小さいほうが良い」という要求です。
コンテナ時代の初期から、コンテナイメージ作成ツールとして使われたのがDebianベースのディストリビューションで使われているのdebootstrapでした。
debootstrapは何もない状態から、Debianのベースシステムを構築するためのツールです。ターゲットのシステムも存在しないため、ホストのツールを使ってパッケージを取得・展開してシステムを構築します。ユーザーはdebootstrapで構築されたベースシステムにchrootし、その中でようやくaptなどのコマンドを使えるようになるのです。
もともとdebootstrapは名前の候補に「debchroot」があったことからもわかるように、Debianベースのchroot環境を構築するツールとして作られたものですが、その後すぐにDebianパッケージ構築の基盤システムとしても広く使われるようになります。今日のDebianパッケージやそれを元にしたDebianシステムの品質は、debootstrapによって「クリーンなベースシステムをかんたんに構築できる」ことも大きな要因のひとつでしょう[1]。
debootstrapは「Debianのベースシステム」を構築するツールではありますが、「動作には必要ではない部分をこそぎ落とした最小のシステム」を構築するツールではありません。また、あくまで起点となるベースシステムを構築するツールである以上、そのシステムを作成した時点ではセキュリティアップデートは適用されません。それらは本来debootstrapではなく、たとえばDebianのインストーラーがchrootしたあとに必要に応じて行うべき作業であるためです。
前置きが長くなりましたが、このようなdebootstrapの目的外の用途でも使えるように構築されたツールが、今回紹介するmmdebstrapです。
mmdebstrapのインストール
mmdebstrapは構築時にaptコマンドを活用することで、複数のミラーに対応し、より複雑な依存関係にも対応できるdebootstrapという位置づけになっています。
結果的にdebootstrapよりも数倍速くなったり、apt/dpkgの設定を活用することで不要なデータのインストールを抑止できます。さらに非特権でも実行できるように、Linuxの名前空間やfakerootなどにも対応しているようです。
まずはmmdebstrapをインストールしましょう。mmdebstrapは比較的新しいパッケージのため、Ubuntu 19.04以降が必要です。DebianならDebian 10以降であればmmdebstrapがパッケージ化されています。最新のリリースが0.5.1で、パッケージとの関係は次のようになっています。
- Ubuntu 19.04、19.10、Debian 10:0.4.1
- Ubuntu focal、Debian testing:0.5.1
今回はUbuntu 19.10とmmdebstrap 0.4.1で説明します。
Ubuntu 19.04以降の環境であれば、mmdebstrapはパッケージをインストールするだけです。
LXD上でmmdebstrapを動かすには
ただ普段使っている環境はUbuntu 18.04 LTSであることも考慮して、LXD上でUbuntu 19.10環境を構築し、そこでmmdebstrapを実行する方法もあわせて紹介しておきましょう。
LXDの初期設定については第521回「入門システムコンテナマネージャーLXD 3.0」を参照してください。今回はホストとディレクトリを共有しやすくするために、一般ユーザーとrootアカウントのIDマッピングをホストと同じにしています。共有するディレクトリはコンテナ・ホストともに「~/rootfs/
」です。
さらにdebootstram/mmdebstrapをコンテナ内部で実行するためには、いくつかのシステムコールを許容する必要があります。そこで今回は単に特権コンテナとして動かし、コンテナ内部で名前空間等の設定も行えるようにしておきます。これらの設定を反映させるために、コンテナを再起動してください。
このような設定だと、コンテナとホストの管理者アカウントが隔離されません。さらに「/etc/sudoers.d/90-cloud-init-users
」により、ubuntuアカウントはパスワード無しでsudo
コマンドを実行できるようになっています。コンテナの扱いには十分に注意してください。
cloud-initなどを使わない場合、パスワードが無効化されたubuntuアカウントしか作られません。さらにOpenSSHサーバーはパスワードログインを無効化しているため、事実上SSHログインができない状態です。ここでは公開鍵をインポートする形で、SSHログインできるようにもしておきましょう。
LaunchpadにSSHの公開鍵を登録しているなら、次のコマンドを実行するだけです。
GitHubに登録した公開鍵を利用したいのであれば、「lp:(LaunchpadのID)
」の部分を「gh:(GitHubのID)」に変更してください。これで「lxc list
」で表示されるIPアドレスに対して、ubuntuアカウントでSSHログインできるはずです。
基本的な使い方
基本的な使い方を見ていきましょう。Ubuntu 19.10(Eoan Ermine)のルートファイルシステムは、次のように作成します。
debootstrapと異なり管理者権限で(sudo
コマンドを付けて)実行する必要はありません。現在のmmdebstrapは、特に指定しなければ環境に応じてunshare
コマンドを用いて隔離空間を作成します。この挙動は「--mode
」オプションを用いて変更可能です。
コマンドオプションにはコンポーネントを指定しています。Debianの場合は原則としてmainだけで良く、contrib/non-freeは必要に応じて付ける形になることが多いのですが、Ubuntuの場合はmain/restricted/universe/multiverseすべて指定することが多いためです。ちなみに0.5.1ではカンマ区切りの指定もサポートしています。
残りのコマンドライン引数はそれぞれ順番に、リリース名、出力先、ミラーURIです。上記の例だとリリース名は「eoan」になります。リリース名はルートファイルシステム内部の/etc/apt/sources.list
を構築する際に利用されます。
2つ目の引数である「eoan-rootfs」は出力先です。出力先は「.tar.gz」など拡張子を付けると圧縮アーカイブ形式で保存でき、サポートしている拡張子が存在しない場合はプレーンなディレクトリとなります。サポートしている拡張子はmanページのCOMPRESSIONの項目を参照してください。「-
」を指定した場合、標準出力に出力します。パイプを経由して別のプロセスで出力結果を処理したい際に便利です。
最後の引数がミラーサーバーのURIです。0.4.1の場合、指定しないと「http://deb.debian.org/debian」が使われます。よってUbuntu用のルートファイルシステムを作る場合は、明示的にミラーサーバーを指定する必要があるのです[2]。ちなみに0.5.1からは、リリース名に応じてミラーサーバーを自動的に切り替える機能が実装されました。ただしリリース名とディストリビューションの対応リストは、mmdebstrapのスクリプト内部に静的に持っているため、より新しいリリース名には対応できない可能性があります。上記のようにミラーを明示しておく方法も覚えておきましょう。
ミラーサーバーは複数指定できます。たとえば通常のリポジトリに加えて、PPAもセットで指定したい場合は、コマンドの末尾にPPAのURIを追加してください[3]。
ミラーサーバーで指定できるURIは次の3種類です。
- 「
-
」:標準入力の内容がそのままsources.listに追加されます
- 「
deb
」もしくは「deb-src
」で始まる文字列:その行がそのままsources.listに追加されます
- 「
://
」が含まれる文字列:「deb URI リリース名 コンポーネント
」の形でsources.listに追加されます
- 上記以外でファイル名の場合:ファイル名の内容がそのままsources.listに追加されます
特に「-
」については、たとえば次のように実行すると、現在のリポジトリ設定をそのまま利用できます。
Debianのルートファイルシステムを作る際の注意点
Ubuntu上でDebianのルートファイルシステムを作ろうとするとリポジトリの鍵がないため、次のようにエラーになります。
これはUbuntuには、Debianアーカイブのリポジトリ鍵がインストールされていないためです。
mmdebstrapは最初の段階では実行ホスト上のapt
コマンドを利用します。その際、apt
コマンドは実行ホスト上に登録されているリポジトリ鍵です。Ubuntuマシンなら、Ubuntuの公式リポジトリの公開鍵とユーザーがPPA登録時などに追加した公開鍵がそれに該当します。これはapt-key
コマンドで参照できます。
しかしながらDebianのルートファイルシステムを作る際に参照するリポジトリはUbuntuではなく、Debianのリポジトリです。よってDebianのリポジトリの公開鍵が必要になるのです。
Debian系のリポジトリには、Debianアーカイブのリポジトリ鍵がパッケージ化されています。それをインストールして、mmdebstrap時に参照するようにしましょう。
これにより/usr/share/kerings
以下に、Debianリポジトリのリリースごとの鍵ファイルがインストールされます。あとはmmdebstrapコマンド実行時に、aptコマンドのオプションとして鍵束ファイルを指定するだけです。
ちなみにmmdebstrapの最新のコードにはdebootstrap
コマンドと同じように、「--keyring
」オプションが追加されています。よって、将来のリリースではもう少し直感的に鍵束ファイル・ディレクトリを指定できるようになるはずです。
ルートファイルシステムのクロスビルド
もちろんホストとは異なるCPUアーキテクチャーのルートファイルシステムの構築も可能です。あらかじめqemu-user-staticとbinfmt-supportをインストールしておきます。これによりホストと異なるアーキテクチャーのバイナリを実行する際に、自動的にqemu-user-staticを経由してくれます。
あとは次のコマンドを実行するだけです。
「--architecture=arm64
」でCPUアーキテクチャーを指定できます。
注意しなければならないのはミラーサーバーの部分です。Ubuntuの公式リポジトリサーバーでサポートしているアーキテクチャーはamd64とi386だけです[4]。それ以外のUbuntuでサポートしているアーキテクチャーについては、「http://ports.ubuntu.com/ubuntu-ports」を指定する必要があります[5]。具体的にはarmhf、arm64、ppc64el、s390xがこれに該当します。
残念ながら、Ubuntu 18.04 LTS上のLXDコンテナ上で実行した場合、上記コマンドは次のように失敗します。
これは権限や名前空間の都合上、binfmt関連の情報をうまく読めないためです。4.15カーネルではうまくいきませんでしたが、5.3カーネルだと設定によっては、ホストにもqemu-user-staticとbinfmt-supportをインストールすればうまく動いたので、将来的にはコンテナ上でもクロスビルドができるようになるかもしれません。
ちなみに「--architectures
」オプションにはカンマ区切りで複数のCPUアーキテクチャーを指定可能です。その場合、2つ目以降の指定は、dpkgで言うところの--add-architecture
オプションが指定された状態でルートファイルシステムが作られます。MultiArchな環境を作りたい場合に便利でしょう。
ルートファイルシステムのサイズを減らす方法
実はこの時点で、ルートファイルシステムのサイズとしては、debootstrapで作ったものと大差ありません。
サイズとしてはそこそこ差が出ていますが、「どちらも大きい」という意味ではあまり差がない状態です。より小さいルートファイルシステムが欲しいのであれば、「--variant
」オプションを利用します。
debootstrapの「--variant
」はminbaseを指定するとさらに小さくなります。mmdebstrapの場合、さらに様々なvariantオプションが存在します。
もっとも小さくなるのはcustomを指定して、「--include
」で個別にインストールするパッケージを指定する方法ですが、必要なパッケージをリストアップする設定が大変です[6]。単に「--variant
」単体で最小となるのはessentialでしょう。
ちなみにessentialはaptコマンドすらインストールしないので、今後のカスタマイズを考えるならessentialよりはaptパッケージも追加でインストールするaptのほうが良いかもしれません。
かなり小さくなりましたね。
dpkgにはパッケージのインストール時に「特定のファイルパスを除外する」オプションが存在します。このオプションを駆使すれば「パッケージについてくるものの目的の用途では不要なもの」を除外できます。よく除外対象になるのが「manpageのファイル」や「翻訳データ」です。
「--dpkgopt
」でdpkg固有のオプションを追加できます。追加できるのはdpkg
コマンドのオプションから先頭のハイフンを取り除いたものです。
上記の例だと「/usr/share/man/」以下と翻訳データであるmoファイルをすべて削除しています。また、ドキュメント類が格納されている「/usr/share/doc/」以下は削除していますが、ルートファイルシステムの再配布も想定してコピーライトファイルだけは残しています。
さらに小さくなりました。「eoan-apt-min-rootfs」は「eoan-essential-min-rootfs」で「--variant=essential
」の代わりに「--variant=apt
」を指定したものです。
これ以上はもう個別にファイルを削除したり、パッケージを省いたりすることになります。ただし、そこまでくるともう、aptシステムを使えるかどうかも怪しくなってきますし、あえてDebianベースで構築する必要はない気もします。
Dockerイメージとして活用する
作ったルートファイルシステムをDockerイメージにしてみましょう。
公式のUbuntu 19.10のイメージと比べると「ほんの少しだけ」小さくなったようです。
Ubuntu BaseやUbuntu Minimalとの比較
Ubuntuには組み込み・コンテナ向けの「小さなイメージファイル」として、Ubuntu BaseやUbuntu Minimalが存在します。これらとサイズを比較してみましょう。Ubuntu 19.10のイメージは、それぞれ次のURLからダウンロードできます。
Ubuntu Baseのほうはeoan-apt-min-rootfsと大差ありませんね。当たり前と言えば当たり前なのですが、Ubuntu Baseも次のような内容の設定ファイルを「/etc/dpkg/dpkg.cfg.d/excludes
」に保存しています。
まさにmmdebstrapで指定した内容です。その結果、同じようなサイズになっているのです。イメージ作成時にカスタムする必要がないのであれば、Ubuntu Baseも選択肢のひとつとして考えておきましょう。
なおUbuntu Baseには「/usr/local/sbin/unminimize
」というコマンドが存在します。これを使うと、上記設定を無効化した上で、除外されていたファイルを再インストールしてくれるのです。
ちなみにUbuntu Minimalは、Ubuntu Baseに加えてubuntu-minimalパッケージをインストールしたイメージです。よってどうしてもサイズは大きくなってしまいます。