[AWS EC2] Cloudwatch + SNS + Lambda 를 이용하여 세계 리전들에 퍼져있는 내 Spot Instance 들의 알림을 하나의 Lambda로 받아보는 방법

달달칩·2020년 10월 23일
0

갠적인 리서치 때문에 세계 곳곳 AWS 데이터 센터 어딘가에 내 EC2 Instance 들을 배포할 일이 있었다.

그리고 아무래도 회사가 아닌 개인 프로젝트 이다보니 모든 서버 비용은 내 불쌍한 통장에서 나가기 때문에 최대한 비용을 절감하기 위하여 Spot Instance를 이용했다.

Spot Instance

AWS에서 제공하는 EC2의 Spot Instance 는 컴퓨팅 파워가 필요할때 잠시 만들어서 사용하는 인스턴스이다.

이 인스턴스의 특징이 KTX 입석과 비슷한 개념으로 사용되지 않는 자원(인스턴스)을 매우 싼값에 이용할 수 있도록 만들었으나 언제든 AWS측에서 필요하면 바로 회수해가는 인스턴스이다.

만약 인스턴스가 비인기 타입이라서 사람들이 잘 사용하지 않는다면 꽤 오랫동안 싼값에 특정 타입의 인스턴스를 사용할 수 있는 특징이 있다. (무려 3달 가는 인스턴스도 있었음. 물론 2시간 만에 끝나는 인스턴스도 많았지만..)

서비스를 위한 인스턴스는 전혀 아니나 언제 갑자기 꺼져도 괜찮은, 무언가 연산을 저렴하게 돌리기에는 적절한 인스턴스이다.

최대한 Spot Instance가 회수되지 않는 비 인기 인스턴스를 어떻게 잘 골라가면서 생성했었다.

그럼에도 불구하고 어느정도 시간이 지나면 나도 모르게 회수되어 돌고 있지 않는 인스턴스를 보면서 슬픔을 느끼고 있었다.

Spot Instance notification

AWS 는 스팟 인스턴스가 종료되어도 기본적으로 메일이나 sms 로 노티를 주지 않지만 Watchdog 같은 느낌인 CloudwatchAmazon SNS(Simple Notification Service)등을 사용하면 노티를 받을 수 있었다.

그런데 나의 경우는 단순 노티 뿐만 아니라 아래 조건을 만족해야 했었다

  • Spot Instance 회수직전 EC2 Spot Instance Interruption Warning 노티를 보내줘야 한다.
  • Spot Instance 회수후 자동으로 추가 작업을 해야 해줘야한다.(외부 api 호출 등)
  • 당시 사용했던 Region 이 5개(아시아, 미국, 유럽 등)정도가 되기 때문에 각 리전마다 cloudwatch, sns 등의 중복 설정을 해줘야 하는게 귀찮고 나중에 수정사항이 생기면 하나씩 또 작업해야 하는걸 피해야 해서 최대한 중복 설정을 줄여야 했다.

그래서 여기저기 검색을 해보니 일반적으로 하는 방법은 아래와 같았다.
1. Spot Instance가 죽기 2분전에 warning 이벤트를 발생시킬 수 있는 Cloudwatch를 사용한다.
2. 1의 이벤트를 수신할 수 있는 Simple Notification Service 를 이용하여 SMS, Email, Lambda, 외부 API 로 전송한다.

나의 경우 Telegram 으로 메세지를 받아야했고 동시에 외부 API를 호출하고 약간의 추가 작업을 해줘야 상황이었다.

그러므로 위 단계에서 AWS Lambda 까지 사용하기로 결정했다.

Cloudwatch + SNS + Lambda

그렇다면 각 5개의 리전에서 Cloudwatch 설정 5번, SNS 설정 5번 을 해줬어야 했는데 Lambda 까지 설정을 5번을 해줘야 하는건가..? 싶었다.

한번정도는 귀찮아도 되지만 혹시나 lambda 함수에 문제가 생기거나 업데이트 할때마다 5개 지역에 각각 배포를 하자니 배보다 배꼽이 커지는 작업같았다.

