Amazon S3 との Amazon OpenSearch Service ゼロETL統合（Direct Query）- プレビュー版

:::note warn
Amazon OpenSearch Service と S3 の zero-ETL integration (Direct Query) は 2023/12 時点で Preview の機能です。
:::






---
# はじめに
2023年11月に「Amazon OpenSearch Service の Amazon S3 とのゼロETL統合」が発表されました。

https://aws.amazon.com/jp/blogs/news/amazon-opensearch-service-zero-etl-integration-with-amazon-s3-preview/

OpenSearchについて概要に触れつつ、今回のゼロETL統合によりどんなことが変わったのかを中心にご紹介します。

* OpenSearchの概要
* 検証①　従来のデータ取り込み方法
* 検証②　今回リリースされた方法
* 追加検証



---
## OpenSearchとは
* オープンソースの分散型検索・分析パッケージ
* OpenSearch Projectで開発される

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/40829fa0-fc0f-9533-0705-f2bba2b21cca.png)

https://github.com/opensearch-project

大きく以下で構成されます。
* OpenSearch：データストア、検索エンジン
* OpenSearch Dashboards：可視化、UI ツール



---
## OpenSearchの開発経緯
もともとElasticsearchと呼ばれるオープンソースです。
2021年1月に開発元のElasitic社が独自ライセンスにすることを発表。

AWSをはじめとする企業が参加したprojectによりフォークされApache License 2.0に準拠したオープンソースとしてOpenSearchがリリースされました。

その際、サービス名も以下のように変更になっています。
* Elasticsearch　→　OpenSearch
* Kibana →　OpenSearch Dashboard

https://aws.amazon.com/jp/what-is/opensearch/



---
## OpenSearchのユースケース
全文検索（自然言語、近似最近傍探索、ベクトル検索）、ストリーム処理分析、分析やAI/MLのベクトルDBとして利用されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/67ed415e-6bec-0839-26ce-f8b51ec60c07.png)
[引用1][1]

---
## Amazon OpenSearch Service とは
 Amazon OpenSearch Service（以下OSS）はOpenSearchクラスターのデプロイ、管理、スケーリングが可能なマネージドサービスです。
サーバレスを選択することも可能です。

https://docs.aws.amazon.com/ja_jp/opensearch-service/latest/developerguide/what-is.html



---
## 前提知識
OpenSearch自体はとても奥の深い内容になります。
概要のみの説明になりますが、前提知識をご紹介します。

必要に応じて参考サイトをご確認ください。

---
### 1. 利用方法
データの登録や検索・分析はクラスタのREST APIを通したやり取りで行われます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/6c98f797-8b47-701b-c697-dde30dd906bc.png)
[引用1][1]

---
### 2. 用語
OpenSearchでは通常のテーブルとデータの持ち方が異なります。
* ドキュメント：JSON形式のデータ（一般的なRDBのレコードに相当）
* インデックス：ドキュメントの格納先（テーブルに相当）
* マッピング：データ型の定義（カラム名やデータ型等に相当）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/21ad1ccf-60e3-c864-a9e9-2f105b247228.png)
[引用1][1]

---
データとマッピング
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/17648f19-a1cc-61b6-e854-d1d7aa98c142.png)
https://opensearch.org/docs/2.0/opensearch/mappings/

---
OSSにおけるドメインとは
* OpenSearch クラスター、およびクラスターと連携する AWS サービスの総称
* ノードはデータノード、専用マスターノード、UltraWarm ノードの 3 種類

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/83647e87-b8b2-1357-f1bb-32ada5a9f2c0.png)
[引用1][1]




---
## 今回のリリース内容
従来の方法ではOpenSearchにデータをロードすることでのみ利用が可能でした。
今回のプレビュー版リリースにより以下が実現できます。
* Glueテーブルを利用することでS3にあるデータを直接クエリできる（ダイレクトクエリ）
* データの複製や複数の分析ツールの管理に伴う運用の複雑さを軽減できる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/3e227e08-bb6e-f962-4607-7cd2b1754238.png)









