7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ZOZOAdvent Calendar 2021

Day 24

Elasticsearch + Cloud Run で検索 API を作る

Last updated at Posted at 2021-12-24

はじめに

elastic-search-logo-color-horizontal.png

この記事は ZOZO Advent Calendar 2021 24日目の記事です。

この記事では全文検索エンジンの一つである ElasticsearchCloud Run にデプロイして検索APIを作ります。
メルカリさんのテックブログを見たことがある方にはわかるかと思いますが、以下の記事に大きく影響を受けています。

上記記事では Apache Solr を使っています。Solr も Elasticsearch も広く利用されている検索エンジンであり、どちらが優れているということもありませんが、両者の違いが気になる方は以下の記事などが参考になるかと思います。

以降、Elasticsearch を "ES" と省略して表記しています。

システム構成

image.png

  • インデックスを作成する search-indexer は Cloud Run で稼働
  • 生成されたインデックスファイルは GCS に保管
  • ユーザーからのリクエストを受ける search-api も Cloud Run で稼働
  • 実際に検索を行う es-instance も Cloud Run で稼働

完全に Cloud Run 頼みですね。
ポイントはユーザーのリクエストを受ける search-api は最小インスタンス数(min_instances)を0にして、検索を行う es-instance の最小インスタンス数は1としている部分です。

Cloud Run ではインスタンスに対するトラフィックがない場合に起動させておくインスタンス数を決めておくことができ、それを min_instances というパラメータで制御します。

料金を極力安く抑えるのであれば全て min_instances=0 と設定すれば良いのですが、 ES には登録されたインデックスを保持しておく必要があり、インスタンスが停止すると登録されたインデックスも初期化されてしまうため、 ES のインスタンスは常時1つのインスタンスが起動し続けるような設定としています。

この記事では上記システム構成のうち、 es-instance を Cloud Run にデプロイするところまでを取り上げます。

Elasticsearch をデプロイする

Cloud Run にデプロイする前にまずはローカル環境で動くものを作ります。
手っ取り早くローカルで動かすなら以下から ES のバイナリをダウンロードして実行する方法が良いと思います。

ESを起動
$ ./bin/elasticsearch

が、今回は最終的に Cloud Run にデプロイするため Docker で動かします。

ローカルでの起動

イメージのビルド

Dockerfile を作成します。日本語を使うためにプラグインを追加しています。

Dockerfile
FROM docker.elastic.co/elasticsearch/elasticsearch:7.15.1
RUN elasticsearch-plugin install analysis-kuromoji
RUN elasticsearch-plugin install analysis-icu

このイメージをビルドします。

$ docker build -t es-run .

動作確認

Elasticsearch を立ち上げ、ヘルスチェックをしてみます。

