istioのメトリクス(Custom Metrics)を使ってHPA(Horizontal Pod Autoscaler)を設定する
こんにちは、Lapi(@dragoneena12)です。
最近SREインターンをさせていただいている株式会社HERPさんで、istioのメトリクス(custom metrics)をもとにHPAを設定する機会があったのですが、特に日本語だとなかなか情報が限定的だったので記事を書いてみました。できるだけ詳しく書いたので、参考になると幸いです。
HPAで使うメトリクスの種類
HPAは何らかのメトリクス値をもとにPod数を自動的に増減させる機能です。使えるメトリクスには以下の3種類があります(Support for metrics APIs | Horizontal Pod Autoscaler user guide)。
- Resource Metrics
- CPU使用率、メモリ使用率など
- Custom Metrics
- kubernetesクラスタ内のリソースと関連付けられるメトリクス(requests_per_second, 99_percentile_durationなど)
- External Metrics
- kubernetesクラスタ内のリソースとは関係しないメトリクス(外部のロードバランサーのメトリクスなど)
今回はrequests_per_secondをもとにHPAを設定したかったので、Custom Metricsを使うことにしました。
istioで得られるメトリクス
istioを導入するとRequest Countなど様々なメトリクスをサービスごとに収集することができます(Istio Standard Metrics)。今回はこの istio_requests_total
を基にHPAを設定しました。
prometheus-adapter
prometheus-adapterとは
istio_requests_total
メトリクスはprometheusに収集されていますが、HPAでこの値を使うためにはkubernetesのカスタムメトリクスAPI(custom.metrics.k8s.io
)に登録してあげる必要があります。
これをできるのが kubernetes-sigs/prometheus-adapter です。以前までDirectXMan12の個人リポジトリでしたが、最近kubernetes-sigsに移行されました。
インストール・設定方法
インストールはhelmで行えて、values.yaml
でprometheusのどのような値をメトリクスAPIに登録するかを設定できます。詳しくは公式リポジトリ内のMetrics Discovery and Presentation Configuration に書かれていますが、簡単に言うと以下の4つにより構成されています。
- Discovery
-
seriesQuery
として設定する部分 - adapterが登録するメトリクスのラベルを確認するクエリ
-
- Association
-
resources
として設定する部分 - 得られたメトリクスをKubernetesのどのリソースと関連付けるかを設定する
-
- Naming
-
name
として設定する部分 - メトリクスの命名
-
- Querying
-
metricsQuery
として設定する部分 - 実際にメトリクス値を算出するクエリ
-
今回はこれらを以下のように設定しました。
rules:
custom:
- seriesQuery: 'istio_requests_total{reporter="destination", destination_service_namespace=~"ns1|ns2|ns3", destination_service_name=~"svc1|svc2|svc3"}'
resources:
overrides:
destination_service_namespace: {resource: "namespace"}
destination_service_name: {resource: "service"}
name:
as: "requests_per_second"
metricsQuery: 'sum(rate(<<.Series>>{<<.LabelMatchers>>, reporter="destination"}[5m])) by (<<.GroupBy>>)'
ポイントとして、istio_requests_total
はsourceとdestinationの双方から報告されるため、reporter="destination"
と指定しています。また istio_requests_total
はサービスごとに算出されているため、resources
でサービスごとにメトリクス値を関連付けしていきます。
メトリクスが登録されていることを確認する
下記のコマンドでカスタムメトリクスがサービスに関連付けて登録されていることを確認できます。
$ kubectl get --raw '/apis/custom.metrics.k8s.io/v1beta1/namespaces/ns1/services/svc1/requests_per_second' | jq
{
"kind": "MetricValueList",
"apiVersion": "custom.metrics.k8s.io/v1beta1",
"metadata": {
"selfLink": "/apis/custom.metrics.k8s.io/v1beta1/namespaces/ns1/services/svc1/requests_per_second"
},
"items": [
{
"describedObject": {
"kind": "Service",
"namespace": "ns1",
"name": "svc1",
"apiVersion": "/v1"
},
"metricName": "requests_per_second",
"timestamp": "2021-02-03T11:21:30Z",
"value": "34857m",
"selector": null
}
]
}
HPAの設定
Custom MetricsをもとにHPAを設定する
次にHPAのマニフェストファイルを書いていきます。今回は下記のように設定しました。
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: hpa1
namespace: ns1
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: deployment1
minReplicas: 1
maxReplicas: 10
metrics:
- type: Object
object:
describedObject:
apiVersion: v1
kind: Service
name: svc1
metric:
name: requests_per_second
target:
type: AverageValue
averageValue: 5
spec.metrics.typeには以下の4種類が設定できます。
- Resource
- CPU使用率、メモリ使用率などのリソースメトリクス
- Pods
- Podごとに関連付けられたカスタムメトリクス
- Object
- Pod以外と関連付けられたカスタムメトリクス
- External
- kubernetesクラスタ内のリソースとは関係しないメトリクス
したがって今回はObjectを設定することになります。
describedObject
ではカスタムメトリクスが関連付けられたリソースを設定し、metric
でメトリクス名を指定します。
target
ではメトリクス値に対しPod数をどのように増減させるかを指定します。これには Value
と AverageValue
の2つの指定方法があります。
どちらもメトリクス値が設定値より大きければPodを増やし、設定値より一定以上下回ればPodを減らすという点では同じですが、Value
はメトリクスAPIから得られた値と設定値をそのまま比較し、AverageValue
はメトリクスAPIから得られた値を現在のPod数で割った値と設定値を比較する点で異なります。したがってPod数が増えることで減少する値(99_Percentile_durationなど)をメトリクス値として用いる場合は Value
を、Pod数とは関係のない値(serviceに対するrequests_countなど)をメトリクス値として用いる場合は AverageValue
を使うのが適当です。
Object metrics support target types of both Value and AverageValue. With Value, the target is compared directly to the returned metric from the API. With AverageValue, the value returned from the custom metrics API is divided by the number of Pods before being compared to the target.
https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#autoscaling-on-multiple-metrics-and-custom-metrics
ArgoCDを使っている場合の注意点
deploymentなどにおけるreplicasの指定を無視する
HPAによって対象となるdeploymentなどにおける replicas
の設定値が書き換えられますが、ArgoCDによって管理している場合はこれによって元のdeploymentがOutOfSyncになってしまいます。これは下記のようなignoreDifferencesの設定によって回避することができます。
ignoreDifferences:
- group: apps
kind: Deployment
name: deployment1
namespace: ns1
jsonPointers:
- /spec/replicas
HPAリソースが常にOutOfSyncとなってしまう問題
HPA ControllerがHPAのマニフェストを書き換えてしまうため、ArgoCDで常にOutOfSyncとなってしまう問題がこちらのissue で報告されています。リソースメトリクスを用いている場合にはspec.metricsの順番を入れ替える(memoryが先でcpuは後)ことで回避できます。
For Horizontal Pod Autoscaling (HPA) objects, the HPA controller is known to reorder spec.metrics in a specific order. See kubernetes issue #74099. To work around this, you can order spec.metrics in Git in the same order that the controller prefers.
https://argoproj.github.io/argo-cd/user-guide/diffing/#diffing-customization
しかしカスタムメトリクスを用いている場合ではspec.metrics.object.target.valueがHPA Controllerによって差し込まれるため、以下のように何らかの正の値に設定してあげる必要がありました。これについてはあまり情報が見つからなかったです。
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: hpa1
namespace: ns1
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: deployment1
minReplicas: 1
maxReplicas: 10
metrics:
- type: Object
object:
describedObject:
apiVersion: v1
kind: Service
name: svc1
metric:
name: requests_per_second
target:
type: AverageValue
averageValue: 5
value: 1 # HPA Controller によって自動的に差し込まれ、OutOfSyncとなるのを防ぐ設定
HPAの動作確認
以上でHPAが設定できたはずなので、以下のコマンドで動作状況を確認できます。
$ kubectl get hpa.v2beta2.autoscaling hpa1 -n ns1 -o yaml
...
status:
conditions:
- lastTransitionTime: '2021-02-04T06:45:53Z'
message: recommended size matches current size
reason: ReadyForNewScale
status: 'True'
type: AbleToScale
- lastTransitionTime: '2021-02-09T01:00:33Z'
message: >-
the HPA was able to successfully calculate a replica count from external
metric requests_per_second(nil)
reason: ValidMetricFound
status: 'True'
type: ScalingActive
- lastTransitionTime: '2021-02-09T11:58:49Z'
message: the desired replica count is less than the minimum replica count
reason: TooFewReplicas
status: 'True'
type: ScalingLimited
currentMetrics:
- object:
current:
averageValue: 389m
value: '0'
describedObject:
kind: ''
name: ''
metric:
name: requests_per_second
type: Object
currentReplicas: 1
desiredReplicas: 1
おわりに
autoscaling/v2beta2
APIで提供されたカスタムメトリクスによるHPAについて、Istioのistio_requests_total
を使ってHPAを動かす例に沿って解説しました。ほかにもdatadogと連携するなどでき、とても自由度が高く便利なシステムだと思うので、ぜひ使ってみてください。
参考文献
Horizontal Pod Autoscaler Walkthrough
Horizontal Pod Autoscaler
Horizontal Pod Autoscaler with Arbitrary Metrics
Prometheus Adapterを利用したPodのオートスケール
Discussion