
Содержание статьи
от Чарли Ли

Слово «бессерверный» было популярно довольно давно. Когда Amazon выпустила службу AWS Lambda в 2015 году, появилось множество инструментов, которые помогали людям создавать бессерверные сервисы только с помощью нескольких команд. По сравнению с традиционными постоянно работающими службами бессерверные службы очень просты в разработке, развертывании и поддержке. Они также чрезвычайно экономичны, особенно для тех простых служб, у которых нет слишком большого трафика.
Итак, что такое бессерверное?
Как следует из названия, бессерверный означает, что вы запускаете службу без сервера. Ну, технически, все еще есть сервер, на котором работает служба, но вам как владельцу службы не нужно беспокоиться об этом сервере.
К примеру, AWS Lambda позволяет развернуть «функцию» для обработки запросов. AWS имеет сервер для выполнения всех функций, когда их запрашивают. Но вам не нужно беспокоиться о том, как работает этот сервер или как заставить ваш код работать с этим сервером. Все, что вам нужно знать, это то, что вы пишете функцию, а затем отправляете ее в службу Lambda.
И самое сладкое то, что это очень дешево. Amazon Lambda предоставляет 1 миллион бесплатных запросов и 400 000 ГБ-секунд бесплатных вычислений в месяц (это означает, что вычисления могут использовать 1 ГБ памяти в течение 400 000 секунд), чего достаточно для большинства небольших служб. По сравнению с EC2, где экземпляр nano будет стоить 0,0058 долларов в час (что составляет 0,14 долларов в день), Lambda гораздо дешевле.
Что мы будем делать здесь
В этой публикации я покажу вам, как создать небольшой персональный веб-сайт без сервера с помощью AWS. Веб-сайт имеет следующие характеристики:
- Включает как фронтенд, так и заднюю часть
- В основном статические или тяжелые передние панели
- Запросы API – они редки, но необходимы
- Бек-энд не требует слишком много памяти или ЦП (например, простой веб-счетчик, требующий только одного доступа к БД)
Наш сервис будет развернут в таких доменах (я использовал поддельные домены в этой публикации):
Бессерверное решение идеально как технически, так и с точки зрения стоимости. Мы будем использовать следующие сервисы AWS:
- Lambda+API Gateway+S3, для сервера API
- DynamoDB для хранения данных
- S3, для статического веб-хостинга
- Cloudfront, для распределенной CDN
- AWS Certificate Manager (ACM) для создания сертификатов для нашего веб-сайта https

Для сервера API мы будем использовать комбинацию Python+Flask и Zappa как бессерверный инструментарий.
Настройка среды AWS
Сначала нам нужно настроить среду AWS, чтобы мы могли получить доступ к AWS из нашего кода и zappa. Для этого нужно два шага:
- Нам нужно создать пользователь AWS для программного доступа
- Нам нужно настроить локальную среду AWS для использования для этого пользователя
Создайте пользователя AWS
Войдите в AWS и выберите службу «IAM» для управления учетными данными пользователя.
Создайте пользователя с именем «myservice-admin» (или любое другое имя пользователя, которое вы хотите использовать), и не забудьте пометить «Программный доступ” вариант.

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

Нажмите кнопку «Далее», затем кнопку «Создать пользователя» и пользователя myservice-admin
будет создан. На последнем экране Идентификатор ключа доступа и Секретный ключ доступа отображаются. Обязательно скопируйте и вставьте их в локальный файл. Это учетные данные API, которые мы будем использовать на следующем шаге.
Примечание: Это единственное место, где можно просмотреть секретные ключи доступа! Если вам не удастся сделать их копию, вам придется перейти на экран пользовательских данных и сгенерировать новую пару ключей доступа и секрета.
Настройте локальную среду AWS
Нам нужно создать локальную среду для использования AWS локально.
Сначала установим awscli
инструмент, который поможет нам настроить среду:
$ sudo apt install awscli
После установки мы настроим AWS с помощью aws configure
команда:
$ aws configureAWS Access Key ID [None]: ******AWS Secret Access Key [None]: ******Default region name [None]: us-east-1Default output format [None]: json
Здесь нам нужно ввести Идентификатор ключа доступа и Секретный ключ доступа мы получили с последнего шага. С точки зрения региона по умолчанию я использовал us-east-1
. Вы можете выбрать любой регион, который вам нравится, но другие регионы могут вызвать определенные проблемы при настройке CloudFront.
Создайте таблицу в DynamoDB
Чтобы сохранить значение счетчика посетителей сайта в DynamoDB, нам нужен постоянный магазин. Следовательно, нам нужно создать таблицу и заполнить в ней первоначальное значение.
На консоли AWS выберите DynamoDB. Затем нажмите кнопку «Создать таблицу” кнопка. На экране «Создать таблицу DynamoDB» заполните Имя таблицы с myservice-dev
и Первичный ключ поле с id
а затем нажмите кнопку Создать таблицу кнопку.