---
# 環境準備

---
## 利用条件
利用にあたって以下の条件があります。

* OpenSearchドメインのバージョン2.11以降で対応
* リージョン：東京、フランクフルト、アイルランド、バージニア、オハイオ、オレゴン
* S3へのダイレクトクエリはGlueデータカタログが必要
* サポートされるデータ型は、Parquet、CSV、および JSON のみ
* OpenSearchドメインとGlueデータカタログは同じAWSアカウントのみ
* S3はドメインと同じリージョンに置く（別アカウントでもよい）
* Athena 経由で作成されたテーブルはサポートされない
* OpenSearch Serverless では使用できない

https://docs.aws.amazon.com/ja_jp/opensearch-service/latest/developerguide/direct-query-s3.html#direct-query-s3-limitations



---
## 料金
Amazon OpenSearch Service では「インスタンス時間、必要なストレージの量、OSSとの間で転送されるデータ」によって課金されます。

インスタンスの種類は以下の通りです。オンデマンドとリザーブの機能は全く同じものです。
| 種類 | 料金 |
|:-:|:-:|
|オンデマンドインスタンス|1時間ごとの利用によって課金される|
|リザーブドインスタンス|1年か3年間でインスタンスを予約することができ費用を抑えられる|
|サーバレス|ワークロードによって消費されたリソースに対してのみ課金される|

詳細は料金ページをご確認ください。

https://aws.amazon.com/jp/opensearch-service/pricing/


---
## ドメインの作成
OSSのダッシュボードからドメインの作成を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/3df4ecc9-8d57-7d14-c247-35bf3a0616a6.png)

---
画面の案内に沿って最小構成で設定します。
インスタンスはテスト用の「t3.small.search」を使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/587f960f-a96f-cd5a-6007-1492fc4293b1.png)

---
東京リージョンでは1時間当たりUSD0.056です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/64b5573f-079a-3789-55c4-c7c7bdd7697c.png)

---
今回の構成は以下の通りです。
* AZ：1
* インスタンス：t3.small.search（テスト用最小）
* ノード：1
* ストレージ：EBS
* EBS：汎用SSD gp3
* EBSサイズ：10（最小）
* ネットワーク：パブリック、デュアルスタック（IPv4、IPv6）
* マスターユーザの作成：ユーザ名admin／パスワード

---
作成を押して数分後にドメインが作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/aae90c2d-d2ca-7ed2-fad5-f505469edeb5.png)

---
ドメインとそのダッシュボードにそれぞれREST API エンドポイントとなるURLが発行されます。違いは末尾「/_dashboards」だけです。
```
OpenSearch URL：
https://search-(ドメイン名)-(省略).aos.ap-northeast-1.on.aws

OpenSearch Dashboards URL：
https://search-(ドメイン名)-(省略).aos.ap-northeast-1.on.aws/_dashboards
```
---
Dashboards URLを開き、作成したマスターユーザーでログインできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/36c0b49a-4729-ac51-a8d4-85761a07ef27.png)


---
# 動作検証

---
## 検証①　従来のデータ取り込み方法
OpenSearchクラスタへデータをロードする方法はいくつかありますが、今回は比較対象としてS3からLambdaを用いたロードを試します。

AWSドキュメントを参考にしています。

https://docs.aws.amazon.com/ja_jp/opensearch-service/latest/developerguide/integrations.html#integrations-s3-lambda



---
### 1. 事前準備
事前にS3バケットおよび必要なIAMロールを作成します。

Lambdaに必要な権限
* S3 読み取り許可
* OpenSearch Service 書き込み許可




---
### 2. デプロイパッケージの作成
以下の手順でデプロイパッケージを作成します。
* ディレクトリを作成する
* ディレクトリ内に.pyファイルを作成しドキュメントにあるコードを貼り付け
* region と host の変数を編集する
* 必要なモジュールをpackage ディレクトリにインストール
* アプリケーションコード一式をパッケージ化する

