Статьи

Бег вокруг блока: первая встреча манекена с AWS Lambda

Все началось с того, что египтяне сунули несколько шариков на деревянную раму, чтобы облегчить свои мозги в простой арифметике; или, возможно, когда греки изобрели механизм антикитера, чтобы отслеживать движение планет с точностью до двух градусов на тысячелетие . В любом случае, компьютерные технологии прошли долгий путь: аналитический движок Чарльза Бэббиджа, разрушитель загадок Алана Тьюринга, карманный калькулятор НАСА, который доставил человека на Луну , Deep Blue победил Гарри Каспарова, шахматного гроссмейстера и так далее. В соответствии с этим, парадигмы программных приложений также резко изменились: с нуля (чисто аппаратное программирование), монолитов, модульности, SOA, облака, а теперь и без серверов.

В данный момент «безсерверный» обычно означает FaaS (функции как услуга); и FaaS буквально означает AWS Lambda , как с точки зрения популярности, так и с точки зрения принятия . Следовательно, не будет преувеличением утверждать, что популярность разработки без серверов будет пропорциональна простоте использования лямбд.

Что ж, лямбда существует с 2015 года , уже интегрирована в большую часть экосистемы AWS и используется в производстве сотнями (если не тысячами) компаний; так что лямбда должна быть довольно интуитивной и простой в использовании, верно?

Ну, похоже нет, по крайней мере, в моем случае. И мой «случай», являющийся одним из официальных примеров AWS, я не совсем уверен, достаточно ли дружелюбна лямбда для новичков.

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

Как программист, я, естественно, начал с консоли управления Lambda . Код уже был написан щедрыми ребятами из AWS, так зачем изобретать велосипед? Скопируйте, вставьте, сохраните, запустите. Та да!

Хм, похоже, мне нужно немного повзрослеть.

Мастер «Создать функцию» был, если честно, довольно привлекательным. С таким количеством готовых чертежей. Жаль, что у него еще не было образца генерации миниатюр S3, или эта история могла бы закончиться прямо здесь!

Поэтому я просто выбрал опцию «Автор с нуля» с красивым именем s3-thumbnail-generator .

Ой, подождите, что это за «Роль»? Это тоже необходимо. К счастью, у него есть опция «Создать новую роль из шаблона (ов)», которая спасла бы мой день. (У меня не было опций в разделе «Выбрать существующую роль», и я слишком молод, чтобы «Создать собственную роль».)

Не принимайте это близко к сердцу. «Имя роли»: s3-thumbnail-generator-role . Но как насчет «шаблона политики»?

Возможно, я должен найти что-то связанное с S3, так как моя лямбда полностью-S3.

Сюрприз! Единственное, что я получаю, когда ищу S3, это «разрешения только для чтения объекта S3». Не имея другого выбора, я просто схватил его. Посмотрим, как далеко я смогу пройти, прежде чем упаду на лицо!

Время нажать «Создать функцию».

Вау, их лямбда-дизайнер выглядит действительно круто!

«Поздравляем! Ваша лямбда-функция «s3-thumbnail-generator» была успешно создана. Теперь вы можете изменить его код и конфигурацию. Нажмите кнопку «Тест», чтобы ввести тестовое событие, когда вы будете готовы протестировать свою функцию ».

Хорошо, время для моей миссии копирования-вставки. «Скопируйте» в исходный код примера , Ctrl + A и Ctrl + V в редакторе лямбда-кода. Просто!

Все зеленые (без красного). Хорошо знать.

«Сохранить» и «Проверить».

О, я должен был знать лучше. Да, если я собираюсь «проверить», мне нужен «тестовый ввод». Очевидно.

Я знал, что тестировать мою новую лямбду будет не так просто, но я не ожидал, что мне нужно будет собрать сериализованное JSON событие вручную . К счастью, ребята также отлично поработали, предоставив готовый шаблон события «S3 Put». Так что еще я бы выбрал? 🙂

