AWS Lambda를 사용하여 Amazon RDS 중지 및 시작

 

일반적인 개발 환경에서 개발 및 테스트 데이터베이스는 일과시간(9:00 ~ 18:00) 동안 사용되며 사용하지 않을 때는 유휴 상태입니다. 그러나 이 유휴 시간 동안에도 컴퓨팅 및 스토리지에 대한 비용이 청구됩니다.

 

비용을 줄이기 위해 Amazon RDS 인스턴스를 일시적으로 중지 할 수 있습니다 . 인스턴스가 중지된 동안에는 스토리지 및 백업에 대한 요금만 부과되며 DB 인스턴스 시간에 대한 요금은 부과되지 않습니다. 중지된 인스턴스는 7일 후에 자동으로 시작됩니다.

본 문서는 컴퓨팅 비용을 절감하기 위해 유휴 데이터베이스를 중지 및 시작하도록 AWS Lambda 및 Amazon EventBridge를 사용하는 방법을 설명합니다.

 

솔루션 개요

AWS Lambda는 서버를 관리하지 않고도 코드를 실행할 수 있는 컴퓨팅 서비스입니다. 

Amazon EventBridge는 이벤트를 생성하고 이에 대한 응답으로 특정 작업을 할당할 수 있는 간단한 규칙을 사용합니다.

Amazon RDS 인스턴스에 대한 컴퓨팅 비용은 시간 단위로 청구됩니다. 

오랜 시간 동안 유휴 상태로 유지되는 개발 또는 테스트 환경에서 프로비저닝된 데이터베이스 인스턴스는 Lambda 함수 및 EventBridge 규칙을 사용하여 야간에 자동으로 중지되고 업무 시간 전에 시작될 수 있습니다.

 

다음 그림은 Lambda함수와 EvnentBridge를 이용한 RDS 자동 중지/기동 아키텍처입니다.

전체적인 구성 단계

  1. 리소스를 프로비저닝
    • RDS 인스턴스용 태그(태그를 이용한 시작/중지 Lambda)
    • Lambda에 대한 AWS Identity and Access Management(IAM) 정책 및 역할
    • 데이터베이스를 중지하고 시작하는 두 개의 Lambda 함수
  2. Lambda 함수를 트리거하는 Amazon EventBridge 규칙을 생성

 

전제 조건

다음이 필요합니다.

  • Amazon RDS에 대한 관리자 액세스 권한이 있는 AWS 계정
  • RDS 인스턴스

 

리소스 프로비저닝

태그, Lambda에 대한 IAM 정책 및 역할, 데이터베이스를 중지하거나 시작하도록 예약하는 Lambda 함수를 생성하는 방법을 설명합니다.

태그 만들기

DB 인스턴스 생성 시 또는 인스턴스 생성 후 인스턴스에 태그를 할당할 수 있습니다. 아래와 같이 태그를 할당합니다.

  1. Amazon RDS 콘솔에서 태그를 추가할 데이터베이스와 해당 데이터베이스 내의 인스턴스를 선택
  2. 인스턴스 세부 정보 아래의 태그 탭에서 태그 추가를 선택
  3. 태그 키 DEV-TEST에 를 입력
  4.  에 Auto-Shutdown를 입력
  5. 추가를 선택

데이터베이스를 중지하는 Lambda 함수 생성

데이터베이스를 중지하고 시작하기 위해 호출할 수 있는 두 개의 Lambda 함수를 생성합니다. 

