クラスタ外のPrometheusサーバでGKEクラスタをガン見したい

タイトル通りのことをします。
GKEコンソールの使い方とかkubectlコマンド、gcloudコマンドとかの説明はしません。

## 前提
GKE(限定公開クラスタ)
マスター承認済みNWは有効
ノード自体のCPU、メモリとかはcloud monitoringで監視
(node exporterで一元管理と迷ったが、クラウドのコンピュートエンジンの体調はクラウド備え付けの監視系で見るのが1番間違いがないってAWS cloud watchの時に思ったので)

## やりたいこと
k8s(GKE)備え付けのapiだとpodごとのCPU使用率とかがどうにも取れないようなので、kube-state-metricsを突っ込んでPrometheusでガン見したい。

なお、当該記事で書いてる方法以外にも
・prometheusのremote read/write使う
・prometheusのfederation機能使う
・prometheus-to-sd使ってstackdriverにkube-state-metricsのメトリクス送り付ける(Prometheusで、ではなくなるけど)

とか色々策は考えられるので、要件と運用次第ではあります。
ただ、今回は複数のクラスタの状態を集約するわけではないのでfederationはちょっと色味が違う気がする。

## 他要件
### k8s備え付けのAPIにも触りたい
今のところ用事はないんだけど触れるように組んでおくと何かと後々使えるかもしれない

### kube-state-metrics用のingress作りたくない
NW内部には公開したいけど外部にはしたくない。
内部LBも考えたが負荷分散しなきゃいけないほど今トラフィックないし、現状(2021-04-11)だと最低でも$0.075/1hかかるっぽいので払わんで済むなら払いたくない。

### PrometheusサーバはGCEで普通にたてたい(クラスタ外にしたい)
最終的にfederation使って集約するケースを考えた場合、クラスタ外から触る構成を1度組んでおきたい

## これでなんとかなった
・監視系専用のノードプールをmax1台CAなしかつ専用タグくっつけて作成
・監視系用のnamedpace,clusterrole,bind,serviceaccountを作成
・kube-state-metricsを専用ノードプールめがけてデプロイ
・NodePort指定のserviceで紐づけ
・普通にGCEで立ててるPrometheusからはgce-sd-config使ってアクセス
・k8sAPIにはクラスタのエンドポイントと証明書、sa tokenでアクセス
・metricsﾁｬﾝ取れたﾔｯﾀｰ!!

## サンプル、公式ドキュメント
https://github.com/syanhaiD/gke_prac_prom
https://github.com/kubernetes/kube-state-metrics/tree/master/examples
https://prometheus.io/docs/prometheus/latest/configuration/configuration/

### 監視系専用のノードプールをmax1台CAなしかつ専用タグくっつけて作成
アプリケーションがデプロイされてるプールに監視系も入れてしまうと、オートスケールとか考えた場合色々面倒なので勝手に増えたり減ったりしないノードプールを専用に作ります。
サンプルだと`prometheus-pool`って名前で作ってます。
また、prometheusから発見するときに利用するため専用にタグをくっつけておきます。
サンプルだと`kube-state-metrics-tag`って名前にしてます。

### 監視系用のnamespace,clusterrole,bind,serviceaccountを作成
どうせ実務で使う時は最強権限の最強SAでﾌﾞﾝﾌﾞｰﾝみたいなことはしないので、サンプルではありますがちゃんとこの辺も作ります。いうても公式ドキュメントにちゃんとexampleが載ってるので対して苦労はしません。サンプルだと
https://github.com/syanhaiD/gke_prac_prom/blob/main/k8s/namespace.yaml
https://github.com/syanhaiD/gke_prac_prom/blob/main/k8s/serviceaccount.yaml
https://github.com/syanhaiD/gke_prac_prom/blob/main/k8s/clusterrole.yaml
https://github.com/syanhaiD/gke_prac_prom/blob/main/k8s/clusterrole_binding.yaml
この辺になります。
kube-systemを使わずに別途namespaceを作るのは単に自分の趣味なのでkube-system使っても全然問題ないかと思います。

### kube-state-metricsを専用ノードプールめがけてデプロイ
deploymentも公式にしっかりあるのでやはり特に苦労はしないかと思います。
サンプルだと
https://github.com/syanhaiD/gke_prac_prom/blob/main/k8s/kube_state_metrics.yaml
これになります。
imageに関して、2021-04-11時点でgcr.ioにあるv1.9.5はデプロイしたらCrushLoopになって、中身見るとなんかプロセス実行にあたって権限が足らんとかで動かなかったのでquayからv1系の最新をもってきています。

### NodePort指定のserviceで紐づけ
ingress作らないのでNodePortでいきます。サンプルは
https://github.com/syanhaiD/gke_prac_prom/blob/main/k8s/service.yaml
これ。この辺も特に「ならでは」の部分はないので問題ないかと。

### クラスタ外Prometheusサーバからアクセス
Prometheusサーバの起動は特に説明しません。バイナリ持ってくるにせよdocker使うにせよほとんど詰まることなくいけると思います。

https://github.com/syanhaiD/gke_prac_prom/blob/main/prometheus/prometheus.yaml

#### k8s APIアクセスへの道のり
GKE クラスタエンドポイントはGKEコンソールから取れます。
GKE クラスタ証明書もGKEコンソールから取れますが、tokenあけるときにまとめて取るほうが楽かも。

service account tokenは、サンプル通りでいくと

```
kubectl -n prometheus get serviceaccounts forprom -o yaml
```
を叩くと

```
secrets:
- name: forprom-token-xxxxxxxxxxx
```
こんな要素が含まれるyamlを吐くので、

```
kubectl -n prometheus get secrets forprom-token-xxxxxxxxxxx -o yaml
```
これの結果に含まれるtokenをbase64デコードしたものになります。
ca.crtもbase64デコードすればGKEコンソールから確認できる証明書と同様のものが出てきます。
デコード結果をファイルにしてprometheusサーバにおいて利用しましょう。

tls_configとauthorizationはkubernetes_sd_configs内と外で1つずつ要ります。
推測も混ざりますがサンプルのように指定した場合の処理の流れとしては

1. kubernetes_sd_configs内で指定しているtls_configとauthorizationでapi_serverの認証通してアクセスし、監視対象を発見、Prometheusは発見したオブジェクトのprivate ipからmetricsを取る(この場合コントロールブレーンのprivate ipだと思う)

2. コントロールブレーンのprivate ipにはクラスタ外からは触れないようなので、public ipの/metricsに触るようにrelabelで書き換える

3. kubernetes_sd_configs内で指定しているtls/authはディスカバリー時に使うもので、スクレイピングの時には使用されない。そのためrelabel_configsと同一の階層にも定義することによってスクレイピング時にも認証情報が渡るようにする

って感じになってるんだろうと思います。まだるっこしい感じはするのでスクレイピング時にもディスカバリーと同じ認証情報を使う、みたいなオプション指定できるようにならんかな。

#### kube-state-metricsノードへの道のり
こちらは説明することないですね。NodePortで紐づけているので、普通にgce_sd_configsで発見してあげれば良いだけです。
1つ注意点があるとすれば、素の状態だと権限が足りなくてGCEの一覧をPrometheusがとってこれないようなので、GCEの閲覧以上の権限を持ったサービスアカウントをPrometheusサーバインスタンスに食わせておくとか、
https://prometheus.io/docs/prometheus/latest/configuration/configuration/#gce_sd_config
に書いてあるように環境変数設定するとか所定の場所にjson置くとかする必要があります。

### metricsﾁｬﾝ取れたﾔｯﾀｰ!!
ﾔｯﾀｰ!!