이제 스팟 요청을 취소하는 것을 잊고 안녕!

개요



개발 환경에 EC2의 스팟 인스턴스를 이용하는 것은 자주 있다고 생각합니다. 그 중에서도 '영구적 요청'을 사용하고 있다면 그냥 인스턴스만을 '종료'하고 스팟 요청을 취소하는 것을 잊지 않겠습니까? 요청 유효 기간 내에 있으면 동일한 유형의 인스턴스가 자동으로 좀비처럼 재작성되어 쓸모없는 청구가 발생합니다.
인스턴스를 삭제할 때 스팟 요청에서 취소하도록 운영 절차로 결정해도 그만 잊기 쉽기 때문에 자동화하는 방법을 소개합니다.

전제 조건 요약
  • 스팟 인스턴스 사용
  • 영구적인 요청입니다
  • 요청 만료일 내에 있습니다

  • 구성



    EventBridge를 사용하여 인스턴스 종료 이벤트 중에 지정한 Lambda를 실행하여 스팟 요청을 취소합니다.



    EventBridge



    이벤트로 인스턴스 상태 변경 알림에서 "Terminated"를 추가합니다.



    eventbridge.json
    {
      "detail-type": [
        "EC2 Instance State-change Notification"
      ],
      "source": [
        "aws.ec2"
      ],
      "detail": {
        "state": [
          "terminated"
        ]
      }
    }
    

    람다



    Lambda에 첨부된 역할에 다음 권한(인스턴스 정보 검색, 스팟 요청 취소)을 포함하면 OK입니다.

    role.json
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "VisualEditor0",
                "Effect": "Allow",
                "Action": [
                    "ec2:CancelSpotInstanceRequests",
                    "ec2:DescribeInstances"
                ],
                "Resource": "*"
            }
        ]
    }
    

    EventBridge에서 전달되는 변수는 다음과 같습니다.
    동시에 여러 인스턴스를 삭제하더라도 하나씩 이벤트로 전송되므로,
    Lambda 측에서는 기본적으로 단수를 상정한 구현으로 좋을 것입니다.

    event.json
    {
      "version": "0",
      "id": "xxxxxxxxxxxxxxxxxxxxxx",
      "detail-type": "EC2 Instance State-change Notification",
      "source": "aws.ec2",
      "account": "xxxxxxxxxx",
      "time": "2020-10-26T09:28:11Z",
      "region": "ap-northeast-1",
      "resources": [
        "arn:aws:ec2:ap-northeast-1:xxxxxx:instance/i-xxxxxxxxxxxx"
      ],
      "detail": {
        "instance-id": "i-xxxxxxxxxxxx",
        "state": "terminated"
      }
    }
    

    이벤트의 instance-id에서 인스턴스 정보를 검색하고 관련 스팟 요청을 취소합니다.

    cancle_spotrequest.py
    
    import json
    import boto3
    
    def lambda_handler(event, context):
        '''
        EC2インスタンス削除時に関連するスポットリクエストをキャンセルする
        '''
    
        print('--------event----------')
        print(event)
        print('------------------')
    
        client = boto3.client('ec2')
    
        event_detail = event['detail']
        if event_detail['state'] != 'terminated':
            return
    
        # インスタンスIDで終了済みのスポットインスタンスを取得する
        response = client.describe_instances(
                        Filters=[{
                            'Name': 'instance-state-name',
                            'Values': ['terminated']
                        },{
                            'Name': 'instance-lifecycle',
                            'Values': ['spot']
                        }],
                        InstanceIds=[event_detail['instance-id']]
                    )
    
        # 対象がなければ、終了
        if len(response['Reservations']) <= 0:
            return
    
        spot_request_id = ''
        for reservations in response['Reservations']:
            for ins in reservations['Instances']:
                spot_request_id = ins['SpotInstanceRequestId']
    
        print('--------cancel id----------')
        print(spot_request_id)
        print('------------------')
    
        # スポットリクエストをキャンセル
        client.cancel_spot_instance_requests(SpotInstanceRequestIds=[spot_request_id])
    
    

    좋은 웹페이지 즐겨찾기