먼저 중지 기능을 하는 Lambda 함수를 생성합니다.

  1. Lambda 콘솔의 탐색 창에서 함수를 선택합니다.
  2. 함수 만들기를 선택합니다 .
  3. 함수 이름 에 stoprds를 입력합니다 .
  4. 런타임 에서 Python 3.10을 선택합니다 .
  5. 실행 역할 에서 기존 역할 사용을 선택합니다 .
  6. 기존 역할 에서 생성한 역할을 선택합니다( rdsLambda).
  7. 함수 만들기를 선택합니다 .
  8. 함수 세부 정보 페이지에서 함수 코드로 이동합니다.
  9. 샘플 코드를 삭제하고 다음을 입력합니다.
    # this Code will help to schedule stop the RDS databasrs using Lambda
    # Yesh 
    # Version -- 2.0
    
    import boto3
    import os
    import sys
    import time
    from datetime import datetime, timezone
    from time import gmtime, strftime
    
    def shut_rds_all():
        region=os.environ['REGION']
        key=os.environ['KEY']
        value=os.environ['VALUE']
    
        
        client = boto3.client('rds', region_name=region)
        response = client.describe_db_instances()
        v_readReplica=[]
        for i in response['DBInstances']:
            readReplica=i['ReadReplicaDBInstanceIdentifiers']
            v_readReplica.extend(readReplica)
        
        for i in response['DBInstances']:
    #The if condition below filters aurora clusters from single instance databases as boto3 commands defer to stop the aurora clusters.
            if i['Engine'] not in ['aurora-mysql','aurora-postgresql']:
    #The if condition below filters Read replicas.
                if i['DBInstanceIdentifier'] not in v_readReplica and len(i['ReadReplicaDBInstanceIdentifiers']) == 0:
                    arn=i['DBInstanceArn']
                    resp2=client.list_tags_for_resource(ResourceName=arn)
    #check if the RDS instance is part of the Auto-Shutdown group.
                    if 0==len(resp2['TagList']):
                        print('DB Instance {0} is not part of autoshutdown'.format(i['DBInstanceIdentifier']))
                    else:
                        for tag in resp2['TagList']:
    #If the tags match, then stop the instances by validating the current status.
                            if tag['Key']==key and tag['Value']==value:
                                if i['DBInstanceStatus'] == 'available':
                                    client.stop_db_instance(DBInstanceIdentifier = i['DBInstanceIdentifier'])
                                    print('stopping DB instance {0}'.format(i['DBInstanceIdentifier']))
                                elif i['DBInstanceStatus'] == 'stopped':
                                    print('DB Instance {0} is already stopped'.format(i['DBInstanceIdentifier']))
                                elif i['DBInstanceStatus']=='starting':
                                    print('DB Instance {0} is in starting state. Please stop the cluster after starting is complete'.format(i['DBInstanceIdentifier']))
                                elif i['DBInstanceStatus']=='stopping':
                                    print('DB Instance {0} is already in stopping state.'.format(i['DBInstanceIdentifier']))
                            elif tag['Key']!=key and tag['Value']!=value:
                                print('DB instance {0} is not part of autoshutdown'.format(i['DBInstanceIdentifier']))
                            elif len(tag['Key']) == 0 or len(tag['Value']) == 0:
                                print('DB Instance {0} is not part of auroShutdown'.format(i['DBInstanceIdentifier']))
                elif i['DBInstanceIdentifier'] in v_readReplica:
                    print('DB Instance {0} is a Read Replica. Cannot shutdown a Read Replica instance'.format(i['DBInstanceIdentifier']))
                else:
                    print('DB Instance {0} has a read replica. Cannot shutdown a database with Read Replica'.format(i['DBInstanceIdentifier']))
    
        response=client.describe_db_clusters()
        for i in response['DBClusters']:
            cluarn=i['DBClusterArn']
            resp2=client.list_tags_for_resource(ResourceName=cluarn)
            if 0==len(resp2['TagList']):
                print('DB Cluster {0} is not part of autoshutdown'.format(i['DBClusterIdentifier']))
            else:
                for tag in resp2['TagList']:
                    if tag['Key']==key and tag['Value']==value:
                        if i['Status'] == 'available':
                            client.stop_db_cluster(DBClusterIdentifier=i['DBClusterIdentifier'])
                            print('stopping DB cluster {0}'.format(i['DBClusterIdentifier']))
                        elif i['Status'] == 'stopped':
                            print('DB Cluster {0} is already stopped'.format(i['DBClusterIdentifier']))
                        elif i['Status']=='starting':
                            print('DB Cluster {0} is in starting state. Please stop the cluster after starting is complete'.format(i['DBClusterIdentifier']))
                        elif i['Status']=='stopping':
                            print('DB Cluster {0} is already in stopping state.'.format(i['DBClusterIdentifier']))
                    elif tag['Key'] != key and tag['Value'] != value:
                        print('DB Cluster {0} is not part of autoshutdown'.format(i['DBClusterIdentifier']))
                    else:
                        print('DB Instance {0} is not part of auroShutdown'.format(i['DBClusterIdentifier']))
    
    def lambda_handler(event, context):
        shut_rds_all()
  10. 저장을 선택합니다 .
    위의 람다 함수는 환경 변수로 전달되는 3개의 매개변수(REGION, KEY, VALUE)가 필요합니다. REGION은 RDS 인스턴스가 현재 실행 중인 위치이고 KEY 및 VALUE는 이전 단계에서 자동 종료가 필요한 인스턴스에 대해 연결한 태그입니다. RDS 인스턴스에 연결한 값은 환경 변수와 정확히 일치해야 합니다. 아래 단계에 따라 입력하십시오.
  11. '구성' 탭으로 이동하여 '환경 변수'를 선택합니다. EDIT를 클릭하고 아래와 같이 환경 변수를 추가합니다.
  12. 테스트를 선택하여 기능을 테스트합니다.

    테스트 이벤트 구성 페이지가 열립니다.
  13. 이벤트 이름 에 를 입력합니다 stop.
  14. 생성을 선택합니다 .
  15. 테스트를 다시 선택하여 기능을 테스트합니다.