``` sample.py
import boto3
import re
import requests
from requests_aws4auth import AWS4Auth

region = '' # e.g. 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 = '' # the OpenSearch Service domain, e.g. https://search-mydomain.us-west-1.es.amazonaws.com
index = 'lambda-s3-index'
datatype = '_doc'
url = host + '/' + index + '/' + datatype

headers = { "Content-Type": "application/json" }

s3 = boto3.client('s3')

# Regular expressions used to parse some simple log lines
ip_pattern = re.compile('(\d+\.\d+\.\d+\.\d+)')
time_pattern = re.compile('\[(\d+\/\w\w\w\/\d\d\d\d:\d\d:\d\d:\d\d\s-\d\d\d\d)\]')
message_pattern = re.compile('\"(.+)\"')

# Lambda execution starts here
def handler(event, context):
    for record in event['Records']:

        # Get the bucket name and key for the new file
        bucket = record['s3']['bucket']['name']
        key = record['s3']['object']['key']

        # Get, read, and split the file into lines
        obj = s3.get_object(Bucket=bucket, Key=key)
        body = obj['Body'].read()
        lines = body.splitlines()

        # Match the regular expressions to each line and index the JSON
        for line in lines:
            line = line.decode("utf-8")
            ip = ip_pattern.search(line).group(1)
            timestamp = time_pattern.search(line).group(1)
            message = message_pattern.search(line).group(1)

            document = { "ip": ip, "timestamp": timestamp, "message": message }
            r = requests.post(url, auth=awsauth, json=document, headers=headers)
```




---
### 3. Lambdaを作成する
用意したS3バケットとロール、デプロイパッケージを用いてLambda関数を作成します。
今回は「OpenSearch_from_S3」として作成し、S3バケットトリガーを設定しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/b5415640-c3b4-5c8a-2ff9-1fbbdf1830f4.png)





---
### 4. テストファイルの用意
以下のjsonファイルを用意しました。
idに加え、author著者とbookTitle著書名をKey・Valueで持たせています。
```sample.json
{
    "data": [
        {
            "id": "book1",
            "bookTitle": "Harry Potter",
            "author": "JK Rowling"
        },
        {
            "id": "book2",
            "bookTitle": "Inferno",
            "author": "Dan Brown"
        }
    ]
}
```





---
### 5. OpenSearchでロールの設定

OpenSearch側でロールを設定する必要があります。
* OpenSearchダッシュボードのSecurity->Roles
* Mapped usersにBackend roleとしてLamdaのIAMロールをアタッチする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/af93f239-5dfe-830c-2a15-d2cc8487be61.png)

参考

https://repost.aws/ja/knowledge-center/opensearch-troubleshoot-cloudwatch-logs




---
### 6. OpenSearch側でMappingを設定
Mappingを事前に設定します。（RDBでいうカラム名やデータ型等）

OpenSearchDashboardsタブからDevToolsを選択するとConsoleが開けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/1bcda993-aab3-c019-6957-04e2dfb75709.png)

---
以下PUTしてこれから格納するデータの定義を行います。
author著者とbookTitle著書名をtextとして定義しています。
``` mapping.json
PUT /books

{
  "settings": {
    "index": {
      "number_of_shards": 2,
      "number_of_replicas": 1
    }
  },
  "mappings": {
    "properties": {
      "author": {
        "type": "text"
      },
      "bookTitle": {
        "type": "text"
      }
    }
  },
  "aliases": {
    "book1": {}
  }
}

```




---
### 7. S3にファイルを配置
S3からOpenSearchへデータをロードする準備ができました。
用意したテストファイルをS3に配置するとLambdaが起動してOpenSearchに反映されます。

コンソールでテストファイルの情報が確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/e16749ce-425a-8240-960c-399804eb2d2c.png)

```get.json
GET _search
{
  "query": {
    "match_all": {}
  }
}
```



