
Сочетание сервисов AWS Lambda и Amazon API Gateway часто используется как архитектура для бессерверных микросервисов и решений на базе API. Эта архитектура позволяет разработчикам сосредоточиться на своих приложениях, а не тратить время на настройку и управление серверами.

API Gateway — это многофункциональное решение, включающее поддержку различных типов API (HTTP, REST, WebSocket), несколько схем аутентификации, возможность версионирования API, развёртывание Canary (Canary Deployment) и многое другое. Однако, если ваши требования скромнее, и всё, что вам нужно, это конечная точка HTTP(S) для вашей Lambda функции(например, для работы в качестве вебхука), вы можете использовать URL-адреса функций Lambda. Когда вы создаёте URL-функции, Lambda автоматически генерирует уникальную конечную точку HTTP(S), предназначенную для вашей функции.
В этой статье мы покажем на практическом примере, как использовать URL-адрес функции Lambda. Вы создадите функцию Go Lambda, которая будет служить бессерверным вебхук бэкендом для Slack.
Данное пошаговое руководство содержит:
- Краткий обзор приложения.
- Настройку и развёртывание функции (наряду с некоторыми проблемами, на которые следует обратить внимание).
- Инструкцию, как настроить Slack, чтобы включить сквозную интеграцию.
- … Далее просто тестируйте приложение и получайте удовольствие.
К концу этого поста вы настроите, интегрируете и развернёте полезное (и, надеюсь, весёлое?) приложение с использованием URL-функции Lambda. В процессе вы получите общее представление об этой возможности и сможете использовать её при создании собственных решений.
Код также доступен на GitHub.
Как это работает
Пример приложения, представленный в этом посте, представляет собой урезанную версию Giphy для Slack. Оригинальное приложение Giphy для Slack возвращает кучу GIF-файлов по поисковому запросу, и пользователь может выбрать один из них. Чтобы упростить задачу, я немного изменил её таким образом, чтобы бессерверный бэкенд просто возвращал одно случайное изображение по ключевому слову поиска с помощью Giphy Random API.
Поскольку решение будет интегрировано как Slash-команда в Slack, конечный пользователь будет вызывать его из рабочего пространства Slack, используя команду
/awsome <ваш поисковый запрос>
(где awsome
— не что иное, как название slash-команды). Это, в свою очередь, вызовет URL-адрес функции Lambda (о её конфигурировании мы расскажем чуть позже), которая позаботится обо всём остальном.К примеру, если вызвать её из рабочего пространства Slack, используя команду
/awsome serverless
, она вернёт случайную гифку (вы сами сможете попробовать это позже).Вот общий обзор того, что делает функция Lambda:
- В результате вызова Slash Commands — Slack на URL-адрес функции Lambda отправляется строка с кодировкой base64 — поэтому первым шагом будет её декодирование.
- Предполагается, что функция вызывается только Slack, и нам нужно убедиться, что это так. Slack делает это возможным, позволяя приложениям проверять запросы, используя подписанный секрет (signing secret) — функция просто реализует Go-версию метода сопоставления подписи, представленного здесь.
- Если проверка подписи прошла успешно (в случае неудачи мы возвращаем клиенту ошибку), запрос Slack парсится, чтобы извлечь текст (
text
) поиска, который отправил пользователь. - Затем вызывается Giphy Random API с поисковым запросом. Если мы получаем успешный ответ, то парсим его и отправляем обратно в Slack в нужном формате.
Наконец, пользователь может увидеть гифку в своём рабочем пространстве Slack.
Я пропущу подробное описание кода, чтобы сосредоточиться на других аспектах такого решения, но подпись функции заслуживает упоминания — она похожа на ту, что вы использовали бы в случае решения на базе API Gateway:
func Funcy(r events.LambdaFunctionURLRequest) (events.LambdaFunctionURLResponse, error) {
...
}
Мы используем events.LambdaFunctionURLRequest в качестве входа и возвращаем events.LambdaFunctionURLResponse. За кулисами Lambda сопоставляет запрос с объектом события, прежде чем передать его функции. Наконец, ответ функции сопоставляется с HTTP-ответом, который Lambda отправляет обратно клиенту через URL-функции.
Подробности вы можете прочитать в документации.
Довольно удобно, верно? Вы можете использовать ассоциации API Gateway без необходимости устанавливать и настраивать такой шлюз.
С этой вводной информацией давайте перейдём к той части, где вы развернёте функцию и попробуете использовать её в Slack. Но перед этим убедитесь, что у вас подготовлено всё необходимое:
Рекомендуемые требования
- Создайте учётную запись AWS (если у вас её ещё нет) и войдите в неё. Пользователь IAM, которого вы используете, должен обладать достаточными полномочиями для выполнения необходимых вызовов служб AWS и управления ресурсами AWS.
- Установите и настройте AWS CLI.
- Установите Go.
- Установите Git.
- Создайте рабочее пространство Slack, если у вас его нет.
- Создайте учётную запись GIPHY (это бесплатно) и создайте приложение. Каждое созданное вами приложение будет иметь свой собственный API-ключ.
Пожалуйста, запишите куда-нибудь ключ API GIPHY, так как вы ещё будете использовать его в дальнейшем
Склонируйте приведённый ниже репозиторий Github и переместите его в нужную директорию:
git clone https://github.com/abhirockzz/awsome-slack-backend
cd awsome-slack-backend/function
В последующих шагах используется AWS CLI — я намеренно использовал AWS CLI, чтобы осветить определённые аспекты процесса. Также рекомендую ознакомиться с этим руководством по CloudFormation и SAM.
Создайте, заархивируйте и разверните функцию
export FUNC_NAME=awsome-slack-backend
export FUNC_GO_BINARY_NAME=awsome
export ZIP_NAME=function.zip
GOOS=linux go build -o $FUNC_GO_BINARY_NAME main.go
zip function.zip $FUNC_GO_BINARY_NAME
Сначала создайте роль IAM для Lambda и подключите политику
AWSLambdaBasicExecutionRole
:export ROLE_NAME=demo-lambda-role
ROLE_ARN=$(aws iam create-role \
--role-name $ROLE_NAME \
--assume-role-policy-document '{"Version":
"2012-10-17","Statement": [{ "Effect": "Allow", "Principal":
{"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]}' \
--query 'Role.[Arn]' --output text)
aws iam attach-role-policy --role-name $ROLE_NAME --policy-arn
arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Создайте функцию:
aws lambda create-function \
--function-name $FUNC_NAME \
--runtime go1.x \
--zip-file fileb://$ZIP_NAME \
--handler $FUNC_GO_BINARY_NAME \
--role $ROLE_ARN
После того как функция будет создана, перейдите к добавлению URL-адреса функции:
aws lambda create-function-url-config \
--function-name $FUNC_NAME \
--auth-type NONE
Для этого пробного приложения мы используем NONE в качестве типа аутентификации. Это означает, что URL-адрес функции Lambda будет общедоступен — подробнее об этом расскажу чуть позже.
Если вы перейдёте в консоль AWS и откроете только что созданную функцию, вы увидите URL-адрес функции, связанный с ней:

Давайте вызовем функцию — скопируйте URL-функции и вставьте его в браузер или используйте любой другой инструмент (например,
curl
).curl -i <FUNCTION_URL>
Вы должны получить ответ
{"Message": "Forbidden"}
с кодом состояния HTTP 403 Forbidden
.Не волнуйтесь, это вполне ожидаемо — я хотел убедиться, что вы столкнётесь с этой проблемой и поймёте её первопричину.
Даже если мы используем
NONE
в качестве схемы аутентификации, пользователи всё равно должны иметь разрешения lambda:InvokeFunctionUrl
, чтобы успешно вызвать URL-функции. Сложность заключается в том, что когда вы создаёте URL-функции (с типом аутентификации NONE
) через консоль или модель бессерверных приложений AWS (AWS Serverless Application Model), Lambda автоматически создаёт заявление для политики на основе ресурсов (подробности в документации). Это исключено, если вы используете AWS CLI (как в нашем примере), AWS CloudFormation или Lambda API напрямую — вы должны добавить разрешения самостоятельно.Давайте сделаем это:
aws lambda add-permission \
--function-name $FUNC_NAME \
--action lambda:InvokeFunctionUrl \
--statement-id FunctionURLAllowPublicAccess \
--principal "*" \
--function-url-auth-type NONE
Чтобы увидеть политику, перейдите к своей функции в консоли AWS: Конфигурация (Configuration) > Разрешения (Permissions).
Вызовите функцию снова:
curl -i <FUNCTION_URL>
На этот раз вы получите другую ошибку с кодом состояния
HTTP 401 Unauthorized
. Это тоже вполне ожидаемо.Давайте завершим остальную часть конфигурации, чтобы всё заработало.
Настроим Slack
Обратите внимание, что большинство инструкций в этом разделе были адаптированы из документации Slack.
Начните со входа в рабочее пространство Slack и создания нового приложения Slack.
После этого создайте (Slash Command) — перейдите на страницу настроек вашего приложения, а затем нажмите на функцию Slash Commands в навигационном меню. Перед вами появится кнопка «создать новую команду» (Create New Command), нажав на которую вы увидите окно, где вам будет предложено определить новую команду с косой чертой с помощью необходимой информации.
Введите необходимую информацию —
/awsome
для команды и URL Lambda функции в Request URL.
Наконец, установите приложение в рабочем пространстве — нажмите на функцию базовой информации (Basic Information) в навигационном меню, выберите пункт установки вашего приложения в рабочее пространство (Install your app to your workspace) и нажмите Install App to Workspace. Это позволит установить приложение в рабочую область Slack для тестирования вашего приложения и генерирования токенов, необходимых для взаимодействия с API Slack.
Как только вы закончите установку приложения, на той же странице появится пункт с учётными данными приложения (App Credentials). Оттуда вам нужно взять свой подписанный секрет Slack (Slack Signing Secret).
Запишите свой подписанный секрет, так как вы ещё будете использовать его позднее.

Обновление функции
Теперь, когда у вас есть подписанный ключ Slack, вам нужно позаботиться о том, чтобы он также был настроен и в функции. Также не забудьте про ключ API GIPHY, поскольку он нужен функции для вызова конечной точки GIPHY REST.
Давайте обновим функцию, чтобы включить их в качестве переменных окружения:
aws lambda update-function-configuration \
--function-name $FUNC_NAME \
--environment "Variables={SLACK_SIGNING_SECRET=<enter Slack
signing secret>,GIPHY_API_KEY=<enter Giphy API key>}"
В этом примере для хранения ключей для Slack и GIPHY используются переменные среды Lambda — это сделано только в демонстрационных целях. Для безопасного хранения и управления учётными данными следует использовать такие решения, как AWS Secrets Manager.
Всё готово!
Перейдите в своё рабочее пространство Slack и вызовите команду. Например, чтобы получить случайную гифку с кошечкой, просто введите:
/awsome cat
Я получил такой ответ с команды. А вы? :-)

Не стесняйтесь побаловаться с получившимся приложением.
Напоследок, приберёмся
Как только вы закончите, удалите функцию вместе с политикой и ролью IAM.
aws lambda delete-function --function-name $FUNC_NAME
aws iam detach-role-policy --role-name $ROLE_NAME --policy-arn
arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
aws iam delete-role --role-name $ROLE_NAME
Заключение
Вы настроили и развернули бессерверный бэкенд для Slack, и в процессе узнали о некоторых аспектах URL-адресов Lambda функций через призму примера этого приложения. Я бы рекомендовал вам изучить другие возможности, такие как аутентификация AWS_IAM, конфигурация CORS, ограничения дросселирования, мониторинг и т.д.
Счастливого кодинга!
НЛО прилетело и оставило здесь промокод для читателей нашего блога:
— 15% на все тарифы VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.