kubectl CLIでのKubernetesのイベント監視

Kubernetes自体やその上で動くPodのステータスを監視したいのだけれどPrometheus等を使えない場合に、シェルスクリプトで`kubectl get pod`等の結果を監視する方法を以下の記事に書いた。

- [kubectl CLIでPodの正常性を確認する](https://qiita.com/sotoiwa/items/7ffe52f4de166cf0cce7)

同じように、kubectl CLIを使ってEventも監視したほうがよさそうなので、そのためのシェルスクリプトを作成したメモ。

## Eventリソース

EventリソースについてのAPIリファレンスは以下。

- https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.15/#event-v1-core

`kubectl get event`は以下の様な出力。

```shell-session
$ kubectl get event --all-namespaces -o wide
NAMESPACE     LAST SEEN   TYPE      REASON             KIND   SOURCE                  MESSAGE                                                                                               SUBOBJECT                 FIRST SEEN   COUNT   NAME
kube-system   4m51s       Warning   FailedScheduling   Pod    default-scheduler       0/4 nodes are available: 1 Insufficient cpu, 3 node(s) didn't match node selector.                                              22h          1533    audit-logging-fluentd-ds-kz289.15b8347ed1d0dc1b
kube-system   12m         Normal    Pulled             Pod    kubelet, 9.188.124.25   Container image "mycluster.icp:8500/ibmcom/icp-management-ingress:2.3.0" already present on machine   spec.containers{router}   9d           2860    monitoring-prometheus-collectdexporter-8c479ffcd-zwn4c.15b57dc4ecb4427d
kube-system   2m9s        Warning   BackOff            Pod    kubelet, 9.188.124.25   Back-off restarting failed container                                                                  spec.containers{router}   9d           63243   monitoring-prometheus-collectdexporter-8c479ffcd-zwn4c.15b57efcbfec5d05
$
```

- `TYPE`という項目があり、`Warning`を監視すればよさそう
- `LAST SEEN`という項目があり、同じイベントはまとめて表示される
- Eventリソースの存続時間は、apiserverの起動オプションの`--event-ttl`で指定されており、デフォルトは1時間。
  - https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/

個別のイベントを見てみると、以下の様な構造。

```shell
kubectl get event -n kube-system audit-logging-fluentd-ds-kz289.15b8347ed1d0dc1b -o yaml
```

```yaml
apiVersion: v1
count: 1538
eventTime: null
firstTimestamp: "2019-08-06T02:29:49Z"
involvedObject:
  apiVersion: v1
  kind: Pod
  name: audit-logging-fluentd-ds-kz289
  namespace: kube-system
  resourceVersion: "11688159"
  uid: 343166a9-b101-11e9-b187-fa163e2fb2ab
kind: Event
lastTimestamp: "2019-08-07T01:25:33Z"
message: '0/4 nodes are available: 1 Insufficient cpu, 3 node(s) didn''t match node
  selector.'
metadata:
  creationTimestamp: "2019-08-06T02:29:49Z"
  name: audit-logging-fluentd-ds-kz289.15b8347ed1d0dc1b
  namespace: kube-system
  resourceVersion: "11844265"
  selfLink: /api/v1/namespaces/kube-system/events/audit-logging-fluentd-ds-kz289.15b8347ed1d0dc1b
  uid: 107c1e3f-b7f2-11e9-b187-fa163e2fb2ab
reason: FailedScheduling
reportingComponent: ""
reportingInstance: ""
source:
  component: default-scheduler
type: Warning
```

## 監視スクリプト

Pod等の状態監視と同じく、`-o json`を使ってjson形式で出力し、`jq`で処理をするのが楽と思われる。このシェルスクリプトをCron等で5分（300秒）に1回実行するとして、その5分以内に観測されたWarningのイベントを監視する。

`jq`には日付を扱える便利な関数もある。マニュアルを参照。

- https://stedolan.github.io/jq/manual/

`kubectl`の側でも、`--sort-by '{lastTimestamp}'`でソートしたり、`--field-selector type=Warning`でフィルタリングすることは可能だが、`jq`のほうができることが多いので、処理は`jq`に寄せる。

以下の様なスクリプトを作成した。

```shell:check-events.sh
#!/bin/bash

set -eu
set -o pipefail

# kubectl get nodeを実行して結果を変数に格納
# "jq -c"は結果を改行等で整形せずコンパクトにするオプション
json=$(kubectl get event --all-namespaces -o json | jq -c .)

# 結果を時刻でソートしてから、
# Warningでフィルタリングし、
# 300秒以内であるかでフィルタリング
warnings=$(echo ${json} | jq -c '.items
  | sort_by( .lastTimestamp )
  | .[]
  | select( .type == "Warning" )
  | select( now - ( .lastTimestamp | fromdate ) <= 300 )'
)

# 結果を整形
# <時刻> <Namespace名> <イベント名> <メッセージ>
# "jq -r"は出力をクオートしないオプション
warnings=$(echo ${warnings} | jq -r '.lastTimestamp + " "
                                      + .involvedObject.namespace + " "
                                      + .involvedObject.name + " "
                                      + .message')

# warnings が空文字の場合は正常
if [ -z "${warnings}" ]; then
  echo "Warningはありません。"
else
  IFS=$'\n'
  for warning in "${warnings}"; do
    echo "${warning}"
  done
  unset IFS
  exit 1
fi
```

実行例は以下の通り。

```shell-session
$ ./check-events.sh
2019-08-07T03:00:33Z kube-system audit-logging-fluentd-ds-kz289 0/4 nodes are available: 1 Insufficient cpu, 3 node(s) didn't match node selector.
2019-08-07T03:03:16Z kube-system monitoring-prometheus-collectdexporter-8c479ffcd-zwn4c Back-off restarting failed container
$
```

## その他

GKEだとStackdriverにEventを送るEvent Exporterがある。SlackにEventを送るOSSもいくつかある。

- [Events in Stackdriver](https://kubernetes.io/docs/tasks/debug-application-cluster/events-stackdriver/)
- [kubernetesのeventをSlackに通知する](https://qiita.com/masahata/items/607f7477d046bed06c51)
- https://github.com/ultimateboy/slack8s