Azure Metadata Service: Windows VM のスケジュールされたイベント

適用対象: ✔️ Windows VM ✔️ フレキシブル スケール セット ✔️ 均一スケール セット

スケジュールされたイベントとは、仮想マシン (VM) のメンテナンスに備えるための時間をアプリケーションに与える Azure Metadata Service です。 今後のメンテナンス イベント (再起動など) に関する情報を提供することで、アプリケーションがイベントの準備を行い、中断を制限できるようにします。 このサービスは、Windows および Linux の、PaaS と IaaS を含むすべての Azure Virtual Machine の種類で利用できます。

Linux のスケジュールされたイベントの詳細については、Linux VM のスケジュールされたイベントに関する記事をご覧ください。

注意

スケジュールされたイベントは、すべての Azure リージョンで一般公開されています。 最新リリースについては、「利用可能なバージョンとリージョン」をご覧ください。

スケジュールされたイベントを使用する理由

多くのアプリケーションに、VM のメンテナンスに備える時間が得られるメリットがあります。 この時間を使用して、可用性、信頼性、およびサービスを向上させる、次のようなアプリケーション固有のタスクを実行できます。

  • チェックポイントと復元
  • 接続のドレイン
  • プライマリ レプリカのフェールオーバー
  • ロード バランサー プールからの削除
  • イベント ログ
  • グレースフル シャットダウン

スケジュールされたイベントを使用すると、アプリケーションはメンテナンスが行われる時期を検出し、その影響を制限するタスクをトリガーできます。

スケジュールされたイベントは、次のユース ケースでイベントを提供します。

基本操作

Metadata Service では、VM 内部からアクセスできる REST エンドポイントを使用した VM の実行に関する情報が公開されます。 情報は、VM の外部に公開されないように、ルーティング不可能な IP 経由で提供されます。

Scope

スケジュールされたイベントの配信先は次のとおりです。

  • スタンドアロンの仮想マシン。
  • Azure クラウド サービス (クラシック) 内のすべての VM。
  • 可用性セット内のすべての VM。
  • 可用性ゾーン内のすべての VM。
  • スケール セットの配置グループすべての VM。

注意

ファブリック コントローラー (FC) テナント内のすべての仮想マシン (VM) の Scheduled Events は、FC テナント内のすべての VM に配信されます。 FC テナントは、可用性ゾーンの使用に関係なく、スタンドアロン VM、クラウド サービス全体、可用性セット全体、および VM Scale Sets (VMSS) の配置グループに相当します。

そのため、イベント内の Resources フィールドをチェックして、影響を受ける VM を特定する必要があります。

エンドポイントの検出

VNET が有効な VM の場合は、静的でルーティング不可能な IP アドレス 169.254.169.254 から Metadata Service を利用できます。 スケジュールされたイベントの最新バージョンのフル エンドポイントは次のとおりです。

http://169.254.169.254/metadata/scheduledevents?api-version=2020-07-01

VM が仮想ネットワーク内で作成されていない場合 (クラウド サービスと従来の VM の既定のケース)、使用する IP アドレスを検出する追加のロジックが必要となります。 ホスト エンドポイントの検出方法の詳細については、このサンプルを参照してください。

利用可能なバージョンとリージョン

スケジュールされたイベントのサービスは、バージョンによって管理されています。 バージョンは必須で、現在のバージョンは 2020-07-01 です。

Version リリースの種類 リージョン リリース ノート
2020-07-01 一般公開 All
  • イベントの期間のサポートを追加しました
  • 2019-08-01 一般公開 All
  • EventSource のサポートを追加しました
  • 2019-04-01 一般公開 All
  • イベントの説明のサポートを追加しました
  • 2019-01-01 一般公開 All
  • 仮想マシン スケール セットの EventType "Terminate" のサポートが追加されました
  • 2017-11-01 一般公開 All
  • スポット VM 削除の EventType 「Preempt」のサポートを追加する
  • 2017-08-01 一般公開 All
  • IaaS VM のリソース名から先頭のアンダースコアを削除
  • すべての要求にメタデータ ヘッダー要件を適用
  • 2017-03-01 プレビュー All
  • 最初のリリース
  • 注意

    スケジュールされたイベントの前のプレビュー リリースでは、api-version として {latest} がサポートされていました。 この形式はサポートされなくなり、今後非推奨となる予定です。

    Scheduled Events の有効化と無効化

    スケジュールされたイベントは、ユーザーが初めてイベントを要求したときに、サービスに対して有効になります。 最初の呼び出しでは、最大 2 分の応答遅延が発生すると予想されます。

    スケジュールされたイベントは、サービスが 24 時間要求を行わないと、サービスに対して無効になります。

    ユーザー開始メンテナンス

    ユーザーが Azure Portal、API、CLI または PowerShell を使用して開始した VM のメンテナンスによって、スケジュールされたイベントが発生します。 これによって、アプリケーションでメンテナンス準備ロジックをテストすることができ、アプリケーションでは、ユーザーが開始したメンテナンスの準備をすることができます。

    VM を再起動すると、型 Reboot のイベントがスケジュールされます。 VM を再デプロイすると、型 Redeploy のイベントがスケジュールされています。 通常、ユーザー イベント ソースを含むイベントは、ユーザーが開始するアクションの遅延を回避するために、すぐに承認できます。

    API の使用

    ヘッダー

    メタデータ サービスのクエリを実行するときには、要求が意図せずリダイレクトされないように、ヘッダー Metadata:true を指定する必要があります。 Metadata:true ヘッダーは、スケジュールされたイベントのすべての要求で必要です。 要求にヘッダーを含めないと、メタデータ サービスから Bad Request (無効な要求) という応答があります。

    イベントのクエリ

    次の呼び出しを行うと、スケジュールされたイベントのクエリを実行できます。

    Bash のサンプル

    curl -H Metadata:true http://169.254.169.254/metadata/scheduledevents?api-version=2020-07-01
    

    PowerShell のサンプル

    Invoke-RestMethod -Headers @{"Metadata"="true"} -Method GET -Uri "http://169.254.169.254/metadata/scheduledevents?api-version=2020-07-01" | ConvertTo-Json -Depth 64
    

    Python のサンプル

    import json
    import requests
    
    metadata_url ="http://169.254.169.254/metadata/scheduledevents"
    header = {'Metadata' : 'true'}
    query_params = {'api-version':'2020-07-01'}
    
    def get_scheduled_events():           
        resp = requests.get(metadata_url, headers = header, params = query_params)
        data = resp.json()
        return data
    
    

    応答には、スケジュールされたイベントの配列が含まれています。 空の配列は、現在スケジュールされているイベントがないことを意味します。 スケジュールされたイベントがある場合は、応答にイベントの配列が含まれます。

    {
        "DocumentIncarnation": {IncarnationID},
        "Events": [
            {
                "EventId": {eventID},
                "EventType": "Reboot" | "Redeploy" | "Freeze" | "Preempt" | "Terminate",
                "ResourceType": "VirtualMachine",
                "Resources": [{resourceName}],
                "EventStatus": "Scheduled" | "Started",
                "NotBefore": {timeInUTC},       
                "Description": {eventDescription},
                "EventSource" : "Platform" | "User",
                "DurationInSeconds" : {timeInSeconds},
            }
        ]
    }
    

    イベントのプロパティ

    プロパティ 説明
    ドキュメント インカネーション イベント配列が変更されたときに増加する整数。 同じインカネーションを持つドキュメントには同じイベント情報が含まれており、イベントが変更されると、インカネーションが増加されます。
    EventId このイベントのグローバル一意識別子。

    例:
    • 602d9444-d2cd-49c7-8624-8643e7171297
    EventType このイベントによって発生する影響。

    値:
    • Freeze:仮想マシンは数秒間の一時停止がスケジュールされています。 CPU とネットワーク接続が中断する場合がありますが、メモリや開いているファイルへの影響はありません。
    • Reboot:Virtual Machine は再起動がスケジュールされています (非永続メモリは失われます)。
    • Redeploy:Virtual Machine は別のノードへの移動がスケジュールされています (一時ディスクは失われます)。
    • Preempt:スポット仮想マシンが削除されています(一時ディスクは失われています)。 このイベントはベスト エフォートで利用可能になります
    • Terminate:仮想マシンは削除がスケジュールされています。
    ResourceType このイベントが影響を与えるリソースの種類。

    値:
    • VirtualMachine
    リソース このイベントが影響を与えるリソースの一覧。

    例:
    • ["FrontEnd_IN_0", "BackEnd_IN_0"]
    EventStatus このイベントの状態。

    値:
    • Scheduled:このイベントは、NotBefore プロパティに指定された時間が経過した後で開始するようにスケジュールされています。
    • Started:このイベントは開始されています。
    Completed や類似の状態が提供されることはありません。 イベントが完了すると、イベントは返されなくなります。
    NotBefore このイベントが開始される時間。 このイベントは、この時刻より前に開始されないことが保証されます。 イベントが既に開始されている場合は空白になります

    例:
    • Mon, 19 Sep 2016 18:29:47 GMT
    説明 このイベントの説明。

    例:
    • ホスト サーバーのメンテナンス中です。
    EventSource イベントのイニシエーター。

    例:
    • Platform:このイベントは、プラットフォームによって開始されています。
    • User:このイベントは、ユーザーによって開始されています。
    DurationInSeconds イベントによって発生する中断の予想される期間。

    例:
    • 9: イベントによって発生する中断は 9 秒間続きます。
    • 0: イベントによって VM が中断されたり、その可用性が影響を受けたりません (例: ネットワークへの更新)
    • -1: 影響期間が不明な場合または適用されない場合に使用される既定値。

    イベントのスケジューリング

    各イベントは、スケジュールされているイベントの種類に基づいて、将来の最小値の時間でスケジュールされます。 この時間は、イベントの NotBefore プロパティに反映されます。

    EventType 最小値の通知
    Freeze 約 15 分
    再起動 約 15 分
    Redeploy 10 分
    Preempt 30 秒
    Terminate ユーザーが構成可能:5 から 15 分

    注意

    Azure では、劣化したハードウェアに起因するホストの故障を予測が可能になり、移行をスケジュールすることでサービスの中断を軽減しようとすることがあります。 影響を受ける仮想マシンには、スケジュールされているイベントと NotBefore が届きます。これは通常、2、3 日先になります。 実際の時間は、予測された故障のリスク評価によって異なります。 Azure では、可能であれば、7 日前に通知を行いますが、実際の時間はさまざまであり、ハードウェアが今にも故障する可能性が高い場合、7 日より短くなることがあります。 システムによって開始される移行の前にハードウェアで障害が発生した場合に備えて、サービスのリスクを最小限に抑えるために、できるだけ早く仮想マシンをご自身で再デプロイすることをお勧めします。

    注意

    ホスト ノードでハードウェア障害が発生した場合、Azure では最小通知期間を無視して、影響を受けた仮想マシンの復旧プロセスを直ちに開始します。 これにより、影響を受けた VM が応答できない場合の復旧時間が短縮されます。 復旧プロセス中に、影響を受けるすべての VM に対して、EventType = Reboot および EventStatus = Started が設定されたイベントが作成されます。

    ポーリング頻度

    更新のエンドポイントは、任意の頻度でポーリングできます。 ただし、要求間の間隔が長くなるほど、発生するイベントに対応するのが遅くなる可能性があります。 ほとんどのイベントは、5 - 15 分前に通知されますが、場合によっては、通知がわずか 30 秒前ということもあります。 対策を講じるための時間をできるだけ多く確保するために、1 秒に 1 回サービスをポーリングすることをお勧めします。

    イベントの開始

    今後予定されているイベントを確認し、グレースフル シャットダウンのロジックを完了すると、EventId を使用してメタデータ サービスに POST 呼び出しを行うことにより、未処理のイベントを承認できます。 この呼び出しは、通知の最小時間を短縮できる (可能な場合) ことが Azure に示されます。 イベントは、承認されてもすぐには開始されない場合があります。場合によっては、イベントを続行する前に、ノードでホストされているすべての VM の承認が Azure で必要とされる場合があります。

    次に示すのは、POST 要求本文で求められている JSON のサンプルです。 要求には、StartRequests の一覧を含める必要があります。 各 StartRequest には、迅速に進める必要があるイベントの EventId が含まれます。

    {
    	"StartRequests" : [
    		{
    			"EventId": {EventId}
    		}
    	]
    }
    

    別の VM によって既に承認されている場合でも、有効なイベント ID であれば、サービスは常に 200 の成功コードを返します。 400 エラー コードは、要求ヘッダーまたはペイロードの形式が正しくないことを示します。

    Bash のサンプル

    curl -H Metadata:true -X POST -d '{"StartRequests": [{"EventId": "f020ba2e-3bc0-4c40-a10b-86575a9eabd5"}]}' http://169.254.169.254/metadata/scheduledevents?api-version=2020-07-01
    

    PowerShell のサンプル

    Invoke-RestMethod -Headers @{"Metadata" = "true"} -Method POST -body '{"StartRequests": [{"EventId": "5DD55B64-45AD-49D3-BBC9-F57D4EA97BD7"}]}' -Uri http://169.254.169.254/metadata/scheduledevents?api-version=2020-07-01 | ConvertTo-Json -Depth 64
    

    Python のサンプル

    import json
    import requests
    
    def confirm_scheduled_event(event_id):  
       # This payload confirms a single event with id event_id
       payload = json.dumps({"StartRequests": [{"EventId": event_id }]})
       response = requests.post("http://169.254.169.254/metadata/scheduledevents", 
                                headers =  {'Metadata' : 'true'}, 
                                params = {'api-version':'2020-07-01'}, 
                                data = payload)    
       return response.status_code
    

    注意

    イベントの受信確認により、イベントを受信確認した VM だけでなく、イベント内のすべての Resources でイベントが続行されます。 そのため、受信確認を調整するリーダーを選択できます。これは、Resources フィールド内の最初のマシンと同様に簡単です。

    応答の例

    次に、別のノードにライブ マイグレーションされた 2 つの VM で発生した一連のイベントの例を示します。

    DocumentIncarnation は、Events に新しい情報があるたびに変化していきます。 イベントの承認により、WestNO_0 と WestNO_1 の両方で凍結を続行できます。 -1 の DurationInSeconds は、操作にどれほどの時間がかかるか、プラットフォームで認識されないことを示します。

    {
        "DocumentIncarnation":  1,
        "Events":  [
                   ]
    }
    
    {
        "DocumentIncarnation":  2,
        "Events":  [
                       {
                           "EventId":  "C7061BAC-AFDC-4513-B24B-AA5F13A16123",
                           "EventStatus":  "Scheduled",
                           "EventType":  "Freeze",
                           "ResourceType":  "VirtualMachine",
                           "Resources":  [
                                             "WestNO_0",
                                             "WestNO_1"
                                         ],
                           "NotBefore":  "Mon, 11 Apr 2022 22:26:58 GMT",
                           "Description":  "Virtual machine is being paused because of a memory-preserving Live Migration operation.",
                           "EventSource":  "Platform",
                           "DurationInSeconds":  5
                       }
                   ]
    }
    
    {
        "DocumentIncarnation":  3,
        "Events":  [
                       {
                           "EventId":  "C7061BAC-AFDC-4513-B24B-AA5F13A16123",
                           "EventStatus":  "Started",
                           "EventType":  "Freeze",
                           "ResourceType":  "VirtualMachine",
                           "Resources":  [
                                             "WestNO_0",
                                             "WestNO_1"
                                         ],
                           "NotBefore":  "",
                           "Description":  "Virtual machine is being paused because of a memory-preserving Live Migration operation.",
                           "EventSource":  "Platform",
                           "DurationInSeconds":  5
                       }
                   ]
    }
    
    {
        "DocumentIncarnation":  4,
        "Events":  [
                   ]
    }
    
    

    Python サンプル

    次のサンプルでは、スケジュールされたイベントのメタデータ サービスにクエリを実行し、未処理の各イベントを承認しています。

    #!/usr/bin/python
    import json
    import requests
    from time import sleep
    
    # The URL to access the metadata service
    metadata_url ="http://169.254.169.254/metadata/scheduledevents"
    # This must be sent otherwise the request will be ignored
    header = {'Metadata' : 'true'}
    # Current version of the API
    query_params = {'api-version':'2020-07-01'}
    
    def get_scheduled_events():           
        resp = requests.get(metadata_url, headers = header, params = query_params)
        data = resp.json()
        return data
    
    def confirm_scheduled_event(event_id):  
        # This payload confirms a single event with id event_id
        # You can confirm multiple events in a single request if needed      
        payload = json.dumps({"StartRequests": [{"EventId": event_id }]})
        response = requests.post(metadata_url, 
                                headers= header,
                                params = query_params, 
                                data = payload)    
        return response.status_code
    
    def log(event): 
        # This is an optional placeholder for logging events to your system 
        print(event["Description"])
        return
    
    def advanced_sample(last_document_incarnation): 
        # Poll every second to see if there are new scheduled events to process
        # Since some events may have necessarily short warning periods, it is 
        # recommended to poll frequently
        found_document_incarnation = last_document_incarnation
        while (last_document_incarnation == found_document_incarnation):
            sleep(1)
            payload = get_scheduled_events()    
            found_document_incarnation = payload["DocumentIncarnation"]        
            
        # We recommend processing all events in a document together, 
        # even if you won't be actioning on them right away
        for event in payload["Events"]:
    
            # Events that have already started, logged for tracking
            if (event["EventStatus"] == "Started"):
                log(event)
                
            # Approve all user initiated events. These are typically created by an 
            # administrator and approving them immediately can help to avoid delays 
            # in admin actions
            elif (event["EventSource"] == "User"):
                confirm_scheduled_event(event["EventId"])            
                
            # For this application, freeze events less that 9 seconds are considered
            # no impact. This will immediately approve them
            elif (event["EventType"] == "Freeze" and 
                int(event["DurationInSeconds"]) >= 0  and 
                int(event["DurationInSeconds"]) < 9):
                confirm_scheduled_event(event["EventId"])
                
            # Events that may be impactful (eg. Reboot or redeploy) may need custom 
            # handling for your application
            else: 
                #TODO Custom handling for impactful events
                log(event)
        print("Processed events from document: " + str(found_document_incarnation))
        return found_document_incarnation
    
    def main():
        # This will track the last set of events seen 
        last_document_incarnation = "-1"
    
        input_text = "\
            Press 1 to poll for new events \n\
            Press 2 to exit \n "
        program_exit = False 
    
        while program_exit == False:
            user_input = input(input_text)    
            if (user_input == "1"):                        
                last_document_incarnation = advanced_sample(last_document_incarnation)
            elif (user_input == "2"):
                program_exit = True       
    
    if __name__ == '__main__':
        main()
    

    次のステップ