---
### 8. OpenSearch Dashboardで確認する
DashboardのDiscoverからもテストファイルの情報が確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/34b1f3e6-4656-e593-cebc-d913e898885b.png)

S3にあるファイルに対してOpenSearchへ取り込むには
Lambda関数などで処理を一通り用意してロードする作業が必要となります。




---
## 検証②　今回リリースされた方法
今回のプレビューであるダイレクトクエリにより、ロード作業がどのように変わるのか確認します。

AWSドキュメントを参考にしています。

https://docs.aws.amazon.com/ja_jp/opensearch-service/latest/developerguide/direct-query-s3.html



---
### 1. テストファイルを用意
S3バケットに以下のcsvファイルを配置します。
ヘッダー（id,bookTitle,author）は除いています。
```book.csv
1,Harry Potter,JK Rowling
2,Inferno,Dan Brown
```




---
### 2. Glueデータカタログを作成
ダイレクトクエリを実行するためS3にあるファイルをテーブルとして扱えるようGlueデータカタログでメタデータを作成します。
* Glueデータベースを作成
* Glueテーブルを作成する

https://docs.aws.amazon.com/ja_jp/glue/latest/dg/start-data-catalog.html

---
今回作成したデータカタログは以下の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/0184824b-10dc-f79d-7758-0754bfc72c6a.png)




---
### 3. データカタログを接続する
OpenSearchドメインにGlueデータカタログを接続します。

作成したドメインの「Connections - preview」タグから「Create」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/db21b7ee-8714-20bb-30bc-cfd338b6d696.png)

---
任意の接続名を入力、IAMロールを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/ed476dd1-f67e-230d-d6bd-0546a2c0e138.png)

ここでOpenSearchからS3とGlueデータカタログに対するIAMロールが必要となります。
またOpenSearch側でロールのアタッチが必要になります。
[必要なアクセス許可](https://docs.aws.amazon.com/ja_jp/opensearch-service/latest/developerguide/direct-query-s3-creating.html#direct-query-s3-permissions)




---
### 4. ダイレクトクエリを実行する

ダッシュボードを開くとデータソースから接続したGlueデータカタログが選択できるようになります。
「opensearch_zero_etl」はGlueデータベースで「csc_csv____」はGlueテーブルです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/d375420f-6c0a-2740-57d9-5084f8d17b13.png)

---
QueryWorkbenchでクエリを発行すると、CSVファイルの中身が確認できました。
必要になるのはGlueデータカタログのみで直接ロードすることができました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/8abb1648-97d6-6500-3653-7a23d67d57f4.png)

---
Observabirityでも確認ができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/3e98fe89-b91a-6559-6889-2be2ab84a29b.png)

現在SQL をサポートしているのは、Observabirityと Query Workbenchの2つのみです。
https://docs.aws.amazon.com/ja_jp/opensearch-service/latest/developerguide/direct-query-s3-query.html




---
## 追加検証1　S3ファイルを変更する
S3ファイルを変更した場合の振る舞いを確認します。
* 上書き：すでにあるファイルの中身を書き換える
* 追加：別のファイルをS3バケットに格納する



---
### 1. ファイルを上書きした場合
先ほどあったファイルを変更し、3行目を追加してみます。
```books.csv
1,Harry Potter,JK Rowling
2,Inferno,Dan Brown
3,銀河鉄道の夜、宮沢 賢治
```
ダッシュボードでクエリすると即座に確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/27f4f125-3e56-463c-e3e0-8b5a9aef2d7e.png)

---
### 2. ファイルを追加した場合
新しいファイルを追加してみます。
```books2.csv
4,羅生門,芥川龍之介
```
同様に追加したデータを確認できます。
特にリフレッシュ等は必要ありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/995f37f5-3d66-fce7-affe-1ffc84675942.png)



