Статьи

Бессерверная разработка с Node.js, AWS Lambda и MongoDB Atlas

Эта статья была первоначально опубликована на mongoDB . Спасибо за поддержку партнеров, которые делают возможным использование SitePoint.

За последние годы ландшафт разработчиков сильно изменился. Для нас, разработчиков, было довольно распространенным явлением запускать все наши инструменты (базы данных, веб-серверы, среды разработки…) на наших собственных машинах, но облачные сервисы, такие как GitHub , MongoDB Atlas и AWS Lambda , радикально меняют игру. Они значительно облегчают разработчикам написание и запуск кода в любом месте и на любом устройстве без (или очень немногих) зависимостей.

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

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

И это именно то, что я постараюсь показать вам в этом посте: как легко интегрировать функцию AWS Lambda Node.js с базой данных MongoDB, размещенной в MongoDB Atlas , DBaaS (база данных как услуга) для MongoDB. Более конкретно, мы напишем простую функцию Lambda, которая создает один документ в коллекции, хранящейся в базе данных MongoDB Atlas. Я проведу вас через этот учебник шаг за шагом, и вы должны сделать это менее чем за час.

Давайте начнем с необходимых требований, чтобы вы начали работать:

  1. Учетная запись веб-служб Amazon доступна пользователю, имеющему административный доступ к службам IAM и Lambda. Если у вас его еще нет, зарегистрируйте бесплатный аккаунт AWS .
  2. Локальный компьютер с Node.js (я говорил вам, что мы не будем так легко избавляться от локальных сред разработки…). Мы будем использовать Mac OS X в приведенном ниже учебном пособии, но эти задачи должны быть относительно простыми в Windows или Linux.
  3. Группа Атласа MongoDB жива и здорова. Если у вас его еще нет, зарегистрируйте бесплатную учетную запись MongoDB Atlas и создайте кластер всего за несколько кликов. Вы даже можете попробовать наш бесплатный уровень кластера M0, идеально подходящий для небольших проектов разработки!).

Теперь, когда вы знаете о требованиях, давайте поговорим о конкретных шагах, которые мы предпримем для написания, тестирования и развертывания нашей функции Lambda:

  1. MongoDB Atlas по умолчанию безопасен, но как разработчики приложений, мы должны предпринять шаги, чтобы убедиться, что наше приложение соответствует рекомендациям по доступу с наименьшими привилегиями . А именно, мы настроим разрешения, создав пользователя базы данных MongoDB Atlas с доступом только на чтение / запись к нашей базе данных приложения.
  2. Мы настроим проект Node.js на нашем локальном компьютере, и мы обязательно проверим наш лямбда-код локально, а затем развернем его в Amazon Web Services.
  3. Затем мы создадим нашу функцию AWS Lambda и загрузим наш проект Node.js для его инициализации.
  4. И последнее, но не менее важное: мы внесем некоторые изменения в нашу функцию Lambda, чтобы зашифровать некоторые конфиденциальные данные (например, строку подключения MongoDB Atlas) и расшифровать их из кода функции.

Краткая заметка о VPC Peering

Я не буду вдаваться в подробности настройки пиринга VPC между нашим кластером MongoDB Atlas и AWS Lambda по двум причинам: 1) у нас уже есть подробная страница документации по пирингу VPC и пост VPC пиринга в атласе, который я настоятельно рекомендую, и 2) Кластеры M0 (которые я использовал для создания этой демонстрации) не поддерживают пиринг VPC .

Вот что происходит, если вы не настроили пиринг VPC:

  1. Вам придется добавить печально известный CIDR-блок 0.0.0.0/0 в белый список IP-адресов кластера MongoDB Atlas, поскольку вы не будете знать, какой IP-адрес AWS Lambda использует для звонков в базу данных Atlas.
  2. Вы будете платить за использование полосы пропускания между вашей функцией Lambda и вашим кластером Atlas.

Если вы только пытаетесь написать этот демонстрационный код, эти 2 предостережения, вероятно, подойдут, но если вы планируете развернуть готовую интеграцию Lambda-Atlas, готовую к работе, настройка пиринга VPC — это лучший метод безопасности, который мы настоятельно рекомендуем , M0 — наше текущее бесплатное предложение; Посетите нашу страницу ценообразования MongoDB Atlas для ознакомления со всем диапазоном доступных размеров экземпляров.

