Давайте узнаем, как использовать Serverless Framework для автоматизации серверной части AWS🔥

Эта статья является частью серии:

Репозиторий GitHub: https://github.com/serverless-guru/real-time-weather

В первой части мы рассказали, как подключить AWS IoT MQTT к интерфейсу ReactJS. Предоставление нашему приложению ReactJS общения в режиме реального времени, при котором каждое устройство будет мгновенно * обновляться при публикации нового сообщения в нашей теме MQTT.

Во второй части мы расширим приложение, включив в него Serverless Framework, и сделаем следующее:

  • Создайте базовый проект Serverless Framework
  • Ознакомьтесь с лучшими практиками с помощью Serverless Framework
  • Добавить ресурсы AWS
  • Развернуть в AWS
  • Протестируйте развернутую серверную часть AWS

Давай займемся этим!

Создайте базовый проект Serverless Framework

Если вы не знакомы с Serverless Framework и / или еще не настроили учетную запись AWS. Пожалуйста, прочтите нашу статью Введение в Serverless Framework или, при желании, посетите наш обучающий сайт и пройдите курс Serverless Introduction.

Создать новый проект

Мы создадим новый проект, используя шаблон Serverless Framework, который автоматически настраивает функцию NodeJS Lambda, готовую к развертыванию в AWS. Поскольку мы уже создали проект в прошлый раз, просто убедитесь, что вы находитесь в каталоге проекта.

sls create -t aws-nodejs -p backend -n real-time-weather

Эта команда создаст новый бессерверный проект с использованием шаблона aws-nodejs, который даст нам некоторый шаблон в папке с именем backend с именем службы, равным real-time-weather 🔥 🔥

Сначала давайте откроем наш любимый текстовый редактор, а затем я покажу вам, как копировать / вставлять, как старший облачный разработчик!

$real-time-weather: code .

Обновите раздел провайдера (добавьте флаги командной строки)

Мы захотим иметь возможность передавать динамическую stage переменную, динамическую region переменную и динамическую profile переменную (для именованных профилей AWS).

Эти переменные помогут обеспечить:

  • имена ресурсов AWS могут быть разными (многоэтапные развертывания)
  • регион может быть другим (многорегиональное развертывание)
  • аккаунт AWS может быть другим (развертывание с несколькими аккаунтами)

Скопируем следующий код в наш serverless.yml файл:

provider:
  name: aws
  runtime: nodejs8.10
  stage: ${opt:stage, "dev"}
  region: ${opt:region, "us-west-2"}
  profile: ${opt:profile, "default"}

Это добавит следующее:

  • поставщик aws
  • время выполнения nodejs8.10
  • необязательный флаг stage со значением по умолчанию stage dev
  • необязательный флаг region со значением по умолчанию region us-west-2
  • флаг опции profile со значением по умолчанию profile default

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

serverless deploy --stage <> --region <> --profile <> -v

Теперь, когда resources/provider.yml изолирован, давайте обновим serverless.yml, чтобы он ссылался на наш отдельный файл.

provider: ${file(resources/provider.yml)}

Теперь, когда мы перейдем к развертыванию, свойство provider будет заполнено файлом resources/provider.yml.

Создать пользовательские переменные

Давайте создадим custom.yml файл в нашей resources папке по адресу resources/custom.yml. Этот файл будет содержать все наши пользовательские переменные, которые мы можем централизовать в одном месте вместо жесткого кодирования в нескольких местах.

tableName: ${self:service}-${self:provider.stage}-data

На что тоже стоит обратить внимание:

  • ${self:service} невероятно похож на real-time-weather
  • ${self:provider.stage} равно dev или результат --stage <>

Теперь, когда resources/custom.yml изолирован, давайте обновим serverless.yml, чтобы он ссылался на наш отдельный файл.

custom: ${file(resources/custom.yml)}

Теперь, когда мы перейдем к развертыванию, свойство custom будет заполнено файлом resources/custom.yml, как если бы оно было написано встроенным.

Добавить ресурсы AWS

Теперь, когда у нас есть бессерверный проект. Давайте начнем добавлять ✨ в наши ресурсы AWS. Мы добавим следующее:

  • DynamoDB - база данных NoSQL
  • Лямбда - обрабатывает нашу внутреннюю логику.
  • API Gateway - управляет маршрутизацией REST API.

Добавить DynamoDB

Теперь давайте создадим general.yml файл в resources/general.yml и скопируем следующее:

WeatherTable:
  Type: AWS::DynamoDB::Table
  Properties:
    KeySchema:
      - AttributeName: zip
        KeyType: HASH
    AttributeDefinitions:
      - AttributeName: zip
        AttributeType: S
    BillingMode: PAY_PER_REQUEST
    TableName: ${self:custom.tableName}

Это создаст таблицу DynamoDB со следующими настройками:

  • первичный ключ zip, который будет строкой
  • режим выставления счетов для оплаты за запрос (например, нет трафика = бесплатно)

Давайте обновим файл serverless.yml, включив в него ссылку на наш resources/general.yml файл.

resources:
  Resources: ${file(resources/general.yml)}

Добавить лямбда-функцию API

Теперь давайте создадим functions.yml файл по адресу resources/functions.yml и скопируем следующее:

api:
  handler: api.handler
  events:
    - http:
        path: /api/weather
        method: GET
        cors: true
  environment:
    TABLE_NAME:
      Ref: WeatherTable

Здесь мы говорим о нескольких вещах:

  • подключите нашу Лямбду к функции handler() внутри api.js файла
  • создать API на /api/weather, который может обрабатывать запросы GET
  • установите cors: true, чтобы разрешить двустороннюю связь через API
  • передать переменную окружения TABLE_NAME в нашу лямбда-функцию
  • динамически ссылаться на имя таблицы DynamoDB через Ref: WeatherTable

