Обработка изображений на AWS Lambda и API Gateway за 10 минут

Давайте попробуем создать программу на Python 3.6, которая будет переводить цветное фото в черно-белый формат и которая использует OpenCV. Причем мы будем реализовывать эту задачу через API, которое принимает данные бинарного типа и возвращает данные такого же типа (формата JPEG). При этом есть два важных условия, которым должно соответствовать решение этой задачи:

  1. Отсутствие готового сервера.
  2. Отсутствие оплаты (за исключением лишь тех случаев, когда трафик превышает бесплатный пакет Lambda).

Первые шаги

Сперва необходимо установить Docker. Для данного примера необходимо использовать сервис от AWS, который называется lamba, который позволяет легко развернуть нужную функцию и ее зависимости, а также быстро подключить ее к API. В свою очередь, для создания API нами будет использоваться решение, которое называется API Gateway. Это решение предоставляется AWS.

Чтобы упростить это руководство, мы развернем код, загрузим его в Lambda, используя веб-консоль AWS. Также мы напишем код функции внутри консоли AWS, чтобы максимально упростить процесс. Во всех остальных случаях необходимо выполнить развертывание, используя AWS CLI.

Итак, нам необходимо выполнить следующую последовательность действий.

  1. Начать со входа в консоль AWS, и осуществить поиск Lambda.
  2. Сделать клик по кнопке «Create function» (создать функцию). Обработка изображений на AWS Lambda и API Gateway за 10 минут
  3. Задаем настройки функции. Пусть наша функция будет называться lambda-demo. Нам необходимо удостовериться, что мы работаем с Python 3.6 в качестве среды выполнения и создать новую роль из шаблонов политики AWS. Обработка изображений на AWS Lambda и API Gateway за 10 минут
  4. После того, как функция будет создана, будет предоставлен определенный шаблон кода в консоли Lambda.
import json

 

def lambda_handler(event, context):

    return {

        'statusCode': 200,

        'body': json.dumps('Hello from Lambda!')

    }

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

После того, как будет создано тестовое событие, необходимо нажать Test. В логах функции должен быть получен такой результат. 

{

  "statusCode": 200,

  "body": "\"Hello from Lambda!\""

}

Превосходно. Теперь давайте попробуем создать что-то полезное. Например, построить функцию, которая будет получать изображение и делать его черно-белым. Для реализации этой задачи мы будем использовать OpenCV. Помимо этого, использование OpenCV может прекрасно подойти для этой задачи. Она показывает, как столь полезная библиотека может быть добавлена в Lambda с относительной легкостью.

Сейчас мы выполним следующие действия.

  1. Генерируем пакет Lambda для OpenCV, заточенный под Python. 
  2. Загружаем пакет в Lambda Layers, чтобы было возможно его использование в какой-угодно функции, которая создается.
  3. Осуществляется импорт OpenCV в функцию Lambda. 

Генерация пакета Lambda

Был собран понятный инструмент – образ Docker, который умеет получать любой pip-пакет и генерировать архив формата ZIP, который может быть загружен в Lambda Layers. Если желаете подробно изучить этот инструмент, его можно найти на GitHub.

Если у вас уже Docker установлен, то достаточно выполнить следующую команду в терминале. 

docker run --rm -v $(pwd):/package tiivik/lambdazipper opencv-python

Это все! В имеющейся рабочей папке можно найти zip-файл, который будет называться opencv-python.

Один из наиболее полезных безсерверных наборов инструментов – serverless. Тем не менее он не будет использоваться сейчас. Ведь нужно согласиться, что изобретение велосипеда – не всегда подходящая идея, если не учитывать те случаи, когда нужно изучить внутреннее его устройство. 

Несмотря на то, что такие функциональные фреймворки, как serverless существуют, лучше найти ряд корневых функций, абстрагируемых данными фреймворками. 

Давайте узнаем, что наш инструмент абстрагирует от нас.

Если вы посмотрите на package.sh, то вы увидите он им была выполнена инструкция установки pip install, в качестве аргумента которого использовался opencv-python. Все это выполнялось с использованием среды amazonlinux:2017.03, которая в определенной степени имитирует среду AWS Lambda. Вы можете изучить среду выполнения в Dockerfile.