테스트는 함수에 지정된 태그로 데이터베이스를 중지합니다. 함수 세부 정보 페이지에서 함수가 성공한 것을 확인할 수 있습니다.

데이터베이스를 시작하는 Lambda 함수 생성

동일한 단계를 반복하여 라는 시작 함수를 만듭니다 rdsstart. 다음 코드를 사용하십시오.

# this Code will help to schedule start the RDS databasrs using Lambda
# Yesh 
# Version -- 2.0

import boto3
import os
import sys
import time
from datetime import datetime, timezone
from time import gmtime, strftime

def start_rds_all():
    region=os.environ['REGION']
    key=os.environ['KEY']
    value=os.environ['VALUE']
    client = boto3.client('rds', region_name=region)
    response = client.describe_db_instances()

    v_readReplica=[]
    for i in response['DBInstances']:
        readReplica=i['ReadReplicaDBInstanceIdentifiers']
        v_readReplica.extend(readReplica)
    
    for i in response['DBInstances']:
#The if condition below filters aurora clusters from single instance databases as boto3 commands defer to start the aurora clusters.
        if i['Engine'] not in ['aurora-mysql','aurora-postgresql']:
#The if condition below filters Read replicas.
            if i['DBInstanceIdentifier'] not in v_readReplica and len(i['ReadReplicaDBInstanceIdentifiers']) == 0:
                arn=i['DBInstanceArn']
                resp2=client.list_tags_for_resource(ResourceName=arn)
#check if the RDS instance is part of the Auto-Shutdown group.
                if 0==len(resp2['TagList']):
                    print('DB Instance {0} is not part of autoshutdown'.format(i['DBInstanceIdentifier']))
                else:
                    for tag in resp2['TagList']:
                        if tag['Key']==key and tag['Value']==value:
                            if i['DBInstanceStatus'] == 'available':
                                print('{0} DB instance is already available'.format(i['DBInstanceIdentifier']))
                            elif i['DBInstanceStatus'] == 'stopped':
                                client.start_db_instance(DBInstanceIdentifier = i['DBInstanceIdentifier'])
                                print('Started DB Instance {0}'.format(i['DBInstanceIdentifier']))
                            elif i['DBInstanceStatus']=='starting':
                                print('DB Instance {0} is already in starting state'.format(i['DBInstanceIdentifier']))
                            elif i['DBInstanceStatus']=='stopping':
                                print('DB Instance {0} is in stopping state. Please wait before starting'.format(i['DBInstanceIdentifier']))
                        elif tag['Key']!=key and tag['Value']!=value:
                            print('DB instance {0} is not part of autoshutdown'.format(i['DBInstanceIdentifier']))
                        elif len(tag['Key']) == 0 or len(tag['Value']) == 0:
                            print('DB Instance {0} is not part of autoShutdown'.format(i['DBInstanceIdentifier']))
            elif i['DBInstanceIdentifier'] in v_readReplica:
                print('DB Instance {0} is a Read Replica.'.format(i['DBInstanceIdentifier']))
            else:
                print('DB Instance {0} has a read replica. Cannot shutdown & start a database with Read Replica'.format(i['DBInstanceIdentifier']))

    response=client.describe_db_clusters()
    for i in response['DBClusters']:
        cluarn=i['DBClusterArn']
        resp2=client.list_tags_for_resource(ResourceName=cluarn)
        if 0==len(resp2['TagList']):
            print('DB Cluster {0} is not part of autoshutdown'.format(i['DBClusterIdentifier']))
        else:
            for tag in resp2['TagList']:
                if tag['Key']==key and tag['Value']==value:
                    if i['Status'] == 'available':
                        print('{0} DB Cluster is already available'.format(i['DBClusterIdentifier']))
                    elif i['Status'] == 'stopped':
                        client.start_db_cluster(DBClusterIdentifier=i['DBClusterIdentifier'])
                        print('Started Cluster {0}'.format(i['DBClusterIdentifier']))
                    elif i['Status']=='starting':
                        print('cluster {0} is already in starting state.'.format(i['DBClusterIdentifier']))
                    elif i['Status']=='stopping':
                        print('cluster {0} is in stopping state. Please wait before starting'.format(i['DBClusterIdentifier']))
                elif tag['Key'] != key and tag['Value'] != value:
                    print('DB Cluster {0} is not part of autoshutdown'.format(i['DBClusterIdentifier']))
                else:
                    print('DB Instance {0} is not part of autoShutdown'.format(i['DBClusterIdentifier']))

