Lambda 함수와 CloudWatch를 이용하여 Daily 백업 및 삭제
 
- 추가사항
백업 대상
백업 주기
백업 시간
보관 주기
삭제 대상
삭제 주기
삭제 시간
기타
실행중인 EC2 Instances AMI, Volumes
매일
당일 새벽 5시
7일
생성 후 7일이 지난 AMI, Snapshots
매일
당일 새벽 6시
 
 
- 순서는 다음과 같습니다.
    1. IAM 설정
    2. Lambda 함수작성 및 테스트
    3. CloudWatch 설정

 

 
 

IAM 설정

: AMI와 볼륨의 Snapshot을 위하여 EC2 Access를 허용하는 정책을 설정하여 줍니다.
 
1. 서비스에서 IAM을 클릭후 대쉬보드로 들어간 뒤 [역할] , [역할 만들기]를 차례대로 클릭하여 줍니다.
 
 
 
 
 
2. Lambda를 이용하여 자동백업, 삭제를 시행할 것이기에 [Lambda]서비스를 클릭한 후 아래의 [다음]을 누릅니다.
 
3.  다음 페이지의 [정책생성] 버튼을 클릭, 그 후 JSON탭을 누르면 보이는 기존의 코드를 지운 후  다음과 같은 코드를 넣어줍니다.
 
# IAM Role 
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:*"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": "ec2:*",
            "Resource": "*"
        }
    ]
}
 
 
4.  아래에 나오는 [정책검토] 버튼을 클릭한 후 설정값에 맞는 이름을 넣어 주고 아래의 [정책생성] 버튼을 클릭합니다.
 
5.  다시 처음의 IAM 대쉬보드에서 [역할], [역할만들기]를 재 클릭 해줍니다.
 
6.  정책에 만들어주었던 이름을 검색하여 선택한 후 아래의 [다음]버튼을 클릭합니다.
 
7. 역할 이름을 지정하여 줍니다. (태그 값은 지정하지 않고 넘어갔습니다.) 그 후 아래의 [역할 만들기] 버튼을 눌러줍니다.
 
 
8.  기존 검색창 리스트에 방금 만들어 논 역할이 보여지게 됩니다.
 
 
 
 

Lambda 함수 작성 및 테스트

: 사전에 실행 중 이던 EC2 인스턴스에 태그를 지정해 주어서 태그 값이 지정된 인스턴스만 백업이 진행되도록 하였습니다.
(Key : tag-key , Value : Backup) 
 
 
1. 서비스의 Lambda 대쉬보드에 들어가 [함수생성] 버튼을 클릭하여 줍니다.
 
 
2.  이름을 지정 한 후 처음에 지정해주었던 IAM역할을 선택해 줍니다. 그 후 [함수생성] 버튼을 클릭합니다.
: Python 2.7 이용
 
 
3.  기존의 Default코드를 삭제한 후 다음과 같은 코드를 넣고 아래의 기본설정 제한시간을 1분으로 조정해 줍니다.
 
# Automated AMI Backups
#
# @author Robert Kozora <bobby@kozora.me>
#
# This script will search for all instances having a tag with "Backup" or "backup"
# on it. As soon as we have the instances list, we loop through each instance
# and create an AMI of it. Also, it will look for a "Retention" tag key which
# will be used as a retention policy number in days. If there is no tag with
# that name, it will use a 7 days default value for each AMI.
#
# After creating the AMI it creates a "DeleteOn" tag on the AMI indicating when
# it will be deleted using the Retention value and another Lambda function
import boto3
import collections
import datetime
import sys
import pprint
ec = boto3.client('ec2',region_name='ap-northeast-2')
#image = ec.Image('id')
def lambda_handler(event, context):
     
    reservations = ec.describe_instances(
        Filters=[
            {'Name': 'tag-key', 'Values': ['backup', 'Backup']},
        ]
    ).get(
        'Reservations', []
    )
    instances = sum(
        [
            [i for i in r['Instances']]
            for r in reservations
        ], [])
    print "Found %d instances that need backing up" % len(instances)
    to_tag = collections.defaultdict(list)
    for instance in instances:
        try:
            retention_days = [
                int(t.get('Value')) for t in instance['Tags']
                if t['Key'] == 'Retention'][0]
        except IndexError:
            retention_days = 7
        #for dev in instance['BlockDeviceMappings']:
        #    if dev.get('Ebs', None) is None:
        #        continue
        #    vol_id = dev['Ebs']['VolumeId']
        #    print "Found EBS volume %s on instance %s" % (
        #        vol_id, instance['InstanceId'])
            #snap = ec.create_snapshot(
            #    VolumeId=vol_id,
            #)
             
            #create_image(instance_id, name, description=None, no_reboot=False, block_device_mapping=None, dry_run=False)
            # DryRun, InstanceId, Name, Description, NoReboot, BlockDeviceMappings
            create_time = datetime.datetime.now()
            create_fmt = create_time.strftime('%Y-%m-%d')
         
            AMIid = ec.create_image(InstanceId=instance['InstanceId'], Name="Lambda - " + instance['InstanceId'] + " from " + create_fmt, Description="Lambda created AMI of instance " + instance['InstanceId'] + " from " + create_fmt, NoReboot=True, DryRun=False)
             
            pprint.pprint(instance)
            #sys.exit()
            #break
         
            #to_tag[retention_days].append(AMIid)
             
            to_tag[retention_days].append(AMIid['ImageId'])
             
            print "Retaining AMI %s of instance %s for %d days" % (
                AMIid['ImageId'],
                instance['InstanceId'],
                retention_days,
            )
    print to_tag.keys()
     
    for retention_days in to_tag.keys():
        delete_date = datetime.date.today() + datetime.timedelta(days=retention_days)
        delete_fmt = delete_date.strftime('%m-%d-%Y')
        print "Will delete %d AMIs on %s" % (len(to_tag[retention_days]), delete_fmt)
         
        #break
     
        ec.create_tags(
            Resources=to_tag[retention_days],
            Tags=[
                {'Key': 'DeleteOn', 'Value': delete_fmt},
            ]
        )
 
 
4.  상단에 [저장] 버튼을 누른 후 옆에 테스트 버튼을 눌러 테스트 파일을 만들고 테스트 버튼을 한번 더 눌러 실행시켜줍니다.
 
5.  EC2 대시보드로 가서 AMI와 볼륨의 Snapshot이 제대로 작동되는지 확인하여 줍니다
 
 

CloudWatch 설정

: 지정된 시간에 함수를 트리거 하기 위하여 CloudWatch를 이용합니다.
 
 
1.  서비스항목에서 CloudWatch를 클릭하여 옆 대시보드에 [규칙] , [규칙생성]을 차례대로 클릭하여 줍니다.
 
 
2.  지정된 시간에 함수를 시행할 것이므로 [일정]탭으로 들어 간 후 [Cron식]으로 매일 새벽 5시에 백업시간을 지정해 줍니다. 그 후 옆에 연결할 Lambda함수를 좀전에 작성하였던 Backup함수를 지정해 줍니다.
: Cron식은 GMT시간으로 계산되기때문에 한국시간에서 -9시간을 해주면 됩니다. 
 
Cron 식
00 20 ? * MON-SUN *
 
 
3.  아래에 [세부 정보 구성] 버튼을 클릭한 후 설정할 CloudWatch의 이름과 설명을 기술한 후 [규칙 생성] 버튼을 클릭합니다.
 
 
 
 
 

Scheduled Delete
 
주기적으로 삭제하는 방법도  Lambda함수코드와 Cron식만 달라지고 2,3번 항목에 대해선 동일한 방법으로 진행됩니다.
 
 
# Automated AMI and Snapshot Deletion
#
# @author Robert Kozora <bobby@kozora.me>
#
# This script will search for all instances having a tag with "Backup" or "backup"
# on it. As soon as we have the instances list, we loop through each instance
# and reference the AMIs of that instance. We check that the latest daily backup
# succeeded then we store every image that's reached its DeleteOn tag's date for
# deletion. We then loop through the AMIs, deregister them and remove all the
# snapshots associated with that AMI.
import boto3
import collections
import datetime
import time
import sys
ec = boto3.client('ec2', 'ap-northeast-2')
ec2 = boto3.resource('ec2', 'ap-northeast-2')
images = ec2.images.filter(Owners=["self"])
def lambda_handler(event, context):
    reservations = ec.describe_instances(
        Filters=[
            {'Name': 'tag-key', 'Values': ['backup', 'Backup']},
        ]
    ).get(
        'Reservations', []
    )
    instances = sum(
        [
            [i for i in r['Instances']]
            for r in reservations
        ], [])
    print "Found %d instances that need evaluated" % len(instances)
    to_tag = collections.defaultdict(list)
    date = datetime.datetime.now()
    date_fmt = date.strftime('%Y-%m-%d')
    imagesList = []
    # Set to true once we confirm we have a backup taken today
    backupSuccess = False
    # Loop through all of our instances with a tag named "Backup"
    for instance in instances:
    imagecount = 0
        # Loop through each image of our current instance
        for image in images:
            # Our other Lambda Function names its AMIs Lambda - i-instancenumber.
            # We now know these images are auto created
            if image.name.startswith('Lambda - ' + instance['InstanceId']):
                # print "FOUND IMAGE " + image.id + " FOR INSTANCE " + instance['InstanceId']
                # Count this image's occcurance
            imagecount = imagecount + 1
                try:
                    if image.tags is not None:
                        deletion_date = [
                            t.get('Value') for t in image.tags
                            if t['Key'] == 'DeleteOn'][0]
                        delete_date = time.strptime(deletion_date, "%m-%d-%Y")
                except IndexError:
                    deletion_date = False
                    delete_date = False
                today_time = datetime.datetime.now().strftime('%m-%d-%Y')
                # today_fmt = today_time.strftime('%m-%d-%Y')
                today_date = time.strptime(today_time, '%m-%d-%Y')
                # If image's DeleteOn date is less than or equal to today,
                # add this image to our list of images to process later
                if delete_date <= today_date:
                    imagesList.append(image.id)
                # Make sure we have an AMI from today and mark backupSuccess as true
                if image.name.endswith(date_fmt):
                    # Our latest backup from our other Lambda Function succeeded
                    backupSuccess = True
                    print "Latest backup from " + date_fmt + " was a success"
        print "instance " + instance['InstanceId'] + " has " + str(imagecount) + " AMIs"
    print "============="
    print "About to process the following AMIs:"
    print imagesList
    if backupSuccess == True:
        myAccount = boto3.client('sts').get_caller_identity()['Account']
        snapshots = ec.describe_snapshots(MaxResults=1000, OwnerIds=[myAccount])['Snapshots']
        # loop through list of image IDs
        for image in imagesList:
            print "deregistering image %s" % image
            amiResponse = ec.deregister_image(
                DryRun=False,
                ImageId=image,
            )
            for snapshot in snapshots:
                if snapshot['Description'].find(image) > 0:
                    snap = ec.delete_snapshot(SnapshotId=snapshot['SnapshotId'])
                    print "Deleting snapshot " + snapshot['SnapshotId']
                    print "-------------"
    else:
        print "No current backup found. Termination suspended."
 
CleanUp Cron 식
00 21 ? * MON-SUN *
 
 
 

+ Recent posts