Kubernetes の垂直オートスケール

この記事ではKubernetesのオートスケールの機能の一つであるVerticalPodAutoScalerについてまとめていきます。

## VerticalPodAutoScaler

VerticalPodAutoScaler(VPA)は、ユーザーがPod内のコンテナに対するリソース要求を設定する必要性をなくします。設定すると、使用状況に基づいて要求が自動的に設定されるため、適切なリソース量が各Podで使用できるように、ノードへの適切なスケジューリングが可能になります。

## VerticalPodAutoScalerの動作例

まずはREADMEにある内容に従ってインストールして動作を見てみましょう。

READMEによると1.9以上のクラスタを用意すると良いようです。今回はminikubeで1.10.0のクラスタを用意しました。
インストール手順に従ってインストールします

```
$ git clone git@github.com:kubernetes/autoscaler.git
$ cd autoscaler/vertical-pod-autoscaler
$ ./hack/vpa-up.sh 
customresourcedefinition.apiextensions.k8s.io "verticalpodautoscalers.poc.autoscaling.k8s.io" created
customresourcedefinition.apiextensions.k8s.io "verticalpodautoscalercheckpoints.poc.autoscaling.k8s.io" created
clusterrole.rbac.authorization.k8s.io "system:metrics-reader" created
clusterrole.rbac.authorization.k8s.io "system:vpa-actor" created
clusterrole.rbac.authorization.k8s.io "system:vpa-checkpoint-actor" created
clusterrole.rbac.authorization.k8s.io "system:evictioner" created
clusterrolebinding.rbac.authorization.k8s.io "system:metrics-reader" created
clusterrolebinding.rbac.authorization.k8s.io "system:vpa-actor" created
clusterrolebinding.rbac.authorization.k8s.io "system:vpa-checkpoint-actor" created
clusterrolebinding.rbac.authorization.k8s.io "system:replicasets-reader" created
serviceaccount "vpa-admission-controller" created
clusterrole.rbac.authorization.k8s.io "system:admission-controller" created
clusterrolebinding.rbac.authorization.k8s.io "system:admission-controller" created
serviceaccount "vpa-updater" created
deployment.extensions "vpa-updater" created
serviceaccount "vpa-recommender" created
deployment.extensions "vpa-recommender" created
Generating certs for the VPA Admission Controller in /tmp/vpa-certs.
Generating RSA private key, 2048 bit long modulus
.....+++
............................................................................................................+++
e is 65537 (0x010001)
Generating RSA private key, 2048 bit long modulus
.......................................................................................................+++
.......................+++
e is 65537 (0x010001)
Signature ok
subject=CN = vpa-webhook.kube-system.svc
Getting CA Private Key
Uploading certs to the cluster.
secret "vpa-tls-certs" created
Deleting /tmp/vpa-certs.
deployment.extensions "vpa-admission-controller" created
service "vpa-webhook" created
```

インストールは以上で完了です。とても簡単にできますね！

それではサンプルのアプリケーションと設定があるのでそれを投入して試します。

```
$ cat examples/hamster.yaml 
# This config creates a deployment with two pods, each requesting 100 millicores
# and trying to utilize slightly above 500 millicores (repeatedly using CPU for
# 0.5s and sleeping 0.5s).
# It also creates a corresponding Vertical Pod Autoscaler that adjusts the requests.
# Note that the update mode is left unset, so it defaults to "Auto" mode.
apiVersion: "poc.autoscaling.k8s.io/v1alpha1"
kind: VerticalPodAutoscaler
metadata:
  name: hamster-vpa
spec:
  selector:
    matchLabels:
      app: hamster
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: hamster
  namespace: default
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: hamster
    spec:            
      containers:
      - name: hamster
        image: k8s.gcr.io/ubuntu-slim:0.1
        resources:
          requests:
            cpu: 100m
            memory: 50Mi
        command: ["/bin/sh"]
        args: ["-c", "while true; do timeout 0.5s yes >/dev/null; sleep 0.5s; done"]

$ kubectl apply -f examples/hamster.yaml 
verticalpodautoscaler.poc.autoscaling.k8s.io "hamster-vpa" created
deployment.extensions "hamster" created

```

