Vector engine for OpenSearch Service ServerlessをCloudFormationで実装してみた #AWSreInvent

2023.12.10

こんにちは、つくぼし(tsukuboshi0755)です!

re:Invent 2023でVector engine for OpenSearch Service ServerlessがGAになりました。

このサービスとBedrockのKnowledge baseを組み合わせる事で、RAGシステムを作成できるようになります。

今回はVector engine for OpenSearch Service ServerlessをCloudFormationで構築し、利用する方法を紹介します!

テンプレート

今回は以下のテンプレートをベースに。コレクションの種類をベクトル検索に変更し作成していきます。

全体のコードは以下の通りです。

CloudFormationコード

template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: Vector engine for OpenSearch Service Serverless

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Parameters:
          - SysName
          - Env
          - CollectionStandbyReplicas
          - AossDataAccessPrincipalArn

Parameters:
  SysName:
    Type: String
    Default: 'cm'
    Description: 'System name for this stack.'
  Env:
    Type: String
    Default: 'dev'
    Description: 'Environment for this stack.'
    AllowedValues:
      - 'prd'
      - 'stg'
      - 'dev'
  CollectionStandbyReplicas:
    Type: String
    Default: 'DISABLED'
    Description: 'DISABLED is suitable for production environments. ENABLED is suitable for development environments.'
    AllowedValues:
      - 'DISABLED'
      - 'ENABLED'
  AossDataAccessPrincipalArn:
    Type: String
    Default: 'arn:aws:iam::123456789012:user/aoss-data-access-user'
    Description: 'The ARN of the IAM user or role that has access to the data in the collection.'

Resources:
  AossCollection:
    Type: 'AWS::OpenSearchServerless::Collection'
    Properties:
      Name: !Sub '${SysName}-${Env}-collection'
      Type: VECTORSEARCH
      StandbyReplicas: !Ref CollectionStandbyReplicas
    DependsOn: AossEncryptionPolicy
  AossEncryptionPolicy:
    Type: 'AWS::OpenSearchServerless::SecurityPolicy'
    Properties:
      Name: !Sub '${SysName}-${Env}-encryption-policy'
      Type: encryption
      Policy: !Sub >-
        {
            "Rules":[
                {
                    "Resource":[
                        "collection/${SysName}-${Env}-collection"
                   ],
                    "ResourceType": "collection"
                }
           ],
            "AWSOwnedKey": true
        }
  AossNetworkPolicy:
    Type: 'AWS::OpenSearchServerless::SecurityPolicy'
    Properties:
      Name: !Sub '${SysName}-${Env}-network-policy'
      Type: network
      Policy: !Sub >-
       [
            {
                "Rules":[
                    {
                        "Resource":[
                            "collection/${SysName}-${Env}-collection"
                       ],
                        "ResourceType": "dashboard"
                    },
                    {
                        "Resource":[
                            "collection/${SysName}-${Env}-collection"
                       ],
                        "ResourceType": "collection"
                    }
               ],
                "AllowFromPublic": true
            }
       ]
  AossDataAccessPolicy:
    Type: 'AWS::OpenSearchServerless::AccessPolicy'
    Properties:
      Name: !Sub '${SysName}-${Env}-data-access-policy'
      Type: data
      Policy: !Sub >-
       [
            {
                "Rules":[
                    {
                        "Resource":[
                            "collection/${SysName}-${Env}-collection"
                       ],
                        "Permission":[
                            "aoss:CreateCollectionItems",
                            "aoss:UpdateCollectionItems",
                            "aoss:DescribeCollectionItems"
                       ],
                        "ResourceType": "collection"
                    },
                    {
                        "Resource":[
                            "index/${SysName}-${Env}-collection/*"
                       ],
                        "Permission":[
                            "aoss:CreateIndex",
                            "aoss:UpdateIndex",
                            "aoss:DescribeIndex",
                            "aoss:ReadDocument",
                            "aoss:WriteDocument"
                       ],
                        "ResourceType": "index"
                    }
               ],
                "Principal":[
                    "${AossDataAccessPrincipalArn}"
               ],
                "Description": ""
            }
       ]

Outputs:
  Endpoint:
    Value: !GetAtt AossCollection.CollectionEndpoint

以下より、本テンプレートで作成される各リソースについて説明します。

コレクション

  AossCollection:
    Type: 'AWS::OpenSearchServerless::Collection'
    Properties:
      Name: !Sub '${SysName}-${Env}-collection'
      Type: VECTORSEARCH
      StandbyReplicas: !Ref CollectionStandbyReplicas
    DependsOn: AossEncryptionPolicy

OpenSearch Serverlessのデータベースであるコレクションを作成します。