Затем нам нужно обновить serverless.yml, чтобы использовать наш файл resources/functions.yml.

functions: ${file(resources/functions.yml)}

Добавить разрешения AWS IAM

Наша функция Lambda должна иметь соответствующие разрешения AWS IAM для доступа к DynamoDB и получения данных о погоде.

Давайте добавим раздел, который поможет в этом, под свойством provider в нашем serverless.yml файле.

provider:
  ...
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:GetItem
      Resource:
        Fn::GetAtt:
          - WeatherTable
          - Arn

Это сделает следующее:

  • предоставьте нашей лямбда-функции доступ для выполнения dynamodb:GetItem
  • указать мы можем только dynamodb:GetItem на WeatherTable

Проверить прогресс

Отлично, давайте пока рассмотрим файл serverless.yml.

service: real-time-weather
provider:
  name: aws
  runtime: nodejs8.10
  stage: ${opt:stage, "dev"}
  region: ${opt:region, "us-west-2"}
  profile: ${opt:profile, "default"}
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:GetItem
      Resource:
        Fn::GetAtt:
          - WeatherTable
          - Arn
custom: ${file(resources/custom.yml)}
functions: ${file(resources/functions.yml)}
resources:
  Resources: ${file(resources/general.yml)}

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

По мере роста ваших проектов у вас может быть 3+ таблиц DynamoDB, целый ряд поддерживающих сервисов AWS и 10+ функций Lambda, которым требуются пользовательские переменные. Как вы понимаете, с этим может стать трудно справиться, если все это будет помещено в файл serverless.yml.

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

Настроить лямбда-функцию API

Во-первых, давайте изменим файл handler.js на api.js. Затем мы можем вставить следующий код:

'use strict';
const AWS = require('aws-sdk');
const api = {};
const tableName = process.env.TABLE_NAME;
api.handler = async (event, context) => {
    console.log('event', event);
    let response = {};
    try {
        let data = {};
        let path = event.path;
        if(path.includes('weather')) {
            data = await api.handleWeather(event);
        }
        response.statusCode = 200;
        response.body = JSON.stringify(data);
    } catch (error) {
        response.statusCode = 500;
        response.body = JSON.stringify(error);
    }
    response.headers = { 'Access-Control-Allow-Origin': '*' };
    console.log('response: ', response);
    return response;
};
api.handleWeather = async (event) => {
    if (event.httpMethod === "GET") {
        return await api.getCurrentWeatherData({ zip: event.queryStringParameters.zipCode });
    } else {
        throw new Error(`event method not known ${event.httpMethod}`);
    }
};
api.getCurrentWeatherData = (key) => {
    return new Promise((resolve, reject) => {
        let documentClient = new AWS.DynamoDB.DocumentClient();
let params = {
            TableName: tableName,
            Key: key
        };
        
        documentClient.get(params, (err, data) => {
            if (err) reject(err);
            else resolve(data);
        });
    });
};
module.exports = api;

Этот код будет делать несколько вещей:

  • импорт aws-sdk, позволяющий нам подключиться к DynamoDB
  • ссылаться на переменную среды через process.env.TABLE_NAME
  • добавить handleWeather() функцию для управления всеми запросами API к /weather
  • добавить getCurrentWeatherData() функцию для запроса погоды по zip

Отсюда у нас должно быть все необходимое для получения данных через zip (например, 97205) и получения ответа из таблицы погоды DynamoDB с текущей погодой в нашем zip.

Разверните серверную часть в AWS

Создана вся наша автоматизация благодаря мощности Serverless Framework. Давайте попробуем развернуть наш бессерверный стек в AWS.

sls --stage test --region us-west-2 --profile default -v

Это развернет наш бэкэнд на этапеtest и в регионе us-west-2 с профилем AWS с именем default.

Загрузите данные в DynamoDB

В настоящее время у нас есть только конечная точка GET API, поэтому мы вручную добавим данные в DynamoDB, а затем попытаемся извлечь эти данные.

Протестируйте серверную часть AWS

После успешного развертывания серверной части мы можем протестировать API через Postman, отправив запрос с параметром queryStringParmeter равным ?zipCode=97205.

Замечательно, мы получили данные из DynamoDB и подтвердили, что наша автоматизация полностью работает, чтобы развернуть весь наш бэкэнд.

Срывать

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

sls remove --stage test --region us-west-2 --profile default -v

Это сделает следующее:

  • удалите нашу лямбда-функцию
  • удалите нашу конечную точку шлюза API на /api/weather
  • удалите нашу таблицу DynamoDB
  • удалите наши политики AWS IAM

В следующий раз мы пойдем еще глубже

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

Это позволяет нашим пользователям автоматически получать обновления погоды в реальном времени!

Дополнительный контент:

Что делает Serverless Guru?

Serverless Guru помогает компаниям создавать масштабируемые и экономичные приложения в облаке. Мы помогаем обучать компании тому, как использовать IAC, бессерверные и облачные сервисы. Мы помогаем перенести существующие приложения в облако и оптимизировать существующие приложения в облаке, чтобы сделать их более рентабельными. Мы являемся партнером по бессерверной разработке и партнером-консультантом AWS.

Что мы упустили?

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

Райан Джонс

Основатель и генеральный директор - Serverless Guru

LinkedIn - @ryanjonesirl

Twitter - @ryanjonesirl

Спасибо за чтение 😃

Если вы хотите узнать больше о Serverless Guru, подпишитесь на нас в Medium, Twitter, Instagram, Facebook или LinkedIn!