Aurora Serverless V2の整理
背景
ちょっとしたことから、PostgreSQLを使いたくなった。
具体的には RAGを実装するうえで、pgvector を使いたくなった。
※pgvector…ポスグレをベクトルDBとして扱える新機能
もう少し具体的に要件を書くと以下の通り。
RDBが使いたい
(普通にシステムのバックエンドとして)お金は極力抑えたい
開発しているものはRAG機能も実装したい
(のでベクトルDBがほしい)AWSで開発している
(ので、AWSをベースに実装したい)
ということで、候補としてAurora Serverless V2 のPostgreSQLが候補に上がった。
調べた内容をメモしておく。
AWSにおけるRAG実装
これがよくまとまっている
Kendra vs Bedrock
大きく、KendraかBedrockかの二択。すごく雑にまとめると以下の通り。
Kendra
Bedrockより昔からある。高機能。
高い。
Bedrock
出たてのサービス。今後、AWSでのAI周りの開発はこいつをベースに考える。
Bedrockよりは安く使える。
Kendraは月額1,000ドルが固定でかかる。
Bedrockは、ベクトルDB(Knowledge Bases)に何を採用するかで値段が変わる。大きく3つの選択肢がある。
Bedrockで扱えるKnowledge Bases
OpenSearch
Aurora PostgreSQL(pgvector)
サードパーティ(Pineconeなど)
OpenSearchは結局高い。
サードパーティは東京リージョン非対応だったりする。
ので、Aurora PostgreSQL(pgvector)に白羽の矢を立てたってわけ。
前述のYoutube動画からの切り抜き。わかりやすい。助かる。
DBの選択肢
RDS
pgvectorが使いたいだけなら、最近のバージョンのPostgreSQLなら、Auroraでなくても問題はない。普通のRDSでもpgvectorは使える。
が、Bedrockの Knowledge Bases として選択できる(サポートしている)のがAurora PostgreSQLのみなので、普通のRDSではだめ。
Aurora Serverless V1/V2
V1は2024年にサポート切れなので、V2を選択するしかない。
ちょっと脇道にそれるけど、V2における「Serverless」という言葉がちょっと混乱を生む。
以下で、V1/V2についてうまく説明してくれている。
👆️からの引用👇️
サーバレスと言っているけど、V2の場合、クラスターは「Serverless」ではなく「Provisioned」になる。
そして、明示的に「停止」処理をしなければ常に稼働しており課金が継続する。V1であれば、リクエストに応じて稼働・課金であった。
これは個人的な直感に反するもので「サーバレスなら、使ったときだけ使った分だけ課金じゃないの?」と思っていたが、V2はそうではない。
インスタンスはServerlessかProvisionedから選択できるが、スケーリングが自動的に柔軟に行われるのが違い、という理解。
ということで、節約したければ「使ってないときは明示的に停止する」がV2では必要。
いずれにしてもBedrockの Knowledge Bases では、Aurora Serverless V2 であることがMustのようなので選択の余地はない。pgvector を使いたいなら。
Terraformによる環境構築
今回はTerraformを使って環境を作ってみた。
開発環境だし、ローカル端末から直接PostgreSQLにアクセスしたいことから、以下の例ではパブリックサブネットに紐づけていることに注意。
(プロダクト版では絶対にプライベートサブネット所属であるべき)
DBの認証情報も雑にベタ書きなのでとても注意。
ディレクトリ構成
└── root
├── main.tf
├── modules
│ ├── aurora
│ │ └── aurora.tf
│ └── network
│ └── network.tf
└── variables.tf
main.tf
変数管理と、ネットワーク周り構築と、Aurora周り構築はそれぞれファイルを分けて呼び出している。
# ##################################
# 基本設定
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
# ##################################
# ##################################
# 各種リソース構築
# ##################################
# ネットワーク周り
module "network" {
source = "./modules/network"
project_prefix = var.project_prefix
env = var.env
}
# Aurora周り
module "aurora" {
source = "./modules/aurora"
vpc_id = module.network.vpc_id
subnet_ids = module.network.public_subnet_ids
security_group_id = module.network.aurora_sg_id
project_prefix = var.project_prefix
env = var.env
aurora_root_username = var.aurora_root_username
aurora_root_password = var.aurora_root_password
}
variables.tf
変数管理。
variable "project_prefix" {
description = "共通プレフィックス"
type = string
default = "project_name"
}
variable "env" {
description = "dev or prod"
type = string
default = "dev"
}
variable "aurora_root_username" {
description = "Auroraのルートユーザー名(マル秘)"
type = string
default = "scotto"
}
variable "aurora_root_password" {
description = "Auroraのルートユーザーパスワード(マル秘)"
type = string
default = "tiger111!"
}
network.tf
ネットワーク周りを構築。
Auroraではサブネット(AZ)を最低2つ利用することが求められるので、サブネットを2つ作っている。
Auroraには、ID/PW認証以外にIAMデータベース認証もできるようにする。そのためには、443ポートも許可してやる必要があるらしい。ので、インバウンドで443も許可するようにしている。
# ############################
# 定義済み変数の利用
# ############################
variable "project_prefix" {}
variable "env" {}
# ############################
# リソース作成
# ############################
# VPC
resource "aws_vpc" "my_vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "${var.project_prefix}-${var.env}-vpc"
}
}
# サブネット
resource "aws_subnet" "public_subnet_a" {
vpc_id = aws_vpc.my_vpc.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-northeast-1a"
tags = {
Name = "${var.project_prefix}-${var.env}-public-subnet-1a"
}
}
resource "aws_subnet" "public_subnet_c" {
vpc_id = aws_vpc.my_vpc.id
cidr_block = "10.0.2.0/24"
availability_zone = "ap-northeast-1c"
tags = {
Name = "${var.project_prefix}-${var.env}-public-subnet-1c"
}
}
# IGW
resource "aws_internet_gateway" "my_igw" {
vpc_id = aws_vpc.my_vpc.id
tags = {
Name = "${var.project_prefix}-${var.env}-igw"
}
}
# ルートテーブル
resource "aws_route_table" "public_rt" {
vpc_id = aws_vpc.my_vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.my_igw.id
}
tags = {
Name = "${var.project_prefix}-${var.env}-public-rt"
}
}
# サブネットとルートテーブル紐づけ
resource "aws_route_table_association" "public_rt_assoc_a" {
subnet_id = aws_subnet.public_subnet_a.id
route_table_id = aws_route_table.public_rt.id
}
resource "aws_route_table_association" "public_rt_assoc_c" {
subnet_id = aws_subnet.public_subnet_c.id
route_table_id = aws_route_table.public_rt.id
}
# セキュリティグループ
resource "aws_security_group" "aurora_sg" {
vpc_id = aws_vpc.my_vpc.id
name = "${var.project_prefix}-${var.env}-aurora-sg"
# インバウンド
# 通常のPostgres接続用ポート
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
# cidr_blocks = ["YOUR_LOCAL_IP/32"] # ローカルIPを指定
cidr_blocks = ["0.0.0.0/0"] # 一旦はどこからでもアクセス可能にしておく
}
# IAMデータベース認証用のポート443も許可
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# アウトバウンド
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_prefix}-${var.env}-aurora-sg"
}
}
# ############################
# 生成したリソースの値を親処理に戻す
# ############################
output "vpc_id" {
value = aws_vpc.my_vpc.id
}
output "public_subnet_ids" {
value = [aws_subnet.public_subnet_a.id, aws_subnet.public_subnet_c.id]
}
output "aurora_sg_id" {
value = aws_security_group.aurora_sg.id
}
Aurora.tf
Aurora(DB)の構築。
前段のネットワーク構築情報を引き継いぎ、Auroraとネットワークの紐づけをおこなう。
前述の通り、V2はサーバレスなのにクラスター作成時
`engine_mode = "provisioned"`
と指定する必要あり(serverless と書くとエラーになる)。パブリックサブネットに所属させてセキュリティグループを適宜設定するだけではなく、 `publicly_accessible = true` といった設定も明示的に行う。
# ############################
# 定義済み変数の利用
# ############################
variable "project_prefix" {}
variable "env" {}
variable "aurora_root_username" {}
variable "aurora_root_password" {}
# ############################
# 前段処理で生成したリソースの値を利用
# ############################
variable "vpc_id" {}
variable "subnet_ids" { type = list(string) }
variable "security_group_id" {}
# ############################
# リソース作成
# ############################
# Auroraクラスター
resource "aws_rds_cluster" "aurora_cluster" {
cluster_identifier = "${var.project_prefix}-${var.env}-aurora-postgres-serverless-v2"
engine = "aurora-postgresql"
engine_mode = "provisioned"
engine_version = "16.4"
storage_encrypted = true
## 認証周り
# rootユーザー情報。扱い注意。
master_username = var.aurora_root_username
master_password = var.aurora_root_password
# パスワード認証だけでなく、IAMデータベース認証も許可しておく。
iam_database_authentication_enabled = true
serverlessv2_scaling_configuration {
min_capacity = 0.5
max_capacity = 5
}
vpc_security_group_ids = [var.security_group_id]
db_subnet_group_name = aws_db_subnet_group.aurora_subnet_group.name
tags = {
Name = "${var.project_prefix}-${var.env}-aurora-cluster"
}
}
# Auroraインスタンス
resource "aws_rds_cluster_instance" "aurora_instance" {
cluster_identifier = aws_rds_cluster.aurora_cluster.id
identifier = "${aws_rds_cluster.aurora_cluster.id}-instance01"
instance_class = "db.serverless"
engine = "aurora-postgresql"
# パブリックインターネットからのアクセスを許可(本番時はNG)
publicly_accessible = true
tags = {
Name = "${var.project_prefix}-${var.env}-aurora-instance"
}
}
# サブネット所属
resource "aws_db_subnet_group" "aurora_subnet_group" {
name = "${var.project_prefix}-${var.env}-aurora-subnet-group"
subnet_ids = var.subnet_ids
tags = {
Name = "${var.project_prefix}-${var.env}-aurora-subnet-group"
}
}