サンプルの設定とアプリケーションをデプロイしてしばらく待ちます。しばらくするとVPAオブジェクトのStatusにRecomendationのリソース設定が追加されます。

```
$ kubectl describe vpa   
Name:         hamster-vpa
Namespace:    default
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"poc.autoscaling.k8s.io/v1alpha1","kind":"VerticalPodAutoscaler","metadata":{"annotations":{},"name":"hamster-vpa","namespace":"default"}...
API Version:  poc.autoscaling.k8s.io/v1alpha1
Kind:         VerticalPodAutoscaler
Metadata:
  Cluster Name:        
  Creation Timestamp:  2018-05-23T05:23:03Z
  Generation:          1
  Resource Version:    7270
  Self Link:           /apis/poc.autoscaling.k8s.io/v1alpha1/namespaces/default/verticalpodautoscalers/hamster-vpa
  UID:                 5de52ff8-5e49-11e8-b4d6-0800271d2683
Spec:
  Selector:
    Match Labels:
      App:  hamster
  Update Policy:
    Update Mode:  Auto
Status:
  Conditions:
    Last Transition Time:  2018-05-23T05:23:28Z
    Status:                True
    Type:                  Configured
    Last Transition Time:  2018-05-23T05:23:28Z
    Status:                True
    Type:                  RecommendationProvided
  Last Update Time:        2018-05-23T05:26:29Z
  Recommendation:
    Container Recommendations:
      Max Recommended:
        Cpu:     1135508m
        Memory:  460504404800
      Min Recommended:
        Cpu:     132m
        Memory:  53677237
      Name:      hamster
      Target:
        Cpu:     788m
        Memory:  319572800
Events:          <none>
```

アプリケーションのPodをみると作り変えられていて以下の設定が追加されていました。

```
      resources:
        limits:
          memory: "424430400"
        requests:
          cpu: 788m
          memory: "319572800"

```

そしてしばらくすると2つ立ち上がっていたPodの片方がPendingになりました。

```
$ kubectl get pod                                
NAME                      READY     STATUS    RESTARTS   AGE
hamster-b445b55f7-9wd76   1/1       Running   0          8m
hamster-b445b55f7-j2874   0/1       Pending   0          7m
```

理由を見てみると `0/1 nodes are available: 1 Insufficient cpu, 1 Insufficient memory.` とのことで要求するリソースが増えたためminikubeのVMのリソースが足りないようです。実際にこれが本番で発生するとサービスダウンに繋がりかねないので注意が必要そうです。

## VerticalPodAutoScalerの仕組み

アーキテクチャの図は[こちら](https://github.com/kubernetes/community/blob/master/contributors/design-proposals/autoscaling/vertical-pod-autoscaler.md#architecture-overview)にあります。この図にもありますが、VerticalPodAutoScalerはつぎの3つのコントローラと一つのCRDリソースによって実現されているようです。

- コントローラ
    - VPA admission controller
    - VPA recommmender
    - VPA updater
- CRD
    - VerticalPodAutoScaler

簡単に動きを説明するとVerticalPodAutoScalerオブジェクトにVPAの設定を記載して、それをもとにrecommenderがmetrics-serverからメトリクスを収集して推奨のリソース設定を算出します。その算出したリソース設定をadmission controllerがPod作成時に設定するという流れのようです。updaterは現在は更新が必要なPodを探し、それを削除(evict)することでReplicaSet controllerにPodを再作成させることによって実現しているようです。ただこの方法は一時的にレプリカ数が減ることや再起動を必要とすること、ReplicaSet Controllerに依存してしまうことから将来的にはリソース設定の部分だけを置き換える(in-place updates)の方式に変更を考えているようです。
推奨のリソースの算出方法はRequestに記載された使用量を超過する時間が一定割合以下(例えば1%)を保つように算出するようです。

## VerticalPodAutoScalerの開発状況

VerticalPodAutoScalerは現在(2018/5/22)はalpha stateです。https://github.com/kubernetes/features/issues/21 でトラックされていますがbetaの予定などもまだ決まっていないようです。

## 参考

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