Amazon Elasticsearch Serviceについてまとめてみる

# はじめに
**Amazon Elasticsearch Service(Amazon ES)**を使ったなかで学んだことをまとめてみました。
Elasticsearch自体に関しては簡単にしか説明しないので、ご注意ください。

# 全文検索とElasticsearchについて
Amazon ESを使ってみようと思われる方はおそらく**全文検索**エンジンに関心があり、その中でも特に**Elasticsearch**に興味をお持ちのはずです。

簡単にではありますが、前提知識として**全文検索**と**Elasticsearch**について説明してみます。

## 全文検索とは
端的に言うと、特定の保存場所(データベースなど)に保存されている複数のファイルから特定の文字列を検索するということです。

grep型とインデックス型があり、検索速度はインデックス型の方が高速らしい。
RDBでインデックスを設定した方が処理速度が高速になることを踏まえれば分かりやすいと思います。
Elasticsearchはインデックス型になります。

参照：[全文検索：Wikipedia](https://ja.wikipedia.org/wiki/%E5%85%A8%E6%96%87%E6%A4%9C%E7%B4%A2)

全文検索を実現するエンジンは複数あり、代表的なものが**Apache Lucene**です。

ElasticsearchやApache Solrの検索エンジン部はLuceneでできています。
Luceneに様々な機能を付加したシステムがElasticsearchやSolrになります。

## Elasticsearchについて
ElasticsearchはApache Luceneを基盤とした検索エンジンです。
どのような特徴があるのでしょうか。

- **スケーラビリティ**
おそらくElasticsearch最大の長所です。相当大規模なシステムでの利用にも耐えるスケーラビリティを有します。
幾つものリクエストに対して、膨大な文書を検索し高速で結果を出力できます。
また、複数のサーバをクラスタリングする際もSolr等に比べて容易だと言われています。

- **スキーマレス**
RDBと異なり、格納するデータ構造をより柔軟に決めることができます。形式はJSONです。

- **豊富なクエリ**
Elasticsearchは全文検索用クエリ**match**を始め、様々なクエリ(Query DSL)を用意しています。
クエリそれぞれで使用できるデータ型などが決まっているなどの違いがあります。
全文検索をする場合、クエリは**Full text queries**のいずれかを使用する必要があります。
クエリのオプションも豊富であり、クエリを使い分けるだけで様々なユースケースに対応できます。  
参照：[Query DSL](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html)
参照：[Full text queries](https://www.elastic.co/guide/en/elasticsearch/reference/current/full-text-queries.html)

- **Elastic Stack(ELK Stack)**
**Elasticsearch**、**Logstash**、**Kibana**などからなる製品群です。まとめて**Elastic Stack**または**ELK Stack**と呼ばれています。
全てElastic社が主体となって開発されている製品であり、相互連携が優れています。
特にKibanaはデータの可視化・分析、Elasticsearchの操作に大変有効であり、Elasticsearchのデータをより有効活用するのに役立ちます。  
参照：[Kibana](https://www.elastic.co/jp/kibana)
参照：[Elastic Stackって何？](https://thinkit.co.jp/article/13857)

- **企業が中心となっているOSS**
営利企業であるElastic社が中心となって開発が進められています。
他のオープンソースの検索エンジンと比較すると開発が停止せず、システムの改良が継続的に行われていく可能性が高いと言えます。

Elasticsearchにまつわる専門用語や全文検索の処理過程に関しては以下の記載が分かりやすかったです。
参考：[Elasticsearchの用語が覚えられないのでまとめた](https://qiita.com/mpls104/items/222a9fe7fbdfe71654d8)
参考：[ElasticSearch の全文検索での analyzer について](https://blog.chocolapod.net/momokan/entry/115)

差し当たり、*インデックスがRDBで言うデータベースであること*、*日本語の文書を全文検索する際はanalyzerの働きや構成要素を理解する必要があること*を抑えていただければ、Amazon ESを利用するにあたっては十分だと思います。

# Amazon ESとは
AWSによるElasticsearchのマネージドサービスです。それだけでは身も蓋もないので特徴を挙げてみます。

- **利用開始が簡単**
AWSのマネジメントコンソールからGUIを使って立ち上げることもできますし、AWS CLIやCloudFormation、AWS CDKを使用して開始することもできます。

- **運用コストが削減される**
マネージドサービスなので当然なのですが、Amazon ESの場合、日本語用プラグインの**kuromoji**が最初から利用できたりします。
Elasticsearch稼働に必要なJavaのインストール・アップデートの手間も不要です。

- **AWSの他サービスとの連携**
Lambdaを使用したデータの前処理、CloudWatchによる監視なども簡単に行えます。

- **オリジナルのElasticsearchでは有料であるサービスを利用できる**
2020/5/22 追記:
@n0z0me さまよりOpen Distro for Elasticsearchの説明についてご指摘をいただきました。
当初の記述を修正いたします。
参考：[AWS、OSSだけで構成される「Open Distro for Elasticsearch」公開]
(https://www.itmedia.co.jp/news/articles/1903/22/news104.html)  
~~Elasticsearchはオープンソースですが、JDBCやSQLを使うためにはElastic社へ料金を支払う必要がありました。
AWSはElasticsearchのプロプライエタリな部分を Open Distro for Elasticsearchとしてオープンソース化しました。~~
OSSとプロプライエタリなコードが混在していたGitHub上のElasticsearchのコードのうち、オープンソースの部分とそのディストリビューションをOpen Distro for ElasticsearchとしてApache 2.0ライセンスの下公開しました。
Amazon ESではOpen Distroに含まれる機能も簡単に利用できます。

- **最新版のElasticsearchは使えない**
マネージドサービスの宿命として、最新版をすぐに使用するのが難しいという問題があります。
どうしても最新版のElasticsearchを使用したい場合は、EC2等に自身でElasticsearchを導入するか、Elastic社が提供している**Elastic Cloud**を利用する必要があります。

- **細かい設定はできない**
最新版を使えないのと同様、細かい設定はできません。
日本語の形態素解析エンジンで最も一般的なのはMeCabですが、Amazon ESの場合はkuromojiに限定されます。
辞書もkuromojiデフォルトのIPA辞書であり、NEologdも使えません。

# 立ち上げてみる
コンソールやCLIからでも立ち上げられますが、AWSのサービスであることを最大限活かすために**AWS CDK**を使って立ち上げてみます。
AWS CDK自体については以下のリンクが参考になると思います。
参考：[AWS CDK が GA! さっそく TypeScript でサーバーレスアプリケーションを構築するぜ【 Cloud Development Kit 】](https://dev.classmethod.jp/articles/aws-cdk-ga-serverless-application/)

また、CDKからAmazon ESドメインを立ち上げる方法は以下のページに詳しく書かれています。
参照：[AWS CDKでAmazon Elasticsearch Serviceのドメイン(クラスタ)を作ってみた](https://dev.classmethod.jp/articles/cdk-elasticsearch-deployment/)

CDKやその他必要なもののバージョンは以下の通りです。

```
% cdk version                                                                                                                                                                              [~]
1.37.0 (build e4709de)
# TypeScriptが必要
% tsc -v                                                                                                                                                                                   [~]
Version 3.4.5
% node -v                                                                                                                                                                                  [~]
v12.14.1
```
CDKのプロジェクトを作ります。

```
% cdk init --language typescript
Applying project template app for typescript
Initializing a new git repository...
Executing npm install...

# 中略

# Welcome to your CDK TypeScript project!

This is a blank project for TypeScript development with CDK.

The `cdk.json` file tells the CDK Toolkit how to execute your app.

# 中略

% exa                                      
drwxr-xr-x    - test  6 5 15:07 bin
.rw-r--r--  160 test  6 5 15:07 cdk.json
.rw-r--r--  130 test  6 5 15:07 jest.config.js
drwxr-xr-x    - test  6 5 15:07 lib
drwxr-xr-x    - test  6 5 15:08 node_modules
.rw-r--r-- 275k test  6 5 15:08 package-lock.json
.rw-r--r--  545 test  6 5 15:07 package.json
.rw-r--r--  543 test  6 5 15:07 README.md
drwxr-xr-x    - test  6 5 15:07 test
.rw-r--r--  598 test  6 5 15:07 tsconfig.json
```
CDKのコード内でAmazon ESを立ち上げられるようにするために以下のパッケージをインストールします。

```
% yarn add @aws-cdk/aws-elasticsearch
```
CDKを始めてデプロイする場合は、CloudFormationで利用するデプロイ用S3バケットを作成する以下のコマンドを入力します。

```
% cdk bootstrap
```
lib配下のtsファイル(stackname-stack.ts)を以下のように記述します。Amazon ESの設定を定義しています。

```typescript
import * as cdk from "@aws-cdk/core";
import * as es from "@aws-cdk/aws-elasticsearch";

export class AmazonesPjStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // The code that defines your stack goes here

    // Amazon ESのバージョン
    const esVersion: string = "7.4";

    // アクセスポリシー設定のためのIPアドレス
    // ローカルのIPアドレスは curl -s https://checkip.amazonaws.com で分かる
    const sourceIP: string = `${MyIP}`;

    // ドメイン名
    const domainName: string = "test-es-domain";

    // Amazon ESドメインの詳細設定
    const domain = new es.CfnDomain(this, domainName, {
      // アクセスポリシー設定
      accessPolicies: {
        Version: "2012-10-17",
        Statement: [
          {
            Effect: "Allow",
            Principal: {
              AWS: ["*"],
            },
            Action: ["es:*"],
            Resource: `arn:aws:es:${cdk.Stack.of(this).region}:${
              cdk.Stack.of(this).account
            }:domain/${domainName}/*`,
            Condition: {
              IpAddress: {
                "aws:SourceIp": `${sourceIP || "127.0.0.1"}`,
              },
            },
          },
          {
            Effect: "Allow",
            Principal: {
              AWS: [cdk.Stack.of(this).account],
            },
            Action: ["es:*"],
            Resource: `arn:aws:es:${cdk.Stack.of(this).region}:${
              cdk.Stack.of(this).account
            }:domain/${domainName}/*`,
          },
        ],
      },
      domainName: domainName,
      ebsOptions: {
        ebsEnabled: true,
        volumeSize: 10,
        volumeType: "gp2",
      },
      elasticsearchClusterConfig: {
        instanceCount: 1,
        instanceType: "t2.small.elasticsearch",
      },
      elasticsearchVersion: esVersion,
      encryptionAtRestOptions: {
        enabled: false,
      },
      nodeToNodeEncryptionOptions: {
        enabled: false,
      },
      snapshotOptions: {
        automatedSnapshotStartHour: 0,
      },
    });
  }
}
```
実際にデプロイします。デプロイには１０分程度時間がかかります。

```
% cdk deploy                                                                                                [~/Documents/code_test/amazones_pj]+[master]
yarn run v1.22.4
warning package.json: No license field
✨  Done in 1.32s.
AmazonesPjStack: deploying...
AmazonesPjStack: creating CloudFormation changeset...
 0/3 | 20:25:25 | CREATE_IN_PROGRESS   | AWS::Elasticsearch::Domain | test-es-domain (testesdomain) 
 0/3 | 20:25:25 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata         | CDKMetadata 
 0/3 | 20:25:26 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata         | CDKMetadata Resource creation Initiated
 1/3 | 20:25:26 | CREATE_COMPLETE      | AWS::CDK::Metadata         | CDKMetadata 
 1/3 | 20:25:27 | CREATE_IN_PROGRESS   | AWS::Elasticsearch::Domain | test-es-domain (testesdomain) Resource creation Initiated
1/3 Currently in progress: testesdomain
 2/3 | 20:39:32 | CREATE_COMPLETE      | AWS::Elasticsearch::Domain | test-es-domain (testesdomain) 
 3/3 | 20:39:33 | CREATE_COMPLETE      | AWS::CloudFormation::Stack | AmazonesPjStack 

 ✅  AmazonesPjStack
```
ドメインを作れました。

```
% aws es list-domain-names                                                                                                                                                                 [~]
{
    "DomainNames": [
        {
            "DomainName": "test-es-domain"
        }
    ]
}
```

# 設定してみる
Amazon ESは最近のElasticsearch同様、インデックスにデータを格納する前にmapping(RDBでいうカラム)を定義する必要はありません。
とはいえ、やはりmappingは事前に定義することをお勧めします。日本語の文章に対して適切なアナライザを使用しなければ全文検索を有効に行えなくなってしまいます。

そこで、以下のようなファイルを用意し、mappingとアナライザの定義を行います。

```json:settings_es.json
{
  "settings": {
    "analysis": {
      "analyzer": {
        "index_analyzer": {
          "type": "custom",
          "tokenizer": "kuromoji_tokenizer",
          "mode": "search",
          "char_filter": ["icu_normalizer", "kuromoji_iteration_mark"],
          "filter": [
            "cjk_width",
            "kuromoji_baseform",
            "kuromoji_part_of_speech",
            "ja_stop",
            "lowercase",
            "kuromoji_number",
            "kuromoji_stemmer"
          ]
        }
      },
      "tokenizer": {
        "kuromoji_tokenizer": {
          "type": "kuromoji_tokenizer",
          "mode": "normal"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "company": {
        "type": "text",
        "analyzer": "index_analyzer"
      },
      "products": {
        "type": "text",
        "analyzer": "index_analyzer"
      }
    }
  }
}
```
mappingsでインデックスに含まれるプロパティのデータ型ならびに使用するアナライザを定義します。
アナライザ部に含まれているのは日本語を全文検索するにあたり必要なプラグインです。各プラグインの詳細は以下のリンク先に詳しく書かれています。
参照：[Elasticsearchを日本語で使う設定のまとめ](https://qiita.com/shin_hayata/items/41c07923dbf58f13eec4)

今回は2つのプロパティを用意します。両方text型です。他にもデータ型はありますが、全文検索を行う場合はtext型を使用する必要があります。

上記のJSONを以下の通りに適用します。オリジナルのElasticsearchと同様です。
エンドポイント以下にインデックス名を付加します。

```
% curl -XPUT "search-test-es-domain-mhwk6krh6562mjoa6wnlk3m5yu.ap-northeast-1.es.amazonaws.com/test?pretty" -H "Content-type: application/json" -d @settings_es.json     
{
  "acknowledged" : true,
  "shards_acknowledged" : true,
  "index" : "test"
}
% curl -XGET "search-test-es-domain-mhwk6krh6562mjoa6wnlk3m5yu.ap-northeast-1.es.amazonaws.com/test/_mapping?pretty"
{
  "test" : {
    "mappings" : {
      "properties" : {
        "company" : {
          "type" : "text",
          "analyzer" : "index_analyzer"
        },
        "products" : {
          "type" : "text",
          "analyzer" : "index_analyzer"
        }
      }
    }
  }
}
```
投入するデータは以下の通りです。

```json:data.json
{"index": {"_index": "news","_type": "_doc","_id": "1"}}
{"company": "Microsoft", "products": "Windows .NET Office 365 Visual Studio Azure"}
{"index": {"_index": "news","_type": "_doc","_id": "2"}}
{"company": "Google", "products": "G Suite GCP Golang Flutter"}
{"index": {"_index": "news","_type": "_doc","_id": "3"}}
{"company": "Apple", "products": "MacOS iOS Swift iPhone"}

```
ちなみにElasticsearchに投入する**JSONは末行を空行にする**必要があります。空行にしないと*The bulk request must be terminated by a newline [\\n]*というエラーが出ます。

投入してみます。

```
% curl -XPOST "search-test-es-domain-mhwk6krh6562mjoa6wnlk3m5yu.ap-northeast-1.es.amazonaws.com/_bulk?pretty" -H "Content-type: application/json" --data-binary @data.json
{
  "took" : 500,
  "errors" : false,
  "items" : [
    {
      "index" : {
        "_index" : "test",
        "_type" : "_doc",
        "_id" : "1",
        "_version" : 1,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 0,
        "_primary_term" : 1,
        "status" : 201
      }
    },
    {
      "index" : {
        "_index" : "test",
        "_type" : "_doc",
        "_id" : "2",
        "_version" : 1,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 0,
        "_primary_term" : 1,
        "status" : 201
      }
    },
    {
      "index" : {
        "_index" : "test",
        "_type" : "_doc",
        "_id" : "3",
        "_version" : 1,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 0,
        "_primary_term" : 1,
        "status" : 201
      }
    }
  ]
}
```
以上で設定が終わりました。

# 使ってみる
格納されているデータに対してクエリを投げつけてみます。

クエリは全文検索用クエリで一般的なmatchを使用してみます。

```json:query.json
{
  "query": { "match": { "products": "MacOS" } }
}
```
```
% curl -XGET "search-test-es-domain-mhwk6krh6562mjoa6wnlk3m5yu.ap-northeast-1.es.amazonaws.com/test/_search?pretty" -H "Content-type: application/json" -d @query.json
{
  "took" : 395,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.2876821,
    "hits" : [
      {
        "_index" : "test",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 0.2876821,
        "_source" : {
          "company" : "Apple",
          "products" : "MacOS iOS Swift iPhone"
        }
      }
    ]
  }
}
```
きちんと取り出せました。

インデックスの削除はDELETEメソッドを使用して行います。

```
% curl -XDELETE "search-test-es-domain-mhwk6krh6562mjoa6wnlk3m5yu.ap-northeast-1.es.amazonaws.com/test?pretty"   
{
  "acknowledged" : true
}
```

# シノニムやユーザー辞書を使用する
## パッケージ機能登場以前
文章を全文検索するにあたりシノニム(類義語)やユーザー辞書(業界独自の用語のような専門的な単語を搭載した辞書)を使えた方が便利です。
従来、Amazon ESではシノニムの利用は不便であり、ユーザー辞書は使えませんでした。

例えば以下のようなデータをAmazon ESに投入します。

```json
{"index": {"_index": "test","_type": "_doc","_id": "1"}}
{"company": "Microsoft", "text": "Microsoftの代表的な製品はWindowsです"}
{"index": {"_index": "test","_type": "_doc","_id": "2"}}
{"company": "マイクロソフト", "text": "マイクロソフトの代表的な製品はWindowsです"}

```
言うまでもなく「Microsoft」と「マイクロソフト」は同一の企業を指します。

以下のようなクエリを投げてみます。

```json:query.json
{
  "query": { "match": { "text": "マイクロソフト" } }
}
```
残念なことにこのままではMicrosoftが含まれる文書を取り出すことはできません。

```terminal
% curl -XGET "search-test-es-domain-mhwk6krh6562mjoa6wnlk3m5yu.ap-northeast-1.es.amazonaws.com/test/_search?pretty" -H "Content-type: application/json" -d @query.json
{
  "took" : 8,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.2876821,
    "hits" : [
      {
        "_index" : "test",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.2876821,
        "_source" : {
          "company" : "マイクロソフト",
          "text" : "マイクロソフトの代表的な製品はWindowsです"
        }
      }
    ]
  }
}
```
## パッケージ機能登場
しかし、2020年4月になってシノニムやユーザー辞書を**パッケージ**というファイルで扱えるようになりました。

[[新機能]Amazon Elasticsearch Service でファイルベースのシノニム、ユーザー辞書などに対応するカスタムパッケージを利用可能になりました](https://dev.classmethod.jp/articles/amazon-elasticsearch-service-filebase-synonym-userdictionary/)

今回はシノニム機能を使ってみます。

シノニム用のファイルは以下のものを使用します。

```text:synonym.txt
Microsoft,マイクロソフト
```
上記テキストファイルをESドメインで使うにはS3バケットにアップロードする必要があります。
バケットに関しては今回はCLIから作成します。

```terminal
% aws s3 mb s3://amazon-es-bucket-test-20200510 
make_bucket: amazon-es-bucket-test-20200510
% aws s3 cp synonym.txt s3://amazon-es-bucket-test-20200510
upload: ./synonym.txt to s3://amazon-es-bucket-test-20200510/synonym.txt
```
CLIからパッケージを作成します。

```terminal
% aws es create-package --package-name amazon-es-test-package --package-type TXT-DICTIONARY --package-source S3BucketName=amazon-es-bucket-test-20200510,S3Key=synonym.txt
{
    "PackageDetails": {
        "PackageID": "F230451472",
        "PackageName": "amazon-es-test-package",
        "PackageType": "TXT-DICTIONARY",
        "PackageStatus": "AVAILABLE",
        "CreatedAt": "2020-05-19T21:43:15.110000+09:00"
    }
}
```


シノニムを使用する場合、アナライザの設定を以下の通りにします。

```json:setting_es.json
{
  "settings": {
    "analysis": {
      "analyzer": {
        "index_analyzer": {
          "type": "custom",
          "tokenizer": "kuromoji_tokenizer",
          "mode": "search",
          "char_filter": ["icu_normalizer", "kuromoji_iteration_mark"],
          "filter": [
            "cjk_width",
            "synonym_filter",
            "kuromoji_baseform",
            "kuromoji_part_of_speech",
            "ja_stop",
            "lowercase",
            "kuromoji_number",
            "kuromoji_stemmer"
          ]
        }
      },
      "filter": {
        "synonym_filter": {
          "type": "synonym",
          "synonyms_path": "analyzers/F230451472"
        }
      },
      "tokenizer": {
        "kuromoji_tokenizer": {
          "type": "kuromoji_tokenizer",
          "mode": "normal"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "index_analyzer"
      },
      "text": {
        "type": "text",
        "analyzer": "index_analyzer"
      }
    }
  }
}
```
ポイントは2点あります。**トークナイザーのmodeをnormalにする**ことと、**シノニム用のフィルターを可能な限り早めに当てる**ことです。
どうやらトークナイザーが文章を加工する過程で特殊文字が混じるために、シノニム機能がうまく処理を行えなくなることが原因のようです。

*コンソール*もしくは*aws es associate-package*コマンドでドメインとパッケージの関連付けを行います。
こうすることでドメイン内のインデックスからパッケージ中のユーザー辞書やシノニムファイルを使用することができます。

先ほどのクエリを投げてみると、両方の文書を取り出すことができました


```terminal
% curl -XGET "search-test-es-domain-mhwk6krh6562mjoa6wnlk3m5yu.ap-northeast-1.es.amazonaws.com/test/_search?pretty" -H "Content-type: application/json" -d @query.json
{
  "took" : 41,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.41501677,
    "hits" : [
      {
        "_index" : "test",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.41501677,
        "_source" : {
          "company" : "マイクロソフト",
          "text" : "マイクロソフトの代表的な製品はWindowsです"
        }
      },
      {
        "_index" : "test",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.41501677,
        "_source" : {
          "company" : "Microsoft",
          "text" : "Microsoftの代表的な製品はWindowsです"
        }
      }
    ]
  }
}
```

# まとめ
Amazon ESはEC2等のサーバ上で運用する生のElasticsearchと比較して制限が多いです。
場合によってはその制限が致命的で採用を見送る場合もあると思います。

しかし、Query DSLの工夫や、つい最近登場したパッケージ機能でかなり使いやすくなるとも思います。

それでもマネージドなのは魅力的で、AWSも積極的にAmazon ESの改良に動いています。
今後ますます使いやすくなると思いますので皆さまもぜひ使ってみていかがでしょうか。