すべてのプロダクト
Search
ドキュメントセンター

Elastic Compute Service:ECS ホストの状態変更イベントの自動 O&M

最終更新日:Nov 22, 2025

このトピックでは、Cloud Monitor を使用して、Simple Message Queue (旧称:MNS) のキューを使用して Elastic Compute Service (ECS) ホストの状態変更イベントを自動的に処理する方法の実用的な例を示します。

前提条件

背景情報

既存のシステムイベントに加えて、ECS は Cloud Monitor を通じてスポットインスタンスの状態変更イベントと中断通知イベントを公開します。ECS ホストの状態が変更されるたびに、ECS 状態変更イベントがトリガーされます。これらの変更は、コンソール、OpenAPI 呼び出し、または SDK を介して開始できます。また、Auto Scaling などのサービス、支払い遅延、またはシステム例外によって自動的にトリガーすることもできます。

Cloud Monitor は、イベントによってトリガーされるアラートを処理するために、Simple Message Queue (旧称:MNS)、Function Compute、URL コールバック、Simple Log Service の 4 つのメソッドを提供します。このトピックでは、Simple Message Queue (旧称:MNS) を例として、ECS ホストの状態変更イベントを自動的に処理するための 3 つのベストプラクティスについて説明します。

手順

Cloud Monitor は、すべての ECS ホストの状態変更イベントを Simple Message Queue (旧称:MNS) に配信します。その後、Simple Message Queue (旧称:MNS) を使用してメッセージを取得および処理できます。

  • ベストプラクティス 1: すべての ECS ホストの作成イベントとリリースイベントを記録する。

    ECS コンソールでは、リリースされたインスタンスをクエリできません。リリースされたインスタンスのクエリを有効にするには、ECS ホストの状態変更イベントを使用して、すべての ECS ホストのライフサイクルをデータベースまたは Simple Log Service に記録します。ECS ホストを作成すると Created イベントが送信され、ECS ホストをリリースすると Deleted イベントが送信されます。

    1. Conf ファイルを編集します。

      Conf ファイルには、Simple Message Queue (旧称:MNS) の endpoint、Alibaba Cloud アカウントの access_keyaccess_key_secretregion_id (例: `cn-beijing`)、および queue_name を含める必要があります。

      [キューの詳細] ページの エンドポイント セクションで、インターネット経由でのアクセスイントラネット経由でのアクセス のエンドポイントを表示できます。
      import os
      
      # ALIBABA_CLOUD_ACCESS_KEY_ID と ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数が設定されていることを確認してください。
      # コードが漏洩すると、AccessKey が公開され、アカウント内のすべてのリソースが危険にさらされる可能性があります。次のコードでは、環境変数を使用して AccessKey を取得します。これは参考用です。セキュリティトークンサービス (STS) などのより安全な方法を使用してください。
      class Conf:
          endpoint = 'http://<id>.mns.<region>.aliyuncs.com/'
          access_key = os.environ['ALIBABA_CLOUD_ACCESS_KEY_ID']
          access_key_secret = os.environ['ALIBABA_CLOUD_ACCESS_KEY_SECRET']
          region_id = 'cn-beijing'
          queue_name = 'test'
          vserver_group_id = 'your_vserver_group_id'
                                          
    2. MNS SDK を使用して、MNS からメッセージを取得する MNS クライアントを作成します。

      # -*- coding: utf-8 -*-
      import json
      from mns.mns_exception import MNSExceptionBase
      import logging
      from mns.account import Account
      from . import Conf
      
      
      class MNSClient(object):
          def __init__(self):
              self.account =  Account(Conf.endpoint, Conf.access_key, Conf.access_key_secret)
              self.queue_name = Conf.queue_name
              self.listeners = dict()
      
          def regist_listener(self, listener, eventname='Instance:StateChange'):
              if eventname in self.listeners.keys():
                  self.listeners.get(eventname).append(listener)
              else:
                  self.listeners[eventname] = [listener]
      
          def run(self):
              queue = self.account.get_queue(self.queue_name)
              while True:
                  try:
                      message = queue.receive_message(wait_seconds=5)
                      event = json.loads(message.message_body)
                      if event['name'] in self.listeners:
                          for listener in self.listeners.get(event['name']):
                              listener.process(event)
                      queue.delete_message(receipt_handle=message.receipt_handle)
                  except MNSExceptionBase as e:
                      if e.type == 'QueueNotExist':
                          logging.error('Queue %s not exist, please create queue before receive message.', self.queue_name)
                      else:
                          logging.error('No Message, continue waiting')
      
      
      class BasicListener(object):
          def process(self, event):
              pass
                                      

      上記のコードは、リスナーを呼び出して Simple Message Queue (旧称:MNS) から取得したデータを消費し、その後メッセージを削除します。

    3. 特定のリスナーを登録してイベントを消費します。この単純なリスナーは、Created イベントと Deleted イベントをチェックし、いずれかを受信するとログエントリを出力します。

       # -*- coding: utf-8 -*-
      import logging
      from .mns_client import BasicListener
      
      
      class ListenerLog(BasicListener):
          def process(self, event):
              state = event['content']['state']
              resource_id = event['content']['resourceId']
              if state == 'Created':
                  logging.info(f'The instance {resource_id} state is {state}')
              elif state == 'Deleted':
                  logging.info(f'The instance {resource_id} state is {state}')
                                      

      main 関数は次のように記述されます。

      mns_client = MNSClient()
      
      mns_client.regist_listener(ListenerLog())
      
      mns_client.run()

      本番環境では、将来の検索や監査のために、イベントをデータベースまたは Simple Log Service (SLS) に保存する必要がある場合があります。

  • ベストプラクティス 2: 停止した ECS ホストを自動的に再起動する。

    一部のシナリオでは、ECS ホストが予期せず停止することがあります。停止した ECS ホストを自動的に再起動するプロセスを設定できます。

    停止した ECS ホストを自動的に再起動するには、最初のベストプラクティスの MNS クライアントを再利用し、新しいリスナーを追加します。リスナーが Stopped イベントを受信すると、その ECS ホストで Start コマンドを実行します。

    # -*- coding: utf-8 -*-
    import logging
    
    from alibabacloud_ecs20140526.client import Client as Ecs20140526Client
    from alibabacloud_ecs20140526.models import StartInstanceRequest
    from alibabacloud_tea_openapi.models import Config
    
    from .config import Conf
    from .mns_client import BasicListener
    
    
    class ECSClient(object):
        def __init__(self, client):
            self.client = client
    
        # ECS ホストを起動します
        def start_instance(self, instance_id):
            logging.info(f'Start instance {instance_id} ...')
            request = StartInstanceRequest(
                instance_id=instance_id
            )
            self.client.start_instance(request)
    
    
    class ListenerStart(BasicListener):
        def __init__(self):
            ecs_config = Config(
                access_key_id=Conf.access_key,
                access_key_secret=Conf.access_key_secret,
                endpoint=f'ecs.{Conf.region_id}.aliyuncs.com'
            )
            client = Ecs20140526Client(ecs_config)
            self.ecs_client = ECSClient(client)
    
        def process(self, event):
            detail = event['content']
            instance_id = detail['resourceId']
            if detail['state'] == 'Stopped':
                self.ecs_client.start_instance(instance_id)

    本番環境では、Start コマンドを実行した後、StartingRunningStopped などの後続のイベントをモニターする必要がある場合があります。その後、タイマーとカウンターを使用して、成功または失敗のシナリオを処理できます。

  • ベストプラクティス 3: スポットインスタンスがリリースされる前に、Server Load Balancer (SLB) インスタンスからスポットインスタンスを自動的に削除する。

    スポットインスタンスがリリースされる約 5 分前に、リリースアラートイベントが送信されます。この短い時間枠を使用して、業務継続性ロジックを実行できます。たとえば、インスタンスがリリースされた後に SLB が削除を処理するのを待つのではなく、Server Load Balancer (SLB) インスタンスのバックエンドサーバーからインスタンスを事前に削除できます。

    最初のベストプラクティスの MNS クライアントを再利用し、新しいリスナーを追加します。リスナーがスポットインスタンスのリリースアラートを受信すると、SLB SDK を呼び出します。

    # -*- coding: utf-8 -*-
    from alibabacloud_slb20140515.client import Client as Slb20140515Client
    from alibabacloud_slb20140515.models import RemoveVServerGroupBackendServersRequest
    from alibabacloud_tea_openapi.models import Config
    
    from .config import Conf
    from .mns_client import BasicListener
    
    
    class SLBClient(object):
        def __init__(self):
            self.client = self.create_client()
    
        def create_client(self):
            config = Config()
            config.access_key_id = Conf.access_key
            config.access_key_secret = Conf.access_key_secret
            config.endpoint = 'slb.aliyuncs.com'
            return Slb20140515Client(config)
    
        def remove_vserver_group_backend_servers(self, vserver_group_id, instance_id):
            request = RemoveVServerGroupBackendServersRequest(
                region_id=Conf.region_id,
                vserver_group_id=vserver_group_id,
                backend_servers="[{'ServerId':'" + instance_id + "','Port':'80','Weight':'100'}]"
            )
            response = self.client.remove_vserver_group_backend_servers(request)
            return response
    
    
    class ListenerSLB(BasicListener):
        def __init__(self, vserver_group_id):
            self.slb_caller = SLBClient()
            self.vserver_group_id = Conf.vserver_group_id
    
        def process(self, event):
            detail = event['content']
            instance_id = detail['instanceId']
            if detail['action'] == 'delete':
                self.slb_caller.remove_vserver_group_backend_servers(self.vserver_group_id, instance_id)
    
    重要

    スポットインスタンスのリリースアラートのイベント名は、以前のものとは異なります。たとえば、リスナー登録の呼び出しは mns_client.regist_listener(ListenerSLB(Conf.vserver_group_id), 'Instance:PreemptibleInstanceInterruption') です。

    本番環境では、サービスの可用性を確保するために、新しいスポットインスタンスをリクエストし、それを SLB インスタンスにアタッチする必要があります。