AWS 서비스 자동 종료 및 실행

석형원·2024년 8월 25일

project

목록 보기
7/11

[AWS Infra - Airflow] 자취를 시작하는 분들을 위한 원룸 추천 웹 서비스 제작

위 프로젝트를 진행하며 리소스 낭비를 줄이고자
Lambda함수를 사용해 AWS 서비스를 주기적으로 자동으로 종료하고 실행시키고자합니다.

AWS 리소스는 지속적으로 비용을 발생시키므로 필요할 때 ON/OFF하는 습관이 필요합니다.
따라서 사용하지 않는 시간 대에 리소스가 자동으로 OFF되도록 세팅을 하려했습니다.

주최 측에서 먼저 Cloudformation을 통해 매 특정 시간마다 ( 21:00, 1:00, 5:00, 9:00 )
실행중인 EC2 인스턴스, Redshift, RDS를 중단시키는 Lambda 함수를 호출하도록 설정을 해주었습니다.

주최 측에서 자동으로 종료시키는 세팅을 해주었으므로, 저는 자동으로 실행시키는 세팅만 해주면 되었습니다.

따라서, 자동으로 리소스들이 실행되도록 Cloudwatch를 event trigger로 설정하여
특정 Lambda 함수가 실행되도록 만들었습니다.

Lambda 함수 - EC2, RDS, Redshift 자동 종료

이 부분은 제가 작성한 것이 아니기에 일부분만 가져와보았습니다.

import boto3
import json
import os
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
  action = event.get('action')

  if action not in ['start', 'stop']:
      logger.error("Invalid event: 'action' must be 'pause' or 'resume'")
      return {
          'statusCode': 400,
          'body': json.dumps("Invalid event")
      }

  regions = os.environ['REGIONS'].split(',')
  response = []

  for region in regions:
      redshift = boto3.client('redshift', region_name=region)

      # Redshift 클러스터 목록 가져오기
      clusters = redshift.describe_clusters()
      cluster_ids = [cluster['ClusterIdentifier'] for cluster in clusters['Clusters'] if cluster['ClusterStatus'] not in ['deleting']]

      if action == 'stop':
          # 클러스터 일시 정지
          for cluster_id in cluster_ids:
              try:
                  redshift.pause_cluster(ClusterIdentifier=cluster_id)
                  logger.info(f'Region {region}: Paused Redshift cluster: {cluster_id}')
                  response.append(f'Region {region}: Paused Redshift cluster: {cluster_id}')
              except Exception as e:
                  logger.error(f'Error pausing Redshift cluster {cluster_id} in region {region}: {e}')

  return {
      'statusCode': 200,
      'body': json.dumps(response)
  }

Lambda 함수 - EC2, RDS, Redshift 자동 실행

  • EventBridge

    특정 시간에 lambda 함수를 Trigger 시키기 위해 EventBridge를 만들어주었습니다.

  • EC2 자동 실행
import boto3
import logging
# 중단되어 있던 특정 EC2를 실행하는 함수

# EventBridge를 통해 KST 기준 매일 09:20에 Trigger 되게 설정

# 필요한 IAM Policy
"""
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:StartInstances",
                "ec2:DescribeInstances",
                "ec2:DescribeInstanceStatus",
            ],
            "Resource": "*"
        }
    ]
}
"""

region = 'ap-northeast-2' 
instances = ['i-0c40c86aa77af7b9d', 'i-07d478a96d7af433c', 'i-00b8613d9f2e92a67']
ec2 = boto3.client('ec2', region_name=region)

def lambda_handler(event, context):
    try : 
        ec2.start_instances(InstanceIds=instances)
        print('start your instances: ' + str(instances))
    except Exception as e:
        logging.error(f"Failed to start EC2 instances: {str(e)}")
        return {'statusCode': 500, 'body': f'Failed to start EC2 instances: {str(e)}'}
  • RDS, Redshift 자동 실행
import json, boto3
import os
import time
import logging

# 중단되어있던 특정 RDS, Redshift를 실행하는 함수

# EventBridge를 통해 KST 기준 매일 09:10에 Trigger 되게 설정

# 필요한 IAM Policy
"""
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "rds:StartDBInstance",
                "rds:DescribeDBInstances",
                "redshift:ResumeCluster",
                "redshift:DescribeClusters"
            ],
            "Resource": "*"
        }
    ]
}
"""

def lambda_handler(event, context):
    rds = boto3.client('rds')
    redshift = boto3.client('redshift')
    
    rds_instances = ['ariel-1-airflow-metadb', 'ariel-1-production-db']
    redshift_clusters = ['team-ariel-1-redshift-cluster']
    
    try:
        # start RDS Instance
        for rds_instance in rds_instances:
            if rds.describe_db_instances(DBInstanceIdentifier=rds_instance)['DBInstances'][0]['DBInstanceStatus'] == 'stopped':
                rds.start_db_instance(DBInstanceIdentifier=rds_instance)
                print(f"Started RDS instance: {rds_instance}")
            else:
                continue
        
    except Exception as e:
        logging.error(f"Failed to start RDS instances: {str(e)}")
        return {'statusCode': 500, 'body': f'Failed to start RDS instances: {str(e)}'}
    
    try:
        # start Redshift Cluster
        for redshift_cluster in redshift_clusters:
            if redshift.describe_clusters(ClusterIdentifier=redshift_cluster)['Clusters'][0]['ClusterStatus'] == 'paused':
                redshift.resume_cluster(ClusterIdentifier=redshift_cluster)
                print(f"Resumed Redshift cluster: {redshift_cluster}")
            else:
                continue

    except Exception as e:
        logging.error(f"Failed to resume Redshift clusters: {str(e)}")
        return {'statusCode': 500, 'body': f'Failed to resume Redshift clusters: {str(e)}'}
    

    return {
        'statusCode': 200,
        'body': 'Instances started successfully'
    }
profile
데이터 엔지니어를 꿈꾸는 거북이, 한걸음 한걸음

0개의 댓글