OpenShift Virtical Pod Autoscalerオペレーターによるリソースの自動調整

# はじめに
Deployment等のマニフェストでは、OpenShiftクラスターに対して、Pod内のコンテナがどの程度リソース(CPU・メモリ)を使用するかを示す要求値や上限値を設定することができます。過剰な要求値が設定されている場合、新規にPodをデプロイしたいときにworkerノードにスケジュールされない可能性があります。また、メモリ使用量が上限を超過した場合はPodが強制終了(OOM)されます。

この記事では、OpenShift 4.8でGAになったVirtical Pod Autoscaler(VPA)により、リソース要求が自動的に設定されることを確認します。

> Now generally available, Vertical Pod Autoscaling (VPA) allows devops to “right size” the workload with “off” mode in VPA for openshift to recommend appropriate size of the workload based on historical CPU and memory usage.

https://cloud.redhat.com/blog/red-hat-openshift-4.8-is-now-generally-available

■ OpenShift 4.8環境

https://qiita.com/y-akio/items/ad0093daa504be746573

# 1. VerticalPodAutoscalerオペレーター導入
事前準備として、OpenShiftコンソールからVerticalPodAutoscalerオペレーターを導入します。
![vpa.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413476/1d4037c0-edad-8641-32a4-02bb4b60e893.png)

オペレーターを導入したネームスペースにPodが作成されます。

```bash:classicノード
oc get pod -n openshift-vertical-pod-autoscaler
### 標準出力↓
NAME                                               READY   STATUS    RESTARTS   AGE
vertical-pod-autoscaler-operator-64866db48-wrj96   1/1     Running   0          35m
vpa-admission-plugin-default-85d8486498-jpnzd      1/1     Running   0          34m
vpa-recommender-default-56cfcd58f8-gvfm7           1/1     Running   0          34m
vpa-updater-default-7b76746876-s5mk4               1/1     Running   0          34m
```

# 2. QuarkusネイティブアプリケーションへのVPA適用
## 2.1. アプリケーションのデプロイ
カスタムリソース「VerticalPodAutoscaler」作成後、以下の記事で作成したアプリケーションをリソース要求「cpu:100m、memory:100Mi」でデプロイします。

https://qiita.com/y-akio/items/7aa7b1a59018e2db0708

```bash:classicノード
oc project quarkus-native
oc apply -f vpa.yaml
oc get vpa
### 標準出力↓
NAME             MODE   CPU   MEM   PROVIDED   AGE
quarkus-native   Auto                          3s

oc apply -f quarkus-native-limits.yaml
oc get pod
### 標準出力↓
NAME                              READY   STATUS    RESTARTS   AGE
quarkus-native-58d489d9c8-2sfhc   1/1     Running   0          7s
quarkus-native-58d489d9c8-crfhh   1/1     Running   0          7s
quarkus-native-58d489d9c8-z4d9d   1/1     Running   0          7s
```

```yaml:vpa.yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: quarkus-native
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: quarkus-native
  updatePolicy:
    updateMode: "Auto"
  resourcePolicy:
    containerPolicies:
      - containerName: '*'
        minAllowed:
          cpu: 10m
          memory: 10Mi
        maxAllowed:
          cpu: 1
          memory: 500Mi
        controlledResources: ["cpu", "memory"]
```

```yaml:quakus-native-limits.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: quarkus-native
  labels:
    app: quarkus-native
spec:
  replicas: 3
  selector:
    matchLabels:
      app: quarkus-native
  template:
    metadata:
      labels:
        app: quarkus-native
    spec:
      containers:
        - name: quarkus-native
          resources:
            requests:
              cpu: 100m
              memory: 100Mi
          image: image-registry.openshift-image-registry.svc:5000/quarkus-native/quarkus-native
          ports:
            - containerPort: 8080
```

## 2.2. リソース要求確認
マニフェストではリソース要求「cpu:100m、memory:100Mi」としましたが、しばらくするとリソース要求「cpu:25m、memory:262144k」で再作成されました。updateModeを「Auto」としているためで、Red Hatのマニュアルには「VPA は、一度に 1 つずつ推奨値で調整されていない Pod を自動的に削除するため、アプリケーションはダウンタイムなしに継続して要求を提供できます。」と記載されています。

```bash:classicノード
oc get pod
### 標準出力↓
NAME                              READY   STATUS    RESTARTS   AGE
quarkus-native-58d489d9c8-77lhf   1/1     Running   0          1m
quarkus-native-58d489d9c8-7s24g   1/1     Running   0          1m
quarkus-native-58d489d9c8-hbgng   1/1     Running   0          1m

oc describe pod quarkus-native-58d489d9c8-77lhf | grep -A 2 "Requests"
### 標準出力↓
    Requests:
      cpu:        25m
      memory:     262144k
```

以下のようにVPAの状況を確認することができます。

```bash:classicノード
oc get vpa
### 標準出力↓
NAME             MODE   CPU   MEM       PROVIDED   AGE
quarkus-native   Auto   25m   262144k   True       3m59s

oc get vpa quarkus-native --output yaml
### 標準出力↓
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
…
spec:
  resourcePolicy:
    containerPolicies:
    - containerName: '*'
      controlledResources:
      - cpu
      - memory
      maxAllowed:
        cpu: 1
        memory: 500Mi
      minAllowed:
        cpu: 10m
        memory: 10Mi
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: quarkus-native
  updatePolicy:
    updateMode: Auto
status:
…
  recommendation:
    containerRecommendations:
    - containerName: quarkus-native
      lowerBound:
        cpu: 25m
        memory: 262144k
      target:
        cpu: 25m
        memory: 262144k
      uncappedTarget:
        cpu: 25m
        memory: 262144k
      upperBound:
        cpu: "1"
        memory: 500Mi
``` 

