Azure 中繼資料服務:Linux VM 的已排定事件

適用於:✔️ Linux VM ✔️ 彈性擴展集 ✔️ 統一擴展集

已排定的事件是一項「Azure 中繼資料服務」,可讓您的應用程式有時間準備虛擬機器維護。 它提供即將進行維護事件 (例如重新啟動) 的相關資訊,讓您的應用程式可進行準備以及限制中斷。 它適用於所有 Azure 虛擬機器類型 (包括 Windows 和 Linux 上的 PaaS 和 IaaS)。

如需 Windows 上已排定事件的資訊,請參閱 Windows VM 的已排定事件

已排定事件可針對近期活動發出主動式通知。有關已發生事件的回應式資訊,請參閱 Azure Resource Graph 中的 VM 可用性資訊,以及為 Azure 虛擬機器建立可用性警示規則

注意

已排定事件已在所有 Azure 區域中正式推出。 請參閱版本和區域可用性以取得最新的版本資訊。

為什麼要使用已排定事件?

許多應用程式可受益於 VM 維護的準備時間。 這些時間可用來執行應用程式特定的工作,能改善可用性、可靠性與服務性,包括:

  • 檢查點和還原。
  • 清空連線。
  • 主要複本容錯移轉。
  • 從負載平衡器移除集區。
  • 事件記錄。
  • 順利關機。

使用已排定事件,應用程式就可以探索維護所發生的時間,以及限制其影響的觸發工作。

排程的事件會提供下列使用案例中的事件:

  • 平台起始的維護 (例如,主機的 VM 重新啟動、即時移轉或記憶體保留更新)。
  • 虛擬機器正於預期即將失敗的降級主機硬體 (英文) 上執行。
  • 虛擬機器在發生硬體失敗的主機上執行。
  • 使用者起始的維護 (例如,使用者重新啟動或重新部署 VM)。
  • 現成 VM現成擴展集執行個體收回。

基本概念

如果您是使用可由 VM 內存取的 REST 端點來執行 VM,中繼資料服務會公開這類相關資訊。 這項資訊是透過無法路由傳送的 IP 所取得,且不會在 VM 之外公開。

範圍

已排定事件會傳送到下列目標,並由其進行認可:

  • 獨立虛擬機器。
  • Azure 雲端服務 (傳統) 中的所有 VM。
  • 可用性設定組中的所有 VM。
  • 擴展集放置群組中的所有 VM。

注意

在整個可用性設定組,或虛擬機器擴展集的放置群組中,不論可用性區域的使用方式,所有虛擬機器 (VM) 的已排定事件都會傳送至相同群組或集合中的所有其他 VM。

因此,請檢查事件中的 Resources 欄位以找出哪些 VM 受到影響。

端點探索

針對已啟用 VNET 的 VM,可以從靜態非可路由 IP 169.254.169.254 取得「中繼資料服務」。 最新版已排定事件的完整端點為:

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

如果 VM 不是建立在虛擬網路內 (雲端服務和傳統 VM 的預設案例),就需要額外的邏輯,才能探索要使用的 IP 位址。 請參閱此範例以了解如何探索主機端點 \(英文\)。

版本和區域可用性

已排定事件服務已進行版本設定。 版本是必要項目;目前版本為 2020-07-01

版本 版本類型 地區 版本資訊
2020-07-01 正式發行 全部
  • 事件持續時間的新增支援
  • 2019-08-01 正式發行 全部
  • 事件來源的新增支援
  • 2019-04-01 正式發行 全部
  • 事件描述的新增支援
  • 2019-01-01 正式發行 全部
  • 已新增支援虛擬機器擴展集 EventType 'Terminate'
  • 2017-11-01 正式發行 全部
  • 已新增支援現成 VM 收回 EventType 'Preempt'
  • 2017 年 8 月 1 日 正式發行 全部
  • 已從 IaaS VM 的資源名稱中移除預留底線
  • 強制所有要求的中繼資料標頭需求
  • 2017-03-01 預覽​​ 全部
  • 初始版本
  • 注意

    先前排定事件的預覽版支援作為 API 版本的 {latest}。 此格式將不再受到支援且之後會遭到取代。

    啟用和停用已排定事件

    系統會在您第一次提出事件要求時,為您的服務啟用「已排定事件」。 您可能會在第一次呼叫中遇到長達兩分鐘的延遲回應。 如果長達 24 小時未向端點提出要求,服務就會停用已排定事件。

    使用者起始的維護

    使用者透過 Azure 入口網站、API、CLI 或 PowerShell 起始的 VM 維護,將會產生「已排定事件」。 這可讓您測試應用程式中的維護準備邏輯,讓應用程式可以為使用者起始的維護預作準備。

    如果您重新啟動 VM,則會排定類型為 Reboot 的事件。 如果您重新部署 VM,則會排定類型為 Redeploy 的事件。 一般而言,可以立即核准具有使用者事件來源的事件,以避免延遲使用者起始的動作。 建議以主要與次要 VM 進行通訊,並核准使用者產生的已排定事件,避免主要 VM 沒有回應。 藉由立即核准事件,可防止在應用程式復原過程中出現延遲。

    只有適用於支援記憶體保留更新的一般用途 VM 大小,才支援 VMSS 客體 OS 升級或重新安裝映像的已排定事件。 但不適用於 G、M、N 和 H 系列。 根據預設,VMSS 客體 OS 升級或重新安裝映像的已排定事件會處於停用狀態。 若要在支援的 VM 大小上為這些作業啟用已排定事件,請先使用 OSImageNotificationProfile 加以啟用。

    使用 API

    高階概述

    處理已排定事件時,可使用準備與復原兩個主要元件。 所有影響 VM 的目前已排定事件,都能透過 IMDS 已排定事件端點進行讀取。 事件到達終止狀態時,就會從事件清單中移除。 下圖說明了單一已排定事件可能經歷的各種狀態轉換:

    State diagram showing the various transitions a scheduled event can take.

    針對 EventStatus 為「已排程」狀態的事件,必須執行相關步驟以準備工作負載。 準備完成後,應使用已排定事件 API 來核准事件。 否則,一旦到達 NotBefore 時間,即會自動核准事件。 如果 VM 位於共用基礎結構上,系統則會等待相同硬體上的所有其他租用戶也核准作業或逾時。 一旦已從所有受影響的 VM 收集到核准,或已到達 NotBefore 時間,Azure 就會產生 EventStatus 為「已啟動」狀態的已排定事件承載,並觸發維護事件。 事件到達終止狀態時,就會從事件清單中移除。 這可做為客戶復原其 VM 的訊號。

    以下虛擬碼示範了如何在應用程式中讀取和管理已排定事件:

    current_list_of_scheduled_events = get_latest_from_se_endpoint()
    #prepare for new events
    for each event in current_list_of_scheduled_events:
      if event not in previous_list_of_scheduled_events:
        prepare_for_event(event)
    #recover from completed events
    for each event in previous_list_of_scheduled_events:
      if event not in current_list_of_scheduled_events:
        receover_from_event(event)
    #prepare for future jobs
    previous_list_of_scheduled_events = current_list_of_scheduled_events
    

    由於已排定事件通常用於具有高可用性需求的應用程式,因此應考慮一些例外狀況:

    1. 已排定事件完成並從陣列中移除後,如果沒有新的事件 (包括另一個 EventStatus 為「已啟動」狀態的事件),便不會再產生影響
    2. Azure 會監視整個機群的維護作業,並在極少數情況下,因認為維護作業的風險太高而判斷無法套用。 在此情況下,已排定事件將會直接由「已排程」狀態,轉而從事件陣列中移除
    3. 若發生硬體失敗,Azure 會略過「已排程」狀態,並立即將 EventStatus 改為「已啟動」狀態。
    4. 雖然事件仍處於 EventStatus 為「已啟動」的狀態,也可能會因持續時間比已排定事件中公告的還短,而產生其他影響。

    不同容錯網域的 VM 不會同時受到例行維護作業的影響,以保障 Azure 的可用性。 然而,作業可一個接一個連續執行。 一個容錯網域中的 VM 可以在另一個容錯網域維護完成後,立即接收 EventStatus 為「已排程」的已排定事件。 無論選擇何種結構,請始終檢查 VM 是否有待處理的新事件。

    雖然事件發生的具體時間各不相同,但可根據下圖提供的粗略指導方針,了解典型維護作業如何是如何進行:

    • 從 EventStatus「已排程」到「核准逾時」:15 分鐘
    • 影響持續時間:7 秒
    • 從 EventStatus「已啟動」到「已完成」(事件從事件陣列中移除):10 分鐘

    Diagram of a timeline showing the flow of a scheduled event.

    標頭

    查詢中繼資料服務時,您必須提供 Metadata:true 標頭以免不小心重新導向要求。 所有排程的事件都需要 Metadata:true 標頭。 要求中未包含標頭會導致中繼資料服務「不正確的要求」回應。

    查詢事件

    您只要進行下列呼叫,即可查詢已排定事件:

    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:虛擬機器已排定要重新開機 (非持續性記憶體都會遺失)。 在極少數情況下,排程 EventType 為「重新啟動」的 VM 可能不會重新啟動,而是遭遇凍結事件。 請遵循上述指示,檢查事件是否已完成,又能否安全地還原工作負載。
    • Redeploy︰虛擬機器已排定要移至另一個節點 (暫時磁碟都會遺失)。
    • 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 最短時間通知
    凍結 15 分鐘
    重新啟動 15 分鐘
    重新部署 10 分鐘
    終止 使用者可設定:5 至 15 分鐘

    這表示在事件發生前的最短通知時間內,您能夠偵測到事件的未來排程。 事件已排定後,會在通過核准或 NotBefore 時間後進入 Started 狀態。 然而,在極少數情況下,Azure 會在作業啟動前取消作業。 在此情況下,事件會從事件陣列中移除,影響也不會如期發生。

    注意

    在某些情況下,Azure 能夠預測主機因為硬體效能下降而失敗,並嘗試排定移轉來減輕服務中斷的影響。 受影響的虛擬機器會收到含有 NotBefore (通常是未來幾天) 的已排定事件。 實際時間依預測的失敗風險評量而有所不同。 Azure 會盡可能提前 7 天通知,但實際時間並不一定,如果預期硬體很可能即將失敗,則時間可能更短。 若要將您服務的風險降至最低,以防萬一硬體在系統起始移轉之前失敗,我們建議您盡快重新部署虛擬機器。

    注意

    如果主機節點遇到硬體失敗,Azure 將會略過最低通知期間,立即開始進行受影響虛擬機器的復原流程。 這可減少受影響 VM 無法回應時的復原時間。 在復原流程中,將會針對所有受影響且具有 EventType = RebootEventStatus = Started 的 VM 建立事件。

    輪詢頻率

    您可以自行決定輪詢端點更新的頻率。 然而,要求間隔時間越久,您可能會花更多時間回應即將發生的事件。 大部分事件都有 5 到 15 分鐘的事先通知,但在某些情況下,預先通知可能不到 30 秒。 為確保盡可能有較多時間採取緩和動作,建議您每秒輪詢一次服務。

    啟動事件

    在您得知即將發生的事件,並完成正常關機邏輯之後,即可使用 EventId 向中繼資料服務進行 POST 呼叫,以核准未處理的事件。 對 Azure 來說,此呼叫可以將通知時間縮到最短 (可能的話)。 在核准時,事件可能不會立即啟動,在某些情況下,Azure 會要求核准節點上託管的所有 VM,再繼續處理事件。

    以下是 POST 要求本文中必須要有的 JSON 範例。 要求需包含 StartRequests 清單。 每個 StartRequest 都包含您需要加速之事件的 EventId

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

    如果服務傳遞了有效的事件識別碼,則即使另一個 VM 已核准事件,也一律會傳回 200 成功碼。 400 錯誤碼表示要求標頭或酬載的格式錯誤。

    注意

    除非是透過 POST 訊息核准,或已過了 NotBefore 時間,否則事件不會繼續。 這包括使用者觸發的事件,例如從 Azure 入口網站重新啟動 VM。

    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
    

    注意

    認可事件時,即會允許事件中所有 Resources 的事件繼續進行,而不僅是認可此事件 VM。 因此,您可以選擇一個領導者來協調認可;最簡單的方法是選擇 Resources 欄位的第一部機器。

    範例回應

    以下事件範例說明了即時移轉至另一個節點的兩部 VM 所看到的內容。

    每當 Events 中有新的資訊時,DocumentIncarnation 就會變動。 事件的核准可讓 WestNO_0 和 WestNO_1 的凍結繼續。 DurationInSeconds 為 -1,表示平台不知道作業需時多久。

    {
        "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 (for example, 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()
    

    下一步