본문 바로가기

Compute/EBS

AWS EBS AMI 백업

1. EBS 백업 방법

1-1. Data Lifecycle Manager

 

 

EBS 태그를 기반 12 또는 24 시간 기준으로 EBS의 Snapshot을 수행한다. 또한 Snapshot의 갯수를 조절할 수 있다. (예시 최근 7개)

 

1-2. CloudWatch Event - EC2 CreateSnapshot API call

 

CloudWatch Event를 통해서도 Snapshot이 가능하다. 문제는 아직 Delete API는 구성되지 않음.

 

2. EC2 Image 백업 방법

아직 AWS에서 EC2 AMI에 대한 자동 백업 서비스는 없음.

그러나 CloudWatch Event -> Lambda(aws sdk)를 통해서 아래와 같이 구현은 가능.

 

ec2List.json: Image를 생성할 EC2와 AMI 이름, expire 기간 등 설정

Lambda_main.py: ec2List.json과 생성한 Image 태그를 기반으로 동작

 

ec2List.json

{
"list":[
{"ec2Id":"i-0b4947ddfd80496ee", "name":"web", "noReboot":true, "expire":"7"},
{"ec2Id":"i-08bafac12c7892301", "name":"db", "noReboot":true, "expire":"3"},
{"ec2Id":"i-023ed8c9d4b97b14a", "name":"app", "noReboot":true, "expire":"1"}
]
}

 

Lambda_main.py

try:
        import boto3
        import json
        import os
        from collections import OrderedDict
        from datetime import date, timedelta, datetime
        from botocore.exceptions import ClientError
except ImportError:
        HAS_BOTO = False
 
client = boto3.client('ec2')
sns = boto3.client('sns')
TODAY = date.today()
 
class Ec2List:
        def __init__(self, ec2Id = None, name = None, noReboot = None, expire = None, createdDate = None, imageId = None):
                self._ec2Id = ec2Id
                self._name = name
                self._noReboot = noReboot
                self._expire = expire
                self._createdDate = str(TODAY)
                self._imageId = imageId
 
        @classmethod
        def get_json_file(cls):
                result = []
                try:
                        with open('./ec2List.json', 'r') as f:
                                createImageList = json.load(f)
                        for each in createImageList['list']:
                                createAmiManager = cls(each['ec2Id'], each['name'], each['noReboot'], each['expire'], TODAY)
                                result.append(createAmiManager)
                        return result
                except IOError as e:
                        print('Exception - Failed EC2 List read file: {}'. format(e))
                        return None
 
        def get_ec2_list(self):
                return self
 
        @property
        def imageId(self):
                return self._imageId
 
        @imageId.setter
        def imageId(self, imageId):
                self._imageId = imageId
 
 
def create_amis(list):
    for each in list:
        ec2List = each.get_ec2_list()
        try:
            response = client.create_image(
                Description='auto-backup-image',
                DryRun=False,
                InstanceId=ec2List._ec2Id,
                Name=ec2List._name + '_' + ec2List._createdDate,
                NoReboot=ec2List._noReboot
            )
            ec2List._imageId = response['ImageId']
        except ClientError as e:
            print('Exceptioin - Failed Create Image: {}'.format(e))
        if ec2List._imageId != None:
            try:
                response = client.create_tags(
                    DryRun=False,
                    Resources=[
                        ec2List._imageId,
                    ],
                    Tags=[
                        {
                            'Key': 'Name',
                            'Value': ec2List._name + '_' + ec2List._createdDate
                        },
                        {
                            'Key': 'ec2Id',
                            'Value': ec2List._ec2Id
                        },
                        {
                            'Key': 'createdDate',
                            'Value': ec2List._createdDate
                        },
                        {
                            'Key': 'expire',
                            'Value': ec2List._expire
                        },
                        {
                            'Key': 'backupManager',
                            'Value': 'auto'
                        },
                        {'Key': 'imageId',
                         'Value': ec2List._imageId
 
                         }
                    ]
                )
            except ClientError as e:
                print('Exceptioin - Failed Create Tag: {}'.format(e))
    return None
 
def delete_amis():
        createdDate = None
        expire = None
        imageId = None
        convertDate = None
        deleteImageList = []
        try:
            response = client.describe_images(
                Filters=[
                    {
                        'Name': 'tag:backupManager',
                        'Values': [
                            'auto'
                        ]
                    }
                ],
                DryRun=False
            )
        except ClientError as e:
            print('Exceptioin - Failed Describe Tag: {}'.format(e))
 
        for each in response['Images']:
            for tags in each['Tags']:
                if tags['Key'] == 'createdDate':
                    createdDate = tags['Value']
                if tags['Key'] == 'expire':
                    expire = tags['Value']
                if tags['Key'] == 'imageId':
                    imageId = tags['Value']
            convertDate = datetime.strptime(createdDate, '%Y-%m-%d').date()
            compare = TODAY - convertDate
            if (int(compare.days) >= int(expire)):
                try:
                    client.deregister_image(
                        ImageId=imageId,
                        DryRun=False
                    )
                except ClientError as e:
                    print('Exception: Failed Delete AMI {}'.format(e))
                deleteImageList.append(each)
        return deleteImageList
 
def sendMail(ec2InstanceList, deletedImageList):
        createdImageList = []
        failedImageList = []
        for each in ec2InstanceList:
            ec2List = each.get_ec2_list()
            result = {
                'ec2Id': ec2List._ec2Id,
                'name': ec2List._name,
                'noReboot': ec2List._noReboot,
                'expire': ec2List._expire,
                'createdDate': str(TODAY),
                'imageId': ec2List._imageId
            }
            if ec2List._imageId != None:
                createdImageList.append(result)
            else:
                failedImageList.append(result)
 
        print('Created Image List: ' + json.dumps(createdImageList))
        print('failed Image List: ' + json.dumps(failedImageList))
        print('Deleted Image List: ' + json.dumps(deletedImageList))
        try:
            response = sns.publish(
                TopicArn='arn:aws:sns:ap-northeast-2:557652101750:ec2_ami_managing',
                Subject='EC2 AMI MANAGING',
                Message='Created Image LIST: ' + json.dumps(createdImageList) + '\n\n\n' +
                        'Failed Image LIST: ' + json.dumps(failedImageList) + '\n\n\n'
                                                                              'Deleted Image LIST: ' + json.dumps(
                    deletedImageList) + '\n\n\n'
            )
        except ClientError as e:
            print('Exception: Failed Send Message {}'.format(e))
 
def main():
    ec2list = Ec2List.get_json_file()
    create_amis(ec2list)
    delete_image_list = delete_amis()
    sendMail(ec2list, delete_image_list)
 
if __name__ == '__main__':
    main()
 
def handler(even, context):
    main()

 

아래와 같이 CloudWatch Event에 Target을 Lambda로

 

그럼 매일 한 번 Image가 아래와 같이 백업되고 expire 기간이 지나면 삭제

 

 

SNS을 사용하여 메일을 받을 수도 있음...

 

그러나 언젠가... AMI life cycle도 나올 듯?

'Compute > EBS' 카테고리의 다른 글

AWS EBS AMI 백업  (2) 2018.09.21
AWS EBS Daily Snapshot  (0) 2016.02.24
AWS EBS mount: unknown filesystem type ‘LVM2_member’ error  (0) 2016.01.28
AWS Elastic Block Store(EBS) 이해  (0) 2015.11.18