今回はベクトル検索を行うため、TypeVECTORSEARCHに指定します。

また、StandbyReplicasDISABLEDにする事で、開発/テストオプションが有効になり、スタンバイレプリカの無効化によりコストを削減できます。

なお本番環境等で可用性を重視する場合は、ENABLEDにしスタンバイレプリカを有効化する事を推奨します。

暗号化ポリシー

  AossEncryptionPolicy:
    Type: 'AWS::OpenSearchServerless::SecurityPolicy'
    Properties:
      Name: !Sub '${SysName}-${Env}-encryption-policy'
      Type: encryption
      Policy: !Sub >-
        {
            "Rules":[
                {
                    "Resource":[
                        "collection/${SysName}-${Env}-collection"
                   ],
                    "ResourceType": "collection"
                }
           ],
            "AWSOwnedKey": true
        }

OpenSearch Serverlessのデータを暗号化するためポリシーを作成します。

今回はAWSが所有するKMSキーを使用するため、AWSOwnedKeytrueに指定します。

なお代わりに自身が所有するKMSキーを使用したい場合は、以下の通りAWSOwnedKeyfalseにし、KmsARNにKMSキーのARNを指定してください。

  AossEncryptionPolicy:
    Type: 'AWS::OpenSearchServerless::SecurityPolicy'
    Properties:
      Name: !Sub '${SysName}-${Env}-encryption-policy'
      Type: encryption
      Policy: !Sub >-
        {
            "Rules":[
                {
                    "Resource":[
                        "collection/${SysName}-${Env}-collection"
                   ],
                    "ResourceType": "collection"
                }
           ],
            "AWSOwnedKey": false,
            "KmsARN": "<KMSキーのARN>"
        }

ネットワークポリシー

  AossNetworkPolicy:
    Type: 'AWS::OpenSearchServerless::SecurityPolicy'
    Properties:
      Name: !Sub '${SysName}-${Env}-network-policy'
      Type: network
      Policy: !Sub >-
       [
            {
                "Rules":[
                    {
                        "Resource":[
                            "collection/${SysName}-${Env}-collection"
                       ],
                        "ResourceType": "dashboard"
                    },
                    {
                        "Resource":[
                            "collection/${SysName}-${Env}-collection"
                       ],
                        "ResourceType": "collection"
                    }
               ],
                "AllowFromPublic": true
            }
       ]

OpenSearch Serverlessをネットワークからアクセスするためのポリシーを作成します。

今回はパブリックアクセスを使用するため、AllowFromPublictrueに指定します。

なおVPCアクセスを使用したい場合は、以下の通りAllowFromPublicfalseにし、SourceVPCEsにVPCのインタフェースエンドポイントIDを指定してください。

  AossNetworkPolicy:
    Type: 'AWS::OpenSearchServerless::SecurityPolicy'
    Properties:
      Name: !Sub '${SysName}-${Env}-network-policy'
      Type: network
      Policy: !Sub >-
       [
            {
                "Rules":[
                    {
                        "Resource":[
                            "collection/${SysName}-${Env}-collection"
                       ],
                        "ResourceType": "dashboard"
                    },
                    {
                        "Resource":[
                            "collection/${SysName}-${Env}-collection"
                       ],
                        "ResourceType": "collection"
                    }
               ],
                "AllowFromPublic": false,
                "SourceVPCEs":[
                  "<OpenSearch ServerlessのインタフェースエンドポイントID>"
               ]
            }
       ]

データアクセスポリシー

  AossDataAccessPolicy:
    Type: 'AWS::OpenSearchServerless::AccessPolicy'
    Properties:
      Name: !Sub '${SysName}-${Env}-data-access-policy'
      Type: data
      Policy: !Sub >-
       [
            {
                "Rules":[
                    {
                        "Resource":[
                            "collection/${SysName}-${Env}-collection"
                       ],
                        "Permission":[
                            "aoss:CreateCollectionItems",
                            "aoss:UpdateCollectionItems",
                            "aoss:DescribeCollectionItems"
                       ],
                        "ResourceType": "collection"
                    },
                    {
                        "Resource":[
                            "index/${SysName}-${Env}-collection/*"
                       ],
                        "Permission":[
                            "aoss:CreateIndex",
                            "aoss:UpdateIndex",
                            "aoss:DescribeIndex",
                            "aoss:ReadDocument",
                            "aoss:WriteDocument"
                       ],
                        "ResourceType": "index"
                    }
               ],
                "Principal":[
                    "${AossDataAccessPrincipalArn}"
               ],
                "Description": ""
            }
       ]

OpenSearch Serverlessのデータにアクセスするためのポリシーを作成します。