Как и ожидалось, первый запуск оказался неудачным:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
{
  "errorMessage": "Cannot find module 'async'",
  "errorType": "Error",
  "stackTrace": [
    "Function.Module._load (module.js:417:25)",
    "Module.require (module.js:497:17)",
    "require (internal/module.js:20:19)",
    "Object. (/var/task/index.js:2:13)",
    "Module._compile (module.js:570:32)",
    "Object.Module._extensions..js (module.js:579:10)",
    "Module.load (module.js:487:32)",
    "tryModuleLoad (module.js:446:12)",
    "Function.Module._load (module.js:438:3)"
  ]
}

Черт, я должен был заметить эти строки. И в любом случае, это плохо, потому что на странице, где я скопировал пример кода, было большое жирное название «Создать пакет развертывания Lambda», и в нем четко объяснялось, как объединить образец в лямбда-развертываемый zip-архив.

Поэтому я создал локальный каталог, содержащий мой код и package.json , и запустил npm install (хорошо, что у меня были предварительно установлены node и npm !). Сборка, архивирование и загрузка приложения были довольно простыми, и, надеюсь, мне не пришлось бы проходить миллионы и один такой цикл, чтобы моя лямбда работала.

(Кстати, я бы хотел сделать это в самом встроенном редакторе; очень жаль, что я не смог придумать, как добавить зависимости.)

Во всяком случае, пришло время для моего второго теста.

1
2
3
4
5
6
7
8
9
{
  "errorMessage": "Cannot find module '/var/task/index'",
  "errorType": "Error",
  "stackTrace": [
    "Function.Module._load (module.js:417:25)",
    "Module.require (module.js:497:17)",
    "require (internal/module.js:20:19)"
  ]
}

index ? Откуда это пришло?

Подожди … мой плохой, мой плохой.

Похоже, что параметр Handler по- прежнему содержит значение по умолчанию index.handler . В моем случае это должен быть CreateThumbnail.handler ( filename.method ).

Давайте попробуем еще раз.

Шутки в сторону? Ни за что!

О да. Бревна не лгут.

1
2
3
4
2018-02-04T17:00:37.060Z    ea9f8010-09cc-11e8-b91c-53f9f669b596
    Unable to resize sourcebucket/HappyFace.jpg and upload to
 sourcebucketresized/resized-HappyFace.jpg due to an error: AccessDenied: Access Denied
END RequestId: ea9f8010-09cc-11e8-b91c-53f9f669b596

Справедливо; У меня нет sourcebucket или sourcebucketresized , но, вероятно, кто-то другой. Отсюда и отказ в доступе. Имеет смысл.

Поэтому я создал свои собственные сегменты, s3-thumb-input и s3-thumb-inputresized , отредактировал ввод своих событий (благодаря раскрывающемуся меню «Настроить тестовое событие») и повторил s3-thumb-inputresized .

1
2
3
2018-02-04T17:06:26.698Z    bbf940c2-09cd-11e8-b0c7-f750301eb569
    Unable to resize s3-thumb-input/HappyFace.jpg and upload to
 s3-thumb-inputresized/resized-HappyFace.jpg due to an error: AccessDenied: Access Denied

Доступ запрещен? Еще раз?

К счастью, основываясь на входных данных события, я выяснил, что 403 фактически указывает на ошибку 404 (не найдена), поскольку в моем HappyFace.jpg действительно не было файла HappyFace.jpg .

Держись, дорогой читатель, пока я спешу к консоли S3 и загружаю свое счастливое лицо в мое новое ведро. Минуточку!

Хорошо, готов к следующему тестовому раунду.

1
2
3
2018-02-04T17:12:53.028Z    a2420a1c-09ce-11e8-9506-d10b864e6462
    Unable to resize s3-thumb-input/HappyFace.jpg and upload to
 s3-thumb-inputresized/resized-HappyFace.jpg due to an error: AccessDenied: Access Denied

Точно такая же ошибка? Еще раз? Давай!

Это не имело смысла для меня; с какой стати моя собственная лямбда, работающая в моей учетной записи AWS, не имеет доступа к моему собственному ведру S3?