Загрузка Lambda Layers для использования в любой созданной функции

Теперь давайте откроем opencv-python.zip в Lambda Layers, чтобы этот пакет можно было использовать во всех функциях. Относитесь к Layers как к данным, которые могут быть использованы в любой из функций, которые были написаны ранее. Это не только модули Python. Могут также использоваться части кодов, бинарные файлы, и так далее. 

Откройте панель Layers в AWS Lambda и сделайте клик по кнопке «Create Layer» (Создать слой). Обработка изображений на AWS Lambda и API Gateway за 10 минут

Укажите название слоя, его описание и загрузите zip-архив. Удостоверьтесь в том, что была выбрана правильная среда выполнения (в случае с нашим примером такой является Python 3.6). Далее нажмите «создать слой» (Create layer).Обработка изображений на AWS Lambda и API Gateway за 10 минут

На момент создания этого материала можно лишь загружать файл размером в 50MB. Хорошо, что наш архив имеет гораздо меньший объем. Учтите то, что Lambda указывает ограничение развертывания пакета в 250 MB.

После того, как функция будет создана, должно появиться уведомление:

Successfully created layer opencv-python version 1.

Превосходно. Давайте вернемся к lambda-demo функции и добавим слой, который будет называться opencv-python, в среду выполнения нашей функции. Для этого необходимо нажать Layers > Add a layer и выбрать слой opencv-python.

Импорт OpenCV

Давайте попробуем стандартным способом импортировать библиотеку. 

import json

import cv2

 

 

def lambda_handler(event, context):

    return {

        'statusCode': 200,

        'body': json.dumps('Hello from Lambda!')

    }

После того, как будет нажата кнопка «Test», будет получен следующий результат. 

{

  "errorMessage": "Unable to import module 'lambda_function'"

}

По определенной причине, у Lambda не получилось найти пакет. По умолчанию, все слои Lambda монтируются в /opt. Давайте уберем наш импорт модуля см2 и посмотрим на то, что внутри /opt.  

import json

#import cv2

from os import listdir

 

 

def lambda_handler(event, context):

    print(listdir("/opt"))

    return {

        'statusCode': 200,

        'body': json.dumps('Hello from Lambda!')

    }

В журналах функции мы можем увидеть наш модуль cv2 и numpy в /opt

[‘bin’, ‘cv2’, ‘numpy’, ‘numpy-1.16.2.dist-info’, ‘opencv_python-4.0.0.21.dist-info’]

По умолчанию, /opt/bin занесен в переменную среды $PATH. Пользователь всегда может удостовериться в этом, почитав официальную документацию AWS. Тем не менее наши модули слоя расположены в /opt/, а не в /opt/bin. Итак, давайте внесем /opt в $PATH, чтобы Lambda увидела наш пакет.

Для этого в разделе Environment Variables необходимо указать такие параметры среды.

  • Ключ: PYTHONPATH
  • значение: /opt/

Обработка изображений на AWS Lambda и API Gateway за 10 минутКак правило, достаточно лишь обычного импорта пакета без изменения пути, но в случае с нами это необходимо для среды Lambda для обнаружения пакета.

Давайте внесем следующие изменения в код. 

import json

import cv2

 

def lambda_handler(event, context):

    print(cv2.__version__)

    return {

        'statusCode': 200,

        'body': json.dumps('Hello from Lambda!')

    }

Далее сохраните внесенные изменения, после чего кликните на Test. В консоли вы увидите 4.0.0, что говорит о том, какая версия OpenCV используется.

Отлично, OpenCV был запущен в Lambda.

Давайте продолжим реализацию корневой логики программы – конвертацию картинок в черно-белый формат. Для этого необходимо обновить код функции Lambda. 

import json

import cv2

import base64

 

 

def write_to_file(save_path, data):

    with open(save_path, "wb") as f:

        f.write(base64.b64decode(data))

 

