前回
はじめに
New Relic記事第3弾。
前回、前々回でAmazon CloudWatchのメトリクスを取り込んでアラート監視をするところまで作成した。
今回は、この手のSaaSを使う醍醐味であるイケてるダッシュボードを作ってみる。
なお、イケてるのはダッシュボード機能そのものであり、今回紹介するダッシュボードの例がイケてるわけではないので、読者の方のデザインセンスを活かしてイケてる内容に仕上げていただきたい。
ダッシュボードの構成要素
ダッシュボードはTerraformのnewrelic_one_dashboard
のリソースを使用する。
ダッシュボードは以下の構成要素の包含関係となっている。
ダッシュボード全体 > ページ > ウィジェット
文字だけだと分かりにくいのでイメージを貼っておく。
画面全体がダッシュボード全体として、ページとウィジェットは以下のように配置された画面構成となる。
たとえば、一つのダッシュボードで、2つのLambda関数を横並びで確認したいような場合、ページを分けて複数ウィジェットのレイアウトを揃えておくとか、サマリのページを追加で用意しておく等の工夫をすることで見やすいダッシュボードを作れるだろう。
ダッシュボードの作成については、Terraformのnewrelic_one_dashboard_json
のリソースを使ってJSONで記載する方法もあるが、NRQLの途中やウィジェットのタイトル等でTerraformの変数や参照を使いたくなるケースが多々発生し、テンプレートのIaCが複雑化するため、今回はこちらの方法は割愛する。
ウィジェットの種類
ダッシュボードでは様々な種類のウィジェットを扱うことができる。
今回は以下の3種類を使用する。
他のウィジェットの概要については、以下の公式ドキュメントを参照。
なぜか棒グラフに関する記載がない。「バー」はおそらくバーチャートを指している。
ダッシュボードをIaCで書く
さて、今回のIaCは非常に長い。ダッシュボードに記載する内容が非常に多いためだ。
今回はサンプルなので、page
をベタ書きしているが、上記のように、複数の類似の機能をpage
で束ねる場合は、dynamicブロック等を使って効率よく書くべきだろう。
IaC全体
resource "newrelic_one_dashboard" "example" {
name = "Dashboard Example"
description = "Dashboard Example"
permissions = "public_read_only"
page {
name = "Page Example1"
description = "Dashboard Page Example1"
widget_billboard {
title = "aws.lambda.issues"
row = 1
height = 2
column = 1
width = 2
legend_enabled = false
refresh_rate = 60000
ignore_time_range = true
critical = 0
nrql_query {
query = <<EOQ
SELECT
count(*) as 'Issues'
FROM
NrAiIssue
WHERE
contains(entity.names, '${aws_lambda_function.example1.function_name}') AND
event = 'activate' AND
issueId NOT IN (
SELECT
issueId
FROM
NrAiIssue
WHERE
contains(entity.names, '${aws_lambda_function.example1.function_name}') AND
event = 'close'
SINCE last month
UNTIL now
LIMIT MAX
)
SINCE last month
UNTIL now
EOQ
}
}
widget_table {
title = "aws.lambda.issues"
row = 1
height = 2
column = 3
width = 10
legend_enabled = true
refresh_rate = 60000
ignore_time_range = true
nrql_query {
query = <<EOQ
SELECT
timestamp,
issueId,
title,
issueLink
FROM
NrAiIssue
WHERE
contains(entity.names, '${aws_lambda_function.example1.function_name}') AND
event = 'activate' AND
issueId NOT IN (
SELECT
issueId
FROM
NrAiIssue
WHERE
contains(entity.names, '${aws_lambda_function.example1.function_name}') AND
event = 'close'
SINCE last month
UNTIL now
LIMIT MAX
)
SINCE last month
UNTIL now
LIMIT 20
EOQ
}
}
widget_line {
title = "aws.lambda.Url5xxCount"
row = 3
height = 3
column = 1
width = 6
legend_enabled = true
refresh_rate = 60000
y_axis_right {
y_axis_right_zero = false
y_axis_right_min = 0
y_axis_right_max = 1
y_axis_right_series = [
"aws.lambda.Url5xxRate"
]
}
units {
series_overrides {
series_name = "aws.lambda.Url5xxRate"
unit = "percentage"
}
}
colors {
series_overrides {
series_name = "aws.lambda.Not.Url5xxCount"
color = "#0000ff"
}
series_overrides {
series_name = "aws.lambda.Url5xxCount"
color = "#ff0000"
}
series_overrides {
series_name = "aws.lambda.Url5xxRate"
color = "#bbbbbb"
}
}
nrql_query {
query = <<EOQ
SELECT
count(`aws.lambda.UrlRequestCount`) - count(`aws.lambda.Url5xxCount`) as 'aws.lambda.Not.Url5xxCount',
count(`aws.lambda.Url5xxCount`) as 'aws.lambda.Url5xxCount',
if( count(`aws.lambda.UrlRequestCount`) = 0, 0, count(`aws.lambda.Url5xxCount`) / count(`aws.lambda.UrlRequestCount`) ) as 'aws.lambda.Url5xxRate'
FROM
Metric
WHERE
`collector.name` = 'cloudwatch-metric-streams' AND
`aws.accountId` = '${data.aws_caller_identity.self.account_id}' AND
`aws.lambda.functionName` = '${aws_lambda_function.example1.function_name}'
SINCE 6 hours AGO
TIMESERIES 1 minute
EOQ
}
}
widget_stacked_bar {
title = "aws.lambda.InsidentCount"
row = 3
height = 3
column = 7
width = 6
legend_enabled = false
refresh_rate = 60000
colors {
series_overrides {
series_name = "Nr Ai Incidents"
color = "#ff0000"
}
}
nrql_query {
query = <<EOQ
SELECT
count(*)
FROM
NrAiIncident
WHERE
conditionName = '${newrelic_nrql_alert_condition.example.name}' AND
event = 'open'
SINCE 6 hours AGO
TIMESERIES 1minute
EOQ
}
}
}
page {
name = "Page Example2"
description = "Dashboard Page Example2"
widget_line {
title = ""
row = 1
column = 1
width = 4
height = 3
refresh_rate = 60000
nrql_query {
query = "SELECT count(`aws.lambda.Url5xxCount`) FROM Metric WHERE `collector.name` = 'cloudwatch-metric-streams' AND `aws.accountId` = '${data.aws_caller_identity.self.account_id}' SINCE 6 HOURS AGO TIMESERIES"
}
}
}
}
共通のプロパティ
ダッシュボード全体や、pageで共通のプロパティについて解説する。
と言っても難しいことはなく、ダッシュボード全体のプロパティは以下しかない。
permissions
は、
-
private
: 自分しか参照できない -
public_read_only
: 自分以外でも参照可能 -
public_read_write
: 自分以外でも参照更新可能
の値を取ることができるが、通常はチーム内で参照が出来ればよいので、public_read_only
を設定する。
resource "newrelic_one_dashboard" "example" {
name = "Dashboard Example"
description = "Dashboard Example"
permissions = "public_read_only"
// 後略
}
また、pageには以下の共通設定がある。
-
row
/column
/height
/width
: ウィジェットの位置を定義する。縦位置/横位置/高さ、幅を示す -
legend_enabled
: 見出しの表示有無 -
refresh_rate
: 各情報の更新頻度。ミリ秒単位 -
ignore_time_range
: ダッシュボード右上の全体のタイムレンジに合わせて表示範囲を変更するか、NRQLで設定したタイムレンジで表示するか。全体のタイムレンジがdefaultの場合、またはこの項目をtrueに設定するとNRQLのSINCE, UNTIL句で設定したタイムレンジ固定になる。たとえば、過去のインシデントをすべて表示したいときはここをtrueに設定しないと、直近のものだけが表示されてしまう。
ウィジェット固有の定義
Lambdaエラー件数の折れ線グラフ
折れ線グラフでは、以下の設定を行うことができる。
- y_axis_right: グラフの縦軸の第2軸の設定。
y_axis_right_series
で指定した情報に対して適用される。 - units: グラフの単位。今回の例では、少数をパーセント表示に変更している。
series_name
で指定した情報に対して適用される。 - colors: グラフの折れ線の色。
series_name
で指定した情報に対して適用される。
NRQLも書いてある通りで特に難しいことはない。通常のSQLと同様に、SELECT句にはcount()も使えるし、asで別名を付けることもできる(asで指定した別名を、他の設定項目の識別子としてseries_name
に指定することも可能)。
resource "newrelic_one_dashboard" "example" {
// 中略
page {
// 中略
widget_line {
title = "aws.lambda.Count"
// 共通の設定ここから
row = 3
height = 3
column = 1
width = 6
legend_enabled = true
refresh_rate = 60000
// 共通の設定ここまで
y_axis_right {
y_axis_right_zero = false
y_axis_right_min = 0
y_axis_right_max = 1
y_axis_right_series = [
"aws.lambda.Url5xxRate"
]
}
units {
series_overrides {
series_name = "aws.lambda.Url5xxRate"
unit = "percentage"
}
}
colors {
series_overrides {
series_name = "aws.lambda.Not.Url5xxCount"
color = "#0000ff"
}
series_overrides {
series_name = "aws.lambda.Url5xxCount"
color = "#ff0000"
}
series_overrides {
series_name = "aws.lambda.Url5xxRate"
color = "#bbbbbb"
}
}
nrql_query {
query = <<EOQ
SELECT
count(`aws.lambda.UrlRequestCount`) - count(`aws.lambda.Url5xxCount`) as 'aws.lambda.Not.Url5xxCount',
count(`aws.lambda.Url5xxCount`) as 'aws.lambda.Url5xxCount',
if( count(`aws.lambda.UrlRequestCount`) = 0, 0, count(`aws.lambda.Url5xxCount`) / count(`aws.lambda.UrlRequestCount`) ) as 'aws.lambda.Url5xxRate'
FROM
Metric
WHERE
`collector.name` = 'cloudwatch-metric-streams' AND
`aws.accountId` = '${data.aws_caller_identity.self.account_id}' AND
`aws.lambda.functionName` = '${aws_lambda_function.example1.function_name}'
SINCE 6 hours AGO
TIMESERIES 1 minute
EOQ
}
}
// 中略
}
}
インシデント発生の棒グラフ
棒グラフでは、以下の設定を行うことができる。
なお、このグラフでは、インシデントの状態ではなくて、インシデント発行された時系列を確認することを意図している。
- colors: グラフの棒の色。
series_name
で指定した情報に対して適用される。
こちらもNRQLは至って素直なものだ。NrAiIncident
のテーブルが、発行やacknowledge、Closeというイベントでもレコード挿入されてカウントされてしまうため、WHERE句でevent
項目を使って絞り込みを行っている。
resource "newrelic_one_dashboard" "example" {
// 中略
page {
// 中略
widget_stacked_bar {
title = "aws.lambda.InsidentCount"
// 共通の設定ここから
row = 3
height = 3
column = 7
width = 6
legend_enabled = false
refresh_rate = 60000
// 共通の設定ここまで
colors {
series_overrides {
series_name = "Nr Ai Incidents"
color = "#ff0000"
}
}
nrql_query {
query = <<EOQ
SELECT
count(*)
FROM
NrAiIncident
WHERE
conditionName = '${newrelic_nrql_alert_condition.example.name}' AND
event = 'open'
SINCE 6 hours AGO
TIMESERIES 1minute
EOQ
}
}
}
// 中略
}
}
クローズされていないIssueの概況ビルボード
ビルボードでは、以下の設定を行うことができる。
- critical: 閾値の設定。取得される値がこれを超える場合はビルボードの背景色が赤くなる。
このNRQLは少し複雑で、副問い合わせを使ってクローズされたIssueのIDを集計している。
NrAiIncident
同様、NrAiIssue
も状態が変更される都度、レコードが出力される。
このため、全体の中から、クローズされたIDを含まないものを選択するようにしている。
resource "newrelic_one_dashboard" "example" {
// 中略
page {
// 中略
widget_billboard {
title = "aws.lambda.issues"
// 共通の設定ここから
row = 1
height = 2
column = 1
width = 2
legend_enabled = false
refresh_rate = 60000
ignore_time_range = true
// 共通の設定ここまで
critical = 0
nrql_query {
query = <<EOQ
SELECT
count(*) as 'Issues'
FROM
NrAiIssue
WHERE
contains(entity.names, '${aws_lambda_function.example1.function_name}') AND
event = 'activate' AND
issueId NOT IN (
SELECT
issueId
FROM
NrAiIssue
WHERE
contains(entity.names, '${aws_lambda_function.example1.function_name}') AND
event = 'close'
SINCE last month
UNTIL now
LIMIT MAX
)
SINCE last month
UNTIL now
EOQ
}
}
// 中略
}
}
クローズされていないIssueの詳細テーブル
テーブルには、特に固有の設定は無い。
※実際には色々設定可能だが、今回は利用していない。
NRQLは、クローズされていないIssueの概況ビルボードと同じ条件で、SELECT句で出力項目を絞っている。
なお、こちらは複数件の情報が取得できるので、LIMIT句を設定している。
resource "newrelic_one_dashboard" "example" {
// 中略
page {
// 中略
widget_table {
title = "aws.lambda.issues"
// 共通の設定ここから
row = 1
height = 2
column = 3
width = 10
legend_enabled = true
refresh_rate = 60000
ignore_time_range = true
// 共通の設定ここまで
nrql_query {
query = <<EOQ
SELECT
timestamp,
issueId,
title,
issueLink
FROM
NrAiIssue
WHERE
contains(entity.names, '${aws_lambda_function.example1.function_name}') AND
event = 'activate' AND
issueId NOT IN (
SELECT
issueId
FROM
NrAiIssue
WHERE
contains(entity.names, '${aws_lambda_function.example1.function_name}') AND
event = 'close'
SINCE last month
UNTIL now
LIMIT MAX
)
SINCE last month
UNTIL now
LIMIT 20
EOQ
}
}
// 中略
}
}
いざ、動かす!
さて、上記が出来上がったらterraform apply
してみよう。
以下の通り、ダッシュボード一覧に作成したダッシュボードが表示されるので、ダッシュボード名をクリックする。
以下のようにダッシュボードで設定した4種類のウィジェットが表示されるはずだ。
さらに、この状態から、第1回で作成したスクリプトを流してアラートを発生させると、
ビルボードとテーブルにインシデントの状況が載った!
なお、「Page Example2」のタブをクリックすると、テキトーに作成した折れ線グラフも表示される。
これで、メトリクスとインシデントを統合的に監視するためのダッシュボードが作れるようになった!