Напоминаем, что для сред разработки и веб-сайтов с низким трафиком размеры экземпляров M0, M10 и M20 должны быть хорошими. Однако для производственных сред, которые поддерживают приложения с большим трафиком или большие наборы данных, рекомендуется использовать экземпляры M30 или более крупных размеров.

Настройка безопасности в вашем кластере MongoDB Atlas

Обеспечение соответствия вашего приложения политикам доступа с наименьшими привилегиями имеет решающее значение для защиты ваших данных от злонамеренных угроз. Вот почему мы настроим конкретного пользователя базы данных, который будет иметь доступ только на чтение / запись к нашей базе данных путешествий. Давайте посмотрим, как этого добиться в MongoDB Atlas:

На странице « Кластеры» выберите вкладку « Безопасность » и нажмите кнопку « Добавить нового пользователя».

Кластеры

Лямбда-пользователь

В разделе « Права пользователя » выберите ссылка на сайт. Это позволяет нам назначать чтение / запись для конкретной базы данных, а не для любой базы данных.

пользовательские привилегии

Затем у вас будет возможность назначить более детальные права доступа:

Контроль доступа

В раскрывающемся списке Select Role выберите readWrite и заполните поле Database с названием базы данных, которую вы будете использовать для хранения документов. Я решил назвать это travel .

Выберите роли

В разделе « Пароль » используйте кнопку « Автогенерация безопасного пароля» (и запишите сгенерированный пароль) или установите пароль по своему вкусу. Затем нажмите кнопку Добавить пользователя , чтобы подтвердить создание этого пользователя.

Давайте возьмем строку подключения к кластеру, пока она у нас, так как она нам понадобится для подключения к нашей базе данных MongoDB Atlas в нашем коде Lambda:

Предполагая, что вы уже создали кластер MongoDB Atlas , нажмите кнопку Connect рядом с вашим кластером:

Подключить кластер

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

Строка подключения URI

Кроме того, если вы не используете пиринг VPC, перейдите на вкладку «Белый список IP-адресов » и добавьте блок CIDR 0.0.0.0/0 или нажмите кнопку « Разрешить доступ из любого места» . Напоминаем, что этот параметр настоятельно НЕ рекомендуется для производственного использования и потенциально делает ваш кластер MongoDB Atlas уязвимым для злонамеренных атак.

Добавить запись в белый список

Создать локальный проект Node.js

Хотя лямбда-функции поддерживаются на нескольких языках, я решил использовать Node.js благодаря растущей популярности JavaScript в качестве универсального языка программирования и огромному успеху стеков MEAN и MERN (сокращений для M ongoDB, E xpress.js, Angular / R eact, N ode.js — ознакомьтесь с отличной серией блогов Эндрю Моргана, посвященной разработчикам на эту тему). Плюс, если честно, мне нравится тот факт, что это интерпретированный, легкий язык, который не требует тяжелых инструментов разработки и компиляторов.

Пришло время написать некоторый код, так что давайте продолжим и будем использовать Node.js в качестве нашего предпочтительного языка для нашей функции Lambda.

Начните с создания папки, такой как lambda-atlas-create-doc

 mkdir lambda-atlas-create-doc && cd lambda-atlas-create-doc 

Затем выполните следующую команду из консоли терминала, чтобы инициализировать наш проект с файлом package.json.

 npm init 

Вам будет предложено настроить несколько полей. Я оставлю их на ваше усмотрение, но учтите, что я решил установить точку входа app.js (вместо index.js по умолчанию), так что вы можете захотеть сделать то же самое.

Нам потребуется использовать драйвер MongoDB Node.js, чтобы мы могли подключиться к нашей базе данных MongoDB (в Atlas) из нашей функции Lambda, поэтому давайте продолжим и установим ее, выполнив следующую команду из корня нашего проекта:

 npm install mongodb --save 