Через пару секунд таблица должна быть создана. Выберите новую таблицу, выберите Предметы вкладку на правой панели, а затем нажмите кнопку Создать элемент и создайте элемент с помощью id='counter'
и counter_value=0
.
Примечание: вам нужно нажать знак плюс слева, чтобы добавить
counter_value
атрибут, и не забудьте установить типcounter_value
к Номер.

Создайте службу API
Далее мы создадим службу API. Для демонстрационных целей эта служба API предоставит счетчику API, который повысит значение счетчика при нажатии. Значение счетчика будет сохранено в DynamoDB. Конечными точками API являются:
POST /counter/increase
увеличивает счетчик и возвращает значение счетчикаGET /counter
возвращает текущее значение счетчика
Кодирование службы API с помощью Python и Flask
Начнем с создания виртуальной среды Python и установки необходимых пакетов:
$ mkdir myservice && cd myservice$ python3 -m venv .env$ source .env/bin/activate(.env)$ pip install flask boto3 simplejson
flask
это веб-фреймворк и boto3
пакет необходим для доступа к DynamoDB. simplejson
может помочь нам решить некоторые проблемы с преобразованием JSON. Давайте создадим сервис, создав файл myservice.py
с содержанием ниже:
import boto3from flask import Flask, jsonify
app = Flask(__name__)
# Initialize dynamodb accessdynamodb = boto3.resource('dynamodb')db = dynamodb.Table('myservice-dev')
@app.route('/counter', methods=['GET'])def counter_get(): res = db.get_item(Key={'id': 'counter'}) return jsonify({'counter': res['Item']['counter_value']})
@app.route('/counter/increase', methods=['POST'])def counter_increase(): res = db.get_item(Key={'id': 'counter'}) value = res['Item']['counter_value'] + 1 res = db.update_item( Key={'id': 'counter'}, UpdateExpression='set counter_value=:value', ExpressionAttributeValues={':value': value}, ) return jsonify({'counter': value})
Создать run.py
файл, чтобы проверить эту службу API локально:
from myservice import appif __name__ == '__main__': app.run(debug=True, host="127.0.0.1", port=8000)
Теперь запустите службу:
(.env)$ python run.py
И мы можем протестировать эту службу с помощью таких команд (откройте другой терминал, чтобы ввести эти команды):
$ curl localhost:8000/counter{ "counter": 0}$ curl -X POST localhost:8000/counter/increase{ "counter": 1}$ curl -X POST localhost:8000/counter/increase{ "counter": 2}$ curl localhost:8000/counter{ "counter": 2}
Мы видим, что наш код работает и он успешно увеличивает счетчик!
Развертывание нашего кода на Lambda с помощью Zappa
Развернуть наш API на Lambda очень легко с zappa. Сначала нам нужно установить zappa:
(.env)$ pip install zappa
Затем инициализируйте среду zappa с помощью zappa init
. Он задаст вам несколько вопросов, но обычно вы можете использовать ответы по умолчанию для всех вопросов:
(.env)$ zappa init...What do you want to call this environment (default 'dev'): ...What do you want to call your bucket? (default 'zappa-ab7dd70x5'):
It looks like this is a Flask application.What's the modular path to your app's function?This will likely be something like 'your_module.app'.We discovered: myservice.appWhere is your app's function? (default 'myservice.app'): ...
Would you like to deploy this application globally? (default 'n') [y/n/(p)rimary]:
Okay, here's your zappa_settings.json:
{ "dev": { "app_function": "myservice.app", "aws_region": "us-east-1", "profile_name": "default", "project_name": "myservice", "runtime": "python3.6", "s3_bucket": "zappa-ab7dd70x5" }}
Does this look okay? (default 'y') [y/n]: ...
После инициализации мы можем увидеть сгенерированный zappa_settings.json
файл. Тогда мы можем начать развертывать наш сервис:
(.env)$ zappa deploy devCalling deploy for stage dev.....Deployment complete!: https://2ks1n5nrxh.execute-api.us-east-1.amazonaws.com/dev
Прекрасно! Наш сервис онлайн. Вы также можете протестировать этот сервис с помощью curl:
(.env)$ curl curl -X POST curl https://2ks1n5nrxh.execute-api.us-east-1.amazonaws.com/dev/counter{"counter":3}
Настройте специальный домен API
Однако с сервисом API есть одна проблема. Автоматически сгенерированная конечная точка API 2ks1n5nrxh.execute-api.us-east-1.amazonaws.com
очень тяжело читать или использовать для потребления человеком. К счастью, мы можем привязать пользовательское доменное имя к этой конечной точке API.
Мы будем использовать пользовательский домен для этой службы API. Поскольку мы хотим обслуживать его с помощью https, нам сначала нужно получить сертификат. AWS предоставляет бесплатный сертификат с сервисом «Менеджер сертификатов», и он очень прост в использовании.
После создания сертификата мы можем использовать его для настройки специального домена для службы в AWS API Gateway.
Подайте заявку на получение сертификата
Перейдите на службу ACM на консоли управления AWS (на самом деле служба называется Certificate Manager, но вы можете ввести ACM, чтобы ее найти). Нажмите Запрос на сертификат кнопку, а затем выберите Спросите публичный сертификат на следующем экране. Сертификат бесплатный, если вы выберете публичный сертификат.
На следующем экране введите имя домена, для которого необходимо применить сертификат, а затем щелкните Дальше. Вот я подал заявку *.example.com
что означает, что сертификат может использоваться всеми субдоменами под example.com
. Таким образом, мы можем использовать тот же сертификат для нашего интерфейса на myfrontend.example.com
без необходимости подавать заявку на новый.