def lambda_handler(event, context):
    start_rds_all()

위의 람다 함수는 환경 변수로 전달되는 3개의 매개변수(REGION, KEY, VALUE)가 필요합니다. REGION은 RDS 인스턴스가 현재 실행 중인 위치이고 KEY 및 VALUE는 이전 단계에서 자동 종료가 필요한 인스턴스에 대해 연결한 태그입니다. RDS 인스턴스에 연결한 값은 환경 변수와 정확히 일치해야 합니다. 아래 단계에 따라 입력하십시오.

'구성' 탭으로 이동하여 '환경 변수'를 선택합니다. EDIT를 클릭하고 아래와 같이 환경 변수를 추가합니다.

함수 테스트는 다음 출력을 제공해야 합니다.

두 기능을 모두 생성하고 테스트한 후 필요에 따라 이러한 기능을 트리거하는 EventBridge 규칙을 생성할 수 있습니다.

Amazon EventBridge 규칙 생성

Amazon EventBridge 규칙은 태그가 지정된 데이터베이스를 중지하거나 시작하기 위해 생성한 함수를 트리거합니다. 이 게시물에서는 일정에 따라 트리거하도록 구성합니다.

  1. EventBridge 콘솔의 탐색 창에 있는 Events 에서 Rules 를 선택합니다 . 규칙 만들기 섹션 에서 아래와 같이 규칙의 이름을 정의합니다.
  2. Define Pattern 섹션 에서 Schedule을 선택하고 Cron 표현식을 클릭한 후 0 23 ? * MON-FRI *아래와 같이 Enter를 누르십시오(이 cron 표현식은 월요일부터 금요일까지 GMT 오후 11시에 데이터베이스를 중지합니다).
  3. 이벤트 버스 선택 섹션 에서 AWS 기본 이벤트 버스를 선택하고 선택한 이벤트 버스에서 규칙을 활성화합니다(기본적으로 활성화됨).
  4. 대상 선택 섹션 에서 첫 번째 드롭다운 상자에서 Lambda 함수를 선택합니다 .
  5. Function 에서 생성한 정지 함수( stoprds)를 선택합니다.
  6. 입력한 세부 정보를 검토하고 페이지 하단에서 만들기를 선택하여 규칙을 만듭니다.
    EventBridge 규칙은 이제 stoprds예약된 시간에 Lambda 함수를 트리거합니다.
  7. rdsstart이 단계를 반복하여 원하는 예약 시간에 Lambda 함수를 트리거하는 규칙을 생성합니다 .

요약

이 게시물에서는 사용하지 않을 때 개발 및 테스트 환경에서 RDS 데이터베이스를 중지하고 시작하도록 Lambda 함수를 예약하는 방법을 보여주었습니다. Lambda를 사용하여 RDS DB 인스턴스의 시작 및 종료를 자동화함으로써 조직은 컴퓨팅 비용을 더욱 절감하고 지속적으로 실행할 필요가 없는 데이터베이스 환경의 관리를 간소화할 수 있습니다.

이 솔루션을 시도하고 AWS Lambda  및  Amazon Relational Database Service 사용의 모든 이점을 활용하는 것이 좋습니다 . AWS Systems Manager를 사용하여 Amazon RDS 중지 및 시작을 수행할 수도 있습니다. 블로그 게시물 " AWS Systems Manager를 사용하여 Amazon RDS 중지 및 시작 예약 "을 확인하십시오.

질문이나 요청 사항이 있으면 언제든지 댓글로 문의해 주세요.

 

 

+ Recent posts