O Elastic Compute Service (ECS) publica eventos de alteração de estado por meio do Cloud Monitor sempre que uma instância transita entre estados, seja por ações no console, chamadas de API, operações de SDK, políticas de Auto Scaling, pagamentos em atraso ou exceções do sistema. Ao encaminhar esses eventos para o Simple Message Queue (anteriormente MNS), você pode criar respostas automatizadas, como registrar o ciclo de vida das instâncias, reiniciar instâncias paradas ou remover instâncias preemptíveis de um balanceador de carga antes da liberação.
Este tópico demonstra três cenários de automação usando Python e uma fila de mensagens do MNS como mecanismo de entrega de eventos.
Como funciona
O Cloud Monitor captura todos os eventos de alteração de estado das instâncias ECS e os entrega a uma fila do Simple Message Queue (anteriormente MNS). Um consumidor Python consulta a fila, analisa cada evento e o despacha para listeners registrados com base no nome do evento (por exemplo, Instance:StateChange ou Instance:PreemptibleInstanceInterruption). Cada listener implementa uma ação de automação específica.
O Cloud Monitor oferece quatro métodos para processar alertas acionados por eventos:
|
Método |
Caso de uso |
|
Simple Message Queue (anteriormente MNS) |
Processamento assíncrono com lógica de consumidor personalizada (utilizado neste tópico) |
|
Function Compute (FC) |
Tratamento de eventos serverless sem gerenciamento de infraestrutura |
|
Callback de URL |
Encaminhamento de eventos para um endpoint HTTP externo |
|
Simple Log Service (SLS) |
Registro e análise centralizada de eventos |
Pré-requisitos
Antes de começar, verifique se você tem:
Uma fila (por exemplo,
ecs-cms-event) criada no Simple Message Queue (formerly MNS) consoleUma regra de alerta acionada por evento de sistema configurada no console do Cloud Monitor (versão antiga)
Python 3.7 ou posterior instalado
O SDK do MNS para Python e o SDK do ECS para Python instalados
As variáveis de ambiente
ALIBABA_CLOUD_ACCESS_KEY_IDeALIBABA_CLOUD_ACCESS_KEY_SECRETdefinidas
Para detalhes sobre a instalação do SDK, consulte Instale o SDK do Python. Para outras linguagens de programação, veja a referência do SDK do MNS e a visão geral do SDK do ECS.
Etapa 1: Crie o arquivo de configuração
Defina o endpoint do MNS, as credenciais, a região e o nome da fila em um arquivo de configuração compartilhado. Todos os três cenários reutilizam essa configuração.
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>'
Substitua os seguintes placeholders pelos valores reais:
|
Placeholder |
Descrição |
Exemplo |
|
|
ID da sua conta Alibaba Cloud |
|
|
|
Região da sua fila MNS |
|
|
|
ID do grupo vServer no Server Load Balancer (SLB), necessário apenas para o Cenário 3 |
|
Encontre o endpoint do MNS na página Queue Details , na seção Endpoint . Estão disponíveis endpoints de Internet Access e de Internal Access .
Nunca codifique credenciais AccessKey diretamente no código source. O exemplo anterior lê as credenciais de variáveis de ambiente. Para cargas de trabalho em produção, utilize credenciais temporárias do Security Token Service (STS).
Etapa 2: Construa o consumidor MNS
Crie um cliente MNS reutilizável que consulte a fila, despache eventos para listeners registrados e exclua as mensagens processadas. Os três cenários compartilham este consumidor.
# -*- 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
O consumidor utiliza long polling (wait_seconds=5) para recuperar mensagens. Quando uma mensagem chega, ele analisa o corpo JSON, localiza os listeners registrados pelo nome do evento e chama o método process de cada listener. Após o processamento, o sistema exclui a mensagem da fila.
Estrutura do payload do evento
Os eventos de alteração de estado utilizam a seguinte estrutura:
|
Campo |
Descrição |
Exemplo |
|
|
Identificador do tipo de evento |
|
|
|
Estado atual da instância |
|
|
|
ID da instância ECS |
|
Cenário 1: Registre eventos de criação e liberação de instâncias
Acompanhe todo o ciclo de vida das instâncias ECS registrando as alterações de estado Created e Deleted. Instâncias liberadas deixam de aparecer no console ECS. Registrar esses eventos fornece um rastro de auditoria para planejamento de capacidade e análise pós-incidente.
Crie o listener
# -*- 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}')
Execute o consumidor
mns_client = MNSClient()
mns_client.regist_listener(ListenerLog())
mns_client.run()
Verifique o resultado
Crie ou libere uma instância ECS no console.
Verifique nos logs do consumidor se há uma mensagem como
The instance i-bp1abc... state is Created.
Em ambientes de produção, armazene eventos em um banco de dados ou no Simple Log Service (SLS) em vez de registrar logs no stdout. Isso permite pesquisar e auditar dados históricos do ciclo de vida das instâncias.
Cenário 2: Reinicie automaticamente instâncias paradas
Uma instância ECS pode parar inesperadamente devido a exceções do sistema ou pagamentos em atraso. Este listener monitora eventos Stopped e chama a API StartInstance do ECS para reiniciar a instância afetada.
Crie o listener
Reutilize o consumidor MNS da Etapa 2 e registre um novo listener:
# -*- 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)
Execute o consumidor
mns_client = MNSClient()
mns_client.regist_listener(ListenerStart())
mns_client.run()
Verifique o resultado
Pare manualmente uma instância ECS em execução.
Verifique nos logs do consumidor a mensagem
Start instance i-bp1abc... ....Confirme se o estado da instância retornou para
Runningno console ECS.
Em produção, monitore as transições de estado subsequentes (Starting,RunningouStopped) após emitir o comando de início. Utilize temporizadores e contadores de tentativas para lidar com casos em que a reinicialização falha.
Cenário 3: Remova uma instância preemptível do SLB antes da liberação
Cerca de cinco minutos antes de uma instância preemptível ser recuperada, o Cloud Monitor envia um evento Instance:PreemptibleInstanceInterruption. Use essa janela para remover proativamente a instância do grupo de servidores backend de uma instância do Server Load Balancer (SLB), em vez de esperar que o SLB detecte a liberação passivamente.
Crie o listener
Reutilize o consumidor MNS da Etapa 2 e registre um listener para o evento de interrupção de instância preemptível:
# -*- 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)
O nome do evento para interrupções de instâncias preemptíveis difere dos eventos de alteração de estado. Registre este listener com o nome de evento Instance:PreemptibleInstanceInterruption:
mns_client = MNSClient()
mns_client.regist_listener(ListenerSLB(Conf.vserver_group_id), 'Instance:PreemptibleInstanceInterruption')
mns_client.run()
Estrutura do evento de interrupção de instância preemptível
Os eventos de interrupção de instância preemptível usam nomes de campos diferentes dos eventos de alteração de estado:
|
Campo |
Descrição |
Exemplo |
|
|
Identificador do tipo de evento |
|
|
|
ID da instância ECS |
|
|
|
Ação de interrupção |
|
Verifique o resultado
Aguarde um evento de interrupção de instância preemptível (ou simule um em ambiente de teste).
Confirme se a instância foi removida do grupo vServer do SLB antes da liberação.
Em produção, solicite uma instância preemptível substituta e anexe-a à instância do SLB imediatamente para manter a disponibilidade do service.
Execute todos os listeners juntos
Registre todos os três listeners em um único consumidor MNS para lidar com vários tipos de eventos simultaneamente:
mns_client = MNSClient()
# Scenario 1: Log lifecycle events
mns_client.regist_listener(ListenerLog())
# Scenario 2: Auto-restart stopped instances
mns_client.regist_listener(ListenerStart())
# Scenario 3: Remove spot instances from SLB before release
mns_client.regist_listener(ListenerSLB(Conf.vserver_group_id), 'Instance:PreemptibleInstanceInterruption')
mns_client.run()
Referência de tipos de eventos
|
Nome do evento |
Gatilho |
Campos principais |
|
|
Transições de estado da instância (Created, Starting, Running, Stopped, Deleted) |
|
|
|
Instância preemptível agendada para liberação (~5 min antes da recuperação) |
|