你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

Azure 元数据服务:适用于 Windows VM 的计划事件

适用于: ✔️ Windows VM ✔️ 灵活规模集 ✔️ 统一规模集

计划事件是一个 Azure 元数据服务,可提供应用程序时间用于准备虚拟机 (VM) 维护。 它提供有关即将发生的维护事件的信息(例如重新启动),使应用程序可以为其准备并限制中断。 它可用于 Windows 和 Linux 上的所有 Azure 虚拟机类型(包括 PaaS 和 IaaS)。

有关 Linux 上的计划事件的信息,请参阅适用于 Linux VM 的计划事件

注意

计划事件在所有 Azure 区域中正式发布。 有关最新版本信息,请参阅版本和区域可用性

为何使用计划事件?

许多应用程序都可以受益于时间来准备 VM 维护。 时间可以用于执行应用程序的特定任务的提高可用性、可靠性和可维护性,包括:

  • 检查点和还原。
  • 连接清空。
  • 主要副本故障转移。
  • 从负载均衡器池删除。
  • 事件日志记录。
  • 正常关闭。

使用计划事件,应用程序可以发现维护的发生,并触发任务以限制其影响。

预定事件提供以下用例中的事件:

  • 平台启动的维护(例如,VM 重新启动、实时迁移或主机的内存保留更新)。
  • 虚拟机在预计很快将出现故障的降级后的主机硬件上运行。
  • 虚拟机在遭受硬件故障的主机上运行。
  • 用户启动的维护(例如,用户重启或重新部署 VM)。
  • 现成 VM现成规模集实例逐出。

基础知识

元数据服务公开在 VM 中使用可访问的 REST 终结点运行 VM 的相关信息。 该信息通过不可路由的 IP 提供,因此不会在 VM 外部公开。

作用域

计划的事件传送到:

  • 独立虚拟机。
  • 云服务中的所有 VM。
  • 可用性集中的所有 VM。
  • 可用性区域中的所有 VM。
  • 规模集位置组中的所有 VM。

注意

某个结构控制器 (FC) 租户中所有虚拟机 (VM) 的 Scheduled Events 传送到另一个 FC 租户中的所有 VM。 在不考虑可用性区域使用情况的条件下,FC 租户等同于一个独立的 VM、一个完整的云服务、一个完整的可用性集和一个 VM 规模集 (VMSS) 的放置组。

