監視用Elastic CloudデプロイメントをTerraformで構築
こんにちは、エンジニアの石川です。
ステージング環境として利用しているElastic Cloudのデプロイメントの監視を行うステージング環境監視用デプロイメントをTerraformで構築したので、今回はそれについての記事になります。
構成図
今回のゴールは、既にステージング環境として運用しているステージングクラスターが所属しているステージングデプロイメントのログやメトリクスを、監視用のデプロイメントに含まれる監視用クラスターに送り、それを監視用デプロイメントのKibanaで確認できるようにすることです。
監視用デプロイメントの構築後は、Elastic Cloudのマネジメントコンソールでステージングデプロイメントのログとメトリクスを監視用デプロイメントに送るように選択するだけ(ステージングデプロイメント->Monitoring->Logs and metrics->Shipt to a deploymentで送信先のデプロイメントを選ぶ)であるため今回は説明を省略し、監視用のデプロイメントを構築する部分についてのみの説明となります。
ステージングデプロイメントのログやメトリクスを監視用クラスターに送る内部的な仕組みはElasticの公式ドキュメントを参照してください。(Monitoring overview | Elasticsearch Guide [8.11] | Elastic)
ファイル構成
ファイル構成は以下のようになります。
workspace │ main.tf │ provider.tf │ variables.tf│ ├───env │ dev-infra.backend.tfvars │ dev-infra.tfvars │ └───modules ├───elasticcloud │ elasticcloud.tf │ output.tf │ provider.tf │ variables.tf │ └───elasticstack elasticstack.tf provider.tf variables.tf |
ルートモジュールでサブモジュールとしてElastic Cloudのデプロイメント構築するモジュールと、Kibana(Elastic Stackの1つ)に対してアラートのルールやアクションコネクターを作成するモジュールを呼び出しています。
ルートモジュールのプロバイダーでElastic CloudやElastic Stackの認証を行っています。本来であればルートモジュールでのプロバイダーの設定(どのsourceのどのバージョンを利用するのか、など)はサブモジュールに受け継がれるのですが、今回利用しているプロバイダーであるelastic/ecとelastic/elasticstackはelasticが作成しているものであるためサブモジュールに受け継がれませんでした。そのためサブモジュールにプロバイダの設定を記述する必要がありました。
今回はリモートバックエンドとしてAmazon S3を利用しています。dev-infra.backend.tfvarsはバックエンド設定用変数ファイルとして$ terraform init に利用しています。以下は利用例です。
$ terraform init -backend-config env/ dev-infra.backend.tfvars |
dev-infra.tfvarsは$terraform planや$terraform apply時に利用される、変数の値を定義しているファイルです。以下は利用例です。
$ terraform plan -var-file env/ dev-infra.tfvars |
ソースコード
全部説明すると長すぎるため、一部抜粋します。
-
workspace/main.tf
サブモジュールであるecとelasticstackを呼び出しています。module "ec" {
source = "./modules/elasticcloud"
deployment_name = var.ec_deployment_name
creator = var.creator
region = var.ec_region
ec_version = var.ec_version
deployment_template_id = var.ec_deployment_template_id
}
module "elasticstack" {
source = "./modules/elasticstack"
creator = var.creator
webhookurl = var.webhookurl
}
-
workspace/provider.tf
terraformのバージョンやプロバイダーの設定、またプロバイダーを利用する際の認証などの設定が書いてあります。ここにあるrequired_providersはサブモジュールに受け継がれないため、各サブモジュールで利用するプロバイダーのrequired_providersを設定する必要があります。
Elastic Cloudで利用するAPIキーは変数の宣言時にsensitiveをtrueにしており、これによってログなどでAPIキーの値が表示されないようにしています。terraform {required_version = ">=1.6.0"backend "s3" {}required_providers {ec = {source = "elastic/ec"version = "0.9.0"}elasticstack = {source = "elastic/elasticstack"version = "0.9.0"}}}provider "ec" {apikey = var.ec_apikey}provider "elasticstack" {kibana {username = module.ec.usernamepassword = module.ec.passwordendpoints = [module.ec.kibana_endpoint]}}
-
workspace/variables.tfの一部分
今回は初めてvalidationブロックを利用しました。
error_messageを書いたらconditionを作成してくれるCopilotには非常に助かりました。...variable "webhookurl" {description = "url for webhook to notify slack"type = stringvalidation {error_message = "webhookurl must start as https://"condition = can(regex("^https://", var.webhookurl))}}...
-
workspace/modules/elasticcloud/provider.tf
サブモジュールに必要なプロバイダーの設定はこのような形です。ルートモジュールから必要な部分だけを抜き出しているだけです。terraform {required_version = ">=1.6.0"required_providers {ec = {source = "elastic/ec"version = "0.9.0"}}}
-
workspace/modules/elasticcloud/elasticcloud.tfの一部分
Elastic Cloudのデプロイメントの構築をしています。resource "ec_deployment" "deployment" {name = var.ec_deployment_nameregion = var.ec_regionversion = var.ec_versiondeployment_template_id = var.ec_deployment_template_idelasticsearch = {hot = {autoscaling = var.elasticsearch_hot.autoscalingzone_count = var.elasticsearch_hot.zone_countsize_resource = var.elasticsearch_hot.size_resourcesize = var.elasticsearch_hot.size}}kibana = {size_resource = var.kibana.size_resourcesize = var.kibana.sizezone_count = var.kibana.zone_count}...}
-
workspace/modules/elasticcloud/variables.tfの一部分
変数の型の用意とそのデフォルトの値を入れています。...
variable "elasticsearch_hot" {description = "Configuration for the Elasticsearch 'hot' tier."type = object({autoscaling : map(any)zone_count : numbersize_resource : stringsize : string})default = {autoscaling = {}zone_count = 2size_resource = "memory"size = "1g"}}variable "kibana" {description = "Configuration for Kibana."type = object({size_resource : stringsize : stringzone_count : number})default = {size_resource = "memory"size = "1g"zone_count = 1}}...
-
workspace/modules/elasticstack/elasticstack.tf
アラートの設定を行っています。今回はどのアラートであってもSlackに通知するため、どのルールでもSlackに通知するアクションコネクターを利用するようにしています。ルールの設定部分でアクションコネクターのidの設定方法に違和感がありますが、このようにしないと正しく作成されません。resource "elasticstack_kibana_action_connector" "slack-connector" {name = "slack_${var.creator}"connector_type_id = ".slack"secrets = jsonencode({webhookUrl = var.webhookurl})}resource "elasticstack_kibana_alerting_rule" "alert_rule" {for_each = { for rule in var.alert_rules : rule.name => rule }consumer = each.value.consumername = "${each.value.name}_${var.creator}"enabled = each.value.enabledinterval = each.value.intervalnotify_when = each.value.notify_whenthrottle = each.value.throttleparams = jsonencode({duration = each.value.params.durationthreshold = each.value.params.thresholdlimit = each.value.params.limit})rule_type_id = each.value.rule_type_idactions {id = element(split("/", elasticstack_kibana_action_connector.slack-connector.id), 1)params = jsonencode({message = each.value.action_message})}}
-
workspace/modules/elasticstack/variables.tfの一部分
こちらはリスト型で変数の型を用意し、そのデフォルトの値を詰め込んでいます。variable "alert_rules" {description = "List of alert rules"type = list(object({consumer : stringname : stringenabled : boolinterval : stringnotify_when : stringthrottle : stringparams : object({duration : stringthreshold : optional(number)limit : optional(string)})rule_type_id : stringaction_message : string}))default = [{consumer = "alerts"name = "jvm_alert"enabled = trueinterval = "1m"notify_when = "onThrottleInterval"throttle = "1m"params = {duration = "5m"threshold = 85}rule_type_id = "monitoring_alert_jvm_memory_usage"action_message = "{{context.internalFullMessage}}"},
...]}
ハマった部分
-
Terraformのプロバイダーのドキュメントだけでは解決できないことがあった
Terraformのelastic/elasticstackのプロバイダーのドキュメントにはアクションコネクターやルールを作成する例がいくつかあるのですが、それでもどの変数にどのような値を設定すべきかという情報が足りない場合がありました。その場合はElasticの公式ドキュメントのAPIに関するドキュメントが役立ちました。今回の場合、ルール作成などに関してはAlerting APIsのドキュメント(Alerting APIs | Kibana Guide [8.11] | Elastic )、アクションコネクターに関してはAction and connector APIsのドキュメント(Action and connector APIs | Kibana Guide [8.11] | Elastic )を参照するとよいと思います。
-
Elasticの公式ドキュメントを読んでも解決できないことがあった
ですがドキュメントを読むだけで欲しい情報が取得できない場合ももちろんあります。自分の場合、elasticstack_kibana_alerting_ruleというルールを作成するためのリソースで必要となるrule_type_idの一覧がなく非常に苦労しました。(結局自分でGet rule types APIを叩く必要がありました。)
-
IaCで作成したものを手動で削除してしまった
$ terrform planなどが成功しなくなっていました。今回は作業者が自分だけでありまた完成版ではなかったため、Terraformのstateから特定のリソースを削除するという手法で解決しましたがもう二度と同じことはしたくないです。
-
モジュール化
Terraformの基本的な知識がまだ不足している部分があるため、特にルートモジュールのプロバイダーの設定がサブモジュールに受け継がれないというエラーの解決に時間がかかりました。
感想
今回はステージング環境監視用デプロイメントをTerraformで構築しました。Elastic CloudをTerraformで構築するために、ElasticCloudやElasticStackの構成なども詳しく理解する必要があったため非常に勉強になりました。
また自分のミスやモジュール化を通してTerraformの基礎部分も学ぶことができました。読んでいただきありがとうございました!
今回利用したドキュメントのリンク
(石川)