$ docker run -it --rm -p 9200:9200 es-run
# ヘルスチェック
$ curl 'http://localhost:9200/'
{
  "name" : "es01",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "wGcmJOWPQlGjYr-7xpK2lw",
  "version" : {
    "number" : "7.15.1",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "83c34f456ae29d60e94d886e455e6a3409bba9ed",
    "build_date" : "2021-10-07T21:56:19.031608185Z",
    "build_snapshot" : false,
    "lucene_version" : "8.9.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

Cloud Run へのデプロイ

注意事項

あとは Cloud Run へデプロイするだけですが、デプロイするにあたっていくつか注意点があります。
まずポイントとなるのは、クラスタをシングルノードで立ち上げることです。
docker-compose を使うと kibana や他のノードへの接続が容易にできますが、Cloud Run で起動できるのは一つのコンテナなのでシングルノードで立ち上げることになります。

シングルノードで立ち上げた場合、ノードが落ちると即サービス停止となるため可用性は低く、本番運用に向いていません。
今回紹介する Elasticsearch + Cloud Run の構成はそうした制約があることを念頭におく必要があります。

各種 ES の設定

シングルノードとなるように ES の設定を行います。自分はここの設定で一番詰まりましたが、以下を見ると結局シンプルな設定で十分ということがわかるかと思います。

ディレクトリ 構成

tree .
.
├── Dockerfile
├── cloudbuild.yml
├── config
│   ├── elasticsearch.yml
│   └── jvm.options
└── security
    └── limits.conf
config/elasticsearch.yaml
node.name: es01
bootstrap.memory_lock: true
network.host: 0.0.0.0
discovery.type: single-node
config/jvm.options
-Xms512m
-Xmx512m
security/limits.conf
elasticsearch soft nofile 65536
elasticsearch hard nofile 65536
elasticsearch memlock unlimited

イメージのビルド・デプロイ

先ほどローカルでビルドしたイメージをそのままデプロイしても良いですが、ここではビルドからデプロイまで全てを Cloud Build で行います。

ポイントは

  • ポート番号を 9200 に指定すること
  • min-instancesを1にすること
  • memory に余裕を持たせておくこと

といったところでしょうか。ちなみに _GITHUB_SHA という環境変数がいるのはこの cloudbuild.yaml を CI/CD で利用できるようにしたことの名残です。

cloudbuild.yaml
steps:
- name: 'gcr.io/cloud-builders/docker'
  id: Build:Image
  args: ['build', '-t', '$_DOCKER_URI:$_GITHUB_SHA', '.']
- name: 'gcr.io/cloud-builders/docker'
  id: Ship:Push
  args: ['push', '$_DOCKER_URI:$_GITHUB_SHA']
- name: 'gcr.io/cloud-builders/gcloud'
  id: Ship:Deploy
  args: ['run', 'deploy', 'es-run',
         '--image', '$_DOCKER_URI:$_GITHUB_SHA',
         '--region', 'asia-northeast1',
         '--platform', 'managed',
         '--min-instances', '1',
         '--cpu', '4',
         '--memory', '4Gi',
         '--port', '9200',
         '--no-allow-unauthenticated'
  ]
timeout: 3600s
images:
- $_DOCKER_URI:$_GITHUB_SHA
options:
 machineType: 'E2_HIGHCPU_8'

Cloud Build で Cloud Run にデプロイするために以下の権限をサービスアカウントまたは自分自身のユーザーアカウントに付与します。

  • roles/run.developer
  • roles/cloudbuild.builds.editor

一番手っ取り早いのは roles/editor を付与してしまうことですね。

続いて Cloud Build を実行します。

$ export PROJECT_ID=<your-project>
$ gcloud builds submit --substitutions=_DOCKER_URI=gcr.io/${PROJECT_ID}/es-run,_GITHUB_SHA=latest

うまくデプロイされると以下のようなログが出力されます。

ID: 5f04ad8a-7724-4a38-a579-78d2ec43a48f
CREATE_TIME: 2021-12-22T03:19:28+00:00
DURATION: 2M26S
SOURCE: gs://<PROJECT_ID>_cloudbuild/source/1640315967.398871-209c689985db46cc93b2c6c8c030cffa.tgz
IMAGES: gcr.io/<PROJECT_ID>/es-run (+1 more)
STATUS: SUCCESS

動作確認

Cloud Run のエンドポイントを叩くため、今回はヘッダーに Bearer トークンを付与します。
この Bearer トークンはユーザー自身の権限に紐づくトークンなので、予め roles/run.invoker の権限を付与しておきましょう。

$ export RUN_URL=<Cloud Run のエンドポイント>
$ curl -s \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $(gcloud auth print-identity-token)" \
${RUN_URL}
{
  "name" : "es01",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "I1xcMEiRQECoQ9iMeqEfYQ",
  "version" : {
    "number" : "7.15.1",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "83c34f456ae29d60e94d886e455e6a3409bba9ed",
    "build_date" : "2021-10-07T21:56:19.031608185Z",
    "build_snapshot" : false,
    "lucene_version" : "8.9.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

ここまで来れば、あとはインデックス用のファイルを作って登録して検索APIとして遊ぶことができます。

注意事項として、上記の手順で作成された Cloud Run のサービスは min_instances=1 で作成しているのでインスタンスの消し忘れに注意が必要です。
デフォルトではmin_instances=0となっているためトラフィックが来ないと自動的にコンテナが停止しますが、上記の設定ではコンテナは停止せず課金され続けます。
GCP に限らず、クラウドサービスで遊んだ後のお片付けは忘れないようにしましょう。

# min_instances を更新する場合
$ gcloud run services update es-run --min-instances=0

# Cloud Run のサービスを削除する場合
$ gcloud run services delete es-run

まとめ

本記事では Elasticsearch と Cloud Run を組み合わせて簡易検索 API を作成しました。
ただAPIとは名ばかりで、 ES を Cloud Run にデプロイしただけなのでインターフェースとなる API も必要となります。
そのインターフェースにあたるものが記事冒頭のシステム構成に記載した search-api ですが、時間の都合上省略してしまったのでまた別な記事でご紹介できればと思います。

明日の記事はいよいよアドベントカレンダー最終日、担当は ZOZO のメシア、我らがそのっつさんです。

7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?