Ubuntu Weekly Recipe

第649回Ubuntu Coreの独自イメージを作成する

第646回ではIoTデバイス用のプラットフォームとして利用されているUbuntu Coreの基本的な使い方を、第647回ではそのUbuntu Coreを用いてRaspberry Piを小型サーバーとして利用する方法を紹介しました。今回はUbuntu Coreの独自イメージを作成する手順を紹介しましょう。

KVM版のUbuntu Coreの使い方

前回までと異なり、今回はKVM版のUbuntu Coreの使い方から説明します。

後半で説明するイメージの作成方法はKVM版とRaspberry Pi版で違いはありません。異なるのは指定するカーネル等のsnapファイルの名前だけです。では、なぜKVM版を紹介するのかというと、それはトライアンドエラーが楽になるためです。

本記事の手順に従ってカスタムイメージの作成方法を把握したあとは、自分自身のベストな設定を見つけるために、イメージの作成・カスタマイズ・デプロイの流れを繰り返して行うことになります。そのときRaspberry Piが対象だと、次のようなステップが必要になります。

  1. PCからSDカードにカスタムイメージを書き込む
  2. Raspberry PiにSDカードを取り付ける
  3. Raspberry Piを起動する
  4. ディスプレイとキーボードを操作し、初期セットアップを行う
  5. SSH経由でログインし、イメージの内容が正しいことを確認する
  6. SDカードを取り外し、PCに接続する
  7. 最初に戻る

要するにイメージを作ったあとのステップがとても煩雑なのです。特に最近のRasypberry PiはSDカードスロットのスプリングロックがなくなったので、ケースの種類によっては引き出すのも一苦労です。奇しくもここ一週間ほど「Raspberry Piの最大の弱点はSDカード部分」って話が話題になったぐらいですしね。

Raspberry Piで一番弱いのがSDカード(画像はイメージです)
図1

本当に簡単に壊れてしまいます[1]⁠。

いずれにせよトライアンドエラーをするなら、この手順を省力化したいところです。SDカードを取り外さずに更新する方法もなくはないのですが、新しめのRaspberry Piが必要だったり、特殊な基板が必要だったりと、汎用的な方法とは言えません。よっていっそのこと、仮想マシンを使ってPCの中で完結した手順でテストし、ある程度動く目処が立ってから実機に移行したほうが楽だと考えたわけです。

まずはKVM版のUbuntu Coreの起動手順を紹介しましょう。特別なことは特になく、Ubuntu Coreのイメージをダウンロードしてきて起動する、それだけです。KVMをあらかじめインストールしておきます。

$ sudo apt install qemu-kvm
$ kvm-ok
INFO: /dev/kvm exists
KVM acceleration can be used
$ sudo adduser $USER kvm

kvmグループへの参加を反映するために、一度ログインし直しておいてください。ちなみにVirtualBoxが動いているとKVMは使えません。VirtualBox関連のカーネルモジュール(vboxXXX)をアンロードすれば利用可能なはずですが、場合によってはVirtaulBoxパッケージを削除してしまったほうが確実かもしれません。VirtualBoxと併用したいのであれば、VirtualBox上に仮想マシンとしてUbuntuを用意し、第604回を参考にNested VMを有効化して、その中でKVMを使うという手もあります。今回はコンソール・ネットワーク経由でしかUbuntu Coreを使わないので、仮想マシンインスタンスはサーバー版でも問題ありません。

KVM環境が用意できたらUbuntu Coreのイメージをダウンロードし、展開しておきます。

$ wget https://cdimage.ubuntu.com/ubuntu-core/18/stable/current/ubuntu-core-18-amd64.img.xz
$ unxz ubuntu-core-18-amd64.img.xz
$ cp ubuntu-core-18-amd64.img{,.bak}

上記のようにイメージのバックアップを取っておけば、いざというとき状態を差し戻せて便利です。あとはKVM/QEMUコマンドでこのイメージを起動します。

$ kvm -smp 2 -m 1500 -netdev user,id=mynet0,hostfwd=tcp::8022-:22 \
  -device virtio-net-pci,netdev=mynet0 -nographic \
  -drive file=ubuntu-core-18-amd64.img,format=raw

今回はUbuntu CoreにSSHログインすることを想定してゲストの22番ポートをホストの8022番ポートに転送しています。たとえばNextcloudをインストールするなら、443や80なども同じような形で転送すると便利でしょう。hostfwd=tcp::8022-:22,hostfwd=tcp::8090-:80のようにカンマ区切りで追記していけば大丈夫です。

