Рубрики
Без рубрики

Автоматизация управления AWS EC2 с помощью Python и Boto3

Автор оригинала: Adam McQuistan.

Вступление

В этой статье я продемонстрирую использование Python вместе с Boto3 Amazon Web Services (AWS) Software Development Kit (SDK), который позволяет людям, хорошо разбирающимся в программировании на Python, использовать сложные API AWS REST для управления своими облачными ресурсами. Из-за обширности AWS REST API и связанных с ним облачных сервисов я сосредоточусь только на сервисе AWS Elastic Cloud Compute (EC2).

Вот темы, которые я буду освещать:

  • Запуск экземпляра EC2
  • Остановка экземпляра EC2
  • Завершение экземпляра EC2
  • Резервное копирование экземпляра EC2 путем создания образа
  • Создание экземпляра EC2 из образа
  • Планирование резервного копирования и очистки с помощью cron на сервере и AWS Lambda

Зависимости и настройка среды

Для начала мне нужно будет создать пользователя в моей учетной записи AWS, который имеет программный доступ к REST API. Для простоты я предоставлю этому пользователю права администратора, но, пожалуйста, обратите внимание, что это только для простоты создания этого учебника. Если вы следуете этому примеру, вам следует ознакомиться с политиками ИТ-безопасности вашей организации, прежде чем использовать этого пользователя в производственной среде.

Шаг 1: В моей консоли AWS я должен перейти в раздел IAM в меню services, затем нажать ссылку Users и, наконец, нажать кнопку Add user, которая приведет меня к экрану, показанному ниже. На этом экране я даю пользователю имя “boto3-user” и ставлю галочку в поле Programmatic access перед нажатием кнопки next.

Шаг 2: На экране разрешений я нажимаю плитку Прикрепить существующие политики напрямую , а затем устанавливаю флажок AdministratorAccess перед нажатием кнопки далее, как показано ниже.

Шаг 3: Нажмите кнопку далее, так как я не добавляю никаких дополнительных тегов.

Шаг 4: Я просматриваю пользователя, который будет создан, а затем нажимаю Create user .

Шаг 5: Наконец, я загружаю учетные данные в виде CSV-файла и сохраняю их.

Далее мне нужно установить необходимые библиотеки Python 3 локально в виртуальной среде, например:

$ python -m venv venv
$ source venv/bin/activate
(venv)$ pip install boto3 pprint awscli

Наконец, я настраиваю учетные данные для библиотеки boto3 с помощью библиотеки aws cli , не забывая добавить учетные данные для ключа доступа и Секретного ключа, которые я загрузил на шаге 5 выше.

$ aws configure
AWS Access Key ID [****************3XRQ]: **************
AWS Secret Access Key [****************UKjF]: ****************
Default region name [None]:
Default output format [None]:

Создание и работа с экземпляром EC2

В этом разделе я расскажу о том, как создать сеанс boto3 для конкретного региона AWS, а также создать экземпляр клиента EC2 с помощью активного объекта сеанса. Затем, используя этот клиент EC2 boto3, я буду взаимодействовать с экземплярами EC2 этого региона, управляя запуском, завершением работы и завершением работы.

Чтобы создать экземпляр EC2 для этой статьи, я выполняю следующие действия:

Шаг 1: Я нажимаю ссылку EC2 в меню Служб, чтобы открыть панель мониторинга EC2, а затем нажимаю кнопку Запустить экземпляр в середине экрана.

Шаг 2: На странице Выбор образа Amazon Machine Image (AMI) я нажимаю кнопку Select рядом с AMI Amazon Linux.

Шаг 3: Примите тип экземпляра t2.micro по умолчанию и нажмите кнопку Обзор и запуск .

Шаг 4: На странице обзора я разворачиваю раздел Теги и нажимаю Edit Tags , чтобы добавить теги для Name и BackUp , затем снова нажимаю Launch Review и Launch , чтобы вернуться на страницу обзора, прежде чем, наконец, нажать кнопку Launch для запуска экземпляра.

Теперь у меня есть запущенный экземпляр EC2, как показано ниже.

Сеанс Boto3 и клиент