Подождите, это может быть связано с этой ролью исполнения; где я слепо назначил S3 разрешения только для чтения ?

Немного Googling привел меня к чрезвычайно всеобъемлющим документам AWS IAM для лямбды , где я узнал, что лямбда выполняет свою собственную роль IAM; и что мне нужно вручную настроить роль в зависимости от того, какие сервисы AWS я буду использовать. Хуже того, чтобы настроить роль, мне нужно пройти весь путь до консоли управления IAM (которая, к счастью, уже связана с выпадающим списком роли выполнения и, что более важно, открывается на новой вкладке).

Скрестим пальцы, пока не загрузится пользовательская страница роли.

О нет … Больше редактирования JSON?

В первоначальном руководстве, ребята из AWS, казалось, также прибегали к исполнению ролей , но было странно, что там не было упоминания о S3 (кроме названия). Они что-то пропустили?

Хорошо, впервые в истории я собираюсь создать свою собственную роль в IAM!

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

Но избавление от синтаксиса JSON решает лишь небольшую часть проблемы; как я могу узнать, какие разрешения мне нужны?

Гугл, приятель? Что-нибудь?

Ооо … Вернуться в документы AWS? Большой…

Ну, это не так уж и плохо, благодаря руководству по разрешениям S3 . Хотя это было несколько ошеломляюще, я догадался, что мне нужны были некоторые разрешения для «операций с объектами», и, к счастью, у документа была хорошая таблица, предполагающая, что мне нужны s3:GetObject и s3:PutObject (в соответствии с s3.getObject(...) и s3.putObject(...) вызывает в коде).

Поразмыслив, я получил «Политику IAM» с указанными выше разрешениями в своем сегменте (названную с утомительным синтаксисом arn:aws:s3:::s3-thumb-input ):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1517766308321",
      "Action": [
        "s3:PutObject"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::s3-thumb-inputresized"
    },
    {
      "Sid": "Stmt1517766328849",
      "Action": [
        "s3:GetObject"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::s3-thumb-input"
    }
  ]
}

И вставил и сохранил его в редакторе ролей IAM (который автоматически вернул меня на страницу консоли лямбды; как приятно!)

Попробуй еще раз:

Та же ошибка ?!

