Skip to content
Troy Köhler

Amazon Lambda, RDS, API Gateway и FastAPI приложение

программирование, облачные расследования5 min read

Сначала я хотел написать, как развернуть FastAPI приложение с использованием Fargate, но на Fargate нету free tier, поэтому я решил использовать бесплатность Amazon Lambda 😊

Для примера я взял вот это приложение, сделанное на FastAPI фреймворке: https://github.com/nsidnev/fastapi-realworld-example-app.

Подготовка к работе

Для того, чтобы начать работать с aws, я скачал и установил себе aws-cli. Это приложение для коммандной строки, которое будет помогать нам работать с сервисами амазона.

Для разработки я создал себе отдельного пользователя в учетной записи Amazon. Я всячески отговариваю вас разрабатывать из-под root юзера. После этого мы должны настроить наше приложение Амазона в терминале.

aws configure

Если у вас уже есть пользователь – например, вы залогинены с рабочей учетной записи – то вам нужно сделать новый профиль, используя свой личный аккаунт. Между профилями можно переключаться вот так.

aws configure --profile my_personal_profile

Потом я установил себе aws sam cli. AWS SAM – это фреймворк от Амазона для разворачивания бессерверных приложений. Его можно скачать из открытых репозиториев, с помощью Homebrew или на сайте Amazon.

Так как приложение использует пакетный менеджер poetry, то я использовал следующие две команды, чтобы установить локально необходимые зависимости. Первая – говорит poetry о том, что папку с зависимостями нужно расположить в папке с проектом, а вторая, что нужно их установить.

poetry config virtualenvs.in-project true && poetry install

Если мы посмотрим в проект, который мы склонили, то можно будет увидеть, что там используется постгрес и есть env.example файл. Этот файл мы копируем и переименовываем в .env. А базу мы попробуем использовать другую так, как мы не планируем разворачивать свой постгрес в Амазоне.

На первый взгляд из того, что нам предлагает Амазон, мы можем взять попробовать Amazon RDS. Я сделал там маленький тестовый postgres с базовыми настройками и паролем, который сгенерировал Амазон. Это все я добавляю в свой .env.

SECRET_KEY=secret
DEBUG=True
DATABASE_URL=postgresql://<master user name>:<master password>@<your host>:<your port, usually 5432>/<your db name>

Можно username и password разнести на две отдельные переменные и с помощью них собирать connection string для базы данных. Уверен, с этим вы справитесь без моей помощи.

Для того, чтобы сделать базу, вы сможете подключиться локально с помощью psql и сделать ее. База с именем postgres уже есть в нашем маленьком RDS постгресе.

Для того, чтобы смочь достучаться до этого экземпляра постгреса, мне нужно сделать новую Security Group, где я разрешу трафик, исходящий с моего IP адреса.

Это можно сделать в настройках VPC - virtual private cloud – моей приватной сети.

Чтобы посмотреть на наш свежесозданный постгрес можно использовать команду:

aws rds describe-db-engine-versions ––default-only ––engine postgres

Внимательно: при создании инстанса постгреса убедитесь, что вы выбрали его настройки доступа Publicly accessible.

Чтобы протестировать подключение локально, я использовал команду psql

psql \
   --host=<my amazon host> \
   --port=5432 \
   --username=postgres \
   --password 

После этого я ввел пароль и меня подключило.

В приложении, которое мы скопировали, нам нужно сделать дополнительно еще файл prod.env, куда мы скопируем содержимое .env пока что.

Чтобы подключить приложение к базе, надо вписать connection string в оба эти файла. После этого исполняем команды

poetry run alembic upgrade head
poetry run uvicorn app.main:app --reload

Если перейти по адресу http://127.0.0.1:8000/docs, то мы увидим, что приложение успешно подключилось локально к постгресу с Амазона и документация сгенерирована.

Так же мы можем смело удалить docker-compose.yml файл из нашего тестового приложения, он нам здесь не понадобится.