Наконец-то я могу начать писать код! Я начинаю с создания пустого файла, модуля Python, называемого awsutils.py и вверху я импортирую библиотеку boto3 , а затем определяю функцию, которая создаст специфичный для региона Сеанс объект.

# awsutils

import boto3

def get_session(region):
    return boto3.session.Session(region_name=region)

Если я запускаю свой интерпретатор Python и импортирую модуль, только что созданный выше, я могу использовать новую функцию get_session для создания сеанса в том же регионе, что и мой экземпляр EC2, а затем создать из него экземпляр объекта EC2.Client , например:

>>> import awsutils
>>> session = awsutils.get_session('us-east-1')
>>> client = session.client('ec2')

Затем я могу использовать этот клиентский объект EC2, чтобы получить подробное описание экземпляра с помощью pprint , чтобы немного облегчить просмотр выходных данных вызова describe_instances на объекте client .

>>> import pprint
>>> pprint.pprint(client.describe_instances())
...

Я опускаю вывод, так как он довольно многословен, но знаю, что он содержит словарь с записью Reservations , которая представляет собой список данных, описывающих экземпляры EC2 в этом регионе, и ResponseMetadata о запросе, который только что был сделан к AWS REST API.

Получение сведений об экземпляре EC2

Я также могу использовать этот же метод describe_instances вместе с параметром Filter для фильтрации выбора по значениям тегов. Например, если я хочу получить свой недавно созданный экземпляр с тегом Name со значением ‘demo-instance’, это будет выглядеть следующим образом:

>>> demo = client.describe_instances(Filters=[{'Name': 'tag:Name', 'Values': ['demo-instance']}])
>>> pprint.pprint(demo)
...

Есть много способов отфильтровать выходные данные describe_instances , и я отсылаю вас к официальным документам для получения более подробной информации.

Запуск и остановка экземпляра EC2

Чтобы остановить демо-экземпляр, я использую метод stop_instances объекта client , который я ранее создал, предоставив ему идентификатор экземпляра в качестве параметра списка одной записи аргумента InstanceIds , как показано ниже:

>>> instance_id = demo['Reservations'][0]['Instances'][0]['InstanceId']
>>> instance_id
'i-0c462c48bc396bdbb'
>>> pprint.pprint(client.stop_instances(InstanceIds=[instance_id]))
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '579',
                                      'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 19:26:30 GMT',
                                      'server': 'AmazonEC2'},
                      'HTTPStatusCode': 200,
                      'RequestId': 'e04a4a64-74e4-442f-8293-261f2ca9433d',
                      'RetryAttempts': 0},
 'StoppingInstances': [{'CurrentState': {'Code': 64, 'Name': 'stopping'},
                        'InstanceId': 'i-0c462c48bc396bdbb',
                        'PreviousState': {'Code': 16, 'Name': 'running'}}]

Вывод последней команды указывает на то, что вызов метода останавливает экземпляр. Если я повторно извлеку демо-экземпляр и напечатаю Состояние Теперь я вижу, что она остановилась.

>>> demo = client.describe_instances(Filters=[{'Name': 'tag:Name', 'Values': ['demo-instance']}])
>>> demo['Reservations'][0]['Instances'][0]['State']
{'Code': 80, 'Name': 'stopped'}

Чтобы запустить тот же экземпляр обратно, существует дополнительный метод под названием start_instances , который работает аналогично методу stop_instances , который я продемонстрирую далее.

>>> pprint.pprint(client.start_instances(InstanceIds=[instance_id]))
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '579',
                                      'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 19:37:02 GMT',
                                      'server': 'AmazonEC2'},
                      'HTTPStatusCode': 200,
                      'RequestId': '21c65902-6665-4137-9023-43ac89f731d9',
                      'RetryAttempts': 0},
 'StartingInstances': [{'CurrentState': {'Code': 0, 'Name': 'pending'},
                        'InstanceId': 'i-0c462c48bc396bdbb',
                        'PreviousState': {'Code': 80, 'Name': 'stopped'}}]}

Непосредственный вывод команды заключается в том, что она находится в ожидании запуска. Теперь, когда я повторно выбираю экземпляр и печатаю его состояние, он показывает, что он снова работает.

>>> demo = client.describe_instances(Filters=[{'Name': 'tag:Name', 'Values': ['demo-instance']}])
>>> demo['Reservations'][0]['Instances'][0]['State']
{'Code': 16, 'Name': 'running'}

