メインコンテンツへスキップ

Amazon OpenSearch Serviceに入門してみた。チュートリアル編② Webアプリを作る

· loading · loading ·
kiitosu
著者
kiitosu
画像処理やデバイスドライバ、データ基盤構築からWebバックエンドまで、多様な領域に携わってきました。地図解析や地図アプリケーションの仕組みにも経験があり、幅広い技術を活かした開発に取り組んでいます。休日は草野球とランニングを楽しんでいます。
目次

全3編です

概要
#

前回に引き続きOpenSearch Serviceのチュートリアルをやってみる。 今回は以下。

以下を参考に環境を作る。

環境構築
#

  1. localstackを起動しておく
localstack start --network ls
  1. OpenSearchのドメインを作成する
awslocal opensearch create-domain --cli-input-json file://./opensearch_domain.json --region us-east-1
  1. 以下で起動状態を確認する しばらく時間がかかる場合もあるのでその場合は少し待つ。
curl -u 'admin:really-secure-passwordAa!1' http://secure-domain.us-east-1.opensearch.localhost.localstack.cloud:4566/_cluster/health | jq .

チュートリアル: Amazon OpenSearch Service を用いて検索アプリケーションを作成する
#

Amazon OpenSearch Serviceを用いて検索アプリケーションを作成する一般的な方法は、サーバーへのユーザクエリを送信するウェブフォームを使用することです。

次にOpenSearch APIを直接呼び出すようにサーバーを承認し、サーバーがOpeSearch Serviceにリクエスをと送信するようにします。

今回はapi-gatewayからlambdaを経由してOpenSearch Serviceを呼び出します。 これによりlambdaで署名を付与しユーザを制限します。

事前準備
#

OpenSearch Serviceのドメインが必要です。今回は冒頭に記載した環境構築でlocalstack環境を作成済みなので、ドメインは以下になります。

http://secure-domain.us-east-1.opensearch.localhost.localstack.cloud:4566

実際のAWSアカウントでやる場合は以下に従いドメインを作りましょう。

Step1. サンプルデータをインデックスする
#

sample-movies.zipをダウンロードします。

次に_bulk API で5000個のドキュメントをmoviesインデックスに追加します。

:::message alert ダウンロードしたファイル名が、AWSのチュートリアルとは異なります。 誤:bulk_movies.json 正:sample-movies.bulk また、sample-movies.buildの最後尾に改行を追加しないと以下のエラーになります。

{“error”:{“root_cause”:[{“type”:“illegal_argument_exception”,“reason”:“The bulk request must be terminated by a newline [\n]”}],“type”:“illegal_argument_exception”,“reason”:“The bulk request must be terminated by a newline [\n]”},“status”:400}% :::

ファイルをダウンロードしたところで以下のコマンドを実行します。

curl -XPOST -u 'admin:really-secure-passwordAa!1' 'http://secure-domain.us-east-1.opensearch.localhost.localstack.cloud:4566/_bulk' --data-binary @sample-movies.bulk -H 'Content-Type: application/json'

Step2. lambda functionを作成する
#

API Gatewayを作る前にlambda関数を作ります。 ここではチュートリアルに記載の方法とは異なる作業となります。 localstackをベースとした作業となるためです。

  1. 適当な場所でディレクトを作成
mkdir opensearch-function
  1. 仮想環境作成
python -m venv test && source test/bin/activate
  1. 必要なライブラリをインストール
pip3 install --target ./opensearch-function boto3
pip3 install --target ./opensearch-function requests
pip3 install --target ./opensearch-function requests_aws4auth
  1. ラムダハンドラのコードを準備 vi ./opensearch-function/opensearch-lambda.py 以下コードを貼り付け

:::details code

import boto3
import json
import requests
from requests_aws4auth import AWS4Auth

region = 'us-west-1' # For example, us-west-1
service = 'es'
credentials = boto3.Session().get_credentials()
awsauth = AWS4Auth(credentials.access_key, credentials.secret_key, region, service, session_token=credentials.token)

host = 'http://secure-domain.us-east-1.opensearch.host.docker.internal.localstack.cloud:4566' # The OpenSearch domain endpoint with https:// and without a trailing slash
index = 'movies'
url = host + '/' + index + '/_search'

