Zabbixを使ってAWS Kinesis Streamをシャードレベルで監視する

この記事はWanoグループ Advent Calendar 2017 の17日目です。

以前シャード単位の監視が必要となるケースがあって対応したので、備忘録も兼ねて残しておこうかなと。
ちなみに使用しているZabbixは3.2ですが、v2.0以降なら大丈夫です。

##Kinesis Streamをシャード単位で監視するメリット

ライトにKinesis Streamを使う分にはStream単位のメトリクスで十分なケースも多いとは思いますが、次の様なケースではシャード単位のメトリクスを有効にしておくと便利です。

* スロットリングを未然に防ぎたい。
* 何らかのエラーで止まってるシャードを識別したい。
* 自前で付与しているパーティションキーが分散の上で適切か判断したい。
* Stream単位のメトリクスだとシャード分割が効果的に働いたのかよくわからない。

##手順

1. AWSコンソールあるいはaws-cliから拡張シャードメトリクスを有効にする
2. Zabbixでシャード一覧を取得できるようにする
3. ZabbixのLLDを設定

タイトル通りZabbix前提です。
LLD自体の詳細説明は[ここ参照](https://www.zabbix.com/documentation/2.2/jp/manual/discovery/low_level_discovery)。ホスト内の監視項目を自動生成するものだと思っておけば間違いない。

### 1.AWSコンソールあるいはAPIから拡張シャードメトリクスを有効にする

デフォルトではシャードレベルのメトリクスは無効になっています。
以前はAPIによる設定のみ対応していましたが、現在はコンソールからも設定可能になっています。
Data Stream -> Streams -> [対象Stream] -> 詳細下部からメトリクス単位で有効化できます。

![スクリーンショット 2017-12-17 16.46.04.png](https://qiita-image-store.s3.amazonaws.com/0/153655/872312ba-b37d-4ae1-9604-7b42108ab84d.png)

拡張モニタリングの例に漏れず少額ながら有料です。
各メトリクスの詳細は[ここらへん](https://docs.aws.amazon.com/ja_jp/streams/latest/dev/monitoring-with-cloudwatch.html)を参照してください。

もしAPIを使って設定したい場合は[ここでも](https://docs.aws.amazon.com/kinesis/latest/APIReference/API_EnableEnhancedMonitoring.html)見るといいですよ。

### 2.Zabbixでシャード一覧を取得できるようにする

この時点でもStream名＆シャード名決め打ちでアイテムを作成すれば監視は可能なのですが
シャード数の増減を考えると手作業で設定するのは辛いので、LLD使いましょう。
今回はStream & シャード単位で監視をしたいので、現在のシャード構成に合わせて以下のようなjsonを吐くスクリプトを組みます

```
{
    "data":[
        {"{#STREAM}":"stream-hoge","{#SHARDID}":"shardId-000000000000"},
        {"{#STREAM}":"stream-hoge","{#SHARDID}":"shardId-000000000001"},
<<中略>>
        {"{#STREAM}":"stream-hoge","{#SHARDID}":"shardId-000000000XXX"}
    ]
}
```

とりあえず試したいだけなら、LLDだけ設定しておいて、jsonは手書きのものを読ませるとかもありかもしれません。

加工元のデータについては、手っ取り早くaws-cliを使って取得するならこんな感じです。

```
aws kinesis describe-stream --stream-name stream-hoge
```

概ね次のように出力されます。

```
{
    "StreamDescription": {
        "RetentionPeriodHours": 24,
        "StreamName": "stream-hoge",
        "Shards": [
            {
                "ShardId": "shardId-000000000000",
                "HashKeyRange": {
                    "EndingHashKey": "85070591730234615865843651857942052863",
                    "StartingHashKey": "0"
                },
                "SequenceNumberRange": {
                    "StartingSequenceNumber": "49569567736569615051581223769010544281739275297005502466"
                }
            },
<<中略>>
        "StreamARN": "arn:aws:kinesis:ap-XXXXXXXX-X:XXXXXXXXXXXX:stream/stream-hoge",
        "EnhancedMonitoring": [
            {
                "ShardLevelMetrics": [
                    "IncomingBytes",
                    "IteratorAgeMilliseconds",
                    "IncomingRecords"
                ]
            }
        ],
        "StreamStatus": "ACTIVE"
    }
}
```

この出力を元に先のjsonを生成しますが、リシャーディングを行なった場合に、親シャードを監視対象外にしたいケースもあるかと思います。その場合はShards内でParentShardIdとAdjacentParentShardIdに指定されているシャードはjsonから除外してください。

例えばperlで適当に処理するならこんな感じ。

```
use JSON::XS qw/decode_json encode_json/;

my $json = decode_json join('',(<STDIN>));
my $shards = {map {$_->{ShardId} => 1} @{$json->{StreamDescription}->{Shards}}};

for my $shard (@{$json->{StreamDescription}->{Shards}}){
    for(qw/ParentShardId AdjacentParentShardId/){
        delete $shards->{$shard->{$_}} if defined $shard->{$_};
    }
}

my $list = {data => [map {{ "{#STREAM}" => $json->{StreamDescription}->{StreamName}, "{#SHARDID}" => $_ }} keys %$shards]};
print encode_json $list;
```

あとは一連の処理をzabbix_agentd.confに追加しましょう。
とりあえず簡素に書いてますが、実際に運用するならaws-cliが失敗した時の事も考えた方がいいです。
それとstreamが複数ある構成ならキー付きのパラメータにした方が楽かもしれませんが、割愛します。

```
UserParameter=aws.kinesis.stream_hoge_shards,aws kinesis describe-stream --stream-name stream-hoge|perl shards.pl
```

### 3.ZabbixのLLDを設定

Zabbixの管理画面からLLDを設定しましょう。
ここについては、マクロ適用後のアイテム名、キー、トリガー名などがホスト内でユニークになるようにだけ設定しておけば、特にはまりどころはないと思います。

前述の設定を行なったホストの設定から、ディスカバリリスト -> ディスカバリルールの追加を選択します。
先ほどのUserParameterの設定であれば、キー名を *aws.kinesis.stream_hoge_shards* としましょう。

次にアイテムの設定ですが、名前やキーに先ほどのjson内で使用されているキーをマクロとして使えます。
IncomingRedordsならこんな感じ。

![スクリーンショット 2017-12-17 18.31.42.png](https://qiita-image-store.s3.amazonaws.com/0/153655/3a67ffbe-5efe-515c-2415-821cd7b48ea9.png)

外部チェックの場合はキーに設定された{#STREAM}と{#SHARDID}がそのままスクリプトの引数として使用されますので、他にも必要な項目があればjsonに吐き出しておくといいですよ。

トリガーなども同様に設定すれば、監視設定は完了です。