---
## 追加検証2　OpenSearchからS3へのデータ操作
クエリによりOpenSearchからS3へデータを操作できるか確認します。
事前にOpenSearchのIAMポリシーでS3へのすべてのアクションを許可しています。（"s3:*"）


---
### 1. INSERT
INSERTしてみます。
```insert.sql
insert into s3test.opensearch_zero_etl.csv_test
    (id, booktitle, author) VALUES(5, 'こころ', '夏目 漱石');
```

---
エラー等はなく実行できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/3a678da8-d5bc-be5a-a9f9-2152f19c9d0e.png)

---
S3バケットを見るとファイルが作成されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/59e44eda-2d02-f679-a4b3-5fb193fe0647.png)

---
INSERTした内容が反映されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/592a58a8-2af2-ae5a-8f87-4ca06e8f12a6.png)





---
### 2. UPDATE
UPDATE文を試します。
```update.sql
update s3test.opensearch_zero_etl.csv_test
set booktitle = 'Harry Potter and the Philosopher''s Stone'
where id = 1
```

---
エラーで実行できませんでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/326c5004-83fc-033c-cf51-d29ad2a16143.png)




---
### 3. DELETE
DELET分を実行してみます。
```delete.sql
delete from s3test.opensearch_zero_etl.csv_test where id = 5
```

---
こちらもエラーで実行できませんでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/b00489ab-a3b1-f84b-1c48-9d4382102931.png)



---
## 動作検証のまとめ
* データロード処理を別途用意することなく、OpenSearchから直接S3にデータをクエリ（ダイレクトクエリ）することが可能
* データはOpenSearchでは持たないので、S3ファイルの更新が即座に反映する
* INSERT文が実行可能





---
# 最後に

今回の検証ではAmazon OpenSearch Service（OSS）におけるS3とのゼロETL統合（ダイレクトクエリ）についてプレビュー版の機能を取り上げました。

S3に対するデータロードを主とした検証となりましたが、S3 にある膨大なログデータ (VPC Flow ログ、ELB ログなど)に対してインシデントがあった時に OpenSearch 上で分析と可視化が簡単に実施可能になるということです。

ログタイプに合わせて、データアクセラレーションというクエリ実行を高速化する3つのオプションが提供されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/a4056c3e-d86e-9b2f-7eb6-e558ddeaabd3.png)
[引用2][2] 動画39:15～

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345257/ee68a13e-70e6-42cd-d79c-3a083a731930.png)
[引用3][3]

---
# 引用
引用1：Amazon OpenSearch Service - AWS サービス活用資料集
https://pages.awscloud.com/rs/112-TZM-766/images/AWS-Black-Belt_2023_Amazon-OpenSearch-Service-Basic_0131_v1.pdf

引用2：AWS re:Invent 2023 - What’s new in Amazon OpenSearch Service (ANT301)
https://www.youtube.com/watch?v=CKYCxw0mMiE&t=2527s

引用3：AWS re:Invent Recap - ソリューション編　Search & Streaming Updates
https://pages.awscloud.com/rs/112-TZM-766/images/reInvent2023-recap-ANALYTICS-1-searchstreaming.pdf

[1]:https://pages.awscloud.com/rs/112-TZM-766/images/AWS-Black-Belt_2023_Amazon-OpenSearch-Service-Basic_0131_v1.pdf

[2]:https://www.youtube.com/watch?v=CKYCxw0mMiE&t=2527s

[3]:https://pages.awscloud.com/rs/112-TZM-766/images/reInvent2023-recap-ANALYTICS-1-searchstreaming.pdf



---
# 
株式会社ジールでは、初期費用が不要で運用・保守の手間もかからず、ノーコード・ローコードですぐに手元データを分析可能なオールインワン型データ活用プラットフォーム「ZEUSCloud」を提供しております。
ご興味がある方は是非下記のリンクをご覧ください：
https://www.zdh.co.jp/products-services/cloud-data/zeuscloud/?utm_source=qiita&utm_medium=referral&utm_campaign=qiita_zeuscloud_content-area