AWS AuroraDB serverless v2 PostgreSQL 作成手順

今回は普通のPostgreSQLのDBを作る手順です。

未完成ですが、自分の参考のために公開しています。

VPC作成

AuroraDBはVPCやsubnetが必要。

default VPCは、セキュリティ設定などが緩く、本番では良くないので、VPCを作成してみます。(Knowlegebaseを作った時に、ここを参考にさせていただきました。 BedrockナレッジベースでAurora Serverlessを使用する際の設定注意点

VPC作成 VPCx1, subnetx2

  1. VPCページに移動
  2. VPCsをクリックし『Create PVC』をクリック
  3. VPC only and more <— moreを選択するとPrivate subnet, Public subnet, Internet gateway, Route tableなど作成できる
  4. name 『myVPC-001-rag-vpc』
  5. IPv4 CIDR manual input 『10.0.0.0/16』
  6. No IPv6 CIDR block
  7. Tenancy 『Default』ーーー専用は有料なので共有のdefaultにした
  8. Availabilityを2 <ーーーVectordb Auroraで必要
  9. Public subnetsも2<ーーー将来Webに必要かもしれない
  10. Private subnetsを2 <ーーーVectordb Auroraで必要
  11. NATとVPC endpointsは両方とも0
  • myVPC-002-rag-subnet-private1-ap-northeast-1a, 10.0.128.0/20
  • myVPC-002-rag-subnet-private2-ap-northeast-1c, 10.0.144.0/20
  • myVPC-002-rag-subnet-public1-ap-northeast-1a, 10.0.0.0/20
  • myVPC-002-rag-subnet-public2-ap-northeast-1c, 10.0.16.0/20

今回は、前回RAGを構築した時に作成した上記VPCとSubnetを使います。 前回はBedrock経由でKnowlegebaseを作ったので、パスワードやSecrets managerの管理はBedrockがしてくましたが、今回はこのあたりも構築しなければなりません。

  1. Secrets manager–>Store a new secret
  2. Secret type: Other type of secret 選択
  3. Key/value pars: adminとpasswordを入れる
  4. Encryption keyはそのままaws/secretsmanager
  5. Next
  6. secret name and description
    • secret name: st.chat
    • description: credential for st chat (これは私の場合)
  7. next
  8. store
  9. 画面をリフレッシュしないと表示されません。

  • aurora and rds –> subnet group –> create DB subnet group
  • name: st-chat-subnet-groupt
  • description: subnet group to chat.history aurora postgresql db
  • 使用するVPCを選択
  • add SubnetsのAvailability zonesでは既存のAvailability zoneを2つ選択する。
    • 私の場合は、ap-northeast-1aとap-northeast-1c
  • Subnetsではpulldownで開いて、ap-northeast-1aのprivate subnetとap-northeast-1cのprivate subnetの2つを選択。

Security Groupを1つ作成

Security Groupはファイヤーウォールのようなもの。 1つはDBに入るのを制限します。 特にポート番号は1つに絞ります。 IPアドレス範囲はVPCに合わせます。

RDS用

  1. vpc–>security group–>create security group
  2. name:st.chat.history.rds
  3. vpcを選択
  4. Inbound rulesでadd rule
    1. type: custom tcp
    2. port range: 5432 <—PostgreSQLのdefault
    3. Source: custom
    4. すぐ右の空白欄に半角で、10.0と打ち込むとリストが現れる。 私はVPCのIPのレンジが10.0.0.0/16なのでそのまま入れる。
  5. Outbound rulesは何も入れない(0.0.0.0/0のまま)
  6. create security group

Secrets manager

AuroraでClusterを作るとSecrets managerが自動的に生成されます。

  • Secret name: st.chat
  • Secret key: admin
  • Secret value: password

上記以外にもengineや他のキー名と値が生成されます。 なので個別で作成する必要はありません。

AuroraDB Serverless V2作成

  • クラスタは計6個のコピーを3つのAvailability zoneに保存して、障害が起きたら自動的に他のインスタンスをプライマリーインスタンスに昇格させてサービス停止しないようにしている。 またデータ修復もする。
  • 読み出しの多い用途では、複数のReadレプリカを活用して高速読み出しを可能にしている。
  • クラスタは40個作ることができる。
  • 各クラスタにwrite instanceは1個でread instanceは15個まで作成できる。
  • Write instanceに
  • ServerlessのACUはクラスタ単位で増減する。
  • 常時稼働のACUはインスタンス単位で増減する。
  • Serverlessと常時稼働のインスタンスは同じクラスタに設置できない。
  • 開発時はServerlessにして、その後Production時は常時稼働にする場合は、クラスタごとスナップショットを保存して、新しい常時稼働のクラスタに変更しないといけない。

私の場合、今回はServerlessで作成し評価して、将来常時稼働にするので、別の新しいクラスタを作成して、DBインスタンスを設定する。 

  1. Aurora and RDS–> Create database
  2. Aurora (PostgreSQL compatible)選択
  3. Hide filters:すべてオフ
  4. Templates:後で面倒臭いのでProduction
    • dev/testからProductionへの変更方法
    • インスタンスをReadとWriteの2つにする
    • インスタンスのクラスをmクラスからrクラスなどに変更
    • バックアップを7日から35日に変更
    • アプリケーションサーバーのセキュリティグループだけを許可するInbound ruleの設定
  5. DB Cluster identifier:dbClusterStChat
  6. Master username: stchat
    • adminは予約後で使えなかった
  7. Credential management: Self managed
  8. Master password:password
  9. Aurora standard
  10. Serverless v2
  11. Minimum ACUs: 0
  12. Maximum ACUs:64
  13. Pause after inactivity: 00:30:00
  14. Create an Aurora replica or Reader node in a different AZ
  15. Don’t connect to an EC2 compute resource
  16. IPv4
  17. 作成したVPCを選択:myVPC-002-rag-vpc(私の場合)
  18. 作成したsubnetを選択:myknowledgebase-subnet-group
  19. Public access:No
  20. VPC security group(firewall); Choose existing
  21. st.chat.history.rdsを選択
  22. RDS proxyは選択しないが、後で別途作成する。
    • 勝手にSecurity groupとかSecretとか作成されないようにする。
  23. Certificate authorigy:rsa4096を選択。(expirationが長いので)
    • これはhttps SSL endpointとして使用する際に使う。
    • Lambdaからでもhttpsのみとした場合に必要になる。
  24. IAM Authentifcationもチェックを入れる。
    • パスワードでも通信できるが、IAM Permission設定だけで通信出来るので便利。
    • これを選択してもパスワードでも通信できる。
    • パスワード通信を無効にしてIAM通信だけにすることも出来る。
  25. MonitoringはDatabase insights-standardを選択

接続しているVPC確認方法

  • Aurora and RDS–>Databasesを選択
  • 作成したクラスタの下にぶら下がっている。 DB Identifierの2列右の項目にRoleがあるが、それらはWrite instanceとRead instanceになっている。 それらをクリックしたら接続されているVPCやRDSに付与されたSecurity group等が表示される。
  • 【注意】一番上のRegional ClusterをクリックしてClusterはVPCに接続されないのでVPCは表示されない。

RDS proxy接続(使用しない)

今回は作成しない。 RDS Proxyは1億人接続など大量の接続時に有効。 それまではData API接続が有効。

Lambdaはサーバーレスなので、毎回PostgresSQLへ接続確立が必要になる。 多数のLambdaなどが接続する際、接続確立で高負荷になり遅延やコストがかかる。 その場合、RDS Proxyを使うと接続をプールして、Lambdaが使える。 しかしFargtate等を使用する際は、接続を維持できるのでProxyは不要。 Serverless V2を使った場合のProxyの最低料金は8ACU でUSD0.025/ACU時間なので30日で$144となる。 Serverlessでなく常時稼働の場合は1vCPUあたり$13なので、常時稼働でLambdaを使う場合はProxyが必要になる。

Data API接続(これを使用する)

Data APIを使うことにする。 これが一番よい。

  1. Aurora and RDS—> Databases
  2. 作成したクラスタ名を選択:dbclusterstchat
  3. 画面を下にスクロールして一番下に、RDS Data APIとあるので『Enable RDS Data API』をクリック
  • Data APIを使うと接続確立が不要になる。
  • Lambdaからのアクセスだと毎回接続確立が必要になる。
  • Data APIはリクエストだけの課金。 USD0.42/100万リクエストなので、10万人x3000回/月でも$13程度なので、LambdaでもData APIを使い、将来Fargateに移行しても同じアクセス方法のData APIを使い続けてもいい。 そして安定したらData APIもProxyもなしに改修したらいい。
  • Data APIは持続的な接続出ないため、フェールオーバーでの接続遅延は発生しない。

Secret作成(もし作りたかったら)

Clusterを作成したら自動的にSecretを作ってくれますが、長い名前になるのでもし自分で作りたかったら以下の手順で作れます。

usernameとpasswordは上記でDBに設定しました。 すると勝手にSecretができていました。 なのでLambdaからはそのSecretを取ってきてDBで接続します。

しかしSecret名が長いので、短い名前で作成したい時は同じ形でSecretを作成します。

  1. Secrets manager–> Store a new secret
  2. Credentials for Amazon RDS databaseを選択
  3. User nameに上記のusernameの値を入れる。 私の場合、stchat
  4. Passwordに上記のpasswordの値を入れる。 私の場合、password
  5. Encryption key:aws/seretsmanagerのまま
  6. 接続するDBを選択する
    • 上記でCredentials for Amazon RDS databaseを選択してるので
    • DBを選択できる。 これを選択しておくと、後日Automatic Rotationを有効にしたら、SecretがLambda関数を自動で作成してSecretがRotateしたpasswordやusernameでDBのusernameとpasswordをAlter(変更)してくれる。
    • しかし初期値のUsernameとPasswordがDBと一致していないといけない。 今回は一致させている。
  7. Secret name:st.chat.new
  8. Resource permissionを入れる。(例を下に記述してます)
  9. Next
  10. Automatic Rotationはしない(実運用時はしたほうがいい)
    • DBを選択している
    • Automatic Rotationを有効にすると、自動的にLambda関数が作成され、SecretのパスワードやUsernameが変更(Rotate)されると、設定してRotation ScheduleがEventBridgeのスケジュール機能を使い、そのLambda関数が実行され、Lambdaが新しいPasswordに切り替えてくれる。
  11. Rotationがうまく行かない場合は以下を設定する。
    • Retrieve secret values–> Edit
    • resourceIdを追加
      • Aurora DBのclusterをクリックするとcluster-で始まるResource IDが表示されるので、valueにコピペする。
    • engineのpostgresからaurora-postgresに変更する。

Lambda設定

  • Data API接続であれば、VPCに接続しなくてもそのまま起動してAurora Serverless v2 PostgreSQLにアクセスできる。

Aurora PostgresSQL DB作成

DB作成

インスタンスはあるが、まだDBもTableもない状態なのでDBでを作成。

  1. Aurora and RDS –> Query editor
  2. Clusterを選択 (dbclusterstchat)
  3. Database username : Add new database credential
  4. Username、Password : clusterを作成した時に作ったusernameとpasswordを使う
    • Username: stchat
    • Password: password
  5. name of the database: postgres
    • まだDBがないので、デフォルトで存在するpostgresという名前のDBに接続する。
  6. Connect to databaseをクリック
  7. Runを実行
    • 下のRow returnsに結果が表示される。
    • Serverless v2なので、最初はエラーになる。 The Aurora DB instance xxxxxxxxxxxx is resuming after being auto-pausedとか表示される。
    • Serverless v2が立ち上がるまで2分強かかる場合がある。 どの程度アクセスがなかったかによる。
    • Resumingエラーが表示されなくなるまで、20秒ごとくらいでRunを実行する。
  8. Rows returnedに表示されたらQuery EditorでClearをクリックして入力フィールドを消して、以下を実行して新しくDBを作成する。
    • CREATE DATABASE st_chat_db;
    • 『.』や『-』は使わないこと。
    • db名やtable名は全て小文字が推奨。
  9. Connect to databaseをクリック

テスト接続

  1. Query Editorが開くので先程作ったDB(st_chat_db)で接続。 UsernameとPasswordは先ほどと同じ。
  2. 接続できればDBができているので、次はTable作成。

Vector DB作成

同じCluster内の同じDBに新しくVector検索用のtableを作成する。

PostgreSQL16以上の場合は、Query editorで以下を実行して、Cluster内のDBでpgvectorを使えるようにする。 pgvectorはPostgreSQLの拡張機能でPostgreSQLでvector類似度検索(コサイン類似度、ユークリッド距離)などができるようになり、Vector RAGとして活用できる。 BedrockのKnowledgebaseよりもフレキシブルに作成できるので、精度を上げることができる。

//Databaseの一覧表示
CREATE EXTENSION vector

Table作成

  • st_chat_dbに接続した状態でQuery Editor以下の通り作成。
  • Query EditorにCREATE TABLE table_nameを記述し()内に各列の定義を記述する。
CREATE TABLE st_chat_history_table (
 primarykey bigserial PRIMARY KEY,
 userid text NOT NULL UNIQUE,
 themename text NOT NULL,
 chatid bigint NOT NULL CHECK (chatid >= 0),
 userinput text,
 botresponse text,
 timestamp timestamptz DEFAULT now()
);
-- userId用のインデックス
CREATE INDEX idx_st_chat_history_userid ON st_chat_history_table (userId);

-- timestamp用のインデックス
CREATE INDEX idx_st_chat_history_timestamp ON st_chat_history_table (timestamp);
//Databaseの一覧表示
SELECT datname FROM pg_database;

//存在するTableの一覧表示
SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';

//特定のテーブルの項目表示
SELECT * FROM information_schema.columns WHERE table_name = 'st_chat_history_table';

//テーブル自体全削除
DROP TABLE st_chat_history_table;

メモ

Aurora PostgreSQL <16と古い場合にvectorを設定する方法。

以下は、古いPostgreSQL engineを使っている場合だった。 私の使っているPostgreSQL 16にはVectorDBが標準装備されているので、Custom Parameter Groupを変更する必要なかった。

Vector DBを操作できるpgvectorを使用できるように設定変更。 Default parameter groupの内容には含まれておらず、default parameter groupの内容は変更できないので、ClusterにDefault parameter groupが選択されていたら、Custom parameter groupを作り、pgvectorを登録する。 さらにClusterが使っているengine typeが必要なので予め調べておく。

  1. Aurora –> Databases –> 対象クラスタをクリック
  2. Configurationタブクリック
  3. Parameter groupとEngine Typeが記載されているのでメモっておく

  1. Aurora–>左メニュー parameter groups-> Create custom parameter group
  2. name: MyCustomParameterGroupPgvector
  3. Description: TO user pgvector
  4. Engine type: Aurora PostgreSQL
  5. Parameter group family: aurora-postgresql16(私の場合は16.6だったので)
  6. Type: DB Cluster Parameter Group
    • Cluster全体に対するパラメータ
  7. Create
  8. 作成できたら、作成したCustom parameter groupを選択して、Edit
  9. Modifiable parameterのshared_preload_librariesのvalueにvectorと追加する。
    • 何も入っていなければ『vector』と入れる
    • なにか値が入っていれば末尾に『,vector』と追加する。スペースは入れない事。

Vector DB用Table作成

  • 以下のようなテーブルを作る
  • Vector embeddingはBedrock Cohere multilingual v3を使うので1024次元
  • Vector検索高速化の為に、chat_vectorにIndexを付ける。
    • HNSWは、大規模なベクターデータセットに対して高速な近似最近傍探索(Approximate Nearest Neighbor Search: ANNS)を実現するためのインデックスアルゴリズムです。
    • ector_l2_ops は、PostgreSQLの拡張機能であるpgvectorにおいて、HNSWインデックスを構築する際に**「どの距離計算方法を使って近さを定義するか」を指定する演算子クラス(Operator Class)**です。 この場合L2距離(ユークリッド距離)。 正規化してl2を使うと距離が角度となりコサイン類似度と完全に一致する。 なのでLambdaでEmbeddingを計算して正規化して格納すること。
CREATE TABLE st_chat_history_vector_table (
    primarykey BIGSERIAL PRIMARY KEY,
    userid TEXT NOT NULL,
    theme TEXT NOT NULL,
    chatid BIGINT NOT NULL CHECK (chatid >= 0),
    chat_summary TEXT NOT NULL,
    metadata JSONB, -- ★推奨: JSONB型に変更
    chat_vector VECTOR(1024) NOT NULL -- ベクターデータは必須のためNOT NULLを追加推奨
);
-- ベクター検索を高速化するためのHNSWインデックス
CREATE INDEX ON st_chat_history_vector_table USING hnsw (chat_vector vector_l2_ops);

Lambdaからのアクセス方法はまだここに書けてません。(が出来ています。時間がある時に追記します。)

Data API か Conn(Proxy)選択

量が少ない場合はData APIが簡単で安い。 しかしTPS(Transaction per second)が増えるとAurora PostgreSQL ClusterごとにProxyが必要

項目Aurora Data APIProxy(Conn 接続)
接続方式コネクションレス(HTTP)コネクション維持(TCP)
Lambda との相性◎ 非常に良い△ コネクション管理が必要
レイテンシ20〜100ms1〜5ms(高速)
スループット低〜中
同時実行耐性
コスト安い(Proxy不要)高い(Proxy料金+DB負荷)
運用の複雑さ低い(簡単)高い(接続管理が必要)
適した用途軽いクエリ、イベント駆動、低頻度高頻度、大量更新、複雑なJOIN
失敗時のリトライ容易コネクション切断時に複雑
Lambda のコールドスタート影響小さい大きい(接続確立が必要)
Aurora CPU 負荷低〜中中〜高(大量接続で増える)

1秒あたりの Aurora 更新数推奨方式
〜50 TPSData API(余裕)
50〜100 TPSData API(問題なし)
100〜300 TPSData API(ギリギリ)
300〜500 TPSグレーゾーン(切り替え検討)
500〜1000 TPSProxy(Conn)推奨
1000 TPS 以上Proxy(Conn)必須

ユーザー管理、課金集計などは1つのAurora PostgreSQL Clusterにする。 読み出しがヘビーなRAGは別Clusterにしなければいけない。

CloudWatchからTPSを取得してDynamoDBに保存するLambdaコード

検証していません。 しかし例として載せておきます。

CloudWatchは自動的にTPSが送られる。 保存期間は15ヶ月で自動削除。

import boto3
import datetime
import os

cloudwatch = boto3.client('cloudwatch')
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(os.environ['TPS_TABLE'])

def lambda_handler(event, context):
    now = datetime.datetime.utcnow()
    start = now - datetime.timedelta(minutes=1)

    # CloudWatch から Aurora の TPS(CommitThroughput)を取得
    response = cloudwatch.get_metric_statistics(
        Namespace='AWS/RDS',
        MetricName='CommitThroughput',
        Dimensions=[
            {'Name': 'DBClusterIdentifier', 'Value': os.environ['AURORA_CLUSTER_ID']}
        ],
        StartTime=start,
        EndTime=now,
        Period=60,
        Statistics=['Maximum']
    )

    datapoints = response.get('Datapoints', [])
    if not datapoints:
        return {"status": "no data"}

    tps = datapoints[0]['Maximum']

    # 月のキー
    yyyymm = now.strftime("%Y%m")

    # DynamoDB に書き込み
    table.put_item(
        Item={
            'PK': f"TPS#{yyyymm}",
            'SK': now.isoformat(),
            'tps': tps
        }
    )

    # トップ10だけ残す(古いものを削除)
    prune_top10(yyyymm)

    return {"status": "ok", "tps": tps}


def prune_top10(yyyymm):
    # 月の全TPSを取得
    response = table.query(
        KeyConditionExpression="PK = :pk",
        ExpressionAttributeValues={":pk": f"TPS#{yyyymm}"}
    )

    items = response['Items']
    if len(items) <= 10:
        return

    # TPS の高い順にソート
    items_sorted = sorted(items, key=lambda x: x['tps'], reverse=True)

    # 11位以下を削除
    for item in items_sorted[10:]:
        table.delete_item(
            Key={'PK': item['PK'], 'SK': item['SK']}
        )

コメント