GUIは使わないので-nographicを追加しています。これによりVGAの中身が標準出力に転送されます。Ubuntu Coreの初期設定も、KVMを起動した端末画面から設定可能です。MirなどGUIを利用するsnapパッケージをインストールするなら-nographicの代わりに-vga qxlなどを指定してください[2]⁠。

いずれにせよ上記でUbuntu Coreを起動したら、起動ログが一通り流れたあとにPress enter to configure.と表示されるはずです。あとは第646回と同じように画面の指示に従ってセットアップしてください。

セットアップが完了したら、次の方法でログインできます。

$ ssh -p 8022 <Ubuntu SSOのユーザー名>@localhost

仮想マシンインスタンスはSSHログインしてshutdownコマンドを実行するか、KVMを起動したターミナルから「Ctrl-a x」と入力してください。Ubuntu Coreならpkillコマンドで、仮想マシンインスタンスのプロセスを落としてしまってもいいでしょう。

ちなみにKVMを起動したターミナルはGRUBメニューを表示する際に、ターミナルの設定が通常と変わった状態になってしまいます。元に戻したい場合はresetコマンドを引数なしで実行してください。

カスタムイメージの作成方法

次にUbuntu Coreのカスタムイメージの作成方法を紹介しましょう。カスタムイメージの作成ができるようになると、次のようなメリットが得られます。

  • 任意のsnapパッケージがインストールされたイメージを構築できる
  • 初回セットアップをスキップできる
  • コンソールから直接ログイン可能に変更できる
  • 任意のカーネル・ブートローダーを利用できる

一番目のメリットは、たとえば最初からNextcloudインストール済みのイメージを作るといった使い方です。実際にUbuntu Applianceではこの仕組みを利用して特定のアプリをバンドルしたイメージを配布しています。

二番目のメリットがインストールの自動化に必要な仕組みです。通常配布されているUbuntu Coreはconsole-confによって初回セットアップ(Ubuntu SSOを利用したアカウントの作成)を必要としています。セットアップ済みのデバイスをmanagedと呼び、未セットアップなデバイスを「unmanaged」と呼びます。しかしながらインストールのたびにHDMIディスプレイとキーボードをつないで操作するのはいろいろと面倒です。そこで用意されているのが「設定済みのファイルをUSBスティックに保存し、それを接続して起動すれば自動セットアップしてくれる」モードです。これを実現するための設定ファイルをsystem-user assertionと呼びます。

system-user assertionはunmanagedなデバイスにのみ適用可能です。またその設定ファイル(アサーション)は、Coreイメージの作成者の鍵で署名されている必要があります。普通のUbuntu CoreはCanonicalの鍵で署名されているイメージのため、アサーションを作成できません。そこで自分の鍵で署名したカスタムイメージが必要になるのです。ちなみにsystem-user assertionの作り方については、次回以降で紹介する予定です。

初回セットアップの画面は「console-conf」によって実現しています。これはサーバー版のインストーラーで開発している機能で、⁠起動時にコンソールにUIを提供する」ためのフレームワークです。Ubuntu Coreではunmanagedなデバイスならセットアップ画面を、managedなデバイスならSSH経由のログイン方法を提示することで、⁠コンソールからのログイン」を抑止しています。しかしながらデバッグ目的でUbuntu Coreを使う場合、コンソールからログインできたほうがうれしいことも多々あります。カスタムイメージを作成する際は、最初からconsole-confを無効にしたイメージを作ることができるのです。これが三番目のメリットです。

四番目のメリットは、独自の組み込み機器にUbuntu Coreを対応させたい場合に必須となります。またKVMやRaspberry PiのようにすでにUbuntu公式で対応済みの機器であっても、カーネルコンフィグを変更したい・サードパーティのモジュールを導入したいならやはり必要な機能です。今回はこの方法については言及しません。詳細を知りたい場合は、kernel snapgadget snapの作り方を解説した、Board enablement overviewのページを参照してください。

これらの理由から、Ubuntu Coreを細かくカスタマイズしたい場合は、配布されているイメージを使うよりも、カスタムイメージを作ったほうが便利です。ただしカスタムイメージを作るためには次のような情報が必要です。

  • Ubuntu SSOアカウント(Snap Store用アカウント)
  • 上記アカウントに紐付いたGPG鍵

理論上はなくても作れるのですが、あったほうが周辺ツールが使えて便利なので、ここではUbuntu SSOアカウントが作成済みである前提で話を進めます。カスタムイメージの作成は次の手順で行います。

  1. account-idの取得
  2. GPG鍵の作成・登録
  3. model assertionファイルの作成
  4. カスタムイメージの作成

account-idの取得

Ubuntu SSOでアカウントを作成済みなら、Snap Store上の開発者向けdashboardから「account-id」を取得できます。これはカスタムイメージ作成時のbrand-idや、イメージ等に署名を行う際のauthority-idとして使うものです[3]⁠。

