VisasQ Dev Blog

ビザスク開発ブログ

TerraformでElastic Cloud上のElasticsearchの立ち上げとElasticsearch自体の設定を自動化する

こんにちは、検索チームのよこやまです。 以前趣味である文書を書いたところはてブホットエントリ入りして喜んでおりました。技術記事もそれぐらい見られたらなあと夢見ております。

ビザスクでは 1 年に 1 回 Elasticsearch のバージョンアップを行っております。 Elastic Cloud を利用しているためローリングアップデートも可能なのですが、リリースオペレーションの都合上、都度新たな Elasticsearch deployment を作成しインデックスを載せ替える方式を取っております。 建て替えを行う際に、手作業で Elastic Cloud の Web コンソールや Kibana の WebUI を操作すると手間がかかる上にミスする確率も高くなります。

そこで Elasticsearch deployment や Elasticsearch 内のリソースを Terraform 内で管理することで、建て替え作業の簡易化に加えアプリケーションへの認証情報の引き渡しを容易にしています。

この記事では 2 つの provider を利用して Elastic Cloud 上のリソースと Elasticsearch 内部のリソースを管理する方法を記載します。

作成するリソース

  • Elasticsearch deployment
  • Elasticsearch 内部のリソース
    • API key
    • ユーザー
      • ユーザに付与するロール

手順

elastic/ec providerを利用してElastic Cloud上にdeploymentを作成する

elastic/ec provider を利用して deployment を作成する方法はこちらの過去の記事をご覧ください。 今回は以下の構成を指定します。

terraform {
  required_providers {
    ec = {
      source  = "elastic/ec"
      version = "0.5.0"
    }
  required_version = "1.3.7"
}

provider "ec" {
  apikey = "my api key"
}

data "ec_stack" "latest" {
  version_regex = "latest"
  region        = "gcp-us-central1"
}

resource "ec_deployment" "cluster" {
  region                 = "gcp-us-central1"
  name                   = "my-elasticsearch-deployment"
  version                = data.ec_stack.latest.version
  deployment_template_id = "gcp-storage-optimized"

  elasticsearch {
    config {
      plugins            = ["analysis-icu", "analysis-kuromoji"]
      user_settings_yaml = <<-EOT
      action.auto_create_index: false
      EOT
    }

    topology {
      zone_count    = 1
      id            = "hot_content"
      size_resource = "memory"
      size          = "1g"
    }
  }
}

Elastic Cloud の WebUI で作成するときと同様、必要なプラグインや Elasticsearch の config を deployment のリソース定義に記載します。 今回は例示のため最新バージョンを自動で取得するようにしますが、プロダクションで利用する際はバージョンを固定します。

elastic/elasticstack providerを利用してElasticsearch内に必要なリソースを作成する

elastic/elasticstack provider を利用して、上記で作成した Elasticsearch 内部に各リソースを作成します。

https://registry.terraform.io/providers/elastic/elasticstack/latest/docs

今回は API key、ロール、ユーザーを定義します。 上記の deployment を定義した tf ファイルを以下のように書き換えます。

terraform {
  required_providers {
    ec = {
      source  = "elastic/ec"
      version = "0.5.0"
    }
    elasticstack = {
      source  = "elastic/elasticstack"
      version = "0.5.0"
    }
  required_version = "1.3.7"
}

provider "ec" {
  # 省略
}

data "ec_stack" "latest" {
  # 省略
}

resource "ec_deployment" "cluster" {
  # 省略
}

provider "elasticstack" {
  elasticsearch {
    endpoints = ["${ec_deployment.cluster.elasticsearch[0].https_endpoint}"]
    username  = ec_deployment.cluster.elasticsearch_username
    password  = ec_deployment.cluster.elasticsearch_password
  }
}

# API key
resource "elasticstack_elasticsearch_security_api_key" "my_api_key" {
  name = "my_api_key"

  role_descriptors = jsonencode({
    read-only-role = {
      indices = [
        {
          names                    = ["my-index-*"],
          privileges               = ["read"]
          allow_restricted_indices = false
        }
      ]
    }
  })
}

# role
resource "elasticstack_elasticsearch_security_role" "my_role" {
  name = "my_role"

  indices {
    names      = ["my-index-*"]
    privileges = ["read"]
  }
}

# user
resource "random_password" "my_password" {
  length  = 20
  special = false
}

resource "elasticstack_elasticsearch_security_user" "my_user" {
  username = "my_user"
  password = random_password.my_password.result
  roles    = [elasticstack_elasticsearch_security_role.my_role.name]
}

今回作成したリソースにおいてアプリケーションで必要な値は下記のように取得できるため、アプリケーション環境も Terraform で管理している場合はそのまま渡せます。

  • Elasticsearch のエンドポイント
    • ec_deployment.cluster.elasticsearch[0].https_endpoint
  • API key
    • elasticstack_elasticsearch_security_api_key.my_api_key.encoded (ID と api_key が : で結合され base64 エンコードされた文字列)
  • ユーザー名
    • elasticstack_elasticsearch_security_user.my_user.username
  • ユーザーのパスワード
    • elasticstack_elasticsearch_security_user.my_user.password

今回は elastic/elasticstack provider に対し provider block で Elasticsearch の認証情報を渡しましたが、provider block で渡さず、下記のような各リソースごとの指定も可能です。

resource "elasticstack_elasticsearch_security_user" "my_user" {
  username = "my_user"
  password = random_password.my_password.result
  roles    = [elasticstack_elasticsearch_security_role.my_role.name]

  elasticsearch_connection {
    endpoints = ["${ec_deployment.cluster.elasticsearch[0].https_endpoint}"]
    username  = ec_deployment.cluster.elasticsearch_username
    password  = ec_deployment.cluster.elasticsearch_password    
  }
}

ただし elasticsearch_connection による指定はこの先非推奨になる見込みであり、使用には注意が必要です。 リソースごとに対象の Elasticsearch を変更したい場合、alias を指定した複数の provider を用意し、各リソースごとに使用する provider を指定します。

終わりに

ビザスクでは過去には行えていなかった Elasticsearch の定期バージョンアップがまずは手作業でできるようになり、今では Terraform で管理することで手作業が減るなど、どんどん改善が進んでいます。 今後も「知見と、挑戦をつなぐ」というミッションのもと、検索の改善を進めていきます。