이두잉의 AWS 세상

AWS EBS AMI 백업

2018.09.21 14:09 - leedoing leedoing

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
try:
        import boto3
        import json
        import os
        from collections import OrderedDict
        from datetime import date, timedelta, datetime
        from botocore.exceptions import ClientError
        from collections import OrderedDict
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()
cs



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


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


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


그러나 아마... 언젠가... Image life cycle도 나올 듯?