На следующем шаге нам нужно доказать, что мы являемся владельцем этого доменного имени. Поскольку я подал заявку на это доменное имя из Google Domains, я выберу Проверка DNS. Нажмите кнопку Обзор затем нажмите кнопку Подтвердить и подать запрос.
Запрос на сертификат будет создан, и отобразится экран проверки. Инструкции показывают, как проверить это доменное имя:

На основе инструкций нам нужно добавить a CNAME
записать и назначить заданное значение. В моем случае я открою Google Domains, найду свое доменное имя example.com
и добавьте указанную запись CNAME:

Примечание: я добавил только случайную строку _2adee19a0967c7dd5014b81110387d11
в поле Имя, не вводя .example.com
часть. Это нужно для того, чтобы избежать суффикса .example.com
часть дублируется.
Теперь нам нужно подождать около 10 минут, пока AWS Certificate Manager проверит это доменное имя. После подтверждения в столбце «Статус» в сертификате появится зеленый цвет «Выдано».

Теперь, когда сертификат готов, мы можем начать привязывать наше пользовательское доменное имя к нашему API.
Настройка специального домена для службы API
Выделите службу API Gateway. С API на левой панели мы видим, что наш API myservice-dev
уже создано zappa.
Нажмите «Пользовательские доменные имена” на панели влево, а затем нажмите кнопку Создайте пользовательское доменное имя на правой панели и заполните необходимые поля.

Здесь я хочу, чтобы моя служба API была открыта через CloudFront, чтобы к ней можно получить доступ с оптимальной скоростью по всему миру. Поэтому я выбрал Край оптимизирован в данной конфигурации. Вы можете выбрать Региональный если вам не требуется CloudFront.
Нажмите кнопку «Добавить отображение” ссылку ниже, а затем выберите наш API myservice-dev
как Пункт назначенияи выберите разв за самый правый ящик. Таким образом, наш API не откроет название среды dev
в URL-адресе. Оставьте Путь поле пустое.

