見出し画像

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"
  }
}

参考にさせてもらったもの


いいなと思ったら応援しよう!