「oc adm top pod」を確認の上、VPA設定でCPUの最小値を10m、memoryの最小値を10Miにしたのですが、実際にはより大きい値となりました(cpu:25m、memory:262144k)。

```
oc adm top pod
### 標準出力↓
NAME                              CPU(cores)   MEMORY(bytes)
quarkus-native-58d489d9c8-77lhf   0m           11Mi
quarkus-native-58d489d9c8-7s24g   0m           10Mi
quarkus-native-58d489d9c8-hbgng   0m           16Mi
```

下の画像はOpenShiftコンソールで確認したリソースの使用状況です。ネイティブアプリケーションであるためか、CPUとメモリの使用量は非常に少ないです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413476/bc17dcbe-2541-c443-3ee1-c12b637dd438.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413476/d01c3c6a-d5d5-660f-aeff-d9bcc4638b3e.png)

# 3. Spring BootアプリケーションへのVPA適用
## 3.1. アプリケーションのデプロイ
カスタムリソース「VerticalPodAutoscaler」作成後、以下の記事で作成したアプリケーションをリソース要求「cpu:100m、memory:100Mi」でデプロイします。

https://qiita.com/y-akio/items/155ae44188a6df02138c

```bash:classicノード
oc project spring-liberty
oc apply -f vpa.yaml
oc get vpa
### 標準出力↓
NAME             MODE   CPU   MEM   PROVIDED   AGE
spring-liberty   Auto                          7s

oc apply -f spring-liberty-limits.yaml
oc get pod
### 標準出力↓
NAME                              READY   STATUS    RESTARTS   AGE
spring-liberty-67898b8fbf-5hq2b   1/1     Running   0          5s
spring-liberty-67898b8fbf-lgqtk   1/1     Running   0          5s
spring-liberty-67898b8fbf-xbcf7   1/1     Running   0          5s
```

```yaml:vpa.yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: spring-liberty
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: spring-liberty
  updatePolicy:
    updateMode: "Auto"
  resourcePolicy:
    containerPolicies:
      - containerName: '*'
        minAllowed:
          cpu: 10m
          memory: 10Mi
        maxAllowed:
          cpu: 1
          memory: 500Mi
        controlledResources: ["cpu", "memory"]
```

```yaml:spring-liberty-limits.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-liberty
  labels:
    app: spring-liberty
spec:
  replicas: 3
  selector:
    matchLabels:
      app: spring-liberty
  template:
    metadata:
      labels:
        app: spring-liberty
    spec:
      containers:
        - name: spring-liberty
          image: image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty
          resources:
            requests:
              cpu: 100m
              memory: 100Mi
          ports:
            - containerPort: 9080
```

## 3.2. リソース要求確認
マニフェストではリソース要求「cpu:100m、memory:100Mi」としましたが、しばらくするとリソース要求「cpu:25m、memory:297164212」で再作成されました。updateModeを「Auto」としているためで、Red Hatのマニュアルには「VPA は、一度に 1 つずつ推奨値で調整されていない Pod を自動的に削除するため、アプリケーションはダウンタイムなしに継続して要求を提供できます。」と記載されています。

```bash:classicノード
oc get pod
### 標準出力↓
NAME                              READY   STATUS    RESTARTS   AGE
spring-liberty-67898b8fbf-bqksp   1/1     Running   0          34s
spring-liberty-67898b8fbf-c2g24   1/1     Running   0          2m34s
spring-liberty-67898b8fbf-g52t7   1/1     Running   0          94s

oc describe pod spring-liberty-67898b8fbf-bqksp | grep -A 2 "Requests"
### 標準出力↓
    Requests:
      cpu:        25m
      memory:     297164212
```

以下のようにVPAの状況を確認することができます。

```bash:classicノード
oc get vpa
### 標準出力↓
NAME             MODE   CPU   MEM         PROVIDED   AGE
spring-liberty   Auto   25m   297164212   True       8m49s

oc get vpa spring-liberty --output yaml
### 標準出力↓
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
…
spec:
  resourcePolicy:
    containerPolicies:
    - containerName: '*'
      controlledResources:
      - cpu
      - memory
      maxAllowed:
        cpu: 1
        memory: 500Mi
      minAllowed:
        cpu: 10m
        memory: 10Mi
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: quarkus-native
  updatePolicy:
    updateMode: Auto
status:
…
  recommendation:
    containerRecommendations:
    - containerName: quarkus-native
      lowerBound:
        cpu: 25m
        memory: 262144k
      target:
        cpu: 25m
        memory: 262144k
      uncappedTarget:
        cpu: 25m
        memory: 262144k
      upperBound:
        cpu: "1"
        memory: 500Mi
``` 

妥当なリソース要求に調整されたと思われます。

```bash:classicノード
oc adm top pod
### 標準出力↓
NAME                              CPU(cores)   MEMORY(bytes)
spring-liberty-67898b8fbf-bqksp   4m           224Mi
spring-liberty-67898b8fbf-c2g24   4m           237Mi
spring-liberty-67898b8fbf-g52t7   5m           227Mi
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413476/0a58b8cd-603c-93d5-1ac1-8c415b92bb5f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413476/57dae65f-59fd-93ef-f9f3-47c92f9ab0a7.png)

# 参考資料
https://access.redhat.com/documentation/ja-jp/openshift_container_platform/4.8/html/nodes/automatically-adjust-pod-resource-levels-with-the-vertical-pod-autoscaler

https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler