Ubuntuのサーバー版イメージには最初からcloud-initがインストールされています。このcloud-initはクラウド上のインスタンスを立ち上げる際、もろもろの初期設定を行ってくれる強力なツールです。今回はそのcloud-init先生に、ローカルのマシンにおいてもがんばってもらいましょう。
cloud-initの位置づけ
まずはcloud-initの位置づけから説明します。cloud-initそのものを知っている・使っているのであれば、次の項目まで読み飛ばしてください。
今世紀に入って「Linuxのインストール」は劇的に簡単になりました。インストールするだけでも一苦労、まともに動けばラッキーだった時代も昔の話。今はUSBデバイスから起動して、いくつかの質問に回答するだけでインストール完了です。さらにWindowsマシンがあるならば「3クリックでデスクトップLinux」が立ち上がりますし、極端な話、Webブラウザーとクレジットカードだけ準備すればマウス操作だけでLinuxサーバーを用立てられる時代です。
ただし、インストールが簡単になったとは言え、インストール時の設定が不要になったわけではありません。たしかにハードウェアの標準化が進んだ結果、自動的に設定される項目が増えたのは事実です。それでもユーザー名やパスワード、インストール先のデバイスなど、利用者固有の設定は必要になります。
従来、これらの設定はインストーラーが担っていました。つまり利用者はインストーラーの内容が表示されるディスプレイの前に座って、インストーラーの質問に対してマウスやキーボードで答える必要があったのです。しかしながら「クラウド」の場合、ディスプレイやキーボード・マウスをどこにどうやって繋ぐのか、という問題が発生します。さらには「たくさんのインスタンスをデプロイできる」クラウドにおいて、デプロイのたびにインストーラーの質問に答えなくてはならないのも煩雑です。
「自動化できるものは自動化する」の観点から作られたのがcloud-initです。と言っても、もともとのパッケージ名が「ec2-init」だったことからかもわかるように、Amazon EC2専用のツールでした[1]。初期のec2-initのパッケージは次のような説明されていたのです。
EC2には「インスタンスメタデータとユーザーデータ」というインスタンスを設定・管理する仕組みが存在します。これはインスタンスに外部から任意のデータを渡すと、インスタンス内部からIPv4のリンクローカルアドレス(169.254.169.254)で取得できる仕組みです。一番最初のec2-initは、この仕組みを用いて、Upstartのジョブから次の2つの設定を実現していました。
- メタデータのpublic-keysを「
/root/.ssh/authorized_keys
」に追加し、インスタンスが起動したらrootでsshログインできるようにする[2]。
- ユーザーデータをそのまま「
/tmp
」以下にコピーして実行する。
その後、Ubuntu 10.04 LTSに向けて大幅に改修・機能追加し、名前を「cloud-init」に変更した上で、今に至ります。
当初はAmazon EC2専用のツールでしたが、その後はさまざまなクラウドサービスの方式(データストア)に対応しました。またアカウントだけではなくリポジトリの設定やパッケージのインストールから、RHELのサブスクリプション登録まで、多種多様な機能をサポートしています。もちろん任意のスクリプトの実行も可能です。
サーバーの構成管理と言えばAnsibleが定番です。cloud-initのできることはAnsibleとかぶる部分もあるものの、どちらかと言うと構成管理よりは「初期セットアップ」が目的です。たとえばSSHログインできるようにするための設定のように、「Ansibleを動かす前にやっておきたいことはcloud-initで実行する」などと使い分けると良いでしょう[3]。
VagrantfileやDockerfileも機能的に近い存在です。設定済みのイメージファイルを作りたいならVagrantfile/Dockerfileを使い、作成済みのイメージファイルを起動する際にセットアップしたいならcloud-initを使うような分担が一般的なようです[4]。
cloud-initの仕組み
cloud-initは「メタデータとユーザーデータ」を「何らかの方法」で受け取って、それを元に初期セットアップを行う仕組みです。
「何らかの方法」は使用するクラウドサービスに依存します。たとえばAmazon EC2ならインスタンスの起動時にWebフォームやコマンドから渡せますし、OpenNebulaならマシンテンプレートとして設定したものがCD-ROMイメージとしてインスタンスに渡ってくるようです。大抵はネットワーク経由ですが、設定によってはrootfsの中にあるファイルを使うことも可能です。
cloud-initはデータの取得方法をサービスごとの「データストア」として実装しています。
メタデータについては大抵の場合クラウドサービスが自動的に付加します。cloud-initはメタデータ内部の情報(インスタンスIDやホスト名設定など)に合わせて、再コンフィグが可能かどうかやユーザーデータ適用前の事前設定を実施します。利用者はメタデータを意識することはないはずですが、そういう仕組みが存在することは覚えておくと良いでしょう。
cloud-initの本体はユーザーデータになります。cloud-initは「#cloud-config
」で始まっているユーザーデータをcloud-init用のデータ(cloud-config)として解釈します。cloud-configの書き方はcloud-initのドキュメントを参照してください。
cloud-utilsでデータストア作成
さて、ローカルマシンでcloud-initを使う方法を考えてみましょう。一般的なクラウドサービスでは、そのサービスがメタデータとユーザーデータそのものを渡す方法やその場所を通知する方法(データストア)を実装しています。しかしながらローカルマシンでは、その方法が定義されていません。そこでcloud-initでは、ローカルのマシンやLXDのようなコンテナなどのクラウドサービス外のデータストアとして「NoCloud」を実装しています。
まず最初に「どこにデータストアがあるか」を指定する方法が必要です。NoCloudには次の3つの方法が用意されています。
- SMBIOS(System Management BIOS)上のシリアル番号
- カーネルのコマンドラインパラメーター
- ラベル「
cidata
」が付けられたファイルシステム
複数指定された場合、上のほうが使われます[5]。
SMBIOSはマシン固有のデータをOSなどのソフトウェアに渡す仕組みです。Linuxであれば「/sys/class/dmi
」以下やdmidecodeコマンドで参照できます。このうち「シリアル番号」とは、Type 1のシステム情報に記録されているデータで、一般的なPCなら「製造番号」などが記録されるフィールドです。
仮想マシンなどで使われているQEMUには、起動時にSMBIOSに任意の値を設定できる仕組みが存在します。そこでシリアル番号をデータストアに関する情報を渡しておけば、cloud-initはそれを解釈するのです。
上記の例なら、「http://10.0.0.1:8000/」にデータストアがあるとして、ネットワーク越しに情報を取得します。
カーネルのコマンドラインパラメーターは、上記のシリアル番号の内容をカーネル起動時のパラメーターとして渡す方法です。GRUBなどのブートローダーで指定したい場合に使えるでしょう。
ファイルシステムのラベルとして「cidata
」が付いているパーティションが存在すれば、cloud-initはそれをデータストアとして使用します。QEMU/KVMベースのNoCloudでもっとも一般的に使う方法です。さらにNoCloud以外のデータストアでも使えることが多いので、今回はこの方法を紹介しましょう。
cidataタイプのデータは次の3つの条件が満たされている必要があります。
- ファイルシステムのラベルが「
cidata
」であること
- システムが解釈できるファイルシステムであること(ISO9660かFATが使われることが一般的です)
- トップディレクトリに「meta-data」と「user-data」の2つのファイルが存在すること
上記の条件を満たすのデータは手作業でも作れますが、cloud-utilsにあるcloud-image-utilsパッケージを使うとよりかんたんに作成できます。
cloud-image-utilsパッケージは、クラウドイメージを操作するために便利なツールが含まれているパッケージです。たとえばパーティションのリサイズや最新のAMIのID取得などが行なえます。今回はこのうちcloud-localdsコマンドを使用します。
オプションからもわかるようにホストネームやネットワークインターフェースも指定可能です。これらはcloud-configではなく、meta-data側に保存されます。
あらかじめcloud-configデータを作っておきます。今回は初期状態で存在するアカウント(ubuntu)のパスワードを「ubuntu」に設定し、パスワードでsshログインできるように変更しているだけです。
cloud-localdsでデータストアを作成します。設定は基本的に規定値で問題ありません。
作られたデータストアを確認しましょう。まず、IS09660形式で、なおかつcidateのラベルが振られていることがわかります。
データストアをマウントして中身を確認してみましょう。
meta-dataにはインスタンスIDが、user-dataには作成したcloud-configが保存されていることがわかります。ちなみにインスタンスIDはその値を変えると、次回起動時にuser-dataの内容が再設定されます。インスタンスIDを変更したい場合は、cloud-localdsの第三引数にmeta-dataファイルを指定してください。
これでローカルマシン用のデータストアが作成されました。
QEMUでNoCloudを使用する
では作成したデータストアをQEMUで使ってみましょう。必要なパッケージとイメージファイルをダウンロードします。
最後のコマンドでイメージサイズを2GiBから10GiBに変更しています。この時点ではディスクのサイズが変わっただけで、パーティションのサイズはオリジナルのままになっています。本来はパーティションのリサイズも必要ですが、実はcloud-initが自動的にルートパーティションのサイズを変更してくれますので、このままでかまいません[6]。
さらに今回は第441回でも紹介したように、UEFI/OVMF版のファームウェアをインストールしていますが、もちろん従来のSeaBIOSでも可能です。
UEFI/OVMFを使うなら、変数領域を別途コピーしておくと良いでしょう。詳しいことは第441回を参照してください。
最後にルートファイルシステムに加えて、先程作ったデータストアもドライブとして指定して起動します。
データストアとして必要なのは最後の1行だけです。起動してしばらくすると、次のようなメッセージが表示されることでしょう。
これでcloud-initの設定完了ですので、設定したアカウントとパスワードでログインできるか確認してください。さらに「hostfwd
」オプションも指定しているので、ホストマシンのポート2222からsshログインできるはずです。
virt-managerでNoCloudを使用する
もう一つの例として、virtinstパッケージにあるvirt-installコマンドからデータストアを追加してみましょう。まずはあらかじめ、必要なパッケージをインストールしておきます。
あとは「cloudtest」の名前で仮想マシンを作ってみましょう。
こちらも渡しているオプションのうちデータストア関連は最後の1行だけです。
実際に仮想マシンを起動し、コンソールから設定したパスワードでログインできるか確認しておきましょう。
SMBIOSで指定する方法
最後にSMBIOSで設定する方法も紹介しておきます。この方法の最も便利な点は、任意のURL上にメタデータとユーザーデータを置けることです。
まずあらかじめ、仮想マシンからHTTP/HTTPS/FTPアクセスできる場所に、meta-dataファイルとuser-dataファイルを配置してください。今回は「https://example.com/cloud-init/」とします。つまり、「https://example.com/cloud-init/meta-data」でmeta-dataファイルそのものが、「https://example.com/cloud-init/user-data」でuser-dataファイルそのものが取得できる状態になっているはずです。
あとはuser-data.imgファイルの代わりにsmbiosオプションを指定して仮想マシンを起動します。
「type=1」でType 1の(つまりシステム情報の)設定を行い、「serial=」でシリアル番号の値を変更しています。シリアル番号の値は「ds=nocloud-net;seedfrom=https://example.com/cloud-init/
」です。中にセミコロンが含まれているのでダブルクオーテーションで括ってください。
「ds=nocloud-net」はネットワーク経由でデータストアを取得することを意味します。これはつまりインスタンスのネットワーク設定が完了するまでは、cloud-initは実施されません。「seedfrom=」で取得するデータのURLを指定します。
「ds=nocloud」と指定することも可能です。この場合はインスタンスのローカルファイルからデータを取得できます。「seedfrom=」は「/」から「file://」で始まる絶対パスを指定してください。ルートファイルシステムイメージにcloud-configのデータまで入れておきたい場合に便利でしょう。
うまく設定できていれば、次のようにDMIの値が変わっているはずです。
cloud-initそのものの実行ログは「/var/log/cloud-init.log
」に、cloud-initによる設定ログは「/var/log/cloud-init-output.log
」に記録されます。うまく動かないようなら、そちらも確認しておくと良いでしょう。ただしクラウドイメージは、そのままだとログインできるアカウントが設定されていません。つまりcloud-initが失敗するとログインできません。トライアンドエラーを行うなら、デバッグ用のアカウントをあらかじめ作成済みのイメージを用意してそちらを使用してください。