本記事で説明するツール類はこのaccount-idが必要になりますので、まずはUbuntu SSOアカウントを作成し、account-idを取得しておいてください。

GPG鍵の作成・登録

イメージを作成するための設定ファイルや、自動的に作成するアカウント情報などは、snapcraft/Ubuntu Coreの文脈ではassertion(アサーション)と呼ばれています[4]⁠。

アサーションを作るためには、GPG鍵が必要です。またSnap Store側でアサーションを検証可能にするために、作成した公開鍵をSnap Storeに登録しなくてはなりません。まずはsnapcraftコマンドをインストールし、ログインしておきましょう。

$ sudo snap install snapcraft --classic
$ snapcraft login
Enter your Ubuntu One e-mail address and password.
If you do not have an Ubuntu One account, you can create one at https://snapcraft.io/account
Email: <Ubuntu SSOのメールアドレス>
Password: <Ubuntu SSOのパスワード>

Login successful.

次にアサーション用のGPG鍵を作成します。

$ snapcraft create-key
Passphrase: <パスフレーズ>
Confirm passphrase: <パスフレーズ>

パスフレーズはGPG鍵を利用する際のそれです。作成されたGPG鍵は~/.snap/gnupg/に保存されますので、必要に応じてバックアップをとっておいてください。

作成した鍵をSnap Storeに登録します。

$ snapcraft register-key
Enter your Ubuntu One e-mail address and password.
If you do not have an Ubuntu One account, you can create one at https://snapcraft.io/account
Email: <Ubuntu SSOのメールアドレス>
Password: <Ubuntu SSOのパスワード>

We strongly recommend enabling multi-factor authentication: https://help.ubuntu.com/community/SSO/FAQs/2FA
Registering key ...
Done. The key "default" (<鍵ID>) may be used to sign your assertions.

複数の鍵を登録可能なので、その場合はsnapcraft regiter-key 鍵名のように名前を指定すると良いでしょう。上記のように未指定の場合はdefaultが使われます。

登録された鍵は次の手順で確認可能です。

$ snapcraft list-keys
    Name     SHA3-384 fingerprint
*   default  <鍵ID>

model assertionファイルの作成

GPG鍵を作成できたので、次にイメージの作成に必要なmodel assertionを作成します。これは作成されたイメージが、誰によって作られ、どのようなデバイスのためのもので、どのようなsnapファイルがインストールされているかなどを記述した署名済みのファイルです。

署名そのものはsnapコマンドで行えますので、まずは元データとなるJSONファイルをpc.jsonという名前で作成しましょう。

{
  "type": "model",
  "authority-id": "account-idの文字列",
  "brand-id": "account-idの文字列",
  "series": "16",
  "model": "pc",
  "architecture": "amd64",
  "base": "core18",
  "gadget": "pc=18",
  "kernel": "pc-kernel=18",
  "timestamp": "2021-01-08T10:54:39+00:00"
}

変更する必要がある部分のみを説明します。

  • authority-id/brand-id:Snap Storeから取得したaccount-idの文字列をそのまま記述してください。
  • model:モデル名を示す任意の文字列です。ファイル名と一緒にしておくとわかりやすいです。
  • architecture:ターゲットのCPUアーキテクチャー名です。PC向けなら「amd64⁠⁠、Raspberry Pi向けなら「arm64」⁠armhf」のいずれかになるでしょう。
  • base:ベースとなるsnapファイルです。Ubuntu Core 18ならcore18で、将来的にリリースされるUbuntu Core 20ならcore20になります。
  • gadget:gadget snapとしてインストールするsnapパッケージ名です。
  • kernel:kernel snapとしてインストールするsnapパッケージ名です。
  • timestamp:date -Iseconds --utcで生成したタイムスタンプです。GPG鍵の登録日時よりも後ろである必要があります。

gadget snapとkernel snapについて補足しておきます。これらはターゲットデバイス依存のsnapパッケージであるため、どのデバイス向けに作るかで指定方法が変わります。公式でサポートしているデバイスの場合、次のような関係になります。

  • KVM/NUC向け:gadgetはpcで、kenrelはpc-kernel
  • Raspberry Pi向け:gadgetはpiで、kernelはpi-kernel

さらにsnapパッケージはパッケージ名の後ろに=channnelのような指定が可能です。PC向けならcore18にはpi=18pi-kernel=18が正式リリースとなります。将来的にcore20がリリースされたら、baseの変更に合わせてpc=20などを指定することになります。

Raspberry Pi向けには18-pi418-pi3などターゲットごとにチャンネルが用意されているようなので、上記のリンク先のバージョン番号のところにあるプルダウンメニューを参照してください。

