このトピックでは、Cloud Monitor を使用して、Simple Message Queue (旧称:MNS) のキューを使用して Elastic Compute Service (ECS) ホストの状態変更イベントを自動的に処理する方法の実用的な例を示します。
前提条件
Simple Message Queue (旧称:MNS) コンソールで、
ecs-cms-eventなどのキューを作成します。詳細については、「キューを作成する」をご参照ください。
Cloud Monitor コンソールでシステムイベントによってトリガーされるアラートルールを作成します。詳細については、「システムイベントによってトリガーされるアラートルールを管理する (旧バージョン)」をご参照ください。
Python の依存関係をインストールします。
このトピックのすべてのコードは、Python 3.7 を例として使用しています。MNS SDK for Python と ECS SDK for Python をインストールする必要があります。
Python SDK のインストール方法の詳細については、「Python SDK のインストール」をご参照ください。
他のプログラミング言語を使用する場合は、「MNS SDK のダウンロードと使用」および「ECS SDK の概要」をご参照ください。
背景情報
既存のシステムイベントに加えて、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イベントが送信されます。Conf ファイルを編集します。
Conf ファイルには、Simple Message Queue (旧称:MNS) の
endpoint、Alibaba Cloud アカウントのaccess_keyとaccess_key_secret、region_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'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) から取得したデータを消費し、その後メッセージを削除します。
特定のリスナーを登録してイベントを消費します。この単純なリスナーは、
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コマンドを実行した後、Starting、Running、Stoppedなどの後続のイベントをモニターする必要がある場合があります。その後、タイマーとカウンターを使用して、成功または失敗のシナリオを処理できます。ベストプラクティス 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 インスタンスにアタッチする必要があります。