После нажатия кнопки Сохранить кнопку, будет создана наша пользовательская привязка домена. Фактическая привязка домена требует до 40 минут для инициализации, но мы можем настроить параметры DNS сейчас.
Из вышеприведенного снимка экрана мы видим, что фактическое доменное имя есть dgt9opldriaup.cloudfront.net
. Нам нужно настроить а CNAME
в нашем DNS, указывая myservice-api.example.com
в субдомен CloudFront dgt9opldriaup.cloudfront.net
.
Перейдите к Google Domains и добавьте CNAME к настройкам DNS:

После этого шага подождите примерно 40 минут, пока не исчезнет сообщение «Инициализация…» в шлюзе API.
Теперь попробуйте наш новый сервис API!
(.env)$ curl /counter{"counter":3}(.env)$ curl -X POST /counter/increase{"counter":4}(.env)$ curl /counter{"counter":4}
Статический веб-сайт для интерфейса
Для следующей задачи мы создадим интерфейс для нашей совершенно новой службы API. Для демонстрационных целей мы создадим простую страницу с запускающей кнопкой /counter/increase
Вызов API.
Кодирование передней части
Давайте создадим новый каталог под названием myfrontend
:
$ mkdir myfrontend && cd myfrontend
Затем создайте простой файл HTML index.html
:
<html><body> <h1>Welcome to my homepage!</h1> <p>Counter: <span id="counter"></span></p> <button id="increase">Increase Counter</button> <script> const setCounter = (counter_value) => { document.querySelector('#counter').innerHTML = counter_value; };
const api = ''; fetch(api + '/counter') .then(res => res.json()) .then(result => setCounter(result.counter));
document.querySelector('#increase') .addEventListener('click', () => { fetch(api + '/counter/increase', { method: 'POST' }) .then(res => res.json()) .then(result => setCounter(result.counter)); } ); </script></body></html>
Опубликуйте Front end в AWS S3
Чтобы создать статический сайт с S3, нам нужно создать ведро с таким же именем, что и наше доменное имя.
Примечание. Если вы придерживаетесь этого руководства, название сегмента myfrontend.example.com может быть недоступно, поскольку названия сегментов являются глобально уникальными. Кроме того, вам нужно будет создать имя сегмента на основе вашего общедоступного домена. Например,
myfrontend.[yourdomain].com
Выделите службу S3 на консоли управления AWS. Поскольку мы хотим разместить статический веб-сайт на myfrontend.example.com
, мы создадим ведро с таким названием. Нажмите кнопку Создать ведро и введите название сегмента, а затем продолжайте нажимать Дальше пока не будет создано ведро.

Далее нам нужно включить статический веб-хостинг из этого ведра. Откройте это ведро, затем выберите Свойства вкладку и выберите Статический веб-хостинг. В диалоговом окне выберите Используйте это ведро для размещения веб-сайтазатем введите index.html
в поле «Индексный документ». Нажмите Сохранить когда окончено.