# Lambda execution starts here
def lambda_handler(event, context):

    # Put the user query into the query DSL for more accurate search results.
    # Note that certain fields are boosted (^).
    query = {
        "size": 25,
        "query": {
            "multi_match": {
                "query": event['queryStringParameters']['q'],
                "fields": ["title^4", "plot^2", "actors", "directors"]
            }
        }
    }

    # Elasticsearch 6.x requires an explicit Content-Type header
    headers = { "Content-Type": "application/json" }

    # Make the signed HTTP request
    r = requests.get(url, auth=awsauth, headers=headers, data=json.dumps(query))

    # Create the response and add some extra content to support CORS
    response = {
        "statusCode": 200,
        "headers": {
            "Access-Control-Allow-Origin": '*'
        },
        "isBase64Encoded": False
    }

    # Add the search results to the response
    response['body'] = r.text
    return response

:::

:::message 上記コードで regionにus-east-1を hostにhttp://secure-domain.us-east-1.opensearch.host.docker.internal.localstack.cloud:4566を設定しています。 :::

  1. コードをzip化 コード階層を深くしないためにopensearch-functionに入ってからzip化する。 これをしないとpythoのimportの解決が面倒になる。
(cd opensearch-function && zip -r ../func.zip ./*)
  1. localstackにdeploy
# venvで作成した環境のバージョンを取得
runtime_version=$(python --version 2>&1 | awk '{print $2}' | cut -d. -f1,2)
# deploy
awslocal --region us-east-1 lambda create-function \
    --function-name opensearch-function \
    --runtime "python$runtime_version" \
    --handler opensearch-lambda.lambda_handler \
    --zip-file fileb://func.zip \
    --role arn:aws:iam::000000000000:role/lambda-role \
    --architectures arm64

Step3. API Gatewayを作る
#

API Gatewayを使うことでOpenSearchのAPI利用を限定し利用を簡略化します。 API Gatewayで認証やスロットリングのリクエストもできます。 本家ではRole等の権限周りの設定の記載がありますが、localstackではそこら辺はあまり気にせずにやります。

lambda,apigatewayのlocalstackを使ったdeployについては以下がわかりやすいです。

  1. ApiGatwayを作成
awslocal --region us-east-1 apigateway create-rest-api --name 'opensearch-api' --endpoint-url=http://localhost:4566
  1. ルートリソースID取得 API_IDは上記コマンドで出力された値を使う。
API_ID=${API_ID}
awslocal --region us-east-1 apigateway get-resources --rest-api-id ${API_ID} --endpoint-url=http://localhost:4566
  1. GETメソッドを使用可能にする RESOURCE_IDは上記コマンドで出力された値を使う
RESOURCE_ID=${RESOURCE_ID}$
awslocal --region us-east-1 apigateway put-method \
  --rest-api-id ${API_ID} \
  --resource-id ${RESOURCE_ID} \
  --http-method GET \
  --authorization-type "NONE" \
  --endpoint-url=http://localhost:4566
  1. lambdaと連携
 awslocal --region us-east-1 apigateway put-integration \
  --rest-api-id ${API_ID} \
  --resource-id ${RESOURCE_ID} \
  --http-method GET \
  --type AWS_PROXY \
  --integration-http-method POST \
  --uri arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:000000000000:function:opensearch-function/invocations \
  --passthrough-behavior WHEN_NO_MATCH \
  --endpoint-url=http://localhost:4566
  1. api-gatwayをdeploy
awslocal --region us-east-1 apigateway create-deployment \
  --rest-api-id ${API_ID} \
  --stage-name dev \
  --endpoint-url=http://localhost:4566

Step4 Webアプリケーションをテストする
#

sample-site.zipをダウンロードし、展開します。

scripts/search.js

var apigatewayendpoint = ‘’;

にエンドポイントを設定します。 今回は例えば、以下のエンドポイントになります。

var apigatewayendpoint = ‘http://localhost:4566/_aws/execute-api/${API_ID}/dev/’

index.htmlを叩いてサイトを起動します。 検索できました!

:::message alert 設定状態によって以下のエラーとなる場合があります。

No 'Basic Authorization' header, send 401 and 'WWW-Authenticate Basic'

これはlocalstackのOpenSearchServiceの設定漏れです。 localで立ち上げたサイトからの動作確認のためにopensearch_domain.jsonAdvancedSecurityOptionsfalseにしておく必要があります。 詳細はこちらAmazon OpenSearch Serviceに入門してみた。localstack準備編 :::

まとめ
#

lambdaとapi-gatewayのdeployで一番手こずってしまいました 😅 検索はできましたが、RDBと比べてのメリットなど特にわかりませんでした。 別途調べる必要がありそうです。

Reply by Email

関連記事

Amazon OpenSearch Serviceに入門してみた。チュートリアル編① ダッシュボードを使う
· loading · loading
Kinesis Data Stream の IeratorAgeMilliseconds がスパイク状の異常値を報告する
CDKでNatGatewayのIPアドレスをOutputsに出力する
· loading · loading