[GKE] VPAを使用したCPUとメモリのリクエストと制限の推奨値を取得する方法

# 概要
VPA（垂直 Pod 自動スケーリング）は、最適な CPU のための Pod と Pod で必要とされるメモリリソースのサイズを決定する自動スケーリング ツールです。CPUとメモリのリクエストと上限の推奨値を指定するか、値を自動的に更新するように垂直 Pod 自動スケーリングを構成できます。
# 全体手順
1. GKEクラスタで垂直 Pod 自動スケーリングを有効にする。
1. VerticalPodAutoscalerオブジェクトを作成してワークロードに垂直Pod自動スケーリング構成にする。
1. 負荷試験を実施する。
1. リソースの推奨値を取得する。
1. リソースを自動的に更新する。
1. トラブルシューティング

# 各手順
## 1. GKE クラスタで垂直 Pod 自動スケーリングを有効にする。
GKE の Standard クラスタで垂直 Pod 自動スケーリングを有効にします。Autopilot クラスタの場合は、デフォルトで有効になっているので 3.2 にスキップしてください。
### 1.1 手順
以下のドキュメントをご参照ください。
[（外部ドキュメント）クラスタの垂直 Pod 自動スケーリングを有効にする](https://cloud.google.com/kubernetes-engine/docs/how-to/vertical-pod-autoscaling#enable-VPA)

## 2. VerticalPodAutoscaler オブジェクトを作成してワークロードに垂直 Pod 自動スケーリング構成にする。
VerticalPodAutoscaler オブジェクトを作成し、リソースの推奨値を把握したいPodを管理しているコントローラ（Deploymentなど）に紐付けます。
### 2.1 VerticalPodAutoscaler の概要
VerticalPodAutoscaler の updateMode フィールドに以下のいずれかのモードを指定することでリクエストと制限の調整方法を変更できます。[*1](https://cloud.google.com/kubernetes-engine/docs/how-to/vertical-pod-autoscaling#overview)

|モード|説明|
|:---|:---|
|**Off**|推奨値を計算し、VerticalPodAutoscaler オブジェクトに示します。Podを自動で変更はしません。レコメンデーションモードとも呼ばれます。|
|**Initial**|推奨値を計算し、Podの作成時にのみリソースリクエストを自動で更新します。後で変更されることはありません。|
|**Auto**|推奨値を計算し、Podのライフサイクル期間中にCPUとメモリのリソースリクエストを自動で更新します。|

自動で更新される場合、制限の値は、初期値のリクエストと制限の比率に応じて変更されます。
例：
初期値（cpuは、requests：limits = 1：2、memoryは、requests：limits = 1：1）

```yaml
 resources:
          limits:
            cpu: 20m
            memory: 5Mi
          requests:
            cpu: 10m
            memory: 5Mi
```
自動更新後の値（リクエストと制限の比率は、初期値を変わらない）

```yaml
resources:
      limits:
        cpu: 30m
        memory: "126877696"
      requests:
        cpu: 15m
        memory: "126877696"
```

### 2.2 VerticalPodAutoscaler オブジェクトの作成方法

1.次の VerticalPodAutoscaler マニフェストを任意の名前のyamlファイルとして保存します。

```yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: {{ 任意の名前 }}
spec:
  targetRef:
    apiVersion: "apps/v1"
    // Pod のセットを管理するコントローラ（ex: Deployment, StatefulSet）
    kind:      {{ コントローラ }}
    name:      {{ コントローラの名前 }}
  updatePolicy:
    // レコメンデーションモード (推奨値を指定するだけで、Podを直接変更しません)
    updateMode: "Off"
```

2.VerticalPodAutoscaler オブジェクトを作成します。

```shell
kubectl apply -f {{ yamlファイルの名前 }}
```

## 3. 負荷試験を実施する。
想定する負荷をかけることで、VerticalPodAutoscalerがリソースの推奨値を算出します。
負荷試験の方法は任意となります。

Google Cloudドキュメント [*2](https://cloud.google.com/architecture/distributed-load-testing-using-gke) で負荷分散テストの手順が記載されています。
必要に応じて、ご参照ください。

## 4. リソースの推奨値を取得する。
Pod が作成されると、コンテナの CPU とメモリのニーズを分析し、推奨値を status フィールドに記録します。
### 4.1 手順
1.作成した VerticalPodAutoscaler を表示します。

```shell
kubectl get vpa {{ 作成したVerticalPodAutoscalerの名前 }} --output yaml
```

2.出力に、CPUとメモリのリクエストの推奨値が表示されます。

```yaml
...
  recommendation:
    containerRecommendations:
    - containerName: my-rec-container
      lowerBound:
        cpu: 1m
        memory: “125829120”
      target:
        cpu: 15m
        memory: “126877696”
      uncappedTarget:
        cpu: 15m
        memory: “126877696” 
      upperBound:
        cpu: 45m
        memory: “169869312”
...
```

### 4.2 recommendationの見方 [*3](https://cloud.google.com/kubernetes-engine/docs/concepts/verticalpodautoscaler#recommendedcontainerresources_v1_autoscalingk8sio)
|フィールド||
|:---|:---|
|**containerName**|推奨値が適用されるコンテナの名前|
|**lowerBound**|そのコンテナに対するCPUとメモリのリクエストの推奨値の下限|
|**target**|そのコンテナに対するCPUとメモリのリクエストの推奨値|
|**uncappedTarget**|オートスケーラーによって計算された最新のリソース推奨値|
|**upperBound**|そのコンテナに対するCPUとメモリのリクエストの推奨値の上限|


## 5 リソースを自動的に更新する。
PodのCPUとメモリのリクエストと制限を自動的に調整するVerticalPodAutoscaler オブジェクトを作成します。
### 5.1 手順
1.次の VerticalPodAutoscaler マニフェストを任意の名前のyamlファイルとして保存します。(4)の手順で既に作成している場合は、作成するために使用したyamlファイルのupdateMode を変更します。

```yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: {{ 任意の名前 }}
spec:
  targetRef:
    apiVersion: "apps/v1"
    // Pod のセットを管理するコントローラ（ex: Deployment, StatefulSet）
    kind:      {{ コントローラ }}
    name:      {{ コントローラの名前 }}
  updatePolicy:
    // Podのリソースを自動で更新します
    updateMode: "Auto"
```

2.VerticalPodAutoscaler オブジェクトを作成または更新します。

```shell
kubectl apply -f {{ yamlファイルの名前 }}
```

3.想定する負荷をかけてから、実行中のPodを表示します。

```shell
kubectl get pods
```

4.実行中の Pod の詳細情報を取得します。

```shell
kubectl get pod {{ Podの名前 }} --output yaml
```

5.出力から、CPUとメモリのリクエストと制限が更新されていることがわかります。
また、更新について記録したアノテーションを確認することもできます。

6.VerticalPodAutoscaler を表示して、推奨値を確認することも可能です。

```shell
kubectl get vpa {{ VerticalPodAutoscalerの名前 }} --output yaml
```


### 6 トラブルシューティング
- VerticalPodAutoscaler の updateMode を “Auto” にしているのに、Podのリソース値が変更されない。
    - コントローラのリソース値の初期値が設定されているかご確認をお願いします。リクエストのみ設定している場合も、リクエストは変更されますが制限は変更されません。

- Pod の詳細からリソース値は自動変更されたことは確認できるが、コントローラ (Deploymentなど) の詳細からは確認できない。
    - コントローラの詳細には、自動変更された値は反映されません。もしVerticalPodAutoscaler の updateMode を ”Auto” から “Off” に変更する場合は、コントローラのリソース値を手動で変更することをお勧めします。手動変更していないとコントローラによってPodが再作成された時に推奨値の値が適用されない可能性があるからです。

# 外部リファレンス
1. [VerticalPodAutoscalerの概要](https://cloud.google.com/kubernetes-engine/docs/how-to/vertical-pod-autoscaling#overview)
1. [GKEを使用した負荷分散テスト](https://cloud.google.com/architecture/distributed-load-testing-using-gke)
1. [RecommendedContainerResources](https://cloud.google.com/kubernetes-engine/docs/concepts/verticalpodautoscaler#recommendedcontainerresources_v1_autoscalingk8sio)