Для того, чтобы развернуть ASGI на AWS Lambda & API Gateway мы используем адаптер, который называется mangum. Мы завернем в него наше приложение, вот так

# app/main.py

from mangum import Mangum

...
app = get_application()
handler = Mangum(app)

Для того, чтобы развернуть наш проект, нам нужно его сбилдить в ZIP файл и положить в S3 бакет.

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

aws s3api create-bucket \
--bucket <your bucket name> \
--region eu-west-1 \
--create-bucket-configuration LocationConstraint=eu-west-1

Только установите другой регион, тот, который вам нравится больше.

Для того, чтобы запустить билд проекта локально, вам нужно будет заставить poetry сгенерировать стандартный requirements.txt файл так, как poetry.lock файлы не считываются sam aws cli.

poetry export -f requirements.txt --without-hashes > requirements.txt

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

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

Я не могу сказать, что я отлично разобрался в том, как писать эти темплэйты, но для образца прикладываю мой файл. Он простой, но работает.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
    FastAPI aws lambda example
Resources:
    FastapiExampleLambda:
        Type: AWS::Serverless::Function
        Properties:
            Events:
                ApiEvent:
                    Properties:
                        RestApiId:
                            Ref: FastapiExampleGateway
                        Path: /{proxy+}
                        Method: ANY
                    Type: Api
            FunctionName: FastAPI-lambda-example
            CodeUri: ./
            Handler: app.main.handler
            Runtime: python3.9
            Timeout: 300 # timeout of your lambda function
            MemorySize: 128 # memory size of your lambda function
            Description: FastAPI aws lambda example
            # other options, see ->
            # https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-template-anatomy-globals.html#sam-specification-template-anatomy-globals-supported-resources-and-properties

    FastapiExampleGateway:
        Type: AWS::Serverless::Api
        Properties:
            StageName: dev
            OpenApiVersion: '3.0.0'

После этого надо запустить sam deploy --guided.

В процессе вы выбираете варианты в консоли, которые считаете подходящими для себя. В итоге сгенерируется новый файл samconfig.toml.

У меня он выглядит так

version = 0.1

[default]
[default.deploy]
[default.deploy.parameters]
stack_name = "sample-fast-api-app"
s3_bucket = "aws-sam-cli-managed-default-samclisourcebucket-mpkd10372eoi"
s3_prefix = "sample-fast-api-app"
region = "us-west-2"
confirm_changeset = true
capabilities = "CAPABILITY_IAM"
image_repositories = []

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

Каждый раз, когда вы будете менять манифест файт template.yaml или что-то другое в проекте, вам понадобится команда sam build. sam build можно запускать с докером, используя аргумент --use-container. Используя его на маке, я столкнулся с тем, что билд зависает на половине пути, поэтому я не использую этот флаг.

Для тестирования локально я рекомендую использовать sam local start-api. Эта команда запускает имитатор Amazon API Gateway и вашего приложения в лямбда функции. Имитатор сделан отлично. Это совсем не то, с чем я столкнулся, работая с Google Cloud Functions.

Если у вас произошло все корректно, то в интерфейсе лямбды в вашем Амазон дашборде, вы сможете найти хост и по адресу <host name>/dev/docs достучаться до вашего приложения.

Но, скорее всего, у вас это не получится так, как вас выбросит по таймауту. Если вы заставите отработать тот же эндпоинт локально, то у вас, как и у меня, скорее всего, отобразится, что он отрабатывает более 10000ms.  

Судя по всему, лямбды не предназначены для таких проектов, как этот. Каждый раз, когда лямбда запускается, она с нуля поднимает проект, коннектится к базе, активирует необходимый эндпоинт, потом дисконнектится от базы и делает graceful shutdown.

О том, как оптимизировать проект под запуск на лямбде, я поисследую в своем следующем посте.

У меня есть email рассылка.

Не могу сказать, что она о чем-то конкретном, поэтому подписывайтесь на свой страх и риск!

Высылаю 1 письмо в месяц.