第646回 ではIoTデバイス用のプラットフォームとして利用されているUbuntu Coreの基本的な使い方を、第647回 ではそのUbuntu Coreを用いてRaspberry Piを小型サーバーとして利用する方法を紹介しました。今回はUbuntu Coreの独自イメージを作成する手順を紹介しましょう。
KVM版のUbuntu Coreの使い方
前回までと異なり、今回はKVM版のUbuntu Core の使い方から説明します。
後半で説明するイメージの作成方法はKVM版とRaspberry Pi版で違いはありません。異なるのは指定するカーネル等のsnapファイルの名前だけです。では、なぜKVM版を紹介するのかというと、それはトライアンドエラーが楽になるためです。
本記事の手順に従ってカスタムイメージの作成方法を把握したあとは、自分自身のベストな設定を見つけるために、イメージの作成・カスタマイズ・デプロイの流れを繰り返して行うことになります。そのときRaspberry Piが対象だと、次のようなステップが必要になります。
PCからSDカードにカスタムイメージを書き込む
Raspberry PiにSDカードを取り付ける
Raspberry Piを起動する
ディスプレイとキーボードを操作し、初期セットアップを行う
SSH経由でログインし、イメージの内容が正しいことを確認する
SDカードを取り外し、PCに接続する
最初に戻る
要するにイメージを作ったあとのステップがとても煩雑なのです。特に最近のRasypberry PiはSDカードスロットのスプリングロックがなくなったので、ケースの種類によっては引き出すのも一苦労です。奇しくもここ一週間ほど「Raspberry Piの最大の弱点はSDカード部分」って話が話題になったぐらいですしね。
Raspberry Piで一番弱いのがSDカード(画像はイメージです)
本当に簡単に壊れてしまいます[1] 。
[1] 念のためお伝えしておくと、「 ここ一週間ほど話題」になっていたのは、SDカードスロットの物理的な耐久性ではなく、SDカードそのものの電気的な耐久性の話です。上記画像のような結果になるのは単に優しさが足りないだけです。
いずれにせよトライアンドエラーをするなら、この手順を省力化したいところです。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 snap やgadget snap の作り方を解説した、Board enablement overview のページを参照してください。
これらの理由から、Ubuntu Coreを細かくカスタマイズしたい場合は、配布されているイメージを使うよりも、カスタムイメージを作ったほうが便利です。ただしカスタムイメージを作るためには次のような情報が必要です。
Ubuntu SSOアカウント(Snap Store用アカウント)
上記アカウントに紐付いたGPG鍵
理論上はなくても作れるのですが、あったほうが周辺ツールが使えて便利なので、ここではUbuntu SSOアカウントが作成済みである前提で話を進めます。カスタムイメージの作成は次の手順で行います。
account-idの取得
GPG鍵の作成・登録
model assertionファイルの作成
カスタムイメージの作成
account-idの取得
Ubuntu SSOでアカウントを作成済みなら、Snap Store上の開発者向けdashboard から「account-id」を取得できます。これはカスタムイメージ作成時のbrand-idや、イメージ等に署名を行う際のauthority-idとして使うものです[3] 。
[3] このあたりの用語や使い方は、たまに意味が変わることに注意が必要です。また、未確認ではありますが、独自のSnap Store(Brand Store)を構築すれば、独自のBrand SSOを利用することになるため、Ubuntu SSOアカウントも不要になるかもしれません。
本記事で説明するツール類はこのaccount-idが必要になりますので、まずはUbuntu SSOアカウントを作成し、account-idを取得しておいてください。
GPG鍵の作成・登録
イメージを作成するための設定ファイルや、自動的に作成するアカウント情報などは、snapcraft/Ubuntu Coreの文脈では「assertion(アサーション) 」と呼ばれています[4] 。
[4] assertion という言葉自体は「宣言」とか「主張」とか「声明」とか「これはこうである」と表明する類の何かを示す名詞です。snapcraftやUbuntu Coreでは、いくつかの設定ファイル・文書についてはGPGにより電子署名されていることを要求しています。このような検証可能なドキュメントを、より強い意味を込めて「アサーション」と呼んでいるようです。
アサーションを作るためには、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パッケージであるため、どのデバイス向けに作るかで指定方法が変わります。公式でサポートしているデバイスの場合、次のような関係になります。
さらにsnapパッケージはパッケージ名の後ろに「=channnel
」のような指定が可能です。PC向けならcore18には「pi=18
」「 pi-kernel=18
」が正式リリースとなります。将来的にcore20がリリースされたら、baseの変更に合わせて「pc=20
」などを指定することになります。
Raspberry Pi向けには「18-pi4
」や「18-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パッケージをバンドルした、カスタムイメージを作ってみてください。