Elastic CloudとTerraformを併用する

blog-thumb-cloud-blue.png

概要:この記事では、Elasticが提供している2種類のTerraformプロバイダーをご紹介します。Terraformは、インフラをコードの形で定義することができるツールです。さらに、そのコードをリポジトリに保存しておくことができるので、変更にも簡単に対応できます。

はじめに

コードとしてのインフラ(IaC)とは、ファイルを使ってITインフラを管理することをいいます。そのメリットは、人間が介入することなく自動で変更を適用できるほか、あらゆる変更が常に適切に文書化されるという点にあります。Terraformは、IoCを実現するツールとして一種の標準としての地位を確立しています。競合としては、CloudFormationやPulumiなどが存在します。今回は、Elastic CloudとTerraformを併用し、CloudのUIを使わずにクラスターの立ち上げ、変更、削除をする方法を説明します。

クラウドAPIキーを作成する

Terraformを利用するためには、クラウドAPIキーが必要です。なお、これはElastic CloudインスタンスのAPIキーではありません。Elastic CloudアカウントのAPIキーです。構成の詳細は、「Elastic Cloud Provider」(Elastic Cloudプロバイダー)の「Authentication」(認証)の部分に記載されています。

Terraformの初期構成

まずは、ほぼ最小限の内容のmain.tfファイルを見てみましょう。

terraform {
  required_version = ">= 1.0.0"

  required_providers {
    ec = {
      source  = "elastic/ec"
      version = "0.4.0"
    }
  }
}

provider "ec" {
}

resource "ec_deployment" "custom-deployment-id" {
  name                   = "My deployment identifier"

  region                 = "gcp-europe-west3"
  version                = "8.1.3"
  deployment_template_id = "gcp-memory-optimized-v2"

  elasticsearch {}

  kibana {}
}

After storing this you can run

terraform init
terraform validate
terraform apply auto-approve

最後の呼び出しでKibanaとElasticsearchのノードが起動するまでには、1~3分かかります。その後は、インスタンスが稼働状態になります。もっとも、インスタンスを利用するという観点では、main.tfで出力を定義しておくと便利です。

output "elasticsearch_endpoint" {
  value = ec_deployment.custom-deployment-id.elasticsearch[0].https_endpoint
}

output "elasticsearch_username" {
  value = ec_deployment.custom-deployment-id.elasticsearch_username
}

output "elasticsearch_password" {
  value = ec_deployment.custom-deployment-id.elasticsearch_password
  sensitive = true
}

output "kibana_endpoint" {
  value = ec_deployment.custom-deployment-id.kibana[0].https_endpoint
}

これで、terraform applyをもう一度実行すると、terraform outputでエンドポイントが確認できるようになります。これを基に、出力を解析するスクリプトを実行し、インデックスやテンプレートの作成などの作業を進めていくことができます。

jqを使うと、terraform outputコマンドの一部を抽出できます(さらには、Terraformの状態ファイルの確認も可能です)。

output=$(terraform output -json)
endpoint=$(echo $output | jq -r ".elasticsearch_endpoint.value")
username=$(echo $output | jq -r ".elasticsearch_username.value")
password=$(echo $output | jq -r ".elasticsearch_password.value")
curl $endpoint -u $username:$password

このように、Terraformの状態ファイルの場所も関係ありません。Elastic Cloudプロバイダーにはほかにも、リモートクラスターの設定機能があります。今度はその仕組みを見てみましょう。

CCS/CCR用にリモートクラスターを設定する

クラスター横断検索(CCS)やクラスター横断レプリケーション(CCR)の機能を実現するためには、あるクラスターが別のクラスターにアクセスするためのいわゆる「リモートクラスター」を設定する必要があります。以下のサンプルスニペットでは、設定に関する部分や不要なoutputの部分を完全に省いて、必要な要素だけを示しています。

resource "ec_deployment" "cluster_1" {
  name                   = "cluster_1"

  region                 = "gcp-europe-west3"
  version                = "8.1.3"
  deployment_template_id = "gcp-memory-optimized-v2"

  elasticsearch {}

  kibana {}
}

resource "ec_deployment" "cluster_2" {
  name                   = "cluster_2"

  region                 = "gcp-europe-west3"
  version                = "8.1.3"
  deployment_template_id = "gcp-memory-optimized-v2"

  elasticsearch {
    remote_cluster {
      deployment_id = ec_deployment.cluster_1.id
      alias         = ec_deployment.cluster_1.name
      ref_id        = ec_deployment.cluster_1.elasticsearch.0.ref_id
    }
  }

  kibana {}
}

この設定では、cluster_2cluster_1にリモート接続できます。

では、いよいよクラスター横断レプリケーションの設定に入りましょう。注:各コマンドの上にあるコメントは、コマンドの実行が必要なクラスターを示しています。

# Cluster 1
PUT my-leader-index

# Cluster 2
PUT my-follower-index/_ccr/follow?wait_for_active_shards=1
{"remote_cluster":"cluster_1","leader_index":"my-leader-index"}

# Cluster 1
PUT /my-leader-index/_doc/my-doc?refresh=true
{"key":"value"}

# Cluster 2, repeat until hit count > 0, should take less than a second usually
GET /my-follower-index/_search

これで、クラスター2にTerraformのリモートクラスター設定に基づくフォロワーインデックスができました。

クラスター横断検索についても方法はほぼ同じで、リモートクラスター接続を使います。

# Cluster 1
PUT /cluster1-index/_doc/1?refresh=true
{"cluster":"cluster1","name":"my name"}

# Cluster 2
PUT /cluster2-index/_doc/1?refresh=true
{"cluster":"cluster2","name":"my name"}

# Cluster 2
GET /cluster2-index,cluster_1:cluster1-index/_search

