Amazon TimestreamのデータをGrafanaで可視化してみた

Amazon Timestreamに蓄積される時系列データをローカルでDockerで起動したGrafanaに表示してみました。
2020.12.20

こんにちは、CX事業本部のうらわです。

今回はAmazon Timestream(以下、Timestream)のデータをGrafanaで可視化してみました。

検証環境

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15.6
BuildVersion:   19G2021

$ docker --version
Docker version 20.10.0, build 7287ab3

$ aws --version
aws-cli/2.1.4 Python/3.7.4 Darwin/19.6.0 exe/x86_64

$ node -v
v12.18.3

$ npm -v
6.14.6

GrafanaをDockerで起動する

Grafanaが用意しているDockerfileを用いて、あらかじめTimestream用のプラグインをインストールしたイメージをビルドします。こちらの手順は下記に記載があります。

$ docker build \
  --build-arg "GRAFANA_VERSION=latest" \
  --build-arg "GF_INSTALL_PLUGINS=grafana-timestream-datasource"
  -t grafana-custom -f Dockerfile .

ビルドできたらコンテナを起動します。

$ docker run -it -d -p 3000:3000 --name=grafana grafana-custom

ブラウザでlocalhost:3000にアクセスするとGrafanaのログイン画面が表示されます。初期設定ではユーザー名/パスワードともにadmin/adminです。

Timestreamを設定する

GrafanaでTimestreamからデータを取得するための設定をします。

Configuration > Data Sources > Add data source で Amazon Timestream を選択します。

Timestremaの接続設定です。今回は以下の通り設定しました。

項目
Authentication Provider Access & secret key
Access Key ID 新規作成したIAMユーザーのアクセスキーID
Secret Access Key 新規作成したIAMユーザーのシークレットアクセスキー
Assume Role ARN 未設定
External ID 未設定
Default Region us-east-1
Endpoint CLIで確認したqueryエンドポイント

Access Key ID & Secret Access Key

今回はローカルかつ検証目的のため、IAMロールは使用せず専用のIAMユーザーを作成しアクセスキーIDとシークレットキーを使用してTimestreamにアクセスします。

このIAMユーザーには以下のようなTimestreamのデータを取得できるIAMポリシーをアタッチします。本来はActionやResourceを最低限に絞るべきですが、検証目的なのでワイルドカードで指定しています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "timestream:*",
            "Resource": "*"
        }
    ]
}

Default Region

今回はus-east-1リージョンにTimestreamのデータベース等を作成するため、us-east-1を指定します。

Endpoint

以下のCLIでquery用のエンドポイントを確認しGrafanaに設定します。

$ aws timestream-query describe-endpoints --region us-east-1
{
    "Endpoints": [
        {
            "Address": "query-{cell}.timestream.us-east-1.amazonaws.com",
            "CachePeriodInMinutes": 1440
        }
    ]
}

ここまで設定したら、Save & Testで接続テストを実施し、「Connection Success」と表示されるか確認します。

以上でTimestreamへの接続設定は完了です。

Timestreamにデータを書き込む

GitHubに公開されているサンプルコードを参考にデータベース・テーブル作成、データ書き込みのためのスクリプトを作成します。

https://github.com/awslabs/amazon-timestream-tools/tree/master/sample_apps/js

今回はTypeScriptを使用します。TypeScriptでコードを書くための細かな設定は割愛します。

以下のスクリプトは、無限ループで1秒おきにランダムなCPU利用率とメモリー使用率の値を書き込みます。

index.ts

import AWS from 'aws-sdk';

AWS.config.update({ region: 'us-east-1' });

const DATABASE_NAME = 'mySampleDB';
const TABLE_NAME = 'mySampleTable';
const HT_TTL_HOURS = 24;
const CT_TTL_DAYS = 7;
const writeClient = new AWS.TimestreamWrite();

const createDatabase = async () => {
  console.log('Creating Database');
  const params = {
    DatabaseName: DATABASE_NAME,
  };
  try {
    await writeClient.createDatabase(params).promise();
    console.log('Database created successfully');
  } catch (e) {
    console.log('Error creating database', e.toString());
  }
};

const createTable = async () => {
  console.log('Creating Table');
  const params = {
    DatabaseName: DATABASE_NAME,
    TableName: TABLE_NAME,
    RetentionProperties: {
      MemoryStoreRetentionPeriodInHours: HT_TTL_HOURS,
      MagneticStoreRetentionPeriodInDays: CT_TTL_DAYS,
    },
  };
  try {
    await writeClient.createTable(params).promise();
    console.log('Table created successfully');
  } catch (e) {
    console.log('Error creating table', e.toString());
  }
};

const writeRecords = async () => {
  console.log('Writing records');
  const currentTime = Date.now().toString();

  const dimensions = [
    { Name: 'region', Value: 'us-east-1' },
    { Name: 'az', Value: 'az1' },
    { Name: 'hostname', Value: 'host1' },
  ];

  const cpuValue =
    Math.round((Math.random() * (200 - 20) + 20) * 1000000) / 1000000;
  const cpuUtilization = {
    Dimensions: dimensions,
    MeasureName: 'cpu_utilization',
    MeasureValue: cpuValue.toString(),
    MeasureValueType: 'DOUBLE',
    Time: currentTime.toString(),
  };

  const memValue =
    Math.round((Math.random() * (100 - 10) + 10) * 1000000) / 1000000;
  const memoryUtilization = {
    Dimensions: dimensions,
    MeasureName: 'memory_utilization',
    MeasureValue: memValue.toString(),
    MeasureValueType: 'DOUBLE',
    Time: currentTime.toString(),
  };

  const records = [cpuUtilization, memoryUtilization];

  const params = {
    DatabaseName: DATABASE_NAME,
    TableName: TABLE_NAME,
    Records: records,
  };

  try {
    await writeClient.writeRecords(params).promise();
    console.log(records);
  } catch (e) {
    console.log('Error writing records:', e);
  }
};

const sleep = () => new Promise((resolve) => setTimeout(resolve, 1000));

(async () => {
  await createDatabase();
  await createTable();
  const loop = true;
  while (loop) {
    await writeRecords();
    await sleep();
  }
})();

こちらをts-nodeで実行し放置します。

$ npx ts-node index.ts

Grafanaでダッシュボード作成

Create > Dashboard で新規ダッシュボードを作成し、 Add new panel でTimestreamのデータを表示するパネルを作成します。

データソースはdefaultがTimestreamに設定されているため、defaultのままでOKです。下部にTimestreamからデータを取得するためのクエリーエディターが表示されます。

今回は現在から2時間前までのデータを取得してtimeでソートします。

以下はcpu_utilizationを取得するクエリーです。memory_utilizationについてはこのクエリーのSELECT句のASとWHERE区のmeasure_nameを変更するだけでOKです。

SELECT measure_name, measure_value::double AS cpu, time FROM "mySampleDB".mySampleTable WHERE measure_name = 'cpu_utilization' AND time > ago(2h) ORDER BY time

クエリーにエラーがなければ以下のようにグラフが表示されます。右上で自動更新のオンオフと間隔を設定できます。

最後に、Save で保存します。ダッシュボードでは以下のように表示されます。

まとめ

GrafanaのTimestream用プラグインを使用すると簡単にデータの可視化を始めることができます。今回は単純なクエリーで簡単なグラフを作成しましたが、組み込み関数を使用するともっと複雑なデータの可視化ができそうです。

AWS Black Belt Online Seminarの資料をみつつ、色々試してみたいと思います。

参考

https://docs.aws.amazon.com/timestream/latest/developerguide/Grafana.html