[AWS] - Lambda를 활용한 EC2 스케줄링

오동훈·2024년 9월 18일
0

AWS Cloud

목록 보기
8/8
post-thumbnail

우선 클라우드 환경을 이용하다보면 많은 비용 부담으로 인해 고민이 많으실 겁니다! 저 또한 비용 절감 방안을 고민하다가 이 글을 작성하게 되었습니다. 특히 개발 및 테스트 환경처럼 비정기적으로 사용되는 EC2 인스턴스는 실행 상태에 따라 요금이 부과되므로, 불필요하게 계속 실행 중인 인스턴스가 있다면 그만큼 비용이 낭비됩니다🥹

이러한 문제를 해결하기 위해 AWS Lambda와 EventBridge를 활용해 EC2 인스턴스를 자동으로 시작하거나 중지하는 스케줄링 작업을 구현해 보았습니다. 이 블로그에서는 Lambda + EventBridge + EC2를 활용한 EC2 인스턴스 자동 스케줄링 방법을 단계별로 설명하겠습니다.

1. EC2 생성

1). EC2 생성

우선 스케줄링 할 대상인 EC2 인스턴스가 존재해야겠죠~?
만약 존재한다면 2번 절차로 넘어가 주셔도 됩니다.

우선 EC2 생성하는 방법에 대해서는 자세하게 설명하지 않겠습니다!
인스턴스를 생성해 준 이후 2번 EC2 태그 설정 단계로 넘어가 줍니다.

2). EC2 태그 설정

기존 및 신규로 생성한 인스턴스를 클릭하면 태그를 설정할 수 있는 옵션이 있습니다.

태그 > 태그 관리 > key, value 설정 후 저장

해당 옵션에 원하는 key, value로 이름을 설정해 주면 되는데, 저는 Key, Value의 값을 각각 Schedule, true로 값을 설정해 주었습니다.
나중에 Lambda 함수가 이 태그를 기반으로 인스턴스의 상태를 제어하게 됩니다.

2. Lambda 생성

이제 구현할 Lambda 함수는 boto3를 사용해 EC2 인스턴스를 제어할 예정입니다.
아래의 코드는 특정 태그가 있는 인스턴스를 조회하고, 해당 인스턴스의 상태에 따라 시작 또는 중지하는 로직을 수행하도록 구현했습니다.

1). Lambda 코드 추가

Lambda 생성 후 아래 코드를 넣어주고 실행해 줍니다.

원래 Lambda의 경우 라이브러리는 별도의 패키징 후 import가 필요한데, AWS에서 사용되는 boto3, botocore의 라이브러리는 기본 탑재되어 있어 별도 패키징이 필요없다고 나와있더라구요!

import boto3

region = 'ap-northeast-2'
tag = {'Schedule': 'true'}
ec2 = boto3.client('ec2', region_name=region)


def get_instances_to_toggle():
    try:
        response = ec2.describe_instances(Filters=[
            {'Name': f'tag:{key}', 'Values': [value]} for key, value in tag.items()
        ])
        return [
            {'InstanceId': instance['InstanceId'],
             'State': instance['State']['Name']}
            for reservation in response.get('Reservations', [])
            for instance in reservation.get('Instances', [])
        ]
    except Exception as e:
        print(f"Error getting instances: {e}")
        return []


def toggle_instance_state(instance):
    instance_id = instance['InstanceId']
    current_state = instance['State']

    try:
        if current_state == 'stopped':
            ec2.start_instances(InstanceIds=[instance_id])
            print(f'Successfully started instance: {instance_id}')
        elif current_state == 'running':
            ec2.stop_instances(InstanceIds=[instance_id])
            print(f'Successfully stopped instance: {instance_id}')
        else:
            print(f"Invalid instance state for {instance_id}: {current_state}")
    except Exception as e:
        print(f"Error toggling instance state for {instance_id}: {e}")


def lambda_handler(event, context):
    try:
        instances_to_toggle = get_instances_to_toggle()
        for instance in instances_to_toggle:
            toggle_instance_state(instance)
    except Exception as e:
        print(f"Lambda execution error: {e}")

짜잔~ 그러면 당연히 오류가 발생하셨을 것 같은데요!