Альтернативный подход к обучению, запуску и остановке

В дополнение к классу EC2.Client , с которым я работал до сих пор, существует также класс EC2.Instance , который полезен в таких случаях, как этот, когда мне нужно иметь дело только с одним экземпляром за раз.

Ниже я использую ранее сгенерированный объект session для получения объекта ресурса EC2, который затем могу использовать для извлечения и создания экземпляра объекта Instance для моего демо-экземпляра.

>>> ec2 = session.resource('ec2')
>>> instance = ec2.Instance(instance_id)

На мой взгляд, основное преимущество использования класса Instance заключается в том, что вы работаете с реальными объектами вместо словарного представления экземпляра в момент времени, но вы теряете возможность выполнять действия сразу с несколькими экземплярами, которые предоставляет класс EC2.Client .

Например, чтобы увидеть состояние демо-экземпляра, который я только что создал выше, это так просто:

>>> instance.state
{'Code': 16, 'Name': 'running'}

Класс Instance имеет много полезных методов, два из которых – start и stop , которые я буду использовать для запуска и остановки своих экземпляров, например:

>>> pprint.pprint(instance.stop())
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '579',
                                      'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 19:58:25 GMT',
                                      'server': 'AmazonEC2'},
                      'HTTPStatusCode': 200,
                      'RequestId': 'a2f76028-cbd2-4727-be3e-ae832b12e1ff',
                      'RetryAttempts': 0},
 'StoppingInstances': [{'CurrentState': {'Code': 64, 'Name': 'stopping'},
                        'InstanceId': 'i-0c462c48bc396bdbb',
                        'PreviousState': {'Code': 16, 'Name': 'running'}}]}

Подождав около минуты, чтобы он полностью остановился… Затем я снова проверяю состояние:

>>> instance.state
{'Code': 80, 'Name': 'stopped'}

Теперь я могу начать все сначала.

>>> pprint.pprint(instance.start())
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '579',
                                      'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 20:01:01 GMT',
                                      'server': 'AmazonEC2'},
                      'HTTPStatusCode': 200,
                      'RequestId': '3cfc6061-5d64-4e52-9961-5eb2fefab2d8',
                      'RetryAttempts': 0},
 'StartingInstances': [{'CurrentState': {'Code': 0, 'Name': 'pending'},
                        'InstanceId': 'i-0c462c48bc396bdbb',
                        'PreviousState': {'Code': 80, 'Name': 'stopped'}}]}

Затем через некоторое время снова проверьте состояние…

>>> instance.state
{'Code': 16, 'Name': 'running'}

Создание резервного образа экземпляра EC2.

Важной темой в управлении серверами является создание резервных копий для восстановления в случае повреждения сервера. В этом разделе я продемонстрирую, как создать резервную копию Amazon Machine Image (AMI) моего демо-экземпляра, который AWS затем сохранит в своем простом сервисе хранения данных (S3). Это позже может быть использовано для воссоздания этого экземпляра EC2, точно так же, как я использовал начальный AMI для создания демо-экземпляра.

Для начала я покажу, как использовать класс EC2.Client и его метод create_image для создания образа AMI демо-экземпляра, указав идентификатор экземпляра и описательное имя для экземпляра.

>>> import datetime
>>> date = datetime.datetime.utcnow().strftime('%Y%m%d')
>>> date
'20181221'
>>> name = f"InstanceID_{instance_id}_Image_Backup_{date}"
>>> name
'InstanceID_i-0c462c48bc396bdbb_Image_Backup_20181221'
>>> name = f"InstanceID_{instance_id}_Backup_Image_{date}"
>>> name
'InstanceID_i-0c462c48bc396bdbb_Backup_Image_20181221'
>>> pprint.pprint(client.create_image(InstanceId=instance_id, Name=name))
{'ImageId': 'ami-00d7c04e2b3b28e2d',
 'ResponseMetadata': {'HTTPHeaders': {'content-length': '242',
                                      'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 20:13:55 GMT',
                                      'server': 'AmazonEC2'},
                      'HTTPStatusCode': 200,
                      'RequestId': '7ccccb1e-91ff-4753-8fc4-b27cf43bb8cf',
                      'RetryAttempts': 0}}