因此,检查事件中的 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 正式版 全部
  • 添加了对 EventSource 的支持
  • 2019-04-01 正式版 全部
  • 添加了对事件说明的支持
  • 2019-01-01 正式版 全部
  • 已添加对虚拟机规模集 EventType“Terminate”的支持
  • 2017-11-01 正式版 All
  • 已添加对现成 VM 逐出 EventType“Preempt”的支持
  • 2017-08-01 正式版 全部
  • 已从 IaaS VM 的资源名称中删除前置下划线
  • 针对所有请求强制执行元数据标头要求
  • 2017-03-01 预览 全部
  • 初始版本
  • 注意

    支持的计划事件的前一预览版 {latest} 发布为 api-version。 此格式不再受支持,并且将在未来弃用。

    启用和禁用 Scheduled Events

    首次为事件发出请求时,为服务启用了计划事件。 首次调用时应该会延迟响应最多两分钟。

    如果 24 小时未发出请求,将为服务禁用计划事件。

    用户启动的维护

    用户通过 Azure 门户、API、CLI 或 PowerShell 启动的 VM 维护会生成计划事件。 然后,可以在应用程序中测试维护准备逻辑,并可以通过应用程序准备用户启动的维护。

    如果重启 VM,将计划 Reboot 类型的事件。 如果重新部署 VM,将计划 Redeploy 类型的事件。 通常,可以立即批准具有用户事件源的事件,以避免用户启动的操作发生延迟。

    使用 API

    头文件

    查询元数据服务时,必须提供标头 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},
            }
        ]
    }
    

    事件属性

    Property 说明
    文档化身 当事件数组发生更改时增加的整数。 具有相同化身的文档包含相同的事件信息,当事件发生更改时,化身将递增。
    EventId 此事件的全局唯一标识符。

    示例:
    • 602d9444-d2cd-49c7-8624-8643e7171297
    EventType 此事件造成的影响。

    值:
    • Freeze:虚拟机计划暂停数秒。 CPU 和网络连接可能会暂停,但对内存或打开的文件没有影响。
    • Reboot:计划重启虚拟机(非永久性内存丢失)。
    • Redeploy:计划将虚拟机移到另一节点(临时磁盘将丢失)。
    • Preempt:正在删除现成虚拟机(临时磁盘将丢失)。 此事件尽最大努力提供
    • Terminate:计划将删除虚拟机。
    ResourceType 此事件影响的资源类型。

    值:
    • VirtualMachine
    资源 此事件影响的资源列表。 它保证最多只能包含一个更新域的计算机,但可能不包含该更新域中的所有计算机。

    示例:
    • ["FrontEnd_IN_0", "BackEnd_IN_0"]
    EventStatus 此事件的状态。

    值:
    • Scheduled:此事件计划在 NotBefore 属性指定的时间之后启动。
    • Started:此事件已启动。
    不提供 Completed 或类似状态。 事件完成后,将不再返回该事件。
    NotBefore 在可以启动此事件之前所要经过的时间。 此事件保证在此时间之前不启动。 如果事件已启动,则此属性为空

    示例:
    • 2016 年 9 月 19 日星期一 18:29:47 GMT
    说明 此事件的说明。

    示例:
    • 主机服务器正在维护中。
    EventSource 事件的发起者。

    示例:
    • Platform:此事件是由平台发起的。
    • User:此事件是由用户发起的。
    DurationInSeconds 事件导致的中断的预期持续时间。

    示例:
    • 9:事件导致的中断将持续 9 秒。
    • -1:影响持续时间未知或不适用时使用的默认值。

    事件计划

    将根据事件类型为每个事件计划将来的最小量时间。 此时间反映在某个事件的 NotBefore 属性上。

    EventType 最小通知
    冻结 15 分钟
    重新启动 15 分钟
    重新部署 10 分钟
    Preempt 30 秒
    Terminate 用户可配置:5 - 15 分钟

    注意

    在某些情况下,由于硬件降级,Azure 能够预测主机故障,并会尝试通过对迁移进行计划来缓解服务中断。 受影响的虚拟机会收到计划事件,该事件的 NotBefore 通常是将来几天的时间。 实际时间因预测的故障风险评估而异。 Azure 会尽可能提前 7 天发出通知,但实际时间可能会有变化,如果预测硬件即将发生故障的可能性很大,则实际时间可能更早。 为了在系统启动迁移之前硬件出现故障时将服务风险降至最低,我们建议你尽快自行重新部署虚拟机。

    注意

    如果主机节点遇到硬件失败,Azure 将绕过最短通知期,立即开始受影响虚拟机的恢复过程。 这减少了受影响 VM 无法响应情况下的恢复时间。 在恢复过程中,将为所有受影响的 VM 创建一个带有 EventType = RebootEventStatus = Started 的事件。

    轮询频率

    可根据需要频繁或偶尔轮询终结点以进行更新。 但是,两次请求之间的时间越长,你拥有的对即将发生的事件做出响应的时间就越少。 大多数事件都会提前 5 到 15 分钟通知,尽管在某些情况下,可能只会提前 30 秒通知。 为确保有尽可能多的时间采取缓解措施,我们建议你每秒轮询一次服务。

    启动事件

    了解即将发生的事件并完成正常关闭逻辑后,可以通过使用 EventId 对元数据服务进行 POST 调用来批准未完成的事件。 此调用指示 Azure 可以缩短最小通知时间(如可能)。 事件可能不会在获得批准后立即启动,在某些情况下,Azure 会要求节点上托管的所有虚拟机都获得批准,然后才能继续该事件。

    下面是 POST 请求正文中所需的 JSON 示例。 请求应包含 StartRequests 列表。 每个 StartRequest 包含想要加速的事件的 EventId

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

    如果事件 ID 有效,该服务将始终返回 200 成功代码,即使该事件已被其他 VM 批准。 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
    

    注意

    确认事件后,即可允许事件针对事件中所有的 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":  -1
                       }
                   ]
    }
    
    {
        "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":  -1
                       }
                   ]
    }
    
    {
        "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()
    

    后续步骤