全文検索エンジンMeilisearchをGKE（Autopilot）上に構築する

# はじめに
Elasticsearch、Algolia、群雄割拠の全文検索エンジン界に現れた新星Meilisearch。
SaaS版（[Meilisearch Cloud](https://www.meilisearch.com/pricing)）とOSS版があり用途に合わせて選択することができます。
OSS版について、公式ドキュメントの[Setup and installation](https://docs.meilisearch.com/learn/getting_started/quick_start.html#setup-and-installation)にはローカルやAWS、GCP、Azureで導入する方法が画面スクショも込みで詳細に記載されており大変親切です。 

しかしGCPへの導入方法については[GCEを利用する](https://docs.meilisearch.com/learn/cookbooks/gcp.html)サンプルしかありません。

ここでは**GKE上にMeilisearchを構築する方法**をまとめていこうと思います。
作成したファイルは下記リポジトリに置いています。

https://github.com/kawamou/meilisearch-kubernetes

### 本稿の流れ

まず、MeilisearchをデプロイするためのGKEを構築します。ここでGKEはAutopilot版を採用しましたが、通常のGKE（GKE Standard）でもほぼ同様の手順で構成できるかと思います。
次にMeilisearchをKubernetes（k8s）上で構成するためのファイルを作成します。
最後に作成したファイルをもとにGKE上にデプロイし、Meilisearchの動作を検証します。

# GKE Autopilotを立ち上げる

ご存知の方も多いと思いますが、GKE AutopilotはGKE Standardとは異なりNodeの管理をする必要がなくまさに「完全マネージドなk8s」です。
全体像をキャッチアップしたい場合はGoogle CloudのKazuuさん記事がオススメです。

https://medium.com/google-cloud-jp/gke-autopilot-87f8458ccf74

自分の場合、普段Cloud Run等のサーバーレスなサービスしか使っておらず、ステートフルなサービスもなるべくマネージドに作りたい思いがあるので好きなサービスです。

### コンソールポチポチか、Terraformか

もちろん[コンソールポチポチで作る](https://future-architect.github.io/articles/20210318/#%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%81%AE%E4%BD%9C%E6%88%90)こともできますし、[コマンドラインで作る](https://medium.com/google-cloud-jp/gke-autopilot-87f8458ccf74)こともできますが、今回はTerraformで構築することにしました。
普段あまりTerraformを触らないので、備忘録も兼ねて、環境構築からの手順を書いていきます。

### Terraformの開発環境を構築する

下記の記事を参考に`brew install tfenv`でTerraform環境を導入しました。
`tfenv`は`nodenv`や`pyenv`等と同様に複数のTerraformバージョンを管理できるモノです。
Terraformはバージョンによって構文が違ったりするのでバージョン管理できると便利です（ex. [Terraformバージョンについての注意](https://qiita.com/kobayashi-m42/items/247cf9708044db8a234e)）。

https://dev.classmethod.jp/articles/beginner-terraform-install-mac/

```sh
❯ tfenv --version
tfenv 3.0.0

❯ terraform --version
Terraform v1.3.6
```

### Terraformのファイルを作成する

Terraformを本番利用する場合は[ディレクトリをどう切るか設計する必要がある](https://qiita.com/reireias/items/253529c889cafb3fa4c7)かと思いますが、今回はひとつのディレクトリに平たく置いています。

```sh
❯ tree
.
├── main.tf # resourceの定義
├── provider.tf # providerの定義
├── terraform.tfvars # variableの値の定義
└── variables.tf # variableの定義
```

`main.tf`に記載のresourceの定義はこちらです：

```tf:main.tf
terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "4.3.0"
    }
  }

  required_version = "~> 1.0"
}

resource "google_compute_network" "vpc" {
  name = "meilisearch-network"
  project = var.project_id
  
}

resource "google_compute_subnetwork" "subnet" {
  name = "meilisearch-subnet"
  project = var.project_id
  ip_cidr_range = "10.0.11.0/24"
  network = google_compute_network.vpc.id
}

resource "google_container_cluster" "meiilisearch" {
  name = "${var.project_id}-meilisearch"
  project = var.project_id
  location = var.region
  initial_node_count = 1
  network = google_compute_network.vpc.name
  subnetwork = google_compute_subnetwork.subnet.name

  enable_autopilot = true
}
```

ポイントは：

- GKE AutopilotはGoogle Cloud Terraformプロバイダが`3.63.0`以上である必要があります。更にTerraformも`0.14`以上である必要があります。そのため`required_providers`周辺でバージョン指定を行っています
    - ref. [Terraform Adds Support for GKE Autopilot](https://www.hashicorp.com/blog/terraform-adds-support-for-gke-autopilot)
    - ref. [GKE AutoPilot Failure For Node Count #10782](https://github.com/hashicorp/terraform-provider-google/issues/10782)
- `enable_autopilot = true`でAutopilot指定しています
- 再利用する部分は基本的に`variables.tf`に切り出して`var`で参照するようにしました
- 分かりやすさのため、ブロック内で指定する属性はなるべく最低限になるようにしました

`provider.tf`と`variables.tf`は説明するまでもないので省略させていただきます。
気になる方はリポジトリをご確認いただければと思います。
`terraform.tfvars`はvariableに値を渡せるもので、下記の記事が詳しいです。

https://capsulecloud.io/terraform-variable

### GKEを立ち上げる

`tf`ファイルを作成したディレクトリ直下で下記を実行します。

```sh
# 初期化
terraform init

# 実行するとどんなリソースが作成されるか確認
terraform plan

# 実行
terraform apply
```

`terraform apply`は結構時間がかかりましたが、5-10分程経過後無事にGKEが作られました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287774/5ad3827c-7509-f0b1-d051-3c9745ba98ba.png)

# MeilisearchをGKE上に構築する

Meilisearchのk8sマニフェストとHelmチャートは公式が公開しています。
このリポジトリの指示通りに行えばMeilisearchをGKE含むk8s環境にデプロイ可能です。

https://github.com/meilisearch/meilisearch-kubernetes

今回はデプロイするMeilisearchに対してインターネットからアクセスしたく、「Ingressを利用する」等の設定を行う必要があります。
Helmチャートは柔軟にパラメータ設定が可能なので、上記リポジトリのHelmチャートを利用することにしました。

>Helmではアプリケーションのデプロイ時にさまざまなパラメータを与えて設定を行う仕組みが用意されている。これらパラメータは、「helm install」コマンドの実行時に「--set ＜パラメータ名＞=＜値＞」オプションで指定するか、もしくはYAML形式でファイルに記述し、「-f ＜YAMLファイル＞」オプションでそのファイルを指定することで設定できる。
>
>[事実上の標準ツールとなっているKubernetes向けデプロイツール「Helm」入門](https://knowledge.sakura.ad.jp/23603/)

### Helmチャートのパラメータを設定する

公式リポジトリの[README](https://github.com/meilisearch/meilisearch-kubernetes/tree/main/charts/meilisearch)にパラメータの変更方法が記載されています。
記載されている通り、`meilisearch-kubernetes/charts/meilisearch/values.yaml`にてパラメータを設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287774/ca5cd0fb-9065-382c-db8b-fcab7ee7cb74.png)

- `service.type`をClusterIPからNodePortに変更
    - Ingressを利用するためNodePortを利用します
- `ingress.enabled`をtrueに変更
- `ingress.className`のnginxを除去し`ingress.annotations`に`kubernetes.io/ingress.class: gce`を追加
    - 公式ドキュメント[GKE Ingressコントローラの動作](https://cloud.google.com/kubernetes-engine/docs/concepts/ingress?hl=ja#controller_summary)に記載の通りGKE Ingressコントローラは基本的に`kubernetes.io/ingress.class`の値を見ています
    - L7ロードバランサを作りたかったので`gce`を設定しています
- `ingress.hosts`を削除

### Helmを使ってGKE上にデプロイする

Helmは導入済み前提で話を進めていきます（[Helm のインストール](https://helm.sh/ja/docs/intro/install/)）。

まずはHelmがクラスタに接続できるように、kubeconfigの指定先をGKEに変更します。
変更するためのコマンドはGCPコンソールから入手します。
```
GCPコンソール > GKEクラスタ > クラスタを選択 > 画面上部の「接続」
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287774/22b073d1-b948-403f-83ed-27b613971d7b.png)

コマンド入力後、`current-context`が変更されていれば成功です。

```sh
# kubectlとHelmはkubeconfigを共有
❯ kubectl config current-context
gke_XXX_asia-northeast1_XXX-meilisearch
```

**Helmチャートのパラメータを設定する**の項で変更した`valies.yaml`を指定してGKEにデプロイします。

```sh
helm upgrade -i meilisearch meilisearch/meilisearch -f values.yaml
```

コンソールを見るとデプロイが確認できます！
下記は`GCPコンソール > GKE > ServicesとIngress > INGRESS`から辿ったIngressです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287774/3e0eda45-ae0f-8bcd-5cd1-c59fe1821c88.png)

### デプロイしたMeilisearchの動作確認

上記のIngressスクショで黒塗りした「フロントエンド」には自動で割り振られたIPアドレスが記載されています。
このIPアドレスを対象にMeilsearchの動作を確認していきます。

```sh
# データ投入
curl \
  -H "Content-Type: application/json" \
  -X POST 'http://<自動で割り振られたIPアドレス>/indexes/musics/documents' \
  --data '[{
      "id": 100,
      "title": "Kid A",
      "by": "Radiohead",
      "release_date": "2000-10-2"
  },{
      "id": 200,
      "title": "The Bends",
      "by": "Radiohead",
      "release_date": "1995-3-13"
  },{
      "id": 300,
      "title": "OK Computer",
      "by": "Radiohead",
      "release_date": "1997-5-21"
  },{
      "id": 400,
      "title": "A Moon Shaped Pool",
      "by": "Radiohead",
      "release_date": "2016-5-8"
  }]'
```

```sh
# 確認
❯ curl -v http://<自動で割り振られたIPアドレス>/indexes/musics/documents
{"results":[{"id":100,"title":"Kid A","by":"Radiohead","release_date":"2000-10-2"},{"id":200,"title":"The Bends","by":"Radiohead","release_date":"1995-3-13"},{"id":300,"title":"OK Computer","by":"Radiohead","release_date":"1997-5-21"},{"id":400,"title":"A Moon Shaped Pool","by":"Radiohead","release_date":"2016-5-8"}],"offset":0,"limit":20,"total":4}
```

ちなみにMeilisearchにはGUIコンソールがついており、ブラウザからIPアドレスに直でアクセスすると確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287774/c4fea869-411e-7047-cf1e-6543755e0665.png)

# 後片付け

検証し終わったら後片付けをしましょう。

```sh
# Meilisearchを削除
helm uninstall meilisearch

# GKEを削除
terraform destroy
```

# おわりに

GKEにMeilisearchをデプロイする方法をまとめました。
あくまで動かすことを目的としており、証明書とかクラスタの設定とかPVCの設定とか諸々ガバガバな部分があります。
そのため本番環境導入の上では考慮事項は増えますが、MeilisearchのOSS版を利用する上では、Autopilotの採用は結構良さそうだと感じました（マネージドみが強いので）。