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

Cloud Monitor:ECS インスタンスの状態変更イベントに基づく O&M の自動化

最終更新日:Aug 28, 2026

Elastic Compute Service (ECS) は、インスタンスが状態間で遷移するたびに、Cloud Monitor を通じて状態変更イベントを発行します。遷移のトリガーには、コンソール操作、API コール、SDK 操作、Auto Scaling ポリシー、料金滞納、システム例外などがあります。これらのイベントを Simple Message Queue (旧称:MNS) にルーティングすることで、インスタンスライフサイクルのログ記録、停止したインスタンスの再起動、またはスポットインスタンスをリリース前にロードバランサーから切り離すなどの自動レスポンスを構築できます。

このトピックでは、Python と MNS メッセージキューをイベント配信メカニズムとして使用する 3 つの自動化シナリオを紹介します。

仕組み

Cloud Monitor はすべての ECS インスタンスの状態変更イベントをキャプチャし、MNS のキューに配信します。Python コンシューマーがキューをポーリングし、各イベントを解析して、イベント名 (例:Instance:StateChangeInstance:PreemptibleInstanceInterruption) に基づいて登録済みのリスナーにディスパッチします。各リスナーは、特定の自動化アクションを実装します。

Cloud Monitor は、イベントアラートを処理するために 4 つの方法をサポートしています:

方法

ユースケース

MNS

カスタムのコンシューマーロジックによる非同期処理 (このトピックで使用)

Function Compute (FC)

インフラストラクチャを管理せずにサーバーレスでイベントを処理

URL コールバック

イベントを外部の HTTP エンドポイントに転送

Simple Log Service (SLS)

イベントログの一元管理と分析

前提条件

開始する前に、次を用意してください:

SDK のインストールの詳細については、「Python SDK のインストール」をご参照ください。その他のプログラミング言語については、「MNS SDK リファレンス」および「ECS SDK の概要」をご参照ください。

手順 1:設定ファイルの作成

MNS エンドポイント、認証情報、リージョン、キュー名を共通の設定ファイルに定義します。3 つのシナリオすべてでこの設定を再利用します。

import os

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 = 'ecs-cms-event'
    vserver_group_id = '<your-vserver-group-id>'

次のプレースホルダーを実際の値に置き換えてください:

プレースホルダー

説明

<id>

Alibaba Cloud アカウント ID

1234567890

<region>

MNS キューのリージョン

cn-beijing

<your-vserver-group-id>

Server Load Balancer (SLB) の vServer グループ ID。シナリオ 3 でのみ必要です。

rsp-bp1abc...

MNS エンドポイントは、[キューの詳細] ページの [エンドポイント] セクションにあります。[インターネットアクセス][内部アクセス] の両方のエンドポイントが利用可能です。
重要

AccessKey 認証情報をソースコードにハードコードしないでください。上記の例では、環境変数から認証情報を読み取ります。本番ワークロードでは、代わりに Security Token Service (STS) の一時的な認証情報を使用してください。

手順 2:MNS コンシューマーの作成

キューをポーリングし、登録済みのリスナーにイベントをディスパッチし、処理済みのメッセージを削除する再利用可能な MNS クライアントを作成します:

# -*- coding: utf-8 -*-
import json
import logging
from mns.mns_exception import MNSExceptionBase
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

このコンシューマーは、ロングポーリング (wait_seconds=5) を使用してメッセージを取得します。メッセージが到着すると JSON ボディを解析し、イベント名で登録済みリスナーを参照して、各リスナーの process メソッドを呼び出します。処理後、キューからメッセージを削除します。

イベントペイロードの構造

状態変更イベントは次の構造を使用します:

フィールド

説明

event['name']

イベントタイプ識別子

Instance:StateChange

event['content']['state']

現在のインスタンス状態

CreatedStartingRunningStoppedDeleted

event['content']['resourceId']

ECS インスタンス ID

i-bp1abc...

シナリオ 1:インスタンスの作成およびリリースイベントのログ記録

CreatedDeleted の状態変更をログに記録して、ECS インスタンスのライフサイクル全体を追跡します。リリースされたインスタンスは ECS コンソールに表示されなくなります。これらのイベントを記録しておくことで、容量計画とインシデント後の分析のための監査証跡として保持できます。