Lambda 실행 중 UnauthorizedOperation 오류가 발생하면, IAM 역할에 적절한 권한이 부여되지 않은 경우일 수 있습니다.
이럴 때는 Lambda 함수가 태그를 읽고, 인스턴스를 시작, 종료할 수 있는 describeInstances, StartInstances, StopInstances 권한을 추가해 주면 됩니다.

START RequestId: a60768fb-86af-4a05-a389-97397e230d53 Version: $LATEST
Error getting instances: An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation. User: arn:aws:sts::905418424719:assumed-role/schedule-test-role-xab1vcnz/schedule-test is not authorized to perform: ec2:DescribeInstances because no identity-based policy allows the ec2:DescribeInstances action
END RequestId: a60768fb-86af-4a05-a389-97397e230d53
REPORT RequestId: a60768fb-86af-4a05-a389-97397e230d53	Duration: 707.21 ms	Billed Duration: 708 ms	Memory Size: 128 MB	Max Memory Used: 87 MB	Init Duration: 501.81 ms

2). Lambda 권한 부여

Lambda 함수가 태그를 읽고, 인스턴스를 시작, 종료할 수 있는 권한을 주도록 해보겠습니다.

IAM > 역할(Roles) > {lambda 이름}-role-{random 값} / AWS 서비스: lambda

첫 번째 방법 - EC2FullAccess 권한 부여하기

우선 포괄적으로 권한을 부여하는 방법입니다. 이렇게 권한을 추가하게 되면 손쉽게 사용이 가능하지만, 추후에 실수 시 서비스 오류까지 이를 수 있고 보안상 취약해 질 수도 있어 작은 부분부터 습관을 마련해 두는게 좋을 것 같습니다.

그렇지만 우선 방법은 알고 있으면 좋으니 소개해 드리겠습니다.

권한 추가 - 정책 연결 > AmazonEC2FullAccess 추가 > 권한 추가

EC2FullAccess 권한을 부여하면 다음과 같이 Lambda 함수에 대해 권한 추가가 되고, EC2 관련한 모든 기능을 사용할 수 있습니다.

두 번째 방법 - 필요한 권한만 부여하기

필요한 권한만 부여하며 사용하는 것이 가장 권장되는 방법입니다.
저희는 EC2 태그를 읽고, 인스턴스 종료 및 시작하는 권한만 있으면 되니 설정해 보도록 하겠습니다.

필요한 권한
1. 인스턴스 정보 모니터링 - describeInstances
2. 인스턴스 시작 - StartInstances
3. 인스턴스 종료 - StopInstances

위에 적어놓은 3가지 권한을 추가한 상황입니다. 블로그 작성하면서 확인한 내용인데 EC2 관련해서 643개의 권한이 있다는 게 너무 놀랍지 않나요?!!
그 중에 3가지 권한만 필요한 상황인데, 이전에 EC2FullAccess 권한을 부여하면 그만큼 실수나 보안에 취약해 질 수 있겠죠~?

Lambda 테스트

권한까지 부여가 완료되었다면 이전에 구성해 놓은 Lambda를 실행해 볼 차례입니다.

인스턴스가 종료되어 있는 상태에서 실행하니 다음과 같이 정상적으로 실행되는 것을 확인할 수 있습니다.

또한 정상적으로 실행된 상태에서 Lambda를 실행 시 정상적으로 종료되는 것을 확인할 수 있습니다.

3. EventBridge 설정

  1. EventBridge 적용할 Lambda 함수 > 구성 > 트리거 > 트리거 추가
  2. EventBridge 선택 > 새 규칙 생성 > 예약 표현식 cron(0 9,18 * * ? *) 설정

cron(0 9,18 * * ? *) 표현식은 매일 오전 9시와 오후 6시에 Lambda 함수를 트리거하여 EC2 인스턴스를 시작하거나 중지하도록 설정합니다.
일반 직장 기준으로 설정해 보았습니다ㅎㅎ 각자 이용하시는 환경에 따라 변경해 주시면 됩니다.


이처럼 AWS LambdaEventBridge를 활용한 EC2 인스턴스 스케줄링은 비용을 절감하는 데 큰 도움이 됩니다.
특히 사용 빈도가 낮은 개발 또는 테스트 환경에서 유용할 것이라고 생각합니다 !!
여러분도 이 방법을 통해 불필요한 인스턴스 비용을 절약해 보세요~!!

profile
삽질의 기록들🐥

0개의 댓글