21
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

CPU/memoryベースのHPAとVPAはなぜ併用できないのか?

Last updated at Posted at 2020-07-31

KubernetesのVPA1のREADMEでは、現在CPU/memoryによるHPAとVPAの併用を禁止している。これを回避するカスタムメトリクスの導入などをしていしたが、HPA/VPAの干渉理由を求められたときにパッと明確な説明ができなかったので書き起こしておく。

Vertical Pod Autoscaler should not be used with the Horizontal Pod Autoscaler (HPA) on CPU or memory at this moment. However, you can use VPA with HPA on custom and external metrics.

HPAはPodのResourceRequestを用いてスケール値をどう算出しているのか?

VPAのWebhookは、PodのResource Request(.spec.template.spec.containers[].resource.requests)をCPUとメモリの消費量に合わせて動的に書き換える。つまりVPAの有無による違いは、このResource Requestが可変だということだ。では、可変なResource RequestはHPAにどのような影響を与えるのだろうか?

HPAのスケール値の算出
desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )]

HPAのスケール値の算出方法は1のように、現在のレプリカ数に実測値と期待値の比を掛けたものだ。 () の部分の実装は指定するメトリクスオプションによって実装は異なるが、PodのResource Requestを利用するのはmetrics.typeが Resource かつtarget.typeが Utilization の場合のみだ。では、実際に()の部分を求めるコードを見てみる2

GetResourceUtilizationRatio
for podName, metric := range metrics {
  request, hasRequest := requests[podName]
  if !hasRequest {
    // we check for missing requests elsewhere, so assuming missing requests == extraneous metrics
    continue
  }

  metricsTotal += metric.Value
  requestsTotal += request
  numEntries++
}

currentUtilization = int32((metricsTotal * 100) / requestsTotal)

return float64(currentUtilization) / float64(targetUtilization), currentUtilization, metricsTotal / int64(numEntries), nil

このコードから、ResourceUtilizationRatioは、実測値の合計とリクエスト値の合計値の比を指定したターゲットで割ったものということが分かった。この値に現在のレプリカ数を掛けたものがスケール値となるが、コントローラが削除/追加するレプリカのResource Requestがこの平均値から外れていると期待した状態にならない。

image.png

具体例を考えてみる。VPAのPodのResource Requestの更新方法もいくつかあるが、Creation方式にするとdesiredMetricvalueがPodによって変わってしまう。それ以外の場合でも、現在の値と次に作成される値が変わるので、予期するリソースを確保できない(or 確保しすぎる)パターンがある。

結論

CPU/memoryのUtilizationによるHPAでは、メトリクス実装値とResource Requestの割合からスケール値を計算してPodの追加や削除を行う。しかし、VPAによってスケール対象のDeployement(をOwnerに持つ)PodのResource Request値が変動すると、外れ値を持ったPodが操作されたときなどに予期したリソース量が確保できない。そのため、現在では併用を禁止しているのだと思う。

つまり、カスタムメトリクスなどを使っていたとしても、CPU/memoryをメトリクスとして同じように計算していると同じ問題を引く。なのでアプリケーションのタイプにもよるが、基本に則ってCPU/memoryでVPAしてRPSやp95latencyでHPAをやっている。

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

  2. https://github.com/kubernetes/kubernetes/blob/cdbd07f64e2f1392fc9b3c68a6c1e06ee22d6449/pkg/controller/podautoscaler/metrics/utilization.go

21
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?