본문 바로가기

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 Option은 존재하지 않는다.

일해라! 아마존!

 

2. EC2 Image 백업 방법

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

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

 

ec2List.json: AMI로 지정할 EC2 ID(ec2Id), AMI 저장 이름(name), 리부팅 옵션(noReboot), 만료일(expire)를 json 파일 생성

Lambda_main.py: ec2List.json에 담긴 List를 토대로 EC2 AMI를 백업

 

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 기간이 지나면 삭제

자동 백업된 AMI

백업이 실행된 후 결과 정보

Created Image List: [
{
    "ec2Id": "i-0b4947ddfd80496ee",
    "name": "web",
    "noReboot": true,
    "expire": "7",
    "createdDate": "2018-09-22",
    "imageId": "ami-0c04ec5874f1a4977"
},
	...
{
    "ec2Id": "i-023ed8c9d4b97b14a",
    "name": "app",
    "noReboot": true,
    "expire": "1",
    "createdDate": "2018-09-22",
    "imageId": "ami-00a96d7622d7d3b29"
}
]

Failed Image List: [
{
    "ec2Id": "i-0b4947ddfd80496ee",
    "name": "web",
    "noReboot": true,
    "expire": "7",
    "createdDate": "2018-09-22",
    "imageId": null
},
	...
{
    "ec2Id": "i-023ed8c9d4b97b14a",
    "name": "app",
    "noReboot": true,
    "expire": "1",
    "createdDate": "2018-09-22",
    "imageId": null
}
]

Deleted Image List: [
{
    "Architecture": "x86_64",
    "CreationDate": "2018-09-21T05:04:28.000Z",
    "ImageId": "ami-0dffad17281f6ec89",
    "ImageLocation": "557652101750/app_2018-09-21",
    "ImageType": "machine",
     ...
    "SriovNetSupport": "simple",
    "Tags": [
        {
            "Key": "expire",
            "Value": "1"
        },
        {
            "Key": "backupManager",
            "Value": "auto"
        },
        {
            "Key": "Name",
            "Value": "app_2018-09-21"
        },
        {
            "Key": "createdDate",
            "Value": "2018-09-21"
        },
        {
            "Key": "ec2Id",
            "Value": "i-023ed8c9d4b97b14a"
        },
        {
            "Key": "imageId",
            "Value": "ami-0dffad17281f6ec89"
        }
    ],
    "VirtualizationType": "hvm"
}
]

 

결과 정보를 AWS SNS를 사용하여 이메일로도 받을 수 있음!

자동 백업 결과

 

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

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

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