def lambda_handler(event, context):

    # Записываем содержимое в файл

    write_to_file("/tmp/photo.jpg", event["body"])

    

    # Чтение изображения

    image = cv2.imread("/tmp/photo.jpg")

    

    # Конвертация в черно белый

    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    

    # Запись чернобелого изображения в /tmp

    cv2.imwrite("/tmp/gray.jpg", gray)

    

    # Конвертация черно белого изображения в кодировку utf-8 base64

    with open("/tmp/gray.jpg", "rb") as imageFile:

        str = base64.b64encode(imageFile.read())

        encoded_img = str.decode("utf-8")

    

    # Возвращаем данные в API Gateway в base64.

    # API Gateway будет обрабатывать конверсию обратно в бинарный вид

    # Настройка типа содержимого в заголовке как image/jpeg.

    

    return {

      "isBase64Encoded": True,

      "statusCode": 200,

      "headers": { "content-type": "image/jpeg"},

      "body":  encoded_img

    }

 API, который будет настроен фактически сейчас, будет принимать бинарное изображение от клиента. После этого оно будет конвертировано в base64 через AWS API Gateway и передано в Lambda. 

Естественно, API Gateway еще не настроен, так что тестирование кода приведет к неудаче. Тем не менее перед тем, как будет выполнен переход к настройке API, который вызывает Lambda, можно протестировать API в консоли Lambda, предоставив base64 в теле событий кодированную картинку.

Далее перенастраиваем Test с этим содержимым. Если интересно, это изображение кошки, которое конвертировано в base64. Обработка изображений на AWS Lambda и API Gateway за 10 минут

{

    "body" : "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AAAAAAtjcm9wUmVjdFRvcGxvbmcAAAAAADhCSU0D7QAAAAAAEACQAAAAAQACAJAAAAABAAI4QklNBCYAAAAAAA4AAAAAAAAAAAAAP4AAADhCSU0EDQAAAAAABAAAAB44QklNBBkAAAAAAAQAAAAeOEJJTQPzAAAAAAAJAAAAAAAAAAABADhCSU0nEAAAAAAACgABAAAAAAAAAAI4QklNA/UAAAAAAEgAL2ZmAAEAbGZmAAYAAAAAAAEAL2ZmAAEAoZmaAAYAAAAAAAEAMgAAAAEAWgAAAAYAAAAAAAEANQAAAAEALQAAAAYAAAAAAAE4QklNA/gAAAAAAHAAAP////////////////////////////8D6AAAAAD/////////////////////////////A+gAAAAA/////////////////////////////wPoAAAAAP////////////////////////////8D6AAAOEJJTQQIAAAAAAAQAAAAAQAAAkAAAAJAAAAAADhCSU0EHgAAAAAABAAAAAA4QklNBBoAAAAAA3cAAAAGAAAAAAAAAAAAAABAAAAAQAAAACEAUwBjAHIAZQBlAG4AcwBoAG8AdAAgADIAMAAxADkALQAwADMALQAxADAAIABhAHQAIAAwADAALgAyADYALgAyADcAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAEAAAAAAABudWxsAAAAAgAAAAZib3VuZHNPYmpjAAAAAQAAAAAAAFJjdDEAAAAEAAAAAFRvcCBsb25nAAAAAAAAAABMZWZ0bG9uZwAAAAAAAAAAQnRvbWxvbmcAAABAAAAAAFJnaHRsb25nAAAAQAAAAAZzbGljZXNWbExzAAAAAU9iamMAAAABAAAAAAAFc2xpY2UAAAASAAAAB3NsaWNlSURsb25nAAAAAAAAAAdncm91cElEbG9uZwAAAAAAAAAGb3JpZ2luZW51bQAAAAxFU2xpY2VPcmlnaW4AAAANYXV0b0dlbmVyYXRlZAAAAABUeXBlZW51bQAAAApFU2xpY2VUeXBlAAAAAEltZyAAAAAGYm91bmRzT2JqYwAAAAEAAAAAAABSY3QxAAAABAAAAABUb3Ag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MCIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSIyOERCRTU2MDNFOTg1OTU0NjZEQjg5ODVFRTU2MEI0MCIgZGM6Zm9ybWF0PSJpbWFnZS9qcGVnIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0iZi5sdXggcHJvZmlsZSIgeG1wOkNyZWF0ZURhdGU9IjIwMTktMDMtMTBUMDA6MjY6MjkrMDI6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDE5LTAzLTEwVDAwOjI3OjMwKzAyOjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDE5LTAzLTEwVDAwOjI3OjMwKzAyOjAwIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MDE4MDExNzQwNzIwNjgxMTgyMkE5MEM0MkExQjY0QzAiIHN0RXZ0OndoZW49IjIwMTktMDMtMTBUMDA6Mjc6MzArMDI6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKE1hY2ludG9zaCkiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjAyODAxMTc0MDcyMDY4MTE4MjJBOTBDNDJBMUI2NEMwIiBzdEV2dDp3aGVuPSIyMDE5LTAzLTEwVDAwOjI3OjMwKzAyOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg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"

}