また「required-snaps」フィールドを追加すると、追加でsnapパッケージをインストール可能です。ここで指定するsnapパッケージはSnap Storeにあるものだけでなく、ローカルディレクトリにあるsnapパッケージも対象になります。Storeにないパッケージをバンドルしたい際に使えるでしょう。なお、required-snapsでインストールしたsnapパッケージは、イメージをインストール後に削除はできなくなるため注意してください。後述のイメージ作成時にも追加のsnapを指定できるので、⁠削除されたくないsnapパッケージ」以外にこのフィールドを利用することはないでしょう。

作成したJSONファイルを署名します。

$ cat pc.json | snap sign -k default &> pc.model

$ cat pc.model
type: model
authority-id: <account-id>
series: 16
(以下略)

「default」snapcraft list-keysの鍵名です。環境に応じてGPG鍵のパスフレーズを問い合わせる画面が出るでしょう。なお、状況によって署名がうまくいかず、次のような内容がpc.modelに記録されてしまうこともあるようです。

error: cannot sign assertion: cannot sign using GPG: /usr/bin/gpg
(中略)
署名に失敗しました: そのようなファイルやディレクトリはありません
(後略)

原因は不明ですが、次のようにgpgコマンドからGPG鍵のディレクトリを指定することで、解消するようです。

$ gpg --homedir ~/.snap/gnupg --detach-sig pc.json

これを実行したあとにもう一度snap signを実行すれば成功しているはずです。うまくmodel assertionが生成されなかったら試してみてください。

ちなみにUbuntu WikiのUbuntuCoreの開発ページには、Ubuntuが公式にリリースしているイメージファイルのリストが表示されています。このページにリストアップされているsnap known ...なコマンドを利用するとイメージごとのアサーションファイルを表示できます。設定に困ったらまずは公式イメージのアサーションを参考にすると良いでしょう。

カスタムイメージの作成

ようやくカスタムイメージを作成するための必要なファイルを準備できました。次にイメージの作成手順に移りましょう。

イメージの作成そのものは、UbuntuのLiveインストーラーの作成でも使われている「ubuntu-image」を利用します。まずはこのコマンドをインストールしておきましょう。

$ sudo snap install ubuntu-image --classic

では実際にイメージを作成します。

$ mkdir pc
$ sudo ubuntu-image snap -O pc pc.model

$ ls -1 pc/
pc.img
seed.manifest
snaps.manifest

イメージの作成はsnapパッケージのダウンロードに時間がかかる程度でほぼすぐに終わる印象です。作られたファイルのうち「pc.img」がイメージファイルです。manifestのほうは利用されたsnapパッケージのリビジョン番号などが記載されています。

このコマンド実行時に--disable-console-confを指定すると、console-confが無効化されます。つまり初回起動時や再起動後にコンソールにログインプロンプトが表示されて、パスワードログインが可能になります。

-c チャンネルでsnapパッケージインストール時に使用する標準のチャンネル名を指定できます。betaやedgeのパッケージをインストールしたい際に利用します。--snap パッケージ名で追加でsnapパッケージをインストールします。パッケージ名=チャンネル名のような指定も可能です。ローカルにあるsnapファイルも指定できます。

他にもいろいろなオプションがあるので、一度ubuntu-image snap --helpを参照しておくと良いでしょう。

作成したイメージは、公式のKVMイメージと同じ方法で起動できます。

$ cp pc/pc.img{,.bak}
$ kvm -smp 2 -m 1500 -netdev user,id=mynet0,hostfwd=tcp::8022-:22 \
  -device virtio-net-pci,netdev=mynet0 -nographic \
  -drive file=pc/pc.img,format=raw

今回は設定がほぼ同じなので、同じように初回セットアップが動きます。

これで独自イメージを作成する手順を確立できました。これの手順を把握しておけば、特定のsnapパッケージをインストール済みのカスタムイメージを簡単に構築できるというわけです。

たとえば第641回のLXDとmicrok8sでシングルサーバーをKubernetesクラスターにするで紹介したように、microk8sは簡単にKubernetesのクラスターを構築できるツールです。ubuntu-image snapのオプションとして--snap microk8s=classicを追加すれば、microk8sインストール済みのイメージとなります。このイメージを複数台のRaspberry Piにインストールすることで、microk8sインストール済みのRaspberry Piたちができあがります。もっと細かいセットアップをしたいのであれば、--cloud-initでCloud-Initのファイルを渡すという手もあります。

ぜひ各自で好みのsnapパッケージをバンドルした、カスタムイメージを作ってみてください。

おすすめ記事

記事・ニュース一覧