Аналогично, я могу использовать метод Instance class create_image для выполнения той же задачи, которая возвращает экземпляр EC2.Image класс, похожий на класс EC2.Instance .

>>> image = instance.create_image(Name=name + '_2')

Маркировка изображений и экземпляров EC2

Очень мощной, но чрезвычайно простой функцией экземпляров EC2 и изображений AMI является возможность добавления пользовательских тегов. Добавлять теги можно как через консоль управления AWS, как я показал при создании демо-экземпляра с именем тегов и резервной копией, так и программно с помощью boto3 и AWS REST API.

Поскольку у меня есть объект EC2.Instance , все еще плавающий в памяти в моем интерпретаторе Python, я буду использовать его для отображения тегов демо-экземпляра.

>>> instance.tags
[{'Key': 'BackUp', 'Value': ''}, {'Key': 'Name', 'Value': 'demo-instance'}]

И EC2.Instance , и EC2.Классы Image имеют идентично функционирующий набор методов create_tags для добавления тегов к представленным им ресурсам. Ниже я продемонстрирую добавление тега Remove к ранее созданному изображению, который связан с датой, в которую он должен быть удален. Используемый формат даты – “ГГГГ ММ ДД”.

>>> image.create_tags(Tags=[{'Key': 'RemoveOn', 'Value': remove_on}])
[ec2.Tag(resource_id='ami-081c72fa60c8e2d58', key='RemoveOn', value='20181222')]

Опять же, то же самое можно сделать с классом EC2.Client , предоставив список идентификаторов ресурсов, но с клиентом вы можете пометить как изображения, так и экземпляры EC2 одновременно, если хотите, указав их идентификаторы в параметре Resource функции create_tags , например:

>>> pprint.pprint(client.create_tags(Resources=['ami-00d7c04e2b3b28e2d'], Tags=[{'Key': 'RemoveOn', 'Value': remove_on}]))
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '221',
                                      'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 20:52:39 GMT',
                                      'server': 'AmazonEC2'},
                      'HTTPStatusCode': 200,
                      'RequestId': '645b733a-138c-42a1-9966-5c2eb0ca3ba3',
                      'RetryAttempts': 0}}

Создание экземпляра EC2 из резервного образа

Я хотел бы начать этот раздел с того, чтобы дать вам пищу для размышлений. Поставьте себя в неудобное положение системного администратора или, что еще хуже, разработчика, притворяющегося системным администратором, потому что продукт, над которым они работают, не имеет его (предостережение… это я), и один из ваших серверов EC2 был поврежден.

Иик! Его время схватки… теперь вам нужно выяснить, какой тип ОС, размер и службы были запущены на неработающем сервере… покопайтесь в настройке и установке базового сервера, а также любых приложений, которые принадлежат ему, и молитесь, чтобы все вышло правильно.

Фу! Сделай вдох и остынь, потому что я собираюсь показать тебе, как быстро встать и бежать, плюс… предупреждение о спойлере… В конце я собираюсь превратить эти одноразовые команды интерпретатора Python в работоспособный набор сценариев, чтобы вы могли их далее модифицировать и использовать.

Ладно, с этим умственным упражнением покончено, позвольте мне вернуться к работе. Чтобы создать экземпляр EC2 из идентификатора образа, я использую метод EC2.Client class run_instances и указываю количество запускаемых экземпляров и тип запускаемого экземпляра.

>>> pprint.pprint(client.run_instances(ImageId='ami-081c72fa60c8e2d58', MinCount=1, MaxCount=1, InstanceType='t2.micro'))
...

Я снова опускаю вывод из-за его многословия. Пожалуйста, ознакомьтесь с официальными документами для метода run_instances , так как существует множество параметров на выбор, чтобы точно настроить способ запуска экземпляра.

Удаление Резервных Копий Образов

В идеале я бы делал резервные копии образов с довольно частым интервалом (то есть, по крайней мере, ежедневно), и вместе со всеми этими резервными копиями приходили бы три вещи, одна из которых довольно хороша, а две другие несколько проблематичны. С хорошей стороны, я делаю снимки известных состояний моего сервера EC2, что дает мне момент времени, чтобы вернуться к нему, если дела пойдут плохо. Однако с другой стороны, я создаю беспорядок в своих ведрах S3 и накапливаю заряды с каждой дополнительной резервной копией, которую я помещаю в хранилище.