Мы также хотим написать и протестировать нашу лямбда-функцию локально, чтобы ускорить разработку и упростить отладку, поскольку создание лямбда-функции каждый раз в Amazon Web Services не особенно быстро (а отладка практически отсутствует, если только вы не фанат функции console.log() ). Я решил использовать пакет lambda-local, потому что он обеспечивает поддержку переменных среды (которые мы будем использовать позже):

 (sudo) npm install lambda-local -g 

Создайте файл app.js Это будет файл, который содержит нашу лямбда-функцию:

 touch app.js 

Теперь, когда вы импортировали все необходимые зависимости и создали файл кода Lambda, откройте файл app.js в выбранном вами редакторе кода (Atom, Sublime Text, Код Visual Studio …) и инициализируйте его следующим фрагментом кода:

 'use strict' var MongoClient = require('mongodb').MongoClient; let atlas_connection_uri; let cachedDb = null; exports.handler = (event, context, callback) => { var uri = process.env['MONGODB_ATLAS_CLUSTER_URI']; if (atlas_connection_uri != null) { processEvent(event, context, callback); } else { atlas_connection_uri = uri; console.log('the Atlas connection string is ' + atlas_connection_uri); processEvent(event, context, callback); } }; function processEvent(event, context, callback) { console.log('Calling MongoDB Atlas from AWS Lambda with event: ' + JSON.stringify(event)); } 

Давайте сделаем небольшую паузу и прокомментируем приведенный выше код, поскольку вы могли заметить несколько своеобразных конструкций:

  • Файл написан точно так же, как и лямбда-код, который ожидает Amazon Web Services (например, с функцией export.handler). Это потому, что мы используем lambda-local для локального тестирования нашей лямбда-функции, что позволяет нам писать наш код именно так, как его ожидает AWS Lambda. Подробнее об этом через минуту.
  • Мы объявляем драйвер MongoDB Node.js, который поможет нам подключиться и выполнить запрос к нашей базе данных MongoDB.
  • Также обратите внимание, что мы объявляем объект cachedDb ВНЕ от функции-обработчика. Как следует из названия, это объект, который мы планируем кэшировать на время работы базового контейнера, который AWS Lambda создает для нашей функции. Это позволяет нам сэкономить драгоценные миллисекунды (и даже секунды) для создания соединения с базой данных между Lambda и MongoDB Atlas. Для получения дополнительной информации, пожалуйста, прочитайте мой следующий пост в блоге о том, как оптимизировать производительность Lambda с помощью MongoDB Atlas .
  • Мы используем переменную окружения под названием MONGODB_ATLAS_CLUSTER_URI для передачи строки соединения uri нашей базы данных Atlas, главным образом в целях безопасности: очевидно, мы не хотим жестко кодировать этот uri в нашем коде функции вместе с очень конфиденциальной информацией, такой как имя пользователя и пароль мы используем. Так как AWS Lambda поддерживает переменные среды с ноября 2016 года (как это делает пакет lambda-local NPM), мы бы не стали их использовать.
  • Код функции выглядит немного запутанным с, казалось бы, бесполезным оператором if-else и функцией processEvent, но все это станет понятным, когда мы добавим подпрограммы дешифрования с использованием AWS Key Management Service (KMS). Действительно, мы не только хотим сохранить нашу строку подключения MongoDB Atlas в переменной среды, но мы также хотим зашифровать ее (используя AWS KMS), поскольку она содержит высокочувствительные данные (обратите внимание, что при использовании AWS KMS могут возникать дополнительные расходы). если у вас есть бесплатный аккаунт AWS).

Теперь, когда мы закончили с комментариями к коду, давайте создадим файл event.json (в корневом каталоге проекта) и заполните его следующими данными:

 { "address" : { "street" : "2 Avenue", "zipcode" : "10075", "building" : "1480", "coord" : [ -73.9557413, 40.7720266 ] }, "borough" : "Manhattan", "cuisine" : "Italian", "grades" : [ { "date" : "2014-10-01T00:00:00Z", "grade" : "A", "score" : 11 }, { "date" : "2014-01-16T00:00:00Z", "grade" : "B", "score" : 17 } ], "name" : "Vella", "restaurant_id" : "41704620" } 

(если вам интересно, этот JSON-файл — это то, что мы отправим в MongoDB Atlas для создания нашего документа BSON )

