GKEでautoscaleする時のハマりポイント

GKE環境でのkubernetesで、HorizontalPodAurtoscalerの設定にハマりまくったため、その時の知見を残しておく。GKEはブラックボックスのため、うまく動かなかった時にハマりやすい。datadogとstackdriverを使ったautoscaleをトライした。

## 前提
- すでにDeploymentでアプリケーションが動いている
- GKEのコマンドがインストールされている
- Pod数は手動で設定している、もしくはv1のautoscale（apiVersion: autoscaling/v1、CPUだけのautoscale）をしている
- datadog、stackdriverをすでに使っている

## やりたいこと
- Pod数を指定した指標に応じてautoscaleさせたい
- 外部指標（今回はdatadogやstackdriver）や複数の指標を使ってスケールさせたい

## GKEの認証
そもそものautoscaleに入る前にGKEの認証に小ハマりしたため、これも残しておく。

GKEの場合はコマンド実行前にcredentialを取らないといけないため、コマンドを実行するときはcredential取得コマンドと同時に行う必要がある。

```
$ gcloud container clusters get-credentials clusterfoo --zone asia-east1-a --project bar && kubectl get pods --namespace buz
```


# scaleのための設定

## node数を増やす
当たり前だけど忘れがちなやつ。スケールしたpodを入れるためのnodeを立ち上げられないと、pod数が増やせない。

clusterのnode-poolの設定から、node数自体もスケールするようにMaxNode数を増やす。

## metrics-server
kubernetesのドキュメント読んでると、kubernetes自体の指標を集めるためにはmetrics-serverが必要と書いてある。GKEの場合はclusterを作るとデフォルトでついてくるので、特に自分で作る必要はない。


# datadogを使ったautoscale

## datadog-cluster-agent
ドキュメント通り作れば良い。

まずはdatadog-agent（各node内で動くagent）を構築する。
https://docs.datadoghq.com/agent/kubernetes/daemonset_setup/?tab=k8sfile

そのあと、cluster-agentを構築する。
https://www.datadoghq.com/blog/autoscale-kubernetes-datadog/

設定がうまくいったかをこの段階で確認しておいた方が良い。後述する見えてるのに取れない指標があるので、いきなり目的の指標を設定せずに、必ず見える指標（system.cpu.userとか）で確認するのが良い。

### datadog-cluster-agentのデバッグ
datadog-cluster-agentは、親切にデバッグ用のツールをimage内に同梱してくれている。
https://github.com/DataDog/datadog-agent/blob/master/docs/cluster-agent/CUSTOM_METRICS_SERVER.md#troubleshooting

コンテナ内に入ってコマンドから指標がちゃんと取得できているか確認できる。

```
$ gcloud container clusters get-credentials clusterfoo --zone asia-east1-a --project bar && kubectl --namespace buz exec -it datadog-cluster-agent-abcdefg sh
> datadog-cluster-agent status
```

### datadogでの指標
metricNameはそのまま指標の値を使う。matchSelector: matchLabels以下にfilterを書く。

### ハマりポイント：直近の計測値が必要
autoscaleをする都合上、指標の最新の計測値は直近1分以内（くらい？）に取れた値じゃないといけない。そのため、表示が遅れてる値だと、「datadog上では表示されてるのに、hpa上では計測できてない」って状況になります。特に、datadogのgcp integrationはちょっと遅れてる（5分くらい）ので、この状況になりやすかった。

GCPの各種指標をdatadog経由でみてautoscaleするつもりだったのだが、当てが外れたのでGCP環境にあるstackdriverを使ってみることにした。


# stackdriverを使ったautoscale
## custom-metrics-stackdriver-adapter
stackdriverの設定もこのままで大丈夫。
https://cloud.google.com/kubernetes-engine/docs/tutorials/external-metrics-autoscaling?hl=ja

ちなみに、datadog-cluster-agentとcustom-metrics-stackdriver-adapterを同時に動かしてもちゃんと指標が取れるので、基本的には両方構築しちゃって良い。

## ハマりポイント：monitoringの権限が必要

```
$ gcloud container clusters get-credentials clusterfoo --zone asia-east1-a --project bar && gcloud container clusters describe clusterfoo

# oauthScopesに"https://www.googleapis.com/auth/monitoring"が入っていることを確認する
```

UIからクラスタを作ってればこの設定は自動で入ってるはずだが、コマンドラインから作ってると入ってない。作成時に`--scopes`オプションで指定する。

```
gcloud container node-pools create "autoscaling-pool" \
 --service-account="someone@$bar.iam.gserviceaccount.com" \
 --project="bar" \
 --cluster="clusterfoo" \
 --zone=asia-east1-a \
 --enable-autoscaling \
 --machine-type="n1-standard-8" \
 --min-nodes 1 \
 --max-nodes 10 \
 --scopes="https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/monitoring"
```

### ハマりポイント：stackdriverでの指標名が表示とちょっと違う
UIに表示されてるのと設定する値がちょっと違った。Metrics Explorerとかだと`view as JSON`から設定するべき値がみられるので、UIに表示されている名前ではなくそっちから指標をとる。

指標名に `/` が入っているが、kubernetesのmetricsに使えないので、 `|` に置き換えられている。
https://cloud.google.com/kubernetes-engine/docs/tutorials/external-metrics-autoscaling?hl=ja#writing_external_metric_specification

あと地味に、view as JSONではfilterのkey名が `resource.label.~~`と表示されているが、`resource.labels.~~` （sがつく）っぽい。（ここはなぜ名前が違うか謎）


# 全体的なハマりポイント
## HPA設定
kubernetesのドキュメントを読むと、マルチメトリクスは`apiVersion: autoscaling/v2beta2`から、みたいに書いてあるが、GKEでは`apiVersion: autoscaling/v2beta1`でも使える。この辺り微妙に仕様が違ってて謎。

## 指標を確認する
GKEのAPIの `/apis/external.metrics.k8s.io` から指標が確認できる。実際に取得できている指標の値は、次のコマンドで確認する。

```
kubectl get --raw "/apis/external.metrics.k8s.io/v1beta1/namespaces/foo/loadbalancing.googleapis.com|https|request_count"
```

JSONが出力されるので、jqなどで整形すると見やすい。

## 指標の見方
コマンドラインで指標をみると、大抵以下のように `m` がついた値になってる。

```
$ gcloud container clusters get-credentials clusterfoo --zone asia-east1-a --project bar && kubectl get hpa --namespace buz
NAME                                                                 REFERENCE                       TARGETS               MINPODS   MAXPODS   REPLICAS   AGE
horizontalpodautoscaler.autoscaling/sample-deployment-autoscaling    Deployment/sample-deploymen     33m/1 (avg), 0%/70%   1         20        1          21h
```

これはmilliのことで、1/1000にすれば実数の値が出てくる。また、値が大きいと `K` も出てくる。これは Kiroで1000倍すれば良い。この表記は、設定ファイル中でも使える。

hpaの設定で使われる値は `podあたり・秒間の値` なので、かなり小さな値になり、このmが頻出する。とにかく普段datadogやstackdriverで見ている値よりかなり小さい。

### 安定する値を使う
kubernetesのautoscaleに限らないが、高負荷などで処理が落ちると結局メトリクスが計測されずスケールされなくなってしまう（下手すると減ったと解釈される）ので、なるべくHorizontalPodAutoscalerでスケールさせたい対象とは別の指標を使った方がスケールは安定します。

GKEの場合、基本的にはGCPのコンポーネントの指標（loadbalancerとかpubsubとか）を使うことになるので、基本的にはstackdriverで取れる値を使うのが良い。