Способ смягчить негативные последствия беспорядка и растущих расходов на хранение-это удалить резервные копии изображений по истечении заданного периода времени, и именно там меня спасут теги, созданные мной ранее. Я могу запросить свои резервные образы EC2 и найти те, которые имеют определенный тег Remove On, а затем удалить их.

Я могу начать с использования метода describe_images в экземпляре класса EC2.Client вместе с фильтром для тега “Remove”, чтобы получить все изображения, которые я пометил для удаления в заданную дату.

>>> remove_on = '201812022'
>>> images = client.describe_images(Filters=[{'Name': 'tag:RemoveOn', 'Values': [remove_on]}])

Далее я перебираю все изображения и вызываю клиентский метод deregister_image передавая ему идентификатор итеративного изображения, и вуаля – больше никакого изображения.

>>> remove_on = '201812022'
>>> for img in images['Images']:
...     client.deregister_image(ImageId=img['ImageId'])

Завершение экземпляра EC2

Итак, рассмотрев запуск, остановку, создание и удаление резервных образов, а также запуск экземпляра EC2 из резервного образа, я приближаюсь к концу этого урока. Теперь все, что осталось сделать, это очистить мои демо-экземпляры, вызвав EC2.Client class terminate_instances и передав идентификаторы экземпляров для завершения. Опять же, я буду использовать describe_instances с фильтром для имени демо-экземпляра, чтобы получить его подробную информацию и захватить его идентификатор экземпляра. Затем я могу использовать его с terminate_instances , чтобы избавиться от него навсегда.

Примечание : Да, это навсегда, так что будьте очень осторожны с этим методом.

>>> demo = client.describe_instances(Filters=[{'Name': 'tag:Name', 'Values': ['demo-instance']}])
>>> pprint.pprint(client.terminate_instances(InstanceIds=[instance_id]))
{'ResponseMetadata': {'HTTPHeaders': {'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 22:14:20 GMT',
                                      'server': 'AmazonEC2',
                                      'transfer-encoding': 'chunked',
                                      'vary': 'Accept-Encoding'},
                      'HTTPStatusCode': 200,
                      'RequestId': '78881a08-0240-47df-b502-61a706bfb3ab',
                      'RetryAttempts': 0},
 'TerminatingInstances': [{'CurrentState': {'Code': 32,
                                            'Name': 'shutting-down'},
                           'InstanceId': 'i-0c462c48bc396bdbb',
                           'PreviousState': {'Code': 16, 'Name': 'running'}}]}

Стягивание вещей вместе для сценария автоматизации

Теперь, когда я прошел через эти функциональные возможности, выдавая команды одну за другой с помощью интерпретатора оболочки Python (что я настоятельно рекомендую читателям сделать хотя бы один раз самостоятельно, чтобы поэкспериментировать с вещами) Я соберу все вместе в два отдельных сценария под названием ec2backup.py и amicleanup.py.

В ec2backup.py скрипт просто запросит все доступные экземпляры EC2, имеющие резервную копию тега, а затем создаст резервный образ AMI для каждого из них, пометив их тегом RemoveOn со значением 3 дня в будущем.

# ec2backup.py

from datetime import datetime, timedelta
import awsutils

def backup(region_id='us-east-1'):
    '''This method searches for all EC2 instances with a tag of BackUp
       and creates a backup images of them then tags the images with a
       RemoveOn tag of a YYYYMMDD value of three UTC days from now
    '''
    created_on = datetime.utcnow().strftime('%Y%m%d')
    remove_on = (datetime.utcnow() + timedelta(days=3)).strftime('%Y%m%d')
    session = awsutils.get_session(region_id)
    client = session.client('ec2')
    resource = session.resource('ec2')
    reservations = client.describe_instances(Filters=[{'Name': 'tag-key', 'Values': ['BackUp']}])
    for reservation in reservations['Reservations']:
        for instance_description in reservation['Instances']:
            instance_id = instance_description['InstanceId']
            name = f"InstanceId({instance_id})_CreatedOn({created_on})_RemoveOn({remove_on})"
            print(f"Creating Backup: {name}")
            image_description = client.create_image(InstanceId=instance_id, Name=name)
            images.append(image_description['ImageId'])
            image = resource.Image(image_description['ImageId'])
            image.create_tags(Tags=[{'Key': 'RemoveOn', 'Value': remove_on}, {'Key': 'Name', 'Value': name}])