Обработка изображений на AWS Lambda и API Gateway за 10 минутВызов этого теста будет успешным. 

Response:

{

  "isBase64Encoded": true,

  "statusCode": 200,

  "headers": {

    "content-type": "image/jpeg"

  },

  "body": "/9j/4AJRgAB.....P+WqHNf//Z" <- long base64 string of black and white image here

}

Теперь мы можем настраивать API, которая вызывает эту функцию Lambda.

Настройка API

Теперь необходимо открыть консоль AWS API Gateway. Для этого нажимаем Create API.Обработка изображений на AWS Lambda и API Gateway за 10 минут

Теперь создается новый REST API, и ему дается имя и описание. В этом случае, API дадим имя lambda-demo.Обработка изображений на AWS Lambda и API Gateway за 10 минут

В Resources > Actions выбираем Create Method, чтобы определить метод POST.Обработка изображений на AWS Lambda и API Gateway за 10 минут

Для типа интеграции выбираем Lambda Function и выбираем функцию Lambda из выпадающего меню. Включите интеграцию Use Lambda Proxy, и нажмите Save.Обработка изображений на AWS Lambda и API Gateway за 10 минут

Нам необходимо, чтобы API мог обрабатывать бинарные данные. 

В Settings > Binary Media Types выбираем Add Binary Media Type, и определяем таким образом типа двоичных данных. 

image/jpeg

image/png

*/*

Затем кликаем по Save Changes.Обработка изображений на AWS Lambda и API Gateway за 10 минут

Переходим снова в метод POST.

Под Method Response добавляем Content-Type Response Header и указываем тип image/jpeg.Обработка изображений на AWS Lambda и API Gateway за 10 минут

Перед публикацией API можно его протестировать путем нажатия кнопки Client Test.Обработка изображений на AWS Lambda и API Gateway за 10 минут

В нашем случае, мы предоставляем само тело изображения в base64, а не объект json. Для того, чтобы было более удобно, вы можете вставить необработанную строку base64 из следующей ссылки в поле Request Body.

В качестве ответа будет base64 черно-белой фотографии.Обработка изображений на AWS Lambda и API Gateway за 10 минут

Сделайте клик по Actions > Deploy API.

Теперь необходимо создать новый этап развертывания, назвать его правильно (например, development) и нажать Deploy. Обработка изображений на AWS Lambda и API Gateway за 10 минут

Наш API теперь опубликован и отлично работает. Вы получите url, в котором он развернут.

https://XXXXX.execute-api.XXXX.amazonaws.com/development

Давайте теперь испытаем. Для этого делаем следующее:

1. Загружаем то же изображение в локальную среду.

curl https://i.imgur.com/offvirS.jpg -o kitty.jpg

2. Через Post-запрос отправляем картинку в бинарном виде. Как следствие, получено черно-белое изображение, которое сохранилось с названием kitty_bw.jpg.
curl -X POST —data-binary @kitty.jpg https://XXXXX.execute-api.eu-central-1.amazonaws.com/development -o kitty_bw.jpg

Здесь необходимо учесть и то, что для простоты понимания мы упростили это руководство, не использовав здесь обработку ошибок, проверку запроса и настройку авторизации. Все эти возможности также содержатся в API Gateway и в коде функции Lambda. 

ОфисГуру
Adblock
detector