Затем убедитесь, что вы правильно настроены, выполнив следующую команду в консоли терминала:

 lambda-local -l app.js -e event.json -E {\"MONGODB_ATLAS_CLUSTER_URI\":\"mongodb://lambdauser:$PASSWORD@lambdademo-shard-00-00-7xh42.mongodb.net:27017\,lambdademo-shard-00-01-7xh42.mongodb.net:27017\,lambdademo-shard-00-02-7xh42.mongodb.net:27017/$DATABASE?ssl=true\&replicaSet=lambdademo-shard-0\&authSource=admin\"} 

Если вы хотите протестировать его с помощью собственной строки подключения URI кластера (как я уверен, вы делаете это), не забудьте экранировать двойные кавычки, запятые и символы амперсанда в параметре E, иначе lambda-local выдаст ошибку (вам также следует заменить ключевые слова $ PASSWORD и $ DATABASE своими собственными значениями).

После того, как вы запустите его локально, вы должны получить следующий вывод консоли:

Консольный вывод

Если вы получили ошибку, проверьте строку подключения и двойные кавычки / запятые / амперсанд и экранирование (как отмечено выше).

Теперь давайте перейдем к createDoc() нашего кода функции, настроив функцию processEvent() и добавив функцию createDoc() :

 function processEvent(event, context, callback) { console.log('Calling MongoDB Atlas from AWS Lambda with event: ' + JSON.stringify(event)); var jsonContents = JSON.parse(JSON.stringify(event)); //date conversion for grades array if(jsonContents.grades != null) { for(var i = 0, len=jsonContents.grades.length; i connecting to database'); MongoClient.connect(atlas_connection_uri, function (err, db) { cachedDb = db; return createDoc(db, jsonContents, callback); }); } else { createDoc(cachedDb, jsonContents, callback); } } catch (err) { console.error('an error occurred', err); } } function createDoc (db, json, callback) { db.collection('restaurants').insertOne( json, function(err, result) { if(err!=null) { console.error("an error occurred in createDoc", err); callback(null, JSON.stringify(err)); } else { console.log("Kudos! You just created an entry into the restaurants collection with id: " + result.insertedId); callback(null, "SUCCESS"); } //we don't need to close the connection thanks to context.callbackWaitsForEmptyEventLoop = false (above) //this will let our function re-use the connection on the next called (if it can re-use the same Lambda container) //db.close(); }); }; 

Обратите внимание, как просто подключиться к базе данных MongoDB Atlas и вставить документ, а также небольшой фрагмент кода, который я добавил для преобразования дат JSON (отформатированных в виде ISO-совместимых строк) в реальные даты JavaScript, которые MongoDB может хранить как даты BSON. ,

Возможно, вы также заметили мои комментарии по оптимизации производительности и вызов context.callbackWaitsForEmptyEventLoop = false. Если вам интересно понять, что они означают (и я думаю, что вам следует!), Пожалуйста, обратитесь к моему последнему сообщению в блоге о том, как оптимизировать производительность Lambda с помощью MongoDB Atlas.

Теперь вы готовы полностью протестировать свою лямбда-функцию локально. Используйте ту же команду lambda-local, что и раньше, и, надеюсь, вы получите хорошее сообщение об успехе «Kudos»:

Консольный вывод

Если все прошло хорошо на вашей локальной машине, давайте опубликуем наш локальный проект Node.js как новую функцию Lambda!

Создайте лямбда-функцию

Первый шаг, который мы хотим сделать, это заархивировать наш проект Node.js, поскольку мы не будем писать функцию лямбда-кода в редакторе лямбда-кода. Вместо этого мы выберем метод zip upload, чтобы наш код был передан в AWS Lambda.

Я использовал инструмент командной строки zip в консоли терминала, но любой метод работает (пока вы архивируете файлы внутри верхней папки, а не самой верхней папки!):

 zip -r archive.zip node_modules/ app.js package.json 

Затем войдите в консоль AWS, перейдите на страницу ролей IAM и создайте роль (например, LambdaBasicExecRole) с политикой разрешений AWSLambdaBasicExecutionRole :

AWS Lambda Basic Исполнение Роль

Теперь перейдем к странице AWS Lambda. Нажмите « Начать сейчас» (если вы никогда не создавали лямбда-функцию) или кнопку « Создать лямбда-функцию» . Мы не собираемся использовать какие-либо схемы и не будем настраивать триггеры, поэтому выберите « Настроить функцию» прямо на левой навигационной панели:

AWS Lambda Configure

На странице настройки функции введите имя для вашей функции (например, MongoDB_Atlas_CreateDoc ). Время выполнения автоматически устанавливается Node.js 4.3 , что идеально для нас, поскольку именно этот язык мы будем использовать. В списке Тип ввода кода выберите Upload a .ZIP file , как показано на снимке экрана ниже:

Настроить функцию

Нажмите кнопку « Загрузить» и выберите ранее созданный файл проекта Node.js.

В разделе обработчика и роли Lambda-функции измените значение поля Handler на app.handler (почему? Вот подсказка: я использовал файл app.js , а не файл index.js для кода своей функции Lambda …) и выберите существующая роль LambdaBasicExecRole которую мы только что создали:

Лямбда-обработчик функций

В разделе « Дополнительные настройки » вы можете увеличить значение « Время ожидания» до 5 или 10 секунд, но это всегда можно настроить позже. Оставьте значения полей ключа VPC и KMS по умолчанию (если вы не хотите использовать ключ VPC и / или ключ KMS) и нажмите Далее .

Наконец, просмотрите вашу лямбда-функцию и нажмите « Создать функцию» внизу. Поздравляем, ваша функция Lambda активна, и вы должны увидеть страницу, похожую на следующий снимок экрана:

Лямбда-функция создания

Но помните ли вы, как мы использовали переменные окружения? Настало время настроить их и использовать сервис управления ключами AWS для их защиты!

Настройте и защитите свои переменные среды Lambda

Прокрутите вниз на вкладке « Код » вашей функции Lambda и создайте переменную среды со следующими свойствами:

имя Значение
MONGODB_ATLAS_CLUSTER_URI YOUR_ATLAS_CLUSTER_URI_VALUE

Переменные среды

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

Установите флажок Включить помощники шифрования и, если вы уже создали ключ шифрования, выберите его (в противном случае вам, возможно, придется его создать — это довольно просто):

Ключ для подписи

Затем нажмите кнопку « Зашифровать» для переменной MONGODB_ATLAS_CLUSTER_URI:

Выберите шифрование

Вернувшись в редактор встроенного кода, добавьте следующую строку вверху:

 const AWS = require('aws-sdk'); 

и замените содержимое оператора else в методе exports.handler следующим кодом:

 const kms = new AWS.KMS(); kms.decrypt({ CiphertextBlob: new Buffer(uri, 'base64') }, (err, data) => { if (err) { console.log('Decrypt error:', err); return callback(err); } atlas_connection_uri = data.Plaintext.toString('ascii'); processEvent(event, context, callback); }); 

(надеюсь, запутанный код, который мы изначально написали, теперь имеет смысл!)

Если вы хотите проверить весь код функции, который я использовал, посмотрите следующую Gist . А для поклонников Git полный исходный код проекта Node.js также доступен на GitHub .

Теперь нажмите кнопку « Сохранить и проверить» и в текстовом редакторе « Входные тестовые события» вставьте содержимое нашего файла event.json:

Входное тестовое событие

Прокрутите и нажмите кнопку Сохранить и проверить .

Если вы все настроили правильно, вы должны получить следующее сообщение об успехе в выводе Lambda Log:

Выход лямбда-лога

Престижность! Вы можете насладиться успехом за несколько минут до того, как начнете читать.

Что дальше?

Я надеюсь, что это учебное пособие по интеграции AWS Lambda-MongoDB Atlas предоставит вам правильные шаги для начала вашего первого проекта Lambda. Теперь вы сможете писать и тестировать функцию Lambda локально и безопасно хранить конфиденциальные данные (например, строку подключения MongoDB Atlas) в AWS KMS.

Так что вы можете сделать дальше?

И, конечно же, не стесняйтесь задавать нам любые вопросы или оставить свой отзыв в комментарии ниже. Удачного кодирования!

Понравился этот пост? Повторите наш вебинар, где у нас есть интерактивное руководство по архитектуре без серверов с AWS Lambda.