Разработка CRUD API для сервиса фильмов
С помощью serverless-технологий можно создать CRUD
Реализация CRUD API использует контейнер Yandex Serverless Containers, который предназначен для работы с базой данных фильмов, развернутой в Yandex Managed Service for YDB.
Контейнер конфигурируется в спецификации API-шлюза Yandex API Gateway по стандарту OpenAPI 3.0
Контейнер будет взаимодействовать с Managed Service for YDB и обрабатывать внешние HTTP-запросы через API-шлюз с использованием HTTP API
Чтобы развернуть проект:
- Настройте окружение.
- Инициализируйте Terraform.
- Создайте базу данных Managed Service for YDB.
- Реализуйте CRUD-операции.
- Разработайте REST API.
- Проверьте работу созданного CRUD API.
Если созданные ресурсы больше не нужны, удалите их.
Перед началом работы
Зарегистрируйтесь в Yandex Cloud и создайте платежный аккаунт:
- Перейдите в консоль управления
, затем войдите в Yandex Cloud или зарегистрируйтесь. - На странице Yandex Cloud Billing
убедитесь, что у вас подключен платежный аккаунт, и он находится в статусеACTIVE
илиTRIAL_ACTIVE
. Если платежного аккаунта нет, создайте его.
Если у вас есть активный платежный аккаунт, вы можете создать или выбрать каталог, в котором будет работать ваша инфраструктура, на странице облака
Подробнее об облаках и каталогах.
Необходимые платные ресурсы
В стоимость ресурсов для работы CRUD API входят:
- Плата за операции с YDB и хранение данных (см. тарифы Managed Service for YDB в бессерверном режиме).
- Плата за количество вызовов контейнера, вычислительные ресурсы, выделенные для выполнения приложения, и исходящий трафик (см. тарифы Serverless Containers).
- Плата за количество запросов к API-шлюзу и исходящий трафик (см. тарифы API Gateway).
Настройте окружение
- Установите утилиту WSL
для использования окружения Linux. - Запустите подсистему Linux (по умолчанию — Ubuntu).
- Настройте окружение так, как описано в инструкции для операционной системы Linux.
Примечание
Если вы используете дистрибутив, отличный от Ubuntu, установите указанные утилиты с помощью команд вашего пакетного менеджера.
-
Последовательно установите следующие утилиты с помощью команд в терминале:
-
sudo apt-get install curl git -y
-
WebStorm
или другая среда разработки с поддержкой TypeScript :sudo snap install webstorm --classic
-
Node.js
не ниже версии16.9.1
:curl --silent --location https://deb.nodesource.com/setup_16.x | sudo -E bash sudo apt-get install nodejs node -v npm -v
-
sudo npm install -g typescript
-
curl https://storage.yandexcloud.net/yandexcloud-yc/install.sh | bash exec -l $SHELL yc version
-
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" --output "awscliv2.zip" unzip awscliv2.zip sudo ./aws/install
-
sudo apt-get update sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release -y curl --fail --silent --show-error --location https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io -y sudo docker run hello-world
-
-
Установите Terraform не ниже версии
1.0.8
. -
Создайте профиль Yandex Cloud CLI с базовыми параметрами.
-
Настройте AWS CLI.
-
Настройте
управление Docker от имени непривилегированного пользователя:sudo groupadd docker sudo usermod -aG docker $USER newgrp docker docker run hello-world
-
Последовательно установите следующие утилиты с помощью команд в терминале:
-
/bin/bash -c "$(curl --fail --silent --show-error --location https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
-
brew install curl git
-
WebStorm
или другая среда разработки с поддержкой TypeScript :brew install --cask webstorm
-
Node.js
не ниже версии16.9.1
:brew install node node -v npm -v
-
npm install -g typescript
-
curl https://storage.yandexcloud.net/yandexcloud-yc/install.sh | bash exec -l $SHELL yc version
-
curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" --output "AWSCLIV2.pkg" sudo installer -pkg AWSCLIV2.pkg -target /
-
brew install --cask docker
-
-
Установите Terraform не ниже версии
1.0.8
. -
Создайте профиль с базовыми параметрами.
-
Настройте AWS CLI.
Инициализируйте Terraform
-
Склонируйте репозиторий с исходными файлами для проекта CRUD API:
git clone https://github.com/yandex-cloud-examples/yc-practicum-serverless-web-application-movie-website.git
Откройте директорию проекта в WebStorm и изучите исходные файлы.
-
Перейдите в директорию
deploy
:cd <путь_к_директории_deploy>
-
Узнайте имя активного профиля (
ACTIVE
) интерфейса командной строки Yandex Cloud CLI. В терминале выполните команду:yc config profile list
-
Получите параметры активного профиля:
yc config profile get <имя_профиля>
-
Скопируйте в файл provider.tf
полученные параметры:token
— OAuth-токен.cloud-id
— идентификатор облака.folder-id
— идентификатор каталога.
-
Экспортируйте идентификатор каталога в переменную окружения:
export FOLDER_ID=<идентификатор_каталога> echo $FOLDER_ID
-
Выполните команду инициализации Terraform:
terraform init
Примечание
Выполняйте все команды Terraform в директории
deploy
.
Создайте базу данных Managed Service for YDB
В проекте используется база данных YDB в режиме serverless. БД состоит из двух таблиц: movies
для хранения информации о фильмах и votes
для хранения оценок пользователей. Каждая запись в таблице содержит идентификатор и конечный набор атрибутов.
-
Конфигурация Terraform для создания БД описана в файле ydb.tf
. Создайте базу данных:terraform apply -target=yandex_ydb_database_serverless.movies_database
Подтвердите создание ресурсов: введите в терминале
yes
и нажмите Enter.В выводе команды будут указаны переменные:
movies_database_document_api_endpoint
— Document API эндпоинт базы данных.movies_database_path
— относительный путь к базе данных.
Проверить, что БД
movies-database
успешно создана, можно в консоли управления или с помощью команды CLIyc ydb database list
. -
Экспортируйте в переменные окружения значения
movies_database_document_api_endpoint
иmovies_database_path
из вывода предыдущей команды:export DOCUMENT_API_ENDPOINT=<Document_API_эндпоинт_базы_данных> echo $DOCUMENT_API_ENDPOINT export MOVIES_DATABASE_PATH=<относительный_путь_к_базе_данных> echo $MOVIES_DATABASE_PATH
-
Создайте в БД
movies-database
таблицыmovies
иvotes
:aws dynamodb create-table \ --table-name movies \ --attribute-definitions \ AttributeName=id,AttributeType=N \ AttributeName=title,AttributeType=S \ AttributeName=type,AttributeType=S \ AttributeName=original_title,AttributeType=S \ AttributeName=original_language,AttributeType=S \ AttributeName=release_date,AttributeType=S \ AttributeName=poster_path,AttributeType=S \ AttributeName=popularity,AttributeType=N \ AttributeName=video,AttributeType=S \ AttributeName=vote_count,AttributeType=N \ AttributeName=vote_average,AttributeType=N \ AttributeName=genres,AttributeType=S \ AttributeName=backdrop_path,AttributeType=S \ AttributeName=adult,AttributeType=S \ AttributeName=overview,AttributeType=S \ --key-schema \ AttributeName=id,KeyType=HASH \ --global-secondary-indexes \ "[ { \"IndexName\": \"PopularityIndex\", \"KeySchema\": [{\"AttributeName\":\"type\",\"KeyType\":\"HASH\"}, {\"AttributeName\":\"popularity\",\"KeyType\":\"RANGE\"}], \"Projection\":{ \"ProjectionType\":\"ALL\" } } ]" \ --endpoint ${DOCUMENT_API_ENDPOINT} aws dynamodb create-table \ --table-name votes \ --attribute-definitions \ AttributeName=id,AttributeType=S \ AttributeName=user_id,AttributeType=S \ AttributeName=movie_id,AttributeType=N \ AttributeName=value,AttributeType=N \ --key-schema \ AttributeName=id,KeyType=HASH \ --global-secondary-indexes \ "[ { \"IndexName\": \"MovieIndex\", \"KeySchema\": [{\"AttributeName\":\"movie_id\",\"KeyType\":\"HASH\"}], \"Projection\":{ \"ProjectionType\":\"ALL\" } } ]" \ --endpoint ${DOCUMENT_API_ENDPOINT}
-
Проверьте, что таблицы создались:
aws dynamodb describe-table \ --table-name movies \ --endpoint ${DOCUMENT_API_ENDPOINT} aws dynamodb describe-table \ --table-name votes \ --endpoint ${DOCUMENT_API_ENDPOINT}
Для каждой таблицы поддерживаются два индекса:
- В таблице
movies
— индекс для быстрого поиска фильма по его идентификатору и индекс для сортировки фильмов по популярности. - В таблице
votes
— индекс для поиска оценки пользователя по фильму и индекс для получения всех оценок фильма.
- В таблице
Реализуйте CRUD-операции
Слой для работы с базой данных используется каждый раз, когда необходимо создавать, получать, обновлять или удалять записи. Эти действия называют CRUD-операциями.
Взаимодействие с базой данных через Document API реализовано с помощью библиотеки AWS SDK for JavaScript v3
- В файле model.ts
через интерфейс TypeScript определены модели фильмаMovie
и оценкиVote
. - В файле repository.ts
реализованы CRUD-операции для работы с этими сущностями.
При выполнении операций с данными для авторизации используются IAM-токены. Для получения IAM-токена перед выполнением операции вызывается сервис метаданных.
Создайте сервисный аккаунт
-
Конфигурация Terraform для создания сервисного аккаунта описана в файле sa.tf
. Создайте сервисный аккаунт:terraform apply -target=yandex_iam_service_account.movies_api_sa
Подтвердите создание ресурсов: введите в терминале
yes
и нажмите Enter. -
В выводе команды, в переменной
movies_api_sa_id
, указан идентификатор созданного сервисного аккаунта. Экспортируйте его в переменную окружения:export MOVIES_API_SA_ID=<идентификатор_сервисного_аккаунта> echo $MOVIES_API_SA_ID
-
Назначьте роли сервисному аккаунту:
yc resource-manager folder add-access-binding ${FOLDER_ID} \ --role ydb.admin \ --subject serviceAccount:${MOVIES_API_SA_ID} yc resource-manager folder add-access-binding ${FOLDER_ID} \ --role container-registry.images.puller \ --subject serviceAccount:${MOVIES_API_SA_ID} yc resource-manager folder add-access-binding ${FOLDER_ID} \ --role serverless.containers.invoker \ --subject serviceAccount:${MOVIES_API_SA_ID}
Где:
--role
— назначаемая роль.--subject serviceAccount
— идентификатор сервисного аккаунта.
Роли назначаются сервисному аккаунту для следующих действий:
- Вызов контейнера в Serverless Containers.
- Выполнение операций в YDB.
Роли назначаются на весь каталог, а не на каждый ресурс в отдельности.
Скомпилируйте исходный код приложения на TypeScript
-
Перейдите в корневую директорию репозитория и установите необходимые зависимости:
cd <путь_к_директории_sls-web-application> npm ci
После выполнения команды в проекте появится директория
node_modules
, в которой будут находиться все необходимые зависимости. -
Запустите сборку проекта:
npm run build
После выполнения команды в проекте появится директория
dist
, в которой будут находиться скомпилированные JS-файлы.
Разработайте REST API
В файле openapi/api.yaml
Для реализации сервиса в соответствии с этой спецификацией используется библиотека OpenAPI Backend
Разверните приложение в Serverless Containers
Соберите приложение в виде Docker-образа и запустите его в Serverless Containers:
-
В спецификации OpenAPI
api.yaml
, в полеx-yc-apigateway.service_account_id
, укажите идентификатор сервисного аккаунта, который создали ранее. -
В файле container-registry.tf
описана конфигурация реестра и репозитория, в которые будет загружаться Docker-образ приложения. Перейдите в директориюdeploy
и создайте ресурсы в Yandex Container Registry:cd <путь_к_директории_deploy> terraform apply -target=yandex_container_registry.default terraform apply -target=yandex_container_repository.movies_api_repository
Подтвердите создание ресурсов: введите в терминале
yes
и нажмите Enter. -
В выводе команды, в переменной
movies_api_repository_name
, указано название репозитория, в который нужно загрузить Docker-образ. Экспортируйте его в переменную окружения:export MOVIES_API_REPOSITORY_NAME=<название_репозитория> echo $MOVIES_API_REPOSITORY_NAME
-
Настройте Docker для работы с созданным репозиторием:
yc container registry configure-docker
-
В файле Dockerfile
описана конфигурация для сборки Docker-образа. Соберите образ и загрузите его в созданный на предыдущем шаге репозиторий:docker build -t ${MOVIES_API_REPOSITORY_NAME}:0.0.1 . docker push ${MOVIES_API_REPOSITORY_NAME}:0.0.1
-
Создайте контейнер Serverless Containers:
yc sls container create \ --name movies-api-container \ --folder-id ${FOLDER_ID}
Где:
--name
— имя контейнера.--folder-id
— идентификатор каталога.
-
В выводе команды указан идентификатор созданного контейнера. Экспортируйте его в переменную окружения:
export MOVIES_API_CONTAINER_ID=<идентификатор_контейнера> echo $MOVIES_API_CONTAINER_ID
-
Создайте ревизию контейнера из Docker-образа с версией
0.0.1
:yc sls container revisions deploy \ --folder-id ${FOLDER_ID} \ --container-id ${MOVIES_API_CONTAINER_ID} \ --memory 512M \ --cores 1 \ --execution-timeout 5s \ --concurrency 4 \ --environment AWS_ACCESS_KEY_ID=FAKE_AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY=FAKE_AWS_SECRET_ACCESS_KEY,DOCUMENT_API_ENDPOINT=${DOCUMENT_API_ENDPOINT} \ --service-account-id ${MOVIES_API_SA_ID} \ --image ${MOVIES_API_REPOSITORY_NAME}:0.0.1
Где:
--folder-id
— идентификатор каталога.--container-id
— идентификатор контейнера.--memory
— объем памяти, доступный контейнеру.--cores
— количество ядер vCPU, доступное контейнеру.--execution-timeout
— таймаут выполнения.--concurrency
— максимальное число одновременных вызовов контейнера. Если вызовов контейнера больше, чем значение параметра--concurrency
, сервис масштабирует контейнер — запускает его дополнительные экземпляры.--environment
— переменные окружения. Через переменную окруженияDOCUMENT_API_ENDPOINT
в приложение передается Document API эндпоинт базы данных.--service-account-id
— идентификатор сервисного аккаунта.--image
— название репозитория.
Разверните API в API Gateway
-
В спецификации OpenAPI
api.yaml
замените переменную${MOVIES_API_CONTAINER_ID}
на идентификатор созданного контейнера. -
В файле api-gateway.tf
описана конфигурация Terraform для создания API-шлюза. Разверните API-шлюз:terraform apply -target=yandex_api_gateway.movies_api_gateway
Подтвердите создание ресурсов: введите в терминале
yes
и нажмите Enter. -
В выводе команды, в переменной
movies_api_gateway_domain
, указано доменное имя API-шлюза. Экспортируйте его в переменную окружения:export MOVIES_API_GATEWAY_DOMAIN=<доменное_имя_API-шлюза> echo $MOVIES_API_GATEWAY_DOMAIN
Проверьте работу созданного CRUD API
Чтобы проверить работу созданного CRUD API, выполните следующие HTTP-запросы с помощью команды curl
:
-
Получите список фильмов:
curl "${MOVIES_API_GATEWAY_DOMAIN}/movies?limit=10"
В ответе должен прийти пустой список
[]
, так как пока в базе нет данных. -
Добавьте информацию о фильме:
curl \ --location \ --request POST 'https://${MOVIES_API_GATEWAY_DOMAIN}/movies' \ --header 'Content-Type: application/json' \ --data-raw '{ "id": "301", "title": "The Matrix", "release_date": 1999 }'
-
Получите информацию о фильме:
curl \ --location \ --request GET 'https://${MOVIES_API_GATEWAY_DOMAIN}/movies/301'
-
Добавьте информацию о другом фильме:
curl \ --location \ --request POST 'https://${MOVIES_API_GATEWAY_DOMAIN}/movies' \ --header 'Content-Type: application/json' \ --data-raw '{ "id": "299", "title": "The Matrix Reloaded", "release_date": 2003 }'
-
Получите список фильмов:
curl \ --location \ --request GET 'https://${MOVIES_API_GATEWAY_DOMAIN}/movies?from=1&limit=5'
Также вы можете загрузить спецификацию в Postmanservers
адрес созданного API-шлюза из переменной ${MOVIES_API_GATEWAY_DOMAIN}
. Это позволит удобно делать вызовы к REST API.
Посмотрите диагностическую информацию о работе контейнера. В консоли управления
Логи и графики мониторинга также можно посмотреть на странице API-шлюза.
Как удалить созданные ресурсы
Чтобы перестать платить за ресурсы, созданные с помощью Terraform, удалите их. В терминале выполните команду:
terraform destroy
Подтвердите удаление ресурсов: введите в терминале yes
и нажмите Enter.