CloudFormationを使用して、OpenSearch Serverlessを作成する
本ページでは、OpenSearch Serverlessを取り上げます。
Amazon OpenSearch Serverless は、Amazon OpenSearch Service 用のオンデマンドサーバーレス設定です。サーバーレスにより、OpenSearch クラスターのプロビジョニング、設定、チューニングの運用上の複雑さがなくなります。
Amazon OpenSearch Serverless とは
今回はCloudFormationを使用して、OpenSearch Serverlessを作成し、IAM UserおよびLambda関数から同リソースにアクセスします。
構築する環境
OpenSearch Serverlessを作成します。
IAMユーザを作成し、OpenSearch Serverlessのダッシュボードにアクセスします。
OpenSearch ServerlessにアクセスするLambda関数を2つ作成します。
一方の関数の働きは、OpenSearch Serverlessにインデックスを作成することです。
もう一方の関数の働きは、OpenSearch Serverlessにドキュメントをインデクシングすることです。
なおLambda関数のランタイム環境はPython3.8です。
CloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。
https://github.com/awstut-an-r/awstut-fa/tree/main/147
テンプレートファイルのポイント解説
OpenSearch Serverless
以下のAWS公式サイトを参考にして、CloudFormationテンプレートを作成します。
https://docs.aws.amazon.com/ja_jp/opensearch-service/latest/developerguide/serverless-cfn.html
OpenSearch Serverlessに関して、以下の4つのリソースを作成します。
- コレクション
- 暗号化ポリシー
- データアクセスコントールポリシー
- ネットワークアクセスポリシー
コレクション
コレクションとは、OpenSearch Serverlessのメインリソースです。
Amazon OpenSearch Serverless のコレクションは、分析ワークロードを表す 1 つまたは複数のインデックスを論理的にグループ化したものです。
Amazon OpenSearch Serverless コレクションの作成、一覧表示、および削除
Resources:
Collection:
Type: AWS::OpenSearchServerless::Collection
DependsOn:
- EncryptionSecurityPolicy
Properties:
Name: !Ref CollectionName
StandbyReplicas: DISABLED
Type: TIMESERIES
Code language: YAML (yaml)
ポイントは2点あります。
1点目ははTypeプロパティです。
以下の3つの中から選択できます。
[Time series] – 運用、セキュリティ、ユーザー行動、およびビジネスに関するインサイトを得るために、マシン生成による大量の半構造化されたデータをリアルタイムで分析することに重点を置いたログ分析セグメント。
[Search] – 社内ネットワーク内のアプリケーション (コンテンツ管理システム、法的文書) や、e コマースウェブサイト検索やコンテンツ検索などのインターネット向けアプリケーションを強化する全文検索。
[ベクトル検索] – ベクトルデータ管理を簡素化し、機械学習 (ML) によって拡張された検索エクスペリエンスと、チャットボット、パーソナルアシスタント、不正検出などの生成系 AI アプリケーションを強化する、ベクトル埋め込みのセマンティック検索。
コレクションタイプを選択する
時系列と検索の大きな違いはデータのキャッシュのされ方です。
[Search] – 社内ネットワークのアプリケーションやインターネットに接続するアプリケーションに使用される全文検索。すべての検索データはホットストレージに保存され、クエリの応答時間を短縮できます。
[Time series] – マシン生成の大量の半構造化データの分析に焦点を当てたログ分析セグメント。少なくとも 24 時間のデータはホットストレージにキャッシュされ、残りはウォームストレージに残ります。
コレクションの作成
今回は「TIMESERIES」を指定して、時系列タイプのコレクションを作成します。
2点目はリソースの作成順序です。
後述する暗号化ポリシーを作成した後でないと、OpenSearch Serverlessコレクションは作成できません。
この順番を守らない場合、以下のようなエラーが発生します。
No matching security policy of encryption type found for collection name: [collection-name]. Please create security policy of encryption type for this collection.
この仕様に対応するため、DependsOnプロパティを使用して、暗号化ポリシーが作成された後に、本リソースが作成されるように設定します。
暗号化ポリシー
暗号化ポリシーを作成することで、OpenSearch Serverless内のデータの暗号化に関する設定を行います。
ユーザーが作成した各 Amazon OpenSearch Serverless コレクションでは、データへの不正アクセスを防止するための、暗号化を使用したセキュリティ機能により、保管中のデータが保護されます。保管時の暗号化では、AWS Key Management Service (AWS KMS) を使用して、暗号化キーの保存と管理を行います。
Amazon OpenSearch Serverless の暗号化
Resources:
EncryptionSecurityPolicy:
Type: AWS::OpenSearchServerless::SecurityPolicy
Properties:
Name: !Sub "${Prefix}-encryption-policy"
Policy: !Sub >-
{"Rules":[{"ResourceType":"collection","Resource":["collection/${CollectionName}"]}],"AWSOwnedKey":true}
Type: encryption
Code language: YAML (yaml)
Policyプロパティで暗号化ポリシーの内容を定義します。
記法については以下のページをご確認ください。
今回のポリシーを確認します。
Resourceに先述のOpenSearch Serverlessコレクションを指定し、本ポリシーの対象を同リソースとします。
そしてAWSOwnedKeyにtrueを指定することで、暗号化にAWSが所有するKMSキーを使用するように設定します。
データアクセスポリシー
データアクセスポリシーを作成することによって、IAMユーザ等に対して、OpenSearch Serverlessへのアクセス権限をきめ細やかに設定することができます。
Amazon OpenSearch Serverless のデータアクセスコントロールを使用すると、アクセスメカニズムやネットワークソースに関係なく、ユーザーがコレクションやインデックスにアクセスできるようにすることができます。
Amazon OpenSearch Serverless のデータアクセスコントロール
Resources:
DataAccessPolicy1:
Type: AWS::OpenSearchServerless::AccessPolicy
Properties:
Name: !Sub "${Prefix}-data-policy-01"
Policy: !Sub >-
[{"Description":"Access for cfn user","Rules":[{"ResourceType":"index","Resource":["index/*/*"],"Permission":["aoss:*"]},
{"ResourceType":"collection","Resource":["collection/${CollectionName}"],"Permission":["aoss:*"]}],
"Principal":["${User1Arn}"]}]
Type: data
Code language: YAML (yaml)
Policyプロパティでデータアクセスポリシーの内容を定義します。
記法については以下のページをご確認ください。
今回のポリシーを確認します。
OpenSearch Serverlessインデックスへの全アクションを許可します。
またコレクションへの全アクションも許可します。
Principalに後述するIAMユーザ1を指定します。
ネットワークアクセスポリシー
ネットワークアクセスポリシーについて、AWS公式では以下の通りに説明しています。
Amazon OpenSearch Serverless コレクションでは、ネットワーク設定によって、そのコレクションに対しインターネット経由でパブリックネットワークからのアクセスが可能か、OpenSearch Serverless が管理する VPC エンドポイントからアクセスする必要があるかが決まります。
Amazon OpenSearch Serverless でのネットワークアクセス
Resources:
NetworkSecurityPolicy:
Type: AWS::OpenSearchServerless::SecurityPolicy
Properties:
Name: !Sub "${Prefix}-network-policy"
Policy: !Sub >-
[{"Rules":[{"ResourceType":"collection","Resource":["collection/${CollectionName}"]},
{"ResourceType":"dashboard","Resource":["collection/${CollectionName}"]}],"AllowFromPublic":true}]
Type: network
Code language: YAML (yaml)
Policyプロパティでネットワークアクセスポリシーの内容を定義します。
記法については以下のページをご確認ください。
https://docs.aws.amazon.com/ja_jp/opensearch-service/latest/developerguide/serverless-network.html
今回のポリシーを確認します。
AllowFromPublicに「true」を指定することによって、本コレクションやダッシュボードに対して、インターネットから直接アクセスできるようになります。
IAMユーザ
Resources:
User1:
Type: AWS::IAM::User
Properties:
LoginProfile:
Password: !Ref Password
Policies:
- PolicyName: AllAllowPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- aoss:*
Resource: "*"
UserName: !Sub "${Prefix}-user-01"
User2:
Type: AWS::IAM::User
Properties:
LoginProfile:
Password: !Ref Password
Policies:
- PolicyName: AllAllowPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- aoss:*
Resource: "*"
UserName: !Sub "${Prefix}-user-02"
Code language: YAML (yaml)
2ユーザ作成します。
設定は両ユーザとも同一です。
先述の通り、データアクセスポリシーによって、IAMユーザ1に対してのみコレクションやダッシュボードへのアクセスを許可しています。
Lambda関数
作成したOpenSearch ServerlessにLambda関数からアクセスします。
関数で実行するコードは以下のページを参考にしています。
https://opensearch.org/docs/latest/clients/python-low-level/
関数1
Resources:
Function2:
Type: AWS::Lambda::Function
Properties:
Architectures:
- !Ref Architecture
Environment:
Variables:
COLLECTION_ENDPOINT: !Sub "${Collection}.${AWS::Region}.aoss.amazonaws.com"
REGION: !Ref AWS::Region
Code:
ZipFile: |
from opensearchpy import OpenSearch, RequestsHttpConnection, AWSV4SignerAuth
import boto3
import os
host = os.environ['COLLECTION_ENDPOINT']
region = os.environ['REGION']
service = 'aoss'
credentials = boto3.Session().get_credentials()
auth = AWSV4SignerAuth(credentials, region, service)
client = OpenSearch(
hosts=[{'host': host, 'port': 443}],
http_auth=auth,
use_ssl=True,
verify_certs=True,
connection_class=RequestsHttpConnection,
pool_maxsize=20,
)
def lambda_handler(event, context):
index_name = "python-test-index"
create_response = client.indices.create(
index_name
)
print(create_response)
document = {
'title': 'Moneyball',
'director': 'Bennett Miller',
'year': '2011'
}
index_response = client.index(
index=index_name,
body=document
)
print(index_response)
FunctionName: !Sub "${Prefix}-function-02"
Handler: !Ref Handler
Layers:
- !Ref LambdaLayer
Runtime: !Ref Runtime
Role: !GetAtt FunctionRole2.Arn
Code language: YAML (yaml)
関数内で実行するコードをインライン形式で記載します。
CloudFormationを使用して、Lambda関数を作成する方法については、以下のページをご確認ください。
この関数はOpenSearch Serverlessにデータを保存します。
具体的には、OpenSearch Serverless用のクライアントオブジェクトを作成後、以下の2つのアクションを実行します。
- クライアントのindices.createメソッドを使用して、インデックス作成
- クライアントのindexメソッドを使用して、ドキュメントのインデクシング
ドキュメントは参考サイトに記載されていたサンプルデータです。
関数2
Resources:
Function3:
Type: AWS::Lambda::Function
Properties:
Architectures:
- !Ref Architecture
Environment:
Variables:
COLLECTION_ENDPOINT: !Sub "${Collection}.${AWS::Region}.aoss.amazonaws.com"
REGION: !Ref AWS::Region
Code:
ZipFile: |
from opensearchpy import OpenSearch, RequestsHttpConnection, AWSV4SignerAuth
import boto3
import os
host = os.environ['COLLECTION_ENDPOINT']
region = os.environ['REGION']
service = 'aoss'
credentials = boto3.Session().get_credentials()
auth = AWSV4SignerAuth(credentials, region, service)
client = OpenSearch(
hosts=[{'host': host, 'port': 443}],
http_auth=auth,
use_ssl=True,
verify_certs=True,
connection_class=RequestsHttpConnection,
pool_maxsize=20,
)
def lambda_handler(event, context):
index_name = "python-test-index"
q = 'miller'
query = {
'size': 5,
'query': {
'multi_match': {
'query': q,
'fields': ['title^2', 'director']
}
}
}
search_response = client.search(
body=query,
index=index_name
)
print(search_response)
FunctionName: !Sub "${Prefix}-function-03"
Handler: !Ref Handler
Layers:
- !Ref LambdaLayer
Runtime: !Ref Runtime
Role: !GetAtt FunctionRole3.Arn
Code language: YAML (yaml)
この関数は、OpenSearch Serverlessのデータを検索します。
具体的には、クライアントのindices.searchメソッドを使用します。
こちらも参考サイトに記載されていたサンプルデータを検索します。
IAMロール
Resources:
FunctionRole2:
Type: AWS::IAM::Role
DeletionPolicy: Delete
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- lambda.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: FunctionRole2Policy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- aoss:APIAccessAll
Resource:
- !Sub "arn:aws:aoss:${AWS::Region}:${AWS::AccountId}:collection/${Collection}"
Code language: YAML (yaml)
両関数用のIAMロールです。
上記は関数2用ですが、関数3用のIAMロールもポリシーは全く同じです。
ポイントはインラインポリシーで指定している「aoss:APIAccessAll」です。
2023 年 5 月 10 日以降、OpenSearch Serverless では、コレクションリソースにこれら 2 つの新しい IAM 許可が必要になります。aoss:APIAccessAll のアクセス許可はデータプレーンアクセスを許可し、aoss:DashboardsAccessAll アクセス許可はブラウザから OpenSearch Dashboards を許可します。2 つの新しい IAM アクセス権限を追加しなかった場合、403 エラーが表示されます。
Amazon OpenSearch Serverless 向けの アイデンティティとアクセス管理
両関数はインデックス作成や検索を行いますから、前者を許可する必要があります。
データアクセスポリシー
Resources:
DataAccessPolicy2:
Type: AWS::OpenSearchServerless::AccessPolicy
DependsOn:
- Function2
Properties:
Name: !Sub "${Prefix}-data-policy-02"
Policy: !Sub
- >-
[{"Description":"Access for Function2","Rules":[{"ResourceType":"index","Resource":["index/*/*"],"Permission":["aoss:CreateIndex","aoss:WriteDocument","aoss:UpdateIndex"]}],
"Principal":["${FunctionRole2Arn}"]}]
- FunctionRole2Arn: !GetAtt FunctionRole2.Arn
Type: data
DataAccessPolicy3:
Type: AWS::OpenSearchServerless::AccessPolicy
DependsOn:
- Function3
Properties:
Name: !Sub "${Prefix}-data-policy-03"
Policy: !Sub
- >-
[{"Description":"Access for Function3","Rules":[{"ResourceType":"index","Resource":["index/*/*"],"Permission":["aoss:ReadDocument"]}],
"Principal":["${FunctionRole3Arn}"]}]
- FunctionRole3Arn: !GetAtt FunctionRole3.Arn
Type: data
Code language: YAML (yaml)
IAMユーザーと同様に、Lambda関数用にもデータアクセスポリシーを作成します。
関数2はOpenSearch Serverlessにデータを投入しますから、書き込み系の権限を与えます。
関数3はデータを検索しますから、読み込み用の権限を与えます。
(参考)CloudFormationカスタムリソースを使用して、Lambdaレイヤーを自動的に作成する
PythonからOpenSearch Serverlessにアクセスするために、AWS公式が提供しているパッケージ(opensearch-py)を使用します。
このパッケージはデフォルトのLambda関数のランタイム環境には含まれていません。
今回はLambdaレイヤーを作成し、これを含めることによって、同パッケージを使用できるようにします。
今回はCloudFormationカスタムリソースを使用して、自動的にLambdaレイヤーを作成します。
詳細につきましては、以下のページをご確認ください。
上記のページでは、pipでインストールするパッケージをSSM Parameter Storeで指定します。
今回は同リソースを以下のように指定します。
Resources:
RequirementsParameter:
Type: AWS::SSM::Parameter
Properties:
Name: !Ref Prefix
Type: String
Value: |
urllib3==1.26.6
opensearch-py
Code language: YAML (yaml)
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。
今回は名前をつけるIAMリソース(IAMユーザ)を作成しますから、以下の通りにオプションを設定します。
$ aws cloudformation create-stack \
--stack-name [stack-name] \
--template-url https://[bucket-name].s3.[region].amazonaws.com/[folder-name]/fa-147.yaml \
--capabilities CAPABILITY_NAMED_IAM
Code language: Bash (bash)
AWS Management ConsoleからOpenSearch Serverlessコレクションを確認します。
コレクションが正常に作成されています。
ネットワークアクセスポリシーを確認します。
このコレクションがパブリックに公開されていることがわかります。
データアクセスポリシーを確認します。
3つありますので順番に確認します。
1つ目のポリシーはIAMユーザ1に対して全アクションを許可する内容です。
2つ目のポリシーは関数2にテストデータを書き込むための権限を与える内容です。
3つ目のポリシーは関数3にデータを検索するための権限を与える内容です。
動作確認
準備が整いましたので、実際の動作を確認します。
データを書き込む
関数2を実行してテストデータを書き込みます。
ログを見ると、確かにログが書き込まれていることがわかります。
正常にインデックス作成と追加ができました。
データを検索する
続いて書き込んだデータを検索します。
ログを見ると、確かに書き込んだデータが返ってきました。
正常に検索が実行できました。
ダッシュボード
IAMユーザからOpenSearch Serverlessダッシュボードにアクセスします。
まずIAMユーザ2でアクセスを試みます。
アクセスできませんでした。
これはIAMユーザ2が使用しているIAMロール的には問題ありませんが、データアクセスポリシーが設定されていないためです。
このようにOpenSearch Serverlessを使用するユーザは、IAMロール等のIAMポリシーだけでなく、データアクセスポリシーも適切に設定する必要があります。
続いてIAMユーザ1でアクセスします。
正常にアクセスできました。
IAMユーザ1はデータアクセスポリシーが適切に設定されているため、ダッシュボードにアクセスすることができました。
最後にダッシュボードから検索を行います。
書き込んだテストデータが返されました。
このようにOpenSearch Serverlessでは、ダッシュボードも使用することができます。
まとめ
CloudFormationを使用して、OpenSearch Serverlessを作成し、IAM UserおよびLambda関数から同リソースにアクセスしました。