Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
AWS Lambda существует уже несколько лет, и она остается самым популярным способом экспериментировать с технологией serverless. Если вы не знакомы с serverless, то это модель разработки, в которой управление, предоставление и масштабирование серверов абстрагируется от разработки приложений. Серверы существуют в бессерверном (serverless) мире, но они полностью управляются облачным провайдером, что позволяет разработчикам сосредоточиться на упаковке своего кода для деплоя.
AWS Lambda — это разновидность функции как услуги (FaaS), которая позволяет выполнять код по требованию в ответ на предварительно сконфигурированные события или запросы. Эта статья познакомит вас с AWS Lambda и поможет создать и развернуть функции Lambda с помощью Node.js и AWS SAM.
Давайте приступим!
Предварительные условия
Прежде чем начинать изучение данного руководства, убедитесь, что на вашем компьютере установлен Node.js 14.x LTS, поскольку это последний выпуск, поддерживаемый AWS Lambda на момент написания статьи. Однако содержание этой статьи должно оставаться актуальным даже в случае поддержки более новых релизов. Вы можете использовать Volta для инсталляции и управления несколькими версиями Node.js на вашем компьютере. Также убедитесь, что вы зарегистрировали бесплатную учетную запись AWS, если у вас ее еще нет.
Если вы собираетесь запускать свою функцию AWS Lambda локально (что я буду демонстрировать в этой статье), то на вашем компьютере необходимо установить Docker. Следуйте этим инструкциям, чтобы настроить Docker для вашей операционной системы, прежде чем приступить к изучению остальной части этого руководства.
Установка AWS CLI и AWS SAM CLI
В этом руководстве мы будем использовать AWS CLI и AWS Serverless Application Model (SAM) CLI для разработки наших бессерверных функций и их деплое в AWS.
Первый взаимодействует с сервисами AWS в командной строке, а второй помогает в построении, отладке, деплое и вызове функций Lambda. Подробнее о AWS SAM читайте в документации.
Конкретный способ установки обоих инструментов CLI будет отличаться в зависимости от вашей операционной системы. Вы можете установить или обновить до последней версии AWS CLI для Linux, macOS и Windows, следуя инструкциям на этой странице. Чтобы установить AWS SAM CLI, следуйте соответствующему руководству для вашей операционной системы:
Установка AWS SAM CLI в Linux
Установка AWS SAM CLI на Windows
Установка AWS SAM CLI на macOS
Вот версии AWS CLI и AWS SAM CLI, которые я установил во время написания этого руководства:
$ aws --version
aws-cli/2.4.1 Python/3.8.8 Linux/4.19.128-microsoft-standard exe/x86_64.ubuntu.20 prompt/off
$ sam --version
SAM CLI, version 1.36.0
После установки обоих инструментов CLI следуйте этому руководству для настройки учетных данных AWS, чтобы вы могли успешно взаимодействовать с вашим аккаунтом AWS через CLI.
$ aws configure list
Name Value Type Location
---- ----- ---- --------
profile <not set> None None
access_key ****************ZFEF shared-credentials-file
secret_key ****************BnOU shared-credentials-file
region us-east-1 config-file ~/.aws/config
Создайте свою первую функцию AWS Lambda с помощью Node.js
Давайте начнем с написания простой функции hello world, чтобы продемонстрировать, как работает AWS Lambda. Выполните приведенную ниже команду для инициализации нового проекта:
$ sam init --runtime nodejs15.x --name aws-lambda-nodejs-example
При появлении запроса выберите AWS Quick Start Templates (шаблоны быстрого запуска) под template source (источник шаблона), Zip под package type (тип пакета) и Hello World Example (пример) под application templates (шаблоны приложений).
Which template source would you like to use?
1 - AWS Quick Start Templates
2 - Custom Template Location
Choice: 1
What package type would you like to use?
1 - Zip (artifact is a zip uploaded to S3)
2 - Image (artifact is an image uploaded to an ECR image repository)
Package type: 1
Cloning from https://github.com/aws/aws-sam-cli-app-templates
AWS quick start application templates:
1 - Hello World Example
2 - Step Functions Sample App (Stock Trader)
3 - Quick Start: From Scratch
4 - Quick Start: Scheduled Events
5 - Quick Start: S3
6 - Quick Start: SNS
7 - Quick Start: SQS
8 - Quick Start: Web Backend
Template selection: 1
После выполнения команды перейдите в только что созданную папку aws-lambda-nodejs-example
. Она должна иметь следующую структуру:
.
├── events
│ └── event.json
├── hello-world
│ ├── app.js
│ ├── package.json
│ └── tests
│ └── unit
│ └── test-handler.js
├── README.md
└── template.yaml
Вот краткое описание важных файлов и каталогов проекта:
template.yaml
: Определяет ресурсы AWS для вашего приложения.hello-world/app.js
: Содержит логику функций Lambda.hello-world/package.json
: Содержит все необходимые приложению Node.js-зависимости.hello-world/tests/
: Содержит модульные тесты для ваших функций Lambda.
Откройте файл template.yaml
в текстовом редакторе и обратите внимание на следующие строки:
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-world/
Handler: app.lambdaHandler
Runtime: nodejs14.x
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
Эти строки описывают имя вашей функции Lambda (HelloWorldFunction
), рантайм, используемый для ее выполнения (nodejs14.x
), и тип триггера для функции (Api
). Это означает, что ваша Lambda-функция будет выполняться, когда GET-запрос отправлен по маршруту /hello
через API Gateway. Обратите внимание, что существуют и другие способы вызова функций Lambda.
Строка CodeUri
показывает, что код для функции HelloWorldFunction
находится в каталоге hello-world
. Свойство Handler
указывает на app.js
как на файл с кодом функции, который должен иметь именованный экспорт под названием lambdaHandler
.
Откройте файл hello-world/app.js
и изучите его содержимое:
let response;
exports.lambdaHandler = async (event, context) => {
try {
response = {
statusCode: 200,
body: JSON.stringify({
message: "hello world",
}),
};
} catch (err) {
console.log(err);
return err;
}
return response;
};
Эта простая функция принимает два параметра и возвращает объект ответа, содержащий сообщение 'hello world'. Первый параметр - это JSON-пейлоад, отправленная инициатором вызова функции, а второй - объект контекста, который содержит информацию о вызове функции и среде выполнения. Этот обработчик является async
, поэтому вы можете использовать return
или throw
для возврата ответа или ошибки, соответственно. Неасинхронные (non-async) обработчики должны использовать третий параметр callback
(здесь не показан).
Измените функцию так, чтобы вместо сообщения hello world
она возвращала зарезервированные переменные окружения, определенные в среде выполнения.
exports.lambdaHandler = async (event, context) => {
let response;
try {
const environmentalVariables = {
handler: process.env._HANDLER,
aws_region: process.env.AWS_REGION,
aws_execution_env: process.env.AWS_EXECUTION_ENV,
aws_lambda_function_name: process.env.AWS_LAMBDA_FUNCTION_NAME,
aws_lambda_function_name: process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE,
aws_lambda_function_version: process.env.AWS_LAMBDA_FUNCTION_VERSION,
aws_lambda_log_group_name: process.env.AWS_LAMBDA_LOG_GROUP_NAME,
aws_lambda_log_stream_name: process.env.AWS_LAMBDA_LOG_STREAM_NAME,
aws_lambda_runtime_api: process.env.AWS_LAMBDA_RUNTIME_API,
lang: process.env.LANG,
tz: process.env.TZ,
lambda_task_root: process.env.LAMBDA_TASK_ROOT,
lambda_runtime_dir: process.env.LAMBDA_RUNTIME_DIR,
path: process.env.PATH,
ld_library_path: process.env.LD_LIBRARY_PATH,
};
response = {
statusCode: 200,
body: JSON.stringify(environmentalVariables),
};
} catch (err) {
response = {
statusCode: 500,
error: err,
};
console.log(err);
}
return response;
};
Мы можем получить доступ к переменным среды через process.env
и, агрегируя их в объект, вернуть их в объекте ответа. API Gateway использует свойство statusCode
для добавления правильного кода состояния HTTP в сгенерированный ответ.
Тестирование вашей функции AWS Lambda на локальном уровне
Перед деплоем функции необходимо протестировать ее локально, чтобы удостовериться, что она работает так, как ожидалось. Для этого выполните следующую команду SAM в корневом каталоге вашего проекта:
$ sam local start-api
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2021-11-25 20:56:52 * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
Для выполнения этой команды требуется Docker, поэтому убедитесь, что он установлен и запущен на вашем компьютере. В противном случае вы можете получить сообщение об ошибке, подобное показанному ниже:
Error: Running AWS SAM projects locally requires Docker. Have you got it installed and running?
Когда приложение работает, сделайте GET-запрос на http://localhost:3000/hello
. Это заставит AWS SAM запустить контейнер Docker для выполнения функции. Как только контейнер запущен и работает, функция будет выполнена, и вернется следующий результат:
$ curl http://localhost:3000/hello
{"handler":"app.lambdaHandler","aws_region":"us-east-1","aws_execution_env":"AWS_Lambda_nodejs14.x","aws_lambda_function_name":"128","aws_lambda_function_version":"$LATEST","aws_lambda_log_group_name":"aws/lambda/HelloWorldFunction","aws_lambda_log_stream_name":"$LATEST","aws_lambda_runtime_api":"127.0.0.1:9001","lang":"en_US.UTF-8","tz":":/etc/localtime","lambda_task_root":"/var/task","lambda_runtime_dir":"/var/runtime","path":"/var/lang/bin:/usr/local/bin:/usr/bin/:/bin:/opt/bin","ld_library_path":"/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib"}
Вы можете использовать jq (если он у вас установлен) для придания выводу более привлекательного вида:
$ curl http://localhost:3000/hello | jq
{
"handler": "app.lambdaHandler",
"aws_region": "us-east-1",
"aws_execution_env": "AWS_Lambda_nodejs14.x",
"aws_lambda_function_name": "128",
"aws_lambda_function_version": "$LATEST",
"aws_lambda_log_group_name": "aws/lambda/HelloWorldFunction",
"aws_lambda_log_stream_name": "$LATEST",
"aws_lambda_runtime_api": "127.0.0.1:9001",
"lang": "en_US.UTF-8",
"tz": ":/etc/localtime",
"lambda_task_root": "/var/task",
"lambda_runtime_dir": "/var/runtime",
"path": "/var/lang/bin:/usr/local/bin:/usr/bin/:/bin:/opt/bin",
"ld_library_path": "/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib"
}
Вы также можете протестировать свою функцию Lambda, не делая HTTP-запрос для ее активации. SAM CLI предоставляет возможность вызвать функцию с помощью предопределенного JSON-файла. Выполните следующую команду в корне вашего проекта, чтобы опробовать этот способ:
$ sam local invoke "HelloWorldFunction" --event events/event.json
Invoking app.lambdaHandler (nodejs14.x)
Skip pulling image and use local one: amazon/aws-sam-cli-emulation-image-nodejs14.x:rapid-1.21.1.
Mounting /home/ayo/aws-lambda-nodejs-example/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container
START RequestId: 5ee01a30-d649-484d-9b9c-124ba3669525 Version: $LATEST
END RequestId: 5ee01a30-d649-484d-9b9c-124ba3669525
REPORT RequestId: 5ee01a30-d649-484d-9b9c-124ba3669525 Init Duration: 2.44 ms Duration: 584.60 ms Billed Duration: 600 ms Memory Size: 128 MB Max Memory Used: 128 MB
{"statusCode":200,"body":"{\"handler\":\"app.lambdaHandler\",\"aws_region\":\"us-east-1\",\"aws_execution_env\":\"AWS_Lambda_nodejs14.x\",\"aws_lambda_function_name\":\"128\",\"aws_lambda_function_version\":\"$LATEST\",\"aws_lambda_log_group_name\":\"aws/lambda/HelloWorldFunction\",\"aws_lambda_log_stream_name\":\"$LATEST\",\"aws_lambda_runtime_api\":\"127.0.0.1:9001\",\"lang\":\"en_US.UTF-8\",\"tz\":\":/etc/localtime\",\"lambda_task_root\":\"/var/task\",\"lambda_runtime_dir\":\"/var/runtime\",\"path\":\"/var/lang/bin:/usr/local/bin:/usr/bin/:/bin:/opt/bin\",\"ld_library_path\":\"/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib\"}
The event that triggers a Lambda function usually comes with a JSON payload.
Событие, инициирующее функцию Lambda, обычно поступает с JSON-пейлоад. Вы должны предоставить эту пейлоад с помощью опции --event
(как показано выше), чтобы активировать функцию локально. Эта пейлоад передается в качестве первого аргумента функции Lambda. Файл event.json
создается SAM CLI при инициализации проекта, поэтому его можно использовать для этой цели. Узнайте больше о событиях.
Когда вы активируете функцию локально, то получите следующую информацию:
образ контейнера Docker, использованный для выполнения функции
как долго она выполнялась
сколько памяти было использовано
Кроме того, вы получите фактическое возвращаемое значение вашей функции ниже информации о рантайме.
Деплой функции Lambda в облако AWS
После того как вы будете удовлетворены работой вашей функции на локальном уровне, можно развернуть ее в облаке AWS Cloud с помощью SAM CLI. Сначала запустите сборку sam build
для того, чтобы сгенерировать артефакты, ориентированные на среду выполнения AWS Lambda:
$ sam build
Building codeuri: /home/ayo/dev/demo/aws-lambda-nodejs-example/hello-world runtime: nodejs14.x metadata: {} architecture: x86_64 functions: ['HelloWorldFunction']
Running NodejsNpmBuilder:NpmPack
Running NodejsNpmBuilder:CopyNpmrc
Running NodejsNpmBuilder:CopySource
Running NodejsNpmBuilder:NpmInstall
Running NodejsNpmBuilder:CleanUpNpmrc
Build Succeeded
Затем запустите sam deploy --guided
для деплоя функции и ответьте на предложенные подсказки, как показано ниже:
$ sam deploy --guided
Configuring SAM deploy
======================
Looking for config file [samconfig.toml] : Not found
Setting default arguments for 'sam deploy'
=========================================
Stack Name [sam-app]:
AWS Region [us-east-1]:
#Shows you resources changes to be deployed and require a 'Y' to initiate deploy
Confirm changes before deploy [y/N]: y
#SAM needs permission to be able to create roles to connect to the resources in your template
Allow SAM CLI IAM role creation [Y/n]: y
#Preserves the state of previously provisioned resources when an operation fails
Disable rollback [y/N]: n
HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: y
Save arguments to configuration file [Y/n]: y
SAM configuration file [samconfig.toml]: y
SAM configuration environment [default]:
. . .
После успешного деплоя вы увидите URL API-шлюза на выходе. Он должен иметь следующую структуру:
https://<API_ID>.execute-api.<AWS_REGION>.amazonaws.com/Prod/hello/
После того как вы сделаете GET-запрос к этому URL, ваша функция будет выполнена, и вы получите результат, аналогичный предыдущему:
$ curl https://s032akg5bh.execute-api.us-east-1.amazonaws.com/Prod/hello/ | jq
{
"handler": "app.lambdaHandler",
"aws_region": "us-east-1",
"aws_execution_env": "AWS_Lambda_nodejs14.x",
"aws_lambda_function_name": "128",
"aws_lambda_function_version": "$LATEST",
"aws_lambda_log_group_name": "/aws/lambda/sam-app-HelloWorldFunction-QK428gXjzGYj",
"aws_lambda_log_stream_name": "2021/11/26/[$LATEST]46ab610defd746fcae6474da20515190",
"aws_lambda_runtime_api": "127.0.0.1:9001",
"lang": "en_US.UTF-8",
"tz": ":UTC",
"lambda_task_root": "/var/task",
"lambda_runtime_dir": "/var/runtime",
"path": "/var/lang/bin:/usr/local/bin:/usr/bin/:/bin:/opt/bin",
"ld_library_path": "/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib"
}
Поздравляем, вы успешно выполнили деплой своей первой функции Lambda в продакшн!
Использование пакетов NPM в функциях Lambda AWS
Давайте продолжим и создадим новую функцию Lambda, которая использует некоторые пакеты NPM для выполнения задачи по веб-скрейпингу. Создайте новую папку quotes-scraper
в каталоге проекта и инициализируйте ее с помощью файла package.json
:
$ mkdir quotes-scraper
$ cd quotes-scraper
$ npm init -y
После этого создайте файл app.js
в корне папки quotes-scraper
и заполните его следующим содержимым:
const axios = require("axios");
const cheerio = require("cheerio");
const url = "http://quotes.toscrape.com/";
exports.lambdaHandler = async (_event, _context) => {
try {
const response = await axios(url);
const $ = cheerio.load(response.data);
const container = $(".container .quote");
const quotes = [];
container.each(function () {
const text = $(this).find(".text").text();
const author = $(this).find(".author").text();
const tags = $(this).find(".tag");
const tagArray = [];
tags.each(function () {
const tagText = $(this).text();
tagArray.push(tagText);
});
quotes.push({
text,
author,
tag: tagArray,
});
});
return {
statusCode: 200,
body: JSON.stringify(quotes),
};
} catch (err) {
console.log(err);
throw err;
}
};
Этот код скрейпит цитаты на этом веб-сайте и возвращает их в виде JSON-объекта. Он использует axios для получения HTML и cheerio при извлечении соответствующих частей. Убедитесь, что вы установили обе зависимости в папку quotes-scraper
:
$ npm install axios cheerio
После этого откройте файл template.yml
в корне вашего проекта и добавьте следующий код в раздел Resources
:
Resources:
. . .
QuotesScraperFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: quotes-scraper/
Handler: app.lambdaHandler
Runtime: nodejs14.x
Events:
QuotesScraper:
Type: Api
Properties:
Path: /quotes
Method: get
Затем добавьте следующий сниппет в раздел Output
:
Output:
. . .
QuotesScraperApi:
Description: "API Gateway endpoint URL for Prod stage for Quotes Scraper function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/quotes/"
QuotesScraperFunction:
Description: "Quotes Scraper Function ARN"
Value: !GetAtt QuotesScraperFunction.Arn
QuotesScraperFunctionIamRole:
Description: "Implicit IAM Role created for Quotes Scraper function"
Value: !GetAtt QuotesScraperFunctionRole.Arn
Сохраните и закройте файл, а потом задействуйте свою новую функцию через SAM CLI:
$ sam build
$ sam local invoke "QuotesScraperFunction" | jq
[
{
text: '“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”',
author: 'Albert Einstein',
tag: [ 'change', 'deep-thoughts', 'thinking', 'world' ]
},
{
text: '“It is our choices, Harry, that show what we truly are, far more than our abilities.”',
author: 'J.K. Rowling',
tag: [ 'abilities', 'choices' ]
},
. . .
]
Далее выполните деплой вашей функции с помощью команды sam deploy
. После этого вы сможете вызывать функцию через эндпоинт API-шлюза:
https://<API_ID>.execute-api.<AWS_REGION>.amazonaws.com/Prod/quotes/
Создание бессерверных API с помощью AWS Lambda и Node.js: подведение итогов и следующие шаги
Надеюсь, данная статья помогла вам изучить основы создания бессерверных API с помощью AWS Lambda и Node.js.
Существует множество других тем, касающихся функций Lambda, которые мы здесь не рассмотрели, включая аутентификацию, логирование и мониторинг, кэширование, сохранение в базе данных и многое другое.
Чтобы закрепить знания, полученные в настоящем руководстве, прочитайте эти темы и ознакомьтесь с некоторыми рекомендациями по эффективной работе с функциями AWS Lambda.
Скоро в OTUS состоится открытое занятие «Многопоточность и обработка данных в Node.js». На нем мы рассмотрим несколько способов реализации многопоточности с Worker API и разберем варианты взаимодействия процессов. Занятие проведет Юрий Дворжецкий, ведущий fullstack-разработчик. Регистрация — по ссылке.