リスナーの作成

# -*- 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}')

コンシューマーの実行

mns_client = MNSClient()

mns_client.regist_listener(ListenerLog())

mns_client.run()

結果の確認

  1. コンソールで ECS インスタンスを作成またはリリースします。

  2. コンシューマーのログで、The instance i-bp1abc... state is Created のようなメッセージを確認します。

本番環境では、stdout へのログ出力ではなく、データベースまたは Simple Log Service (SLS) にイベントを保存してください。これにより、過去のインスタンスライフサイクルデータを検索および監査できます。

シナリオ 2:停止したインスタンスの自動再起動

ECS インスタンスは、システム例外または料金滞納により予期せず停止する場合があります。このリスナーは Stopped イベントを監視し、ECS の StartInstance API を呼び出して影響を受けたインスタンスを再起動します。

リスナーの作成

手順 2 の MNS コンシューマーを再利用し、新しいリスナーを登録します:

# -*- 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

    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)

コンシューマーの実行

mns_client = MNSClient()

mns_client.regist_listener(ListenerStart())

mns_client.run()

結果の確認

  1. 実行中の ECS インスタンスを手動で停止します。

  2. コンシューマーのログで、Start instance i-bp1abc... ... を確認します。

  3. ECS コンソールでインスタンスの状態が Running に戻ることを確認します。

本番環境では、起動コマンドを発行した後の後続の状態遷移 (StartingRunningStopped) を監視してください。再起動に失敗するケースに備えて、タイマーとリトライカウンターを使用します。

シナリオ 3:解放前にスポットインスタンスを SLB から切り離す

スポットインスタンスが解放される約 5 分前に、Cloud Monitor は Instance:PreemptibleInstanceInterruption イベントを送信します。この猶予時間を利用して、SLB がリリースを受動的に検知するのを待つのではなく、Server Load Balancer (SLB) インスタンスのバックエンドサーバーグループからインスタンスを事前に削除します。

リスナーの作成

手順 2 の MNS コンシューマーを再利用し、スポットインスタンスの割り込みイベント用のリスナーを登録します:

# -*- 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)
重要

スポットインスタンスの割り込みイベント名は、状態変更イベントとは異なります。このリスナーは、イベント名 Instance:PreemptibleInstanceInterruption で登録してください:

mns_client = MNSClient()

mns_client.regist_listener(ListenerSLB(Conf.vserver_group_id), 'Instance:PreemptibleInstanceInterruption')

mns_client.run()

スポットインスタンス割り込みイベントの構造

スポットインスタンスの割り込みイベントは、状態変更イベントとは異なるフィールド名を使用します:

フィールド

説明

event['name']

イベントタイプ識別子

Instance:PreemptibleInstanceInterruption

event['content']['instanceId']

ECS インスタンス ID

i-bp1abc...

event['content']['action']

割り込みアクション

delete

結果の確認

  1. スポットインスタンスの割り込みイベントを待機します (またはテスト環境でシミュレートします)。

  2. 解放前にインスタンスが SLB の vServer グループから削除されていることを確認します。

本番環境では、サービス可用性を維持するために、代替のスポットインスタンスをリクエストし、直ちに SLB インスタンスにアタッチしてください。

すべてのリスナーの同時実行

単一の MNS コンシューマーに 3 つのリスナーをすべて登録し、複数のイベントタイプを同時に処理します:

mns_client = MNSClient()

# シナリオ1:ライフサイクルイベントのログ記録
mns_client.regist_listener(ListenerLog())

# シナリオ2:停止したインスタンスの自動再起動
mns_client.regist_listener(ListenerStart())

# シナリオ3:解放前にスポットインスタンスをSLBから削除
mns_client.regist_listener(ListenerSLB(Conf.vserver_group_id), 'Instance:PreemptibleInstanceInterruption')

mns_client.run()

イベントタイプのリファレンス

イベント名

トリガー

主要フィールド

Instance:StateChange

インスタンスの状態遷移 (Created、Starting、Running、Stopped、Deleted)

content.statecontent.resourceId

Instance:PreemptibleInstanceInterruption

スポットインスタンスの解放予定 (解放の約 5 分前)

content.instanceIdcontent.action

関連ドキュメント