Примечание. Ссылка «Конечная точка» показана в диалоговом окне выше. Мы проверим наш статический веб-сайт с этим URL позже.
Последнее, что нам нужно сделать, – это включить публичный доступ к ведру. Это можно сделать, добавив политику сегмента. Откройте это ведро и выберите Разрешения вкладку, а затем нажмите кнопку Политика ведра кнопку.
Введите следующее содержимое в качестве политики и нажмите кнопку Сохранить кнопку (не забудьте заменить myservice.example.com
с вашим доменным именем).
{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::myfrontend.example.com/*" } ]}
После сохранения мы сможем увидеть оранжевый знак «общедоступный». Политика ведра кнопка и Разрешения вкладка, указывающая, что наше ведро общедоступно.
Теперь ведро создано, но оно все еще пусто. Нам нужно загрузить наши файлы кода интерфейса в это ведро. Убедитесь, что мы в новом myfrontend
каталог и введите следующую команду:
# Make sure you are in the `myfrontend` directory...$ aws s3 sync . s3://myfrontend.example.com
Приведенная выше команда копирует все файлы из текущего .
каталог на S3.
Готово! Теперь мы можем протестировать этот статический веб-сайт с отображаемым ранее URL-адресом. Откройте этот URL-адрес в любом браузере (в моем случае, и увидите результат!
Ой! Счетчик вообще не отображается. ?

И, похоже, мы получили ошибку JavaScript. Мы видим такую ошибку в консоли:
Failed to load /counter: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin ' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Очевидно, нам нужно установить заголовок CORS, чтобы этот скрипт работал, поскольку серверный API расположен в другом домене. Но поскольку мы собираемся настроить пользовательский домен для интерфейса, URL изменится, поэтому мы будем беспокоиться о CORS позже.
Настройка CloudFront для нашего статического веб-сайта
Последним шагом является настройка CloudFront для нашего интерфейса. Поскольку мы уже создали сертификат для *.example.com
этот шаг будет очень лёгким.
Выделите службу CloudFront на консоли управления AWS. Нажмите Создать распространение кнопку, а затем нажмите кнопку Начните кнопку в разделе «Веб».
На экране «Создать распространение» необходимо внести пять изменений:
- Нажмите кнопку Начальное доменное имя поле ввода и выберите наше ведро S3
myfrontend.example.com.s3.amazonaws.com
. - Затем смените Политика протокола просмотра чтобы «Направить HTTP на HTTPS», чтобы принудительно получить https.
- В Альтернативные доменные имена введите наш пользовательский домен. В этом случае вводим
myfrontend.example.com
. - Прокрутите вниз до Сертификат SSL выберите «Специальный сертификат SSL», затем выберите наш
*.example.com
сертификат. - Изменить Корневой объект по умолчанию к
index.html
.
После создания дистрибутива мы увидим домен CloudFront в списке рассылки.

Хотя статус все еще «В разработке», мы можем настроить нашу запись DNS сейчас. Выделите Google Domains и добавьте CNAME для этого домена:

Затем подождите, пока статус распространения не изменится на «Развернут». Теперь откройте браузер и попробуйте получить доступ myfrontend.example.com
. Мы видим тот же статический веб-сайт!
Решите проблему CORS
Теперь единственный оставшийся вопрос – это CORS. Поскольку мы используем другое доменное имя на серверной и передней панели, нам нужно добавить поддержку CORS.
Общий доступ к ресурсам между источниками (CORS) — это механизм, позволяющий запрашивать ограниченные ресурсы (например, шрифты) на веб-странице из другого домена вне домена, из которого обслуживается первый ресурс. — Википедия
Вернитесь в наш каталог API (myservice
) и активируйте среду Python. Затем установите flask_cors
пакет.
$ cd myservice$ source .env/bin/activate(.env)$ pip install flask_cors
Затем отредактируйте myservice.py
и добавьте следующие строки (жирным):
import boto3from flask import Flask, jsonifyfrom flask_cors import CORS
app = Flask(__name__)CORS(app, origins=['https://myfrontend.example.com'])
Перенесите обновленную службу в AWS Lambda:
(.env)$ zappa update dev
Теперь попробуйте обновить наш браузер. Мы видим, что счетчик отображается правильно. Нажатие кнопки «Увеличить счетчик» может также увеличить счетчик.

Вывод
В этой публикации мы рассмотрели разные службы AWS, необходимые для создания простого сервиса без сервера. Если вы не знакомы с AWS, вам может показаться, что служб AWS слишком много, но большинство используемых здесь служб AWS предназначены для одноразового использования. После того как они настроены, нам не нужно вообще их касаться при дальнейшей разработке. Все, что вам нужно сделать, это бежать zappa update
и aws s3 sync
.
Кроме того, это гораздо проще, чем настроить приватный VPS, установить веб-серверы и написать задачи Дженкинса для постоянного развертывания.
Подводя итог, вот основные выводы из этой публикации:
- Лямбда может запускать простую службу. Эта служба может быть открыта через API Gateway.
- zappa – отличный инструмент, если вы хотите писать бессерверные службы на Python.
- S3 bucket можно использовать для статического хостинга.
- Подайте заявку на получение сертификата от AWS ACM, если хотите использовать https.
- API Gateway и CloudFront поддерживают пользовательские доменные имена.
Надеюсь, вам понравился этот пост, и вы не стесняйтесь хлопать? для меня если бы ты сделал! Подпишитесь на меня, если вы хотите узнать больше о веб-разработке.