インスタンスを自分で管理するだけでなく、特定のクラスターの構成や設定を変更したい場面も出てくるかもしれません。そのようなときは、2つめのTerraformプロバイダー「elasticstack」を使用します。このプロバイダーを使うと、Elastic Cloudとオンプレミスのどちらにあるクラスターも構成できます。

elasticstackプロバイダーを使用する

elasticstackを使うと、Elastic Stackを部分的に管理することができます。たとえば、クラスター設定、インデックスやコンポーネントのテンプレート、ユーザー、ロール、インジェストパイプライン、インジェストプロセッサーなどの管理が可能になります。次の例は、プロセッサーを2つ備えたインデックスパイプラインを作成するものです。

terraform {
  required_version = ">= 1.0.0"

  required_providers {
    ec = {
      source  = "elastic/ec"
      version = "0.4.0"
    }
    elasticstack = {
      source = "elastic/elasticstack",
      version = "0.3.3"
    }
  }
}

provider "ec" {
}

resource "ec_deployment" "custom-deployment-id" {
  name                   = "custom-deployment-id"

  region                 = "gcp-europe-west3"
  version                = "8.1.3"
  deployment_template_id = "gcp-memory-optimized-v2"

  elasticsearch {}

  kibana {}
}

provider "elasticstack" {
  elasticsearch {
    username  = ec_deployment.custom-deployment-id.elasticsearch_username
    password  = ec_deployment.custom-deployment-id.elasticsearch_password
    endpoints = [ec_deployment.custom-deployment-id.elasticsearch[0].https_endpoint]
  }
}

data "elasticstack_elasticsearch_ingest_processor_set" "set_field_terraform" {
  field = "pipeline-source"
  value = "terraform"
}

data "elasticstack_elasticsearch_ingest_processor_grok" "grok_the_log" {
  field    = "message"
  patterns = ["%%{TIMESTAMP_ISO8601:@timestamp} %%{LOGLEVEL:level} %%{GREEDYDATA:message}"]
}

resource "elasticstack_elasticsearch_ingest_pipeline" "ingest" {
  name = "my-ingest-pipeline"

  processors = [
    data.elasticstack_elasticsearch_ingest_processor_set.set_field_terraform.json,
    data.elasticstack_elasticsearch_ingest_processor_grok.grok_the_log.json
  ]
}

This creates a pipeline named my-ingest-pipeline after bringing up the cluster. You could now go to Management/Ingest Pipeline in Kibana and see that the pipeline has been created, or just run the following simulate pipeline call:

POST _ingest/pipeline/my-ingest-pipeline/_simulate
{
  "docs": [
    {
      "_source": {
        "message": "2022-03-03T12:34:56.789Z INFO ze message"
      }
    }
  ]
}

This will return the following document as part of the response:

"_source" : {
  "@timestamp" : "2022-03-03T12:34:56.789Z",
  "level" : "INFO",
  "pipeline-source" : "terraform",
  "message" : "ze message"
}

There is one final step, which is not yet as easy as it should be, but the following little trick does it at least for most of my PoCs.

ダッシュボードを追加する

elasticstackプロバイダーは現在、Elasticsearchの設定の一部を変更できるにとどまります。しかし、スタックにはほかにもKibanaをはじめとする要素があります。

私はElastic Stackのデモをする際、GitHubのリポジトリにサンプルをすべて上げておいて、その立ち上げを可能な限り簡単にしてみせるということをよくやります。

その一環として、デモに使用するデータを表示するためのダッシュボードをインストールする作業があります。Kibanaヘルパーを使わずに、Terraformのelasticstackプロバイダーでこの作業をするにはどうすれば良いでしょうか?答えは、null_resourceの中でcurlコマンドを使用するのです。すると、プラットフォームの依存関係を追加し、実行時にcurlを必須にすることができます。

terraform {
  required_version = ">= 1.0.0"

  required_providers {
    ec = {
      source  = "elastic/ec"
      version = "0.4.0"
    }
  }
}

provider "ec" {
}

data "local_file" "dashboard" {
    filename = "${path.module}/dashboard.ndjson"
}

resource "ec_deployment" "custom-deployment-id" {
  name                   = "custom-deployment-id"

  region                 = "gcp-europe-west3"
  version                = "8.1.2"
  deployment_template_id = "gcp-memory-optimized-v2"

  elasticsearch {}

  kibana {}
}

resource "null_resource" "store_local_dashboard" {
  provisioner "local-exec" {
    command = "curl -X POST -u ${ec_deployment.custom-deployment-id.elasticsearch_username}:${ec_deployment.custom-deployment-id.elasticsearch_password} ${ec_deployment.custom-deployment-id.kibana[0].https_endpoint}/api/saved_objects/_import?overwrite=true -H \"kbn-xsrf: true\" --form file=@dashboard.ndjson"
  }
  depends_on = [ec_deployment.custom-deployment-id]
  triggers = {
    dashboard_sha1 = "${sha1(file("dashboard.ndjson"))}"
  }
}

このほか、null_resourcetriggersの部分が気になった方もいるかもしれません。これは、ダッシュボードファイルの変更を考慮し、JSONファイルのsha1sumが変わっていればcurlの呼び出しを再度実行するためのものです。

まとめ

この記事では、ecelasticstackの2種類のTerraformプロバイダーの使い方を説明しました。お楽しみいただけましたでしょうか。どちらのプロバイダーも開発中の段階です。ご興味をお持ちいただけましたら、ぜひGitHubのリポジトリ(ecelasticstack)のフォローをお願いいたします。

このほか、何か問題が発生した場合には、対応するGitHubリポジトリでご報告ください。皆様からのフィードバックをお待ちしております。それでは、良いTerraformライフを!