今回はPrincipalにあるAossDataAccessPrincipalArnに対して任意のIAMユーザまたはIAMロールのARNを指定し、データの作成/変更/閲覧をできるように設定します。

検索の確認

上記のテンプレートをデプロイする事で、OpenSearch Serverlessを用いて正常にデータをベクトル検索できるか確認します。

CloudFormationスタックのデプロイ

今回は以下のパラメータで、CloudFormationスタックをデプロイします。

AossDataAccessPrincipalArnには、今回作成するコレクションにアクセスするIAMユーザまたはIAMロールのARNを指定してください。

スタックの作成が完了すると、以下の通りOpenSearch ServerlessのエンドポイントURLが出力されます。

このURLは後で使用するのでメモしておいてください。

データアップロード/ベクトル検索

今回の確認で使用するデータ及び確認手順としては、以下の公式ドキュメントを参照します。

上記の公式ドキュメントに記載されている検索を実施するには、PostmanawscurlといったHTTPツールを使用する必要があります。

今回はawscurlを導入し、使用する場合の手順を紹介します。

代わりにPostmanを使用したい場合は、以下の記事をご参照ください。

また実行環境にはCloudShellを利用します。


初めにpip3コマンドでawscurlをインストールします。

$ pip3 install awscurl

次に、以下のコマンドを実行し、先ほどメモしたOpenSearch ServerlessのエンドポイントURLを設定します。

$ AOSS_ENDPOINT=<OpenSearch ServerlessのエンドポイントURL>

それではOpenSearch Serverlessのインデックスを、以下のコマンドで作成しましょう。

$ awscurl --service aoss --region ${AWS_REGION} \
     -X PUT \
     -d '{"settings": {"index.knn": true}, "mappings": {"properties": {"housing-vector": {"type": "knn_vector", "dimension": 3}, "title": {"type": "text"}, "price": {"type": "long"}, "location": {"type": "geo_point"}}}}' \
     ${AOSS_ENDPOINT}/housing-index | jq -r .

インデックス作成が問題なく完了すると、以下のようなレスポンスが返ります。

# レスポンス例
{
  "acknowledged": true,
  "shards_acknowledged": true,
  "index": "housing-index"
}

続いて作成したOpenSearch Serverlessのインデックスに対して、以下のコマンドでサンプルドキュメントをアップロードしましょう。

$ awscurl --service aoss --region ${AWS_REGION} \
     -X POST \
     -d '{"housing-vector":[10, 20, 30], "title": "2 bedroom in downtown Seattle", "price": "2800", "location": "47.71, 122.00"}' \
     ${AOSS_ENDPOINT}/housing-index/_doc | jq -r .

ドキュメントアップロードが問題なく完了すると、以下のようなレスポンスが返ります。

# レスポンス例
{
  "_index": "housing-index",
  "_id": "1%3A0%3AqgCYL4wBz5Soks5im65k",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 0,
    "successful": 0,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 0
}

最後に作成したOpenSearch Serverlessのインデックスに対して、以下のコマンドで特定のプロパティに類似したデータをベクトル検索しましょう。

$ awscurl --service aoss --region ${AWS_REGION} \
     -X GET \
     -d '{"size": 5, "query": {"knn": {"housing-vector": {"vector":[10, 20, 30], "k": 5}}}}' \
     ${AOSS_ENDPOINT}/housing-index/_search | jq -r .

正しく手順が実施できていれば、以下のように先ほどアップロードしたデータがベクトル検索でヒットする事が分かります。

# レスポンス例
{
  "took": 73,
  "timed_out": false,
  "_shards": {
    "total": 0,
    "successful": 0,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 1.0,
    "hits":[
      {
        "_index": "housing-index",
        "_id": "1%3A0%3AAHx3Q4wBwuFCx3ceGjz8",
        "_score": 1.0,
        "_source": {
          "housing-vector":[
            10,
            20,
            30
         ],
          "title": "2 bedroom in downtown Seattle",
          "price": "2800",
          "location": "47.71, 122.00"
        }
      }
   ]
  }
}

上記の手順を踏む事で、ベクトル検索が正常に実施できる事を確認できました!


なお、アクセスキー及びシークレットアクセスキーを使用する事で、CloudShellにプリインストールされているcurlでもベクトル検索が可能です。

詳細は以下記事をご参照ください。

最後に

今回はVector engine for OpenSearch Service ServerlessをCloudFormationで構築し、利用する方法を紹介しました。

今後BedrockのKnowledge baseを利用する場合、こちらのサービスを合わせて構築する機会が増えていくのではないでしょうか。

そのような場合に、この記事が少しでもお役に立てれば幸いです。

以上、つくぼし(tsukuboshi0755)でした!