ダッシュボードを使う理由
CloudPeekを使うのにダッシュボードは必要ありません。プリセットとAIウィジェットはダッシュボードなしで使えます。ダッシュボードが役立つのは、監視するメトリクスを正確に選びたいときです。独自のカスタムメトリクス、特定のリソース、メトリクス数式、しきい値、自分で選んだアラームなどです。適切な名前空間とディメンションを探し、メトリクス数式を書き、しきい値を設定するのは、キーボードと大きな画面のほうがずっと簡単で、CloudWatchにはそのためのツールがすでにあります。
CloudWatchダッシュボードは、監視したいものの一覧になります:
- 各ウィジェットがピン留めできる単位です。ウィジェットは1つ以上のメトリクスをまとめたものです。3つのインスタンスのCPUを1つの折れ線グラフに、上位5つのコストを1つの棒グラフに、2つのメトリクスから計算したエラー率を1つの数値に、といった具合です。
- 各ダッシュボードは、関連するウィジェットのまとまりです。サービスごとに1つ、支出用に1つ、オンコールのローテーション用に1つ、というように作れます。ピン留めは、いくつのダッシュボードやアカウントからでもできます。
- ダッシュボードは単なるJSONなので、CloudFormation、Terraform、AWS CDKなど、文字列を書き出せるものなら何でも定義でき、監視対象のサービスと一緒にデプロイできます。
ダッシュボードがすでにある場合は、そこから直接ピン留めできます。または、ひと目で確認したいウィジェットだけを集めた、スマートフォン専用の小さなダッシュボードを作ることもできます。
AWSコンソールで設計する
- CloudWatchコンソールを開き、ダッシュボード › ダッシュボードの作成を選択して、名前を付けます。
- ウィジェットの種類を選びます:折れ線、積み上げ面、数値、ゲージ、棒、円、表、テキスト、アラームステータス。どれもCloudPeekで使えます(ウィジェットリファレンスを参照)。
- 参照タブで名前空間(ApplicationELBや独自の名前空間など)を選び、このウィジェットに表示するメトリクスにチェックを入れます。
- グラフ化したメトリクスタブで、各メトリクスの統計、期間、ラベルを設定します。エラー率などの式には数式を追加を選択し、描画しない入力のチェックを外します。
- オプションタブで、しきい値の水平注釈を追加し、Y軸の上限と下限を設定します。ゲージには最小値と最大値の両方が必要です。
- ウィジェットの作成を選択し、短いタイトルを付けて、ダッシュボードを保存します。
CloudPeekでダッシュボードを開くと、各ウィジェットがホーム画面での見た目どおりに描画されます。必要なものをピン留めするか、ウィジェットを作成 › ダッシュボードからで追加します(ウィジェットを作成するを参照)。
スマートフォンで見やすくする
ホーム画面のウィジェットは幅が数センチしかありません。いくつかのコツを押さえるだけで、ずっと読みやすくなります:
- 1つのウィジェットに1つの問い。「APIは正常か?」は、8本の線を描いた1つのグラフより、エラー率の数値とレイテンシーのゲージで示すほうが読み取りやすくなります。
- グラフの系列は少なく。中サイズと大サイズのウィジェットの凡例には系列が4つまで表示され、それ以上は「+N件」となります。小サイズのウィジェットには凡例がありません。
- タイトルは短く。iPhoneとiPadでは、ロック画面のインラインウィジェットに表示されるのはタイトルの約18文字、円形ウィジェットでは約8文字です。
- ひと目で読める表示を選ぶ。
"sparkline": trueを指定した数値は最新の値とその推移を示し、ゲージは設定した範囲に対する値を示し、棒グラフは項目を順位付けします。各タイプの見た目を参照してください。 - 見たい時間範囲を設定する。ウィジェット自体の
startはダッシュボードの設定より優先され、どちらもない場合、CloudPeekは過去3時間を表示します。どのウィジェットにも、対象の時間範囲が表示されます。 - IDよりラベル。メトリクスに
labelを付けると、凡例に「app/checkout-api/50dc6c495c0c9188」ではなく「Checkout API」と表示されます。${PROP('Dim.ServiceName')}のような動的ラベルを使うと、各系列にディメンションの値に基づいた名前が付きます。
例:APIの稼働状況ダッシュボード
Application Load Balancer用の3個のウィジェットです。エラー率を数値で、p99レイテンシーをゲージで、リクエストとエラーを折れ線グラフで表示します。CloudPeekでは次のように表示されます:
{
"start": "-PT3H",
"widgets": [
{
"type": "metric",
"x": 0,
"y": 0,
"width": 6,
"height": 6,
"properties": {
"title": "5xx error rate",
"view": "singleValue",
"region": "us-east-1",
"period": 300,
"sparkline": true,
"metrics": [
[ { "expression": "100 * FILL(m2, 0) / m1", "label": "Errors (%)", "id": "e1" } ],
[ "AWS/ApplicationELB", "RequestCount", "LoadBalancer", "app/checkout-api/50dc6c495c0c9188", { "stat": "Sum", "id": "m1", "visible": false } ],
[ ".", "HTTPCode_Target_5XX_Count", ".", ".", { "stat": "Sum", "id": "m2", "visible": false } ]
]
}
},
{
"type": "metric",
"x": 6,
"y": 0,
"width": 6,
"height": 6,
"properties": {
"title": "Latency p99",
"view": "gauge",
"region": "us-east-1",
"stat": "p99",
"period": 300,
"yAxis": { "left": { "min": 0, "max": 2 } },
"metrics": [
[ "AWS/ApplicationELB", "TargetResponseTime", "LoadBalancer", "app/checkout-api/50dc6c495c0c9188", { "label": "p99 (s)" } ]
]
}
},
{
"type": "metric",
"x": 12,
"y": 0,
"width": 12,
"height": 6,
"properties": {
"title": "Requests and errors",
"view": "timeSeries",
"region": "us-east-1",
"stat": "Sum",
"period": 300,
"metrics": [
[ "AWS/ApplicationELB", "RequestCount", "LoadBalancer", "app/checkout-api/50dc6c495c0c9188", { "label": "Requests" } ],
[ ".", "HTTPCode_Target_5XX_Count", ".", ".", { "label": "5xx", "yAxis": "right", "color": "#d62728" } ]
]
}
}
]
}
- エラー率:式
100 * FILL(m2, 0) / m1は、5xxレスポンス数をリクエスト数で割ります。FILLにより、エラーのない期間は空白にならず0として数えられます。入力のメトリクスはどちらも"visible": falseなので、表示されるのはエラー率だけです。 - レイテンシー:ゲージはY軸の
minとmaxを範囲として使います(この例では0〜2秒)。指定がない場合、CloudPeekは0〜100とみなします。 - リクエストとエラー:
"yAxis": "right"で5xxの件数に専用の目盛りを持たせているので、数件のエラーが数千件のリクエストに埋もれて平らになることはありません。"."は、1つ上の行の同じ位置にある値を繰り返します。 app/checkout-api/50dc6c495c0c9188を、ロードバランサーのARNのうちloadbalancer/より後ろにある名前に置き換えるか、後述のようにCloudFormationやTerraformで埋め込みます。
コードで管理する
コンソールでダッシュボードを納得のいくまで設計したら、アクション › ソースの表示/編集からJSONをコピーして、インフラストラクチャのコードに入れます。それ以降は、監視対象のサービスと一緒にレビュー、バージョン管理、デプロイされ、デプロイ先のどのアカウントとリージョンでも同じダッシュボードになります。
ダッシュボードをコードで管理する必要はありません。コンソールで編集したダッシュボードも同じように、次回の更新時にCloudPeekに反映されます。
ダッシュボードをCloudFormationやTerraformで管理している場合は、コンソールを下書き用と考えてください。コンソールで保存した変更は、次のデプロイで上書きされます。コンソールでアイデアを試したら、ソースをコードにコピーし直します(アクション › ソースの表示/編集)。
JSONをバージョン管理し、put-dashboardでデプロイします。コンソールで設計したダッシュボードから始めるには:
aws cloudwatch get-dashboard \
--dashboard-name Checkout-API \
--query DashboardBody --output text > api-health.json
aws cloudwatch put-dashboard \
--dashboard-name Checkout-API \
--dashboard-body file://api-health.json
put-dashboardはダッシュボード全体を置き換えるため、常にファイルがダッシュボードの完全な定義になります。
AWS::CloudWatch::DashboardはJSONを文字列として受け取ります。!Subで囲むと、IDを貼り付ける代わりに、ロードバランサーのディメンションなど、同じスタックの値を埋め込めます:
CheckoutApiDashboard:
Type: AWS::CloudWatch::Dashboard
Properties:
DashboardName: Checkout-API
DashboardBody: !Sub
- |
{
"start": "-PT3H",
"widgets": [
{
"type": "metric", "x": 0, "y": 0, "width": 6, "height": 6,
"properties": {
"title": "5xx error rate",
"view": "singleValue",
"region": "${AWS::Region}",
"period": 300,
"sparkline": true,
"metrics": [
[ { "expression": "100 * FILL(m2, 0) / m1", "label": "Errors (%)", "id": "e1" } ],
[ "AWS/ApplicationELB", "RequestCount", "LoadBalancer", "${Alb}", { "stat": "Sum", "id": "m1", "visible": false } ],
[ ".", "HTTPCode_Target_5XX_Count", ".", ".", { "stat": "Sum", "id": "m2", "visible": false } ]
]
}
},
{
"type": "metric", "x": 6, "y": 0, "width": 6, "height": 6,
"properties": {
"title": "Latency p99",
"view": "gauge",
"region": "${AWS::Region}",
"stat": "p99",
"period": 300,
"yAxis": { "left": { "min": 0, "max": 2 } },
"metrics": [
[ "AWS/ApplicationELB", "TargetResponseTime", "LoadBalancer", "${Alb}", { "label": "p99 (s)" } ]
]
}
},
{
"type": "metric", "x": 12, "y": 0, "width": 12, "height": 6,
"properties": {
"title": "Requests and errors",
"view": "timeSeries",
"region": "${AWS::Region}",
"stat": "Sum",
"period": 300,
"metrics": [
[ "AWS/ApplicationELB", "RequestCount", "LoadBalancer", "${Alb}", { "label": "Requests" } ],
[ ".", "HTTPCode_Target_5XX_Count", ".", ".", { "label": "5xx", "yAxis": "right", "color": "#d62728" } ]
]
}
}
]
}
- Alb: !GetAtt LoadBalancer.LoadBalancerFullName
LoadBalancerは同じテンプレート内のAWS::ElasticLoadBalancingV2::LoadBalancerです。そのLoadBalancerFullNameが、CloudWatchのLoadBalancerディメンションが求める値そのものです。!Subの中では、CloudFormationはすべての${…}を自身の変数として読み取ります。${LAST}のようなCloudWatchの動的ラベルは、${!LAST}と書きます。- ダッシュボードはグローバルですが、各ウィジェットはリージョンを指定します。
${AWS::Region}を使うと、スタックのリージョンになります。
aws_cloudwatch_dashboardはJSONを文字列として受け取ります。HCLで書いてjsonencodeで囲むと、IDを貼り付ける代わりに、参照、変数、for式を使えます:
resource "aws_lb" "checkout" {
name = "checkout-api"
subnets = ["subnet-11111111", "subnet-22222222"]
}
locals {
alb = aws_lb.checkout.arn_suffix # the LoadBalancer dimension: app/checkout-api/…
region = "us-east-1" # where the load balancer is
}
resource "aws_cloudwatch_dashboard" "checkout_api" {
dashboard_name = "Checkout-API"
dashboard_body = jsonencode({
start = "-PT3H"
widgets = [
{
type = "metric", x = 0, y = 0, width = 6, height = 6
properties = {
title = "5xx error rate"
view = "singleValue"
region = local.region
period = 300
sparkline = true
metrics = [
[{ expression = "100 * FILL(m2, 0) / m1", label = "Errors (%)", id = "e1" }],
["AWS/ApplicationELB", "RequestCount", "LoadBalancer", local.alb, { stat = "Sum", id = "m1", visible = false }],
[".", "HTTPCode_Target_5XX_Count", ".", ".", { stat = "Sum", id = "m2", visible = false }],
]
}
},
{
type = "metric", x = 6, y = 0, width = 6, height = 6
properties = {
title = "Latency p99"
view = "gauge"
region = local.region
stat = "p99"
period = 300
yAxis = { left = { min = 0, max = 2 } }
metrics = [
["AWS/ApplicationELB", "TargetResponseTime", "LoadBalancer", local.alb, { label = "p99 (s)" }],
]
}
},
{
type = "metric", x = 12, y = 0, width = 12, height = 6
properties = {
title = "Requests and errors"
view = "timeSeries"
region = local.region
stat = "Sum"
period = 300
metrics = [
["AWS/ApplicationELB", "RequestCount", "LoadBalancer", local.alb, { label = "Requests" }],
[".", "HTTPCode_Target_5XX_Count", ".", ".", { label = "5xx", yAxis = "right", color = "#d62728" }],
]
}
},
]
})
}
aws_lbのarn_suffixが、CloudWatchのLoadBalancerディメンションが求める値そのものです。- Terraformは文字列内の
${…}を補間として読み取ります。${LAST}のようなCloudWatchの動的ラベルは、$${LAST}と書きます。 - JSONをファイルのまま管理したい場合は、
dashboard_body = file("api-health.json")も使えます。値を埋め込むならtemplatefile()を使います。 - コンソールで作成したダッシュボードをTerraformの管理下に置くには、ダッシュボード名を
idに指定したimportブロックを追加するか、terraform import aws_cloudwatch_dashboard.checkout_api Checkout-APIを実行します。
AWS CDKやPulumiなどのツールも同じように使えます。ダッシュボードの本文を設定できるものなら何でも、CloudPeekに表示する内容を定義できます。
変更がスマートフォンに反映される仕組み
- ピン留めしたウィジェットがAWSから更新されるたびに、CloudPeekはダッシュボードを読み込み直すため、コンソールで保存した変更やコードからデプロイした変更は次回の更新で表示されます。すぐに確認するには、ウィジェットの更新ボタンをタップします。
- ダッシュボードウィジェットを移動したり、サイズ、タイトル、メトリクスを変更したりしても、CloudPeekのウィジェットはそれに追従します。どのウィジェットだったか判別できない場合、CloudPeekは最後に見つけたバージョンを表示し続けます。
- ウィジェットを作成のプレビューで付けたタイトルやリージョン、CloudPeekの編集で選んだタイトル、リージョン、時間範囲はそのまま残り、ダッシュボードの内容に適用されます(ウィジェットの編集を参照)。
- ダッシュボードを削除するか名前を変更すると、ウィジェットは最後のデータを保持したままダッシュボード削除済みと表示します。
- 同じウィジェットをもう一度追加すると、それを表示していたホーム画面ウィジェットが再びつながります(同じウィジェットをもう一度追加するを参照)。