그러나 다행히도 SNS 에서는 이벤트를 수신하고 Lambda로 송신할때는 타 리전에 존재하는 Lambda 함수로 이벤트를 전송할 수 있음을 발견했다.

그러므로 Telegram 으로 노티를 쏴주는 것과 기타 작업을 해주는 내용의 코드는 서울리전의 Lambda 에 한번만 짜두면 된다.

그 후 각 5개의 리전의 SNS에서 보낸 이벤트의 수신처를 서울 Lambda 로 연결하면 된다!

세팅

우선 이곳에선 테스트로 EC2의 Spot instance termination/creation 관련 이벤트를 받는 기준으로 설명을 해보려고 한다.

Cloudwatch와 SNS는 eu-central-1 에서, Lambda는 ap-northeast-2 에서 생성하고 진행한다.

1. SNS Topic 생성

우선 SNS의 새로운 Topic 을 생성해야 한다.

위와같이 Standard 로 하나 만들어주자

*주의: Encryption의 Enable encryption 설정할 경우 작동하지 않을 수 있음. (이걸로 몇일넘게 삽질함..)

2. Cloudwatch 이벤트 설정


Cloudwatch 서비스에 들어가서 왼쪽에 Events의 Rules -> Create rule 선택

그 다음엔 Cloudwatch에서 EC2를 감시하면서 스팟인스턴스가 죽기 2분전에 이벤트를 감지하는 기능을 활성화 시키기 위해서 아래와 같이 생성한다. (Event pattern preview 옆에 Edit 을 누르면 위와같이 수정 가능)

{
  "source": [
    "aws.ec2"
  ],
  "detail-type": [
    "EC2 Spot Instance Request Fulfillment",
    "EC2 Spot Instance Interruption Warning"
  ]
}

다음은 이벤트를 수신할 Targets 를 설정해야 하는데, 이곳에서 Lambda 로 보낼 수 있지만 같은 리전의 Lambda 함수로만 이벤트 전송이 가능하므로 지금 같이 여러 리전의 이벤트를 받기엔 부적절하다

그러므로 Target은 1번에서 생성한 SNS Topic 으로 한다.

다음으로 넘기면 이름을 설정하라고 하는데 test_cloudwatch_rule 로 이름을 설정했다.

3. Lambda 함수 생성

이번엔 서울리전에서 새로운 Lambda 함수를 제작한다.

적절하게 함수이름과 런타임을 설정해서 Create function 을 누르자.

4. SNS Subscription 설정

다시 SNS로 돌아와서 이제는 SNS가 가져오는 이벤트를 받을 대상을 설정해줘야 한다.

Subscription 탭으로 들어와서 방금 만든 Lambda 함수가 이 이벤트를 수신해야 하니 관련된 설정을 해보자.

Topic ARN은 방금 만든 SNS 로 설정해주고 Protocol 은 AWS Lambda 로, Endpoint 는 방금 서울리전에서 만든 test_lambda_func를 endpoint 로 설정해준다.

5. 확인

아까 만든 Lambda 함수로 돌아와서 보면 SNS 가 트리거로 이어져 있는것을 확인할 수 있다.

그리고 이제 본인의 코드를 짜서 돌리면된다.

나는 간단하게 결과를 확인하기 위해서 날라오는 event 를 노티로 보내주는 코드를 짜서 돌려봤다.

성공!

후기

사실 간단해 보이긴 하지만 SNS의 설정 중 Server side encryption 설정때문에 많은 삽질을 했다..

저 옵션 때문에 어디선가 이벤트가 전달이 되지 않는 문제를 겪었고 로깅을 열심히 해보면서 나름 디버깅을 해봤으나 실패했다.

그래서 결국 의심되는 옵션을 꺼보니 작동 했다.

혹시 저 SNS의 암호화 설정 관련해서 코멘트가 가능하신분은 댓글로 남겨주시면 감사하겠습니다.

profile
보안과 개발을 둘다 잘 하고 싶은 사람입니다.

0개의 댓글