背景
VMSSで動作させるイメージを作る際、イメージ作成時はイメージからVMを作成しそのVMで作業してイメージ化、イメージ化したあとはVMSSで実稼働させる形になると思います。
しかし、新しいイメージ化するにあたりVM上で作業しているときにサービスが立ち上がって動き始めると困るときがあります。そうなると、VMとVMSSを区別する手段が必要そうです。
※そもそも、こうやって作業を継ぎ足していくと、秘伝のタレと化したVMイメージが出来上がり、OSアップデートや後任が引き継いだ時にメンテナンスが困難になるので、再現性のある手順にするべきでしょう。packerやAzureのImage Builderを使い設定から生成できるようにすることで、自動起動設定だけしたイメージを都度作るといった手はあります。。。ですが、私の力不足でそこまでできませんでした
方法
VMとVMSSを区別するといった特殊なことはInstance MetaData Service(IMDS)を使うとできます。おそらくWindowsも同様にできることでしょう。
メタデータ内にVMScaleSetの名前の情報があるので、この有無で判別すればよいでしょう。
Bashコード例
Bashではこんな感じに試すことができます。AzureのVM上で実行しないと意味がありません。
vmScaleSetName=$(curl -s -H "Metadata:true" --noproxy "*" "http://169.254.169.254/metadata/instance/compute/vmScaleSetName?api-version=2021-05-01&format=text")
if [ "$vmScaleSetName" != "" ]; then
echo "VMSS"
else
echo "VM"
fi
結果はVMの場合は空文字列、VMScaleSetの場合はVMScaleSetの名前が設定されます。HTTPステータスコード自体は200になるので注意です。
また、vmScaleSetName
は比較的新しいAPI-VERSIONでないと取得できません。
また、format=text
の場合、/metadata/instance
の後ろにcompute/vmScaleSetName
と辿るようにすると、その値だけが抜き出せるAPIになっています。便利。
Systemdを使ったサンプル
例えば、VMSSではない場合はtd-agentを起動できないようにしてみましょう。
(td-agentには恨みはなくイメージ化で作るVMでは動いてほしくないという感じです)
td-agentの起動の管理はSystemdの仕組みを使っているので、Systemdでどうにかしてみます。
まずはVMSSであれば成功するサービスを書きます。サービス名はisvmss.service
としましたが、もう少しいい名前をつけてあげてください。
また、スクリプトファイルを用意するのを横着して直接bash -c
で実行していますがスクリプトに分けても構いません。
[Unit]
Description=Check if running on VMSS.
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/bin/bash -c '[ "$(curl -s -H "Metadata:true" --noproxy "*" "http://169.254.169.254/metadata/instance/compute/vmScaleSetName?api-version=2021-05-01&format=text")" != "" ]'
Type=oneshot
ネットワークが機能していないとcurlが使えません。network-online.target
を依存関係に加えることでネットワークがオンラインになるまで待つようにします。
次にVMSSでないときは起動してほしくないサービスのUnitファイルをいじります。
今回はtd-agentのUnitファイルを書き換えます。以下、書き換え例です。
[Unit]
After=network-online.target isvmss.service
Requires=isvmss.service
Afterにisvmss.service
を加えて、Requiresにisvmss.service
を設定します。
Afterにisvmss.service
を与えることで、td-agent.service
を起動する前にisvmss.service
を起動するように指示します。
Requiresにisvmss.service
が起動できていることを前提にします。Requiresが書けると起動の成否を問わなくなるためAfterとRequiresの2つで設定しましょう。
その後、systemctlを操作して設定を反映します。
# Unitファイルの反映
sudo systemctl daemon-reload
ここまでで設定はOKです。
試してみましょう。
# おもむろに起動してみる
sudo systemctl start td-agent.service
# => A dependency job for td-agent.service failed. See 'journalctl -xe' for details.
# 失敗しているので、journalctl -xeで様子をみる
journalctl -xe
出てくるログを見てみるとisvmss.serviceが失敗したので、td-agentの起動を試さずにfailed扱いになっていることがわかります。
Sep 16 15:17:30 fukasawahvmxxx systemd[1]: Check if running on VMSS....
-- Subject: Unit isvmss.service has begun start-up
-- Defined-By: systemd
-- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
--
-- Unit isvmss.service has begun starting up.
Sep 16 15:17:30 fukasawahvmxxx systemd[1]: isvmss.service: main process exited, code=exited, status=1/FAILURE
Sep 16 15:17:30 fukasawahvmxxx systemd[1]: Failed to start Am I working on VMSS?.
-- Subject: Unit isvmss.service has failed
-- Defined-By: systemd
-- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
--
-- Unit isvmss.service has failed.
--
-- The result is failed.
Sep 16 15:17:30 fukasawahvmxxx systemd[1]: Dependency failed for td-agent: Fluentd based data collector for Treasure Data.
-- Subject: Unit td-agent.service has failed
-- Defined-By: systemd
-- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
--
-- Unit td-agent.service has failed.
--
-- The result is dependency.
Sep 16 15:17:30 fukasawahvmxxx systemd[1]: Job td-agent.service/start failed with result 'dependency'.
Sep 16 15:17:30 fukasawahvmxxx systemd[1]: Unit isvmss.service entered failed state.
Sep 16 15:17:30 fukasawahvmxxx systemd[1]: isvmss.service failed.
ただ、アップデートしたときに都度Unitファイルを書き直さないといけなくなり筋が悪いやりかたかもしれません。どうにかできないものか。
なお、IMDSエンドポイントはスロットリング(リクエスト制限)を行うため、他の何かがIMDSエンドポイントを使っている場合に制限にかかるかもしれません。なので、リトライすべきかもしれません。気になる場合は、次を参考にしてみましょう。