みなさんこんにちは。マネージドサービス部MSS課の塩野(正)です。
最近抹茶スイーツにハマってまして、甘さの中にあるほろ苦さがたまらなく好きです♪
さてさて、今回はTerraformのお話になります。 Terraformを使って設定をすることはよくありますが、そういえばTerraformを使った場合のこの挙動ってどんなんだっけ?というのを実環境で試してした結果をまとめてみました。
目次
使用したツール
- IaCツールのTerraform
- 設定を確認する環境としてのNew Relic
検証したポイント
- Terraformのコード側を変更せずに、IaCで設定した先のリソースをIaC以外で設定した場合の挙動
- Terraformのリソースブロックの追加方法
検証環境の説明と初期設定
まずは基本的なTerraformコードを使ってNew Relicの設定を行います。今回の検証でTerraformの設定先リソースとしてNew Relicを選定した理由は・・特にありません(汗
Terraformのサンプルコード
さて、Terraformコードの解説ですが、このコードにはNew Relicプロバイダーの設定や必要な変数の定義、そしてCPU使用率を監視するためのアラートコンディションが含まれています。
# Configure the New Relic provider terraform { required_providers { newrelic = { source = "newrelic/newrelic" version = "~> 3.0" } } } # Provider configuration provider "newrelic" { account_id = var.account_id api_key = var.api_key region = "US" } # Variables variable "account_id" { description = "New Relic Account ID" type = number } variable "api_key" { description = "New Relic API Key" type = string sensitive = true } # Alert Policy resource "newrelic_alert_policy" "resource_policy" { name = "CPU Usage Policy" } # NRQL Alert Condition resource "newrelic_nrql_alert_condition" "cpu_condition" { account_id = var.account_id policy_id = newrelic_alert_policy.resource_policy.id type = "static" name = "High CPU Usage" description = "Alert when CPU usage is too high" enabled = true violation_time_limit_seconds = 3600 nrql { query = "SELECT average(host.cpuPercent) FROM Metric WHERE host.hostname = 'example-host' " } critical { operator = "above" threshold = 90 threshold_duration = 300 threshold_occurrences = "all" } }
Terraformの実行
Terraformコードを保存したら、以下のコマンドを使用してリソースを作成してみます。
terraform init terraform plan terraform apply
問題なくリソースの作成ができました。
検証の実施
Terraformのコード側を変更せずに、IaCで設定した先のリソースをIaC以外で設定した場合の挙動
それでは、確認したかったポイントを順番に確認していきましょう。 よくある話だと思いますが、IaCツールで設定を一気に流し込んで、トラブル対応などの関係でGUIからポチポチ設定修正してしまうというパターン。せっかく履歴管理も含めてコード側でコントロールしようとしているのに、別の方法で設定変更できてしまうし、そっちの方が早いので暫定的に設定したはいいけど、設定変更したことを忘れるなんてあるあるだと思います。それをIaC側で確認できるかどうかを実際の挙動で確認するというのが今回の目的となります。
ドキュメント上では下記のように記載があるため、実環境の設定を確認した上でPlanで差分を表示してくれるという認識です。
デフォルトでは、Terraform がplanを作成すると次の処理が行われます。
・既存のリモート オブジェクトの現在の状態を読み取り、Terraform の状態が最新であることを確認します。
・現在の構成を以前の状態と比較し、違いがあれば記録します。
・適用するとリモート オブジェクトが構成と一致するようにする一連の変更アクションを提案します。
それでは、実環境の閾値を変更してみましょう。アラートの閾値を90%から99%に変更してみました。
それではTerraform側でどのような表示になるかterraform planで確認してみましょう。
Terraform will perform the following actions の欄にGUIで設定変更した内容を元に戻すよ?という提案がなされています。つまりtfstateの内容からではなく実環境の内容を確認した上で差分を元にもどすかどうか提案されている状況になりますので、ドキュメントの通り意図した動作であることが確認できました。この状態でコード側を修正するとどうなるでしょうか。下記のようにGUIの設定をコードに反映してみました。
resource "newrelic_nrql_alert_condition" "cpu_condition" { : critical { operator = "above" threshold = 99 ←ここ :
変更した結果を確認してみると・・
Terraform側のコードと実環境に差異がないと表示されましたので、Planで検知された内容をそのままコード側に反映しても問題なさそうですね。
Terraformのリソースブロックの追加方法
私がよく書くコードの場合は、variables.tfにリソース情報をまとめにまとめてresourceブロックはなるべくシンプルにする構成が好きなのですが、大規模に展開しない場合はそこまでしなくてもいいよねという感じもあるので、そういえば他の方法ってあったっけ?というのを調査してみました。
1. 既存のリソースブロックをコピーして修正する方法
例えば、CPU使用率以外にメモリ使用率も監視したい場合は、リソースブロック毎コピーして編集したものを追記する方法があります。 あまりキレイな方法ではありませんが、このコードをTerraformのサンプルコードの下に追記することで、追記した設定を追加することができます。
# Memory NRQL Alert Condition resource "newrelic_nrql_alert_condition" "memory_condition" { account_id = var.account_id policy_id = newrelic_alert_policy.resource_policy.id type = "static" name = "High Memory Usage" description = "Alert when Memory usage is too high" enabled = true violation_time_limit_seconds = 3600 nrql { query = "SELECT average(memoryUsedPercent) FROM SystemSample WHERE entityGuid = 'YOUR_ENTITY_GUID_HERE'" } critical { operator = "above" threshold = 90 threshold_duration = 300 threshold_occurrences = "all" } }
コードの動作確認してみましたが、問題なく設定を追加することができました。
2. 変数にパラメータをオフロードする方法
リソースブロックごと追記するのはあまり建設的なアプローチではないものの、追加するリソースが非常に少ない場合はコード改修が楽なのでひとつの選択肢として考えてもいいかとは思いますが、コードの再利用の観点ではやはりあまりいいアプローチではないため、リソースブロックを固定化するのがプラクティスとしては正解かと思います。私がよく使っている方法ですが、こちらは可変部分を変数にオフロードするパターンになります。
# NRQL Alert Condition resource "newrelic_nrql_alert_condition" "condition" { for_each = { for i in local.module_alert_parameter : i.name => i } account_id = var.account_id policy_id = newrelic_alert_policy.resource_policy.id type = "static" name = each.value.name description = each.value.description enabled = true violation_time_limit_seconds = 3600 nrql { query = each.value.query } critical { operator = "above" threshold = each.value.critical_threshold threshold_duration = 300 threshold_occurrences = "all" } } locals { module_alert_parameter = [ { name = "High CPU Usage" description = "Alert when CPU usage is too high" query = "SELECT average(cpuPercent) FROM SystemSample WHERE entityGuid = 'example-host'" critical_threshold = 90 }, { name = "High Memory Usage" description = "Alert when Memory usage is too high" query = "SELECT average(memoryUsedPercent) FROM SystemSample WHERE entityGuid = 'example-host'" critical_threshold = 90 } ] }
もともとのサンプルとの違いは、リソースブロックで固定値を設定していた部分をすべてfor eachで値を可変にできるようにしてあり、可変部分についてはlocal変数に仕込んでいます。簡易的なコードのためlocal変数にまとめていますが、大規模なコードの場合はmodulesなどのフォルダに分けた上でvariables.tfなど外部変数として可変部分を定義しておき、それをメインのモジュールから呼び出して設定するような流れになります。
コードの動作確認してみましたが、問題なく設定を追加することができました。
まとめ
Terraformを使用してIaC側でない方の設定を変更したり、IaCの設定を追加する方法を試してみましたが、頭では理解できているけれども実際そうなんだっけ?という部分がとてもクリアになりました。
こちらの記事がどなたかのお役に立てれば幸いです。