Azureでcloud-initを利用できるLinuxのマシンイメージを作るノウハウ

私はこれがわかるまで数日かかった。沢山の落とし穴と地雷原があります。砲兵の移動弾幕射撃に合わせて突撃、戦線突破しろ。

cloud-initとwaagent

AWSその他をご利用の皆さんであればお馴染みのCluod-init。多くのクラウドサービスで利用できるので使ってる人は多いんじゃないでしょうか。様々なプロビジョニングツールがありますが、それらを使っている人でもcloud-initからキックさせてるという人もいるかと思われます。

Azureでもcloud-initは利用できます。現時点(2020年2月)でcloud-initが利用できるイメージは、下記のページに書いてあるとおりUbuntu Server, RedHat, CentOS, Oracleだそうです。SuSEとDebianは2〜3月に対応と書かれてますね。4月時点で試した所、サポートされていないと出てきたので対応が遅れてると思われます。

Azure での仮想マシンに対する cloud-init のサポート

さて、Azureではcloud-initの他にwaagentというのがいます。これは「Microsoft Azure Linux エージェント」の略だそうですが、「w」はどこから来たんだ…?まあいいや。

Azure Linux エージェントの理解と使用

waagentはcloud-initと似たようなもので、両者はcloud-init対応イメージであってもどちらでも動作しています。waagentがなぜ入ってるかは知りません。プロセスが常駐して何かを行っています。たぶん、CPU使用率などのメトリックをサンプルするのに使っているのでしょう。

waagentの設定は /etc/waagent.conf です。上記のページに書いてあるとおり、ここの設定はcloud-init対応Linuxイメージだと

Provisioning.Enabled が既定で "n" に設定されています。
次の構成パラメーターは、cloud-init を使用してリソース ディスクとスワップ領域を管理する Ubuntu Cloud Image に影響を与えません。
ResourceDisk.Format
ResourceDisk.Filesystem
ResourceDisk.MountPoint
ResourceDisk.EnableSwap
ResourceDisk.SwapSizeMB

となっています。要するに、このあたりはcloud-initに処理を移譲していてwaagentは何もやってないということですね。

cloud-init対応イメージの作成方法

さて、オートスケール(AzureではVirtual Machine Scale Set略してVMSS)を本気で使い始めるとカスタムイメージを作る必要性が出てきますね。ではcloud-initに対応したカスタムイメージはどうやって作ったら良いのでしょうか。

対応するドキュメントはこちらです。

cloud-init で使用するための既存の Linux Azure VM イメージの準備

要点をかいつまんで説明すると、

  • gdisk cloud-utils-growpart cloud-init のインストール
    /etc/cloud/cloud.cfg の設定
  • /etc/waagent.conf を修正してwaagentの初期化機能を無効化
  • /etc/cloud/cloud.cfg.d/91-azure_datasource.cfgdatasource_list: [ Azure ] と書く
  • swapoffする
  • sudo cloud-init clean sudo waagent -deprovision+user -force を実行

だそうです。

私がUbuntu Serverイメージで試した限りでは最後の手順以外は既に実施済みで、かつ他のcloud-init対応ディストリも同じだと思うので、実質やることは最後の

  • sudo cloud-init clean sudo cloud-init clean --logs sudo waagent -deprovision+user -force を実行

だけです。が!!!!!

私が試した限り、この手順は不要です。ちなみにサポートに聞いてますがなかなか回答が来ないのでイライラしてます。

まず、 sudo cloud-init clean sudo cloud-init clean --logs はお好みに応じて実行してもしなくてもOKです。

cloud-init CLI Interface

clean
Remove cloud-init artifacts from /var/lib/cloud to simulate a clean instance. On reboot, cloud-init will re-run all stages as it did on first boot.
--logs: optionally remove all cloud-init log files in /var/log/
--reboot: reboot the system after removing artifacts

だそうです。 /var/lib/cloud にはcloud-initで使用した各種設定やスクリプト、実行したか否かを表すセマフォの役割のファイルなどが入っています。それは消さないとイメージを作ってそこからインスタンスを起動した時にcloud-initが走らないのでは?古いユーザーデータが実行されるのでは?と思われるかも知れませんが、このセマフォその他はインスタンスIDごとに別々のディレクトリで管理されてるので大丈夫です。

とはいっても、ログが貯まっていくのはうざいので sudo cloud-init clean --logs くらいはやっておいてもいいんじゃないでしょうか。

sudo waagent -deprovision+user -force は私の予想ですが(※サポート回答が来ないので分からない)、これはwaagentを使ってプロビジョニングされた場合のみに必要だと思われます。

cloud-init clean のみを実行した場合
- sudo waagent -deprovision+user -force のみを実行した場合
- どちらも実行した場合
− 何もしなかった場合

の4パターンをテストしましたが、いずれもcloud-initが動作するイメージが作成できました。

また、 -deprovision+user オプションはユーザーのホームディレクトリも全部消えるので注意して下さい。ホームディレクトリを消さない場合は -deprovision です。詳しくはヘルプを見て下さい。

仮想マシンのボリュームからイメージを作る

azコマンドをセットアップして下記コマンドを打ちます。

$ az vm deallocate --resource-group myResourceGroup --name sourceVmName
$ az vm generalize --resource-group myResourceGroup --name sourceVmName

ここで注意が必要なのは、generalizeを実行するとそのVMはもう起動できなくなります。これはVMにアタッチされていたボリュームをそのままイメージに変換するためだと思われます。イメージに変換されればそこからまたVMを作れるのですごく困るわけではないですが一応覚えておきましょう。

たとえば、ベースイメージを作る用のVMを一つ用意しておいて、更新する時はそれを起動してSSHで入って作業してイメージを再度作ってVMシャットダウン…みたいな秘伝のタレを作る的な使い方はやるな、Infrastructure as a Codeでいけ、というMicrosoft様のメッセージなのでしょう。知らんけど。

また、azコマンドでやっていることはポータル画面上からも同様のことができます。ポータル画面で対象のVMを開き、「キャプチャ」ボタンを押します。なぜdeallocate/generalizeが「キャプチャ」になるのか私には意味がわからないのですが、英語ネイティブの人たちには自然なんでしょう。知らんけど。MS製品は昔から私はお世話になっていますが、こういう細かいところを気にしてたら仕事が進みません。山中で一人瞑想して自然と一体になり、ニホンカモシカと友達になったかのような落ち着いた心でキーボードをタイプしていきましょう。

本当関係ない話で恐縮ですが、私は地元(秋田)周辺の峠道を車で走っていた時、山中に巨大なカモシカがいきなり出てきて腰を抜かしそうになりました。カモシカ自体は結構見るのですが、あんなにでかいカモシカはみたことなかったのですごくびっくりしました。もののけ姫のシシ神が出てきたかとおもったわ。

話をAzureに戻します。「キャプチャ」ボタンを押すと

という警告が出てきますが、これは前述したとおりおそらく不要ですし、何も考えず実行するとユーザーディレクトリが全部消えるので注意しましょう。

押したら画面上に進捗が表示されるのですが、ここでも注意が必要です。これが実行されている間、ブラウザは閉じないで下さい。どうも、インスタンスの停止、deallocate, generalizeを順々にRESTAPIを叩いているのだと思われますが、途中で閉じると最後まで処理が進みません。おそらく、前述の操作でアトミックにはなってるので途中から手動でコマンドを叩けば復帰可能だとは思いますが、変な中間状態で止まってしまう可能性もゼロではないのでやめときましょう。

cloud-init対応カスタムイメージを使う

azコマンドで

az vm create -n vmName -g resourceGroup --image imageName --custom-data cloud-init.txt

と打てばイメージからVMが作られ、cloud-initのユーザーデータが実行されます。なんでユーザーデータがcustom-dataになってるんだというのは気にしてはいけません。そんなのは細かいことです。

ちなみに指定するファイルの中身はbase64エンコードされていない通常のplain-textでOKです。そうすればcloud-initが実行されます。

ポータル画面上からは私が試した限りだと、cloud-initは実行されてもユーザーデータが指定できません。ユーザーデータを指定する欄には「選択したイメージは、クラウドの初期化をサポートしていません。」と表示されてユーザーデータを指定するボックスが表示されません。サポートに聞いた所、これは仕様らしいです。

なお、「クラウドの初期化」とはcloud-initのことです。MSのドキュメントにはcloud-initと書いているんですが、部分的に日本語訳されて「クラウドの初期化」になってます。逆に分かりにくいとかいうのは気にしてはいけません。そんなのは細かいことです。

VMSSで使う

Virtual Machine Scale Set(VMSS)から使う時はどうすれば良いのでしょうか?前述したとおり、ポータル画面上ではcloud-initが使用できないイメージと認識されます。これはVMSSの作成画面でも同様です。なので、azコマンドで作成するか、terraformで作成することになります。

terraformで作成するには以下を参照してください。

azurerm_virtual_machine_scale_set - HashiCorp

custom_data - (Optional) Specifies custom data to supply to the machine. On linux-based systems, this can be used as a cloud-init script. On other systems, this will be copied as a file on disk. Internally, Terraform will base64 encode this value before sending it to the API. The maximum length of the binary array is 65535 bytes.

CLIで作成する時は、とりあえず何らかの方法で作成したあとでazコマンドを使ってupdateだけするのが楽だと思われます。azコマンドでユーザーデータのみを設定するには、

$ az vmss update --name vmssName --resource-group resourceGroupName --set virtualMachineProfile.osProfile.customData=<Base64 Encoded String>

を実行します。

ただし、ここにも落とし穴があり、ここで設定しても az vmss show をすると常にnullが表示されます。

virtualMachineProfile.osProfile.customData always returns null

上記issueを見ると、「それはわざと表示させてない。表示させるには -d オプションを使え」と書かれています。ただしissueの後半で指摘されている通り、 -d オプションは既に廃止されており使えません。ポータル画面上にも表示されません。なので現状は正しくユーザーデータが設定されたかどうかを確認する術がありません。…と思います。これもサポート問い合わせ中ですが以下略。わかったら記事に追記します。→(追記)問い合わせを試みましたが途中で心が折れました。もういいです。「設定したユーザーデータは確認できない」、これが仕様です、たぶん。

まとめ

  • cloud-init対応イメージを作成するには、既に対応されている公式のディストリイメージを流用するのが簡単
  • waagent -deprovision は不要。間違ってユーザーを消さないように注意(ユーザーを流用する場合)
  • 作ったイメージはポータル画面上はcloud-init非対応だと表示されますが、実際は対応されています
  • 心を落ち着かせて作業しましょう。細かいことは気にせずに。

余談

色々試しているとazコマンドでのVMを作成する時、Pythonのスタックトレースが出てきて失敗しました。メッセージを読むと

CloudError("The template deployment 'vm_deploy_......' is not valid according to the validation procedure. The tracking id is '.......'. See inner errors for details.",) 

のような文言が並んでいます。何かがおかしいんでしょうけど、何がおかしいかは分かりません。このtracking idなるものの内容を表示するにはPowerShellを使う必要があるらしいです。マジかよ…。でもマジかよ…と思っていてはMS製品は使えません。サバンナで大地と一体になったイメージでビル・ゲイツ及び母なる大地への感謝しつつキーボードをタイプしましょう。

PowerShellを使うのにWindowsは要りません。ポータル画面の上の方にあるコンソールのアイコンをクリックすればPowerShellがすぐ使えます。調べてみたらリージョン内のコア数の制限に達したとのことでした。10個がデフォルトらしいです。結構すぐに10個には達してしまいますね…。というか、このくらいのエラーは流石にazコマンドの結果で標準(エラー)出力で返してほしいし、上のエラーメッセージからクォータに達したという雰囲気は全く読み取れないんだけどな…とか思ってはいけません。禅です。禅の心がAzureを使いこなすのには大事です。心を落ち着かせましょう。