Оглядываясь на документацию по разрешениям S3, я заметил, что в разрешениях для объектов, по-видимому, присутствует звездочка (суффикс /* , вероятно, указывающий файлы) под именем ресурса. Итак, давайте попробуем это также с новой настраиваемой политикой:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1517766308321",
      "Action": [
        "s3:PutObject"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::s3-thumb-inputresized/*"
    },
    {
      "Sid": "Stmt1517766328849",
      "Action": [
        "s3:GetObject"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::s3-thumb-input/*"
    }
  ]
}

Опять же (это начинает ощущаться как хлыст ):

1
2
3
2018-02-04T17:53:45.484Z    57ce3a71-09d4-11e8-a2c5-a30ce229e8b7
    Successfully resized s3-thumb-input/HappyFace.jpg and uploaded to
 s3-thumb-inputresized/resized-HappyFace.jpg

Ух ты !!!

И, resized-HappyFace.jpg файл resized-HappyFace.jpg только что появился в моей s3-thumb-inputresized ; Да уж!

Теперь, как я могу настроить мой лямбда на автоматический запуск, когда я помещаю файл в мое ведро?

К счастью, лямбда-консоль (с ее интуитивно понятным расположением «триггер-функция-разрешения») дала кристально ясное представление о том, что я хотел, это триггер S3. Поэтому я добавил один, с «Object Created (All)» в качестве «Типа события» и «jpg» в качестве суффикса, сохранил все и сразу же бросил файл JPG в мое ведро.

Да, работает как шарм.

Чтобы увидеть, сколько времени занял весь процесс (в реальном исполнении, в отличие от «тестов»), я щелкнул ссылку «logs» в (предыдущей) области результатов выполнения и вошел в новейший «поток журналов», показанный там; ничего такого!

И что еще более подозрительно, последний журнал в новейшем потоке журналов был журналом «отказано в доступе», хотя я преодолел эту точку и даже достиг успешного изменения размера. Возможно мое последнее изменение сломало способность регистрации лямбды?

Благодаря Google и StackOverflow я обнаружил, что моя исполнительная роль должна содержать некоторые разрешения, связанные с журналированием; действительно, теперь я помню, что были некоторые разрешения в текстовом поле редактора разрешений, когда я начал создавать свою пользовательскую роль, и снова я был достаточно невежественен, чтобы вставить свои политики S3 прямо поверх них.

Еще один раунд редактирования политики:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1517766308321",
      "Action": [
        "s3:PutObject"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::s3-thumb-inputresized/*"
    },
    {
      "Sid": "Stmt1517766328849",
      "Action": [
        "s3:GetObject"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::s3-thumb-input/*"
    },
    {
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:logs:*:*:*"
    }
  ]
}

Еще одно удаление файла, и на этот раз и изменение размера, и журналы работали безупречно … Наконец-то!

Теперь, когда все выправлено, и моя миниатюра ждет в моем целевом буфере, я запустил свой браузер, набрав http://s3-thumb-inputresized.s3.amazonaws.com/resized-HappyFace.jpg (в соответствии с Виртуальный хостинг S3 ) и нажмите Enter, ожидая в ответ симпатичного эскиза.

1
2
3
4
5
6
<Error>
  <Code>AccessDenied</Code>
  <Message>Access Denied</Message>
  <RequestId>C8BAC3D4EADFF577</RequestId>
  <HostId>PRnGbZ2olpLi2eJ5cYCy0Wqliqq5j1OHGYvj/HPmWqnBBWn5EMrfwSIrf2Y1LGfDT/7fgRjl5Io=</HostId>
</Error>

Уже надоело это сообщение «AccessDenied»!

Очевидно, что, хотя мой код генерирует файл, он не делает файл общедоступным (но что хорошего в частной миниатюре, да?)

Просматривая документы AWS, я вскоре обнаружил параметр ACL операции putObject , который позволяет общедоступному загруженному файлу S3. В надежде, что это решит все проблемы на планете, я быстро обновил свой код, чтобы установить ACL файла для public-read :

1
2
3
4
5
6
7
8
9
s3.putObject({
                    Bucket: dstBucket,
                    Key: dstKey,
                    Body: data,
                    ContentType: contentType,
                    ACL: 'public-read'
                },
                next);
            }

Сохраните функцию и нажмите Test:

1
2
3
2018-02-04T18:06:40.271Z    12e44f61-19fe-11e8-92e1-3f4fff4227fa
    Unable to resize s3-thumb-input/HappyFace.jpg and upload to
 s3-thumb-inputresized/resized-HappyFace.jpg due to an error: AccessDenied: Access Denied

Еще раз?? Ты шутишь, что ли?!

К счастью, на этот раз я знал достаточно, чтобы перейти непосредственно к руководству по разрешениям S3 , которое быстро показало, что мне также необходимо иметь разрешение s3:PutObjectAcl в моей политике, чтобы использовать параметр ACL в моем вызове putObject . Итак, еще одно путешествие к редактору политик, к панели управления IAM и обратно к лямбда-консоли.

1
2
3
2018-02-04T18:15:09.670Z    1d8dd7b0-19ff-11e8-afc0-138b93af2c40
    Successfully resized s3-thumb-input/HappyFace.jpg and uploaded to
 s3-thumb-inputresized/resized-HappyFace.jpg

И на этот раз, к моему большому удовлетворению, браузер с радостью показал мне миниатюру моего счастливого лица, когда я ввел в него URL-адрес хостинга http://s3-thumb-inputresized.s3.amazonaws.com/resized-HappyFace.jpg .

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

Может быть, я должен был следовать этому официальному руководству, с самого начала … но, опять же, наааа 🙂

Смотреть оригинальную статью здесь: Бег вокруг блока: первая встреча манекена с AWS Lambda

Мнения, высказанные участниками Java Code Geeks, являются их собственными.