if __name__ == '__main__':
    backup()

Следующий шаг-это amicleanup.py скрипт, который запрашивает все изображения AMI, имеющие Удаление по налогу, равное дате дня, на который он был запущен, в форме “YYYYMMDD” и удаляет их.

# amicleanup.py

from datetime import datetime
import awsutils

def cleanup(region_id='us-east-1'):
    '''This method searches for all AMI images with a tag of RemoveOn
       and a value of YYYYMMDD of the day its ran on then removes it
    '''
    today = datetime.utcnow().strftime('%Y%m%d')
    session = awsutils.get_session(region_id)
    client = session.client('ec2')
    resource = session.resource('ec2')
    images = client.describe_images(Filters=[{'Name': 'tag:RemoveOn', 'Values': [today]}])
    for image_data in images['Images']:
        image = resource.Image(image_data['ImageId'])
        name_tag = [tag['Value'] for tag in image.tags if tag['Key'] == 'Name']
        if name_tag:
            print(f"Deregistering {name_tag[0]}")
        image.deregister()

if __name__ == '__main__':
    cleanup()

Реализация Cron

Относительно простым способом реализации функциональности этих двух сценариев было бы запланировать две задачи cron на сервере Linux для их запуска. В приведенном ниже примере я настроил задачу cron для запуска каждый день в 11 вечера, чтобы выполнить ec2backup.py сценарий, а затем еще один в 11:30 вечера, чтобы выполнить amicleanup.py сценарий.

0 23 * * * /path/to/venv/bin/python /path/to/ec2backup.py
30 23 * * * /path/to/venv/bin/python /path/to/amicleanup.py

Реализация AWS Lambda

Более элегантным решением является использование AWS Lambda для запуска этих двух функций в виде набора функций. Есть много преимуществ в использовании AWS Lambda для запуска кода, но для этого случая использования нескольких функций Python для создания и удаления резервных образов наиболее подходящими являются высокая доступность и отсутствие необходимости платить за простаивающие ресурсы. Оба эти преимущества лучше всего реализуются при сравнении использования Лямбды с запуском двух заданий cron, описанных в последнем разделе.

Если бы я настроил свои два задания cron для запуска на существующем сервере, то что произойдет, если этот сервер выйдет из строя? У меня не только болит голова от необходимости создавать резервную копию этого сервера, но и есть вероятность пропустить запланированный запуск заданий cron, контролирующих процесс резервного копирования и очистки сервера EC2. Это не проблема с AWS Lambda, поскольку она разработана с избыточностью, чтобы гарантировать чрезвычайно высокую доступность.

Другое основное преимущество отсутствия необходимости платить за простаивающие ресурсы лучше всего понять в примере, где я, возможно, развернул экземпляр только для того, чтобы управлять этими двумя сценариями, работающими один раз в день. Этот метод не только подпадает под потенциальный недостаток доступности последнего элемента, но и вся виртуальная машина теперь подготовлена для запуска двух сценариев один раз в день, что составляет очень небольшое количество вычислительного времени и много потраченных впустую ресурсов, простаивающих без дела. Это отличный пример использования AWS Lambda для повышения операционной эффективности.

Еще одна оперативная эффективность, возникающая в результате использования Лямбды, – это отсутствие необходимости тратить время на обслуживание выделенного сервера.

Чтобы создать функцию AWS Lambda для резервного копирования образа экземпляра EC2, выполните следующие действия:

Шаг 1. В меню Сервис выберите пункт Lambda в разделе Компьютер.

Шаг 2. Нажмите кнопку Создать функцию .

Шаг 3. Выберите опцию Author from scratch , введите “ec2 backup” в качестве имени функции, выберите Python 3.6 из параметров среды выполнения, затем добавьте boto3-user для роли и нажмите Create Function , как показано ниже:

Шаг 4. В конструкторе выберите CloudWatch Events и добавьте задание cron cron(0 11 * ? * *) что приведет к тому, что функция будет работать каждый день в 11 вечера.

Шаг 5. В редакторе кода добавьте следующий код:

import boto3
import os
from datetime import datetime, timedelta

def get_session(region, access_id, secret_key):
    return boto3.session.Session(region_name=region,
                                aws_access_key_id=access_id,
                                aws_secret_access_key=secret_key)

def lambda_handler(event, context):
    '''This method searches for all EC2 instances with a tag of BackUp
       and creates a backup images of them then tags the images with a
       RemoveOn tag of a YYYYMMDD value of three UTC days from now
    '''
    created_on = datetime.utcnow().strftime('%Y%m%d')
    remove_on = (datetime.utcnow() + timedelta(days=3)).strftime('%Y%m%d')
    session = get_session(os.getenv('REGION'),
                          os.getenv('ACCESS_KEY_ID'),
                          os.getenv('SECRET_KEY'))
    client = session.client('ec2')
    resource = session.resource('ec2')
    reservations = client.describe_instances(Filters=[{'Name': 'tag-key', 'Values': ['BackUp']}])
    for reservation in reservations['Reservations']:
        for instance_description in reservation['Instances']:
            instance_id = instance_description['InstanceId']
            name = f"InstanceId({instance_id})_CreatedOn({created_on})_RemoveOn({remove_on})"
            print(f"Creating Backup: {name}")
            image_description = client.create_image(InstanceId=instance_id, Name=name)
            image = resource.Image(image_description['ImageId'])
            image.create_tags(Tags=[{'Key': 'RemoveOn', 'Value': remove_on}, {'Key': 'Name', 'Value': name}])

Шаг 6. В разделе под редактором кода добавьте несколько переменных окружения.

  • РЕГИОН со значением региона экземпляров EC2 для резервного копирования, который в данном примере равен us-east-1
  • ACCESS_KEY_ID со значением ключа доступа из раздела, где был настроен boto3-пользователь
  • SECRET_KEY со значением секретного ключа из раздела, где был настроен boto3-пользователь

Шаг 7. Нажмите кнопку Сохранить в верхней части страницы.

Для функции очистки изображения выполните те же действия со следующими изменениями.

Шаг 3. Я даю ему название “amcleanup”

Шаг 4. Я использую немного другую временную конфигурацию cron(30 11 * ? * *) запуск в 11:30 вечера

Шаг 5. Используйте следующую функцию очистки:

import boto3
from datetime import datetime
import os

def get_session(region, access_id, secret_key):
    return boto3.session.Session(region_name=region,
                                aws_access_key_id=access_id,
                                aws_secret_access_key=secret_key)

def lambda_handler(event, context):
    '''This method searches for all AMI images with a tag of RemoveOn
       and a value of YYYYMMDD of the day its ran on then removes it
    '''
    today = datetime.utcnow().strftime('%Y%m%d')
    session = get_session(os.getenv('REGION'),
                          os.getenv('ACCESS_KEY_ID'),
                          os.getenv('SECRET_KEY'))
    client = session.client('ec2')
    resource = session.resource('ec2')
    images = client.describe_images(Filters=[{'Name': 'tag:RemoveOn', 'Values': [today]}])
    for image_data in images['Images']:
        image = resource.Image(image_data['ImageId'])
        name_tag = [tag['Value'] for tag in image.tags if tag['Key'] == 'Name']
        if name_tag:
            print(f"Deregistering {name_tag[0]}")
        image.deregister()

Вывод

В этой статье я рассказал о том, как использовать библиотеку AWS Python SDK Boto3 для взаимодействия с ресурсами EC2. Я демонстрирую, как автоматизировать задачи оперативного управления созданием резервных копий образов AMI для экземпляров EC2 и последующей очисткой этих резервных образов с помощью запланированных заданий cron либо на выделенном сервере, либо с помощью AWS Lambda.

Если вам интересно узнать, как использовать Boto и AWS Simple Storage Service (S3), ознакомьтесь со статьей Скотта Робинсона здесь, на StackAbuse.

Как всегда, спасибо за чтение и не стесняйтесь комментировать или критиковать ниже.