🔭

kubernetesのDeploymentにPrometheusからカスタムメトリクスを登録してみる

2024/04/27に公開

背景と目的

kubernetesのカスタムメトリクスは Pod のオートスケールでよく用いられる。
Pod それぞれのメトリクスなどをベースにスケールさせたい場合については、Prometheus Adapterのドキュメントに整理されている。
一方、Pod のメトリクスに関係なくオートスケールしたい状況(たとえば、事前にスケジュールしたい場合など)も想定される。
外部(External)メトリクスとして登録することもできるが、 Pod を取りまとめる Deployment に関連するメトリクスであるため、Deployment に登録したい。

本記事では Deployment に簡単なカスタムメトリクスを登録する方法について試した設定例をもとに述べる。
実際に使えるかどうかは未検討である。
次のようなポイントがある:

  • Prometheus + Prometheus Adapter + kube-state-metricsを構成する。
  • kube-state-metricsの Deployment に関するメトリクス(kube_deployment_created など)を使う。
  • Prometheus Adapterに Deployment リソースの参照権限を付与する。

試した設定例

helm コマンドがインストール済みで、kubernetesに cluster-admin として接続可能な環境で、次のコマンドを実行する(各ファイルの中身については後述する):

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm upgrade -i prometheus prometheus-community/prometheus -f prometheus-values.yaml --version 25.20.0
helm upgrade -i prometheus-adapter prometheus-community/prometheus-adapter -f prometheus-adapter-values.yaml --version 4.10.0
kubectl create role prometheus-adapter-deployment-reader --verb get,list,watch --resource deployments.apps
kubectl create rolebinding prometheus-adapter-deployment-reader --role prometheus-adapter-deployment-reader --serviceaccount default:prometheus-adapter

Prometheus AdapterのHelmチャートで作成される ClusterRole には Deployment に対する権限がないため、付与する。
今回は ClusterRole でなく Role としているが、他のネームスペースの Deployment にもカスタムメトリクスを紐づける必要があれば、 ClusterRole を作成する。

PrometheusのHelmチャートの設定ファイルでは、次のようにレコーディングルールの設定と、不要なコンポーネントの無効化を行う:

prometheus-values.yaml
serverFiles:
  recording_rules.yml:
    groups:
    - name: schedule
      rules:
      - record: schedule_desired_replicas
        expr: |
          (
            timestamp(vector(0))
              + ignoring(deployment, namespace) group_right
            0 * sum(kube_deployment_created{namespace!="", deployment!="") by (deployment, namespace)
              -
            1714194827
          ) / 100

nodeExporter:
  enabled: false
prometheus-pushgateway:
  enabled: false
alertmanager:
  enabled: false
prometheus-node-exporter:
  enabled: false

レコーディングルールには、ネームスペースや Deployment リソース名が設定されたラベルがないと、後続のPrometheus Adapterでカスタムメトリクスに紐づけられなくなる。
これらのラベルがクエリ対象のメトリクスに付いていない場合は、 ignoringgroup_right で無理やり付与できたが、よりうまいやり方があるかもしれない。
kube_deployment_created の値に0を乗じて消し、 timestamp の値(その時点のUNIX時間)だけを残している。
設定例では、徐々にスケールアウトだけさせる想定で、適当な数に調整している。

Prometheus AdapterのHelmチャートの設定ファイルでは、次のようにPrometheusサーバの接続設定と、レコーディングルールを Deployment に紐づける設定を行う:

prometheus-adapter-values.yaml
prometheus:
  url: http://prometheus-server
  port: 80
rules:
  default: false
  custom:
  - seriesQuery: schedule_desired_replicas{namespace!="", deployment!=""}
    resources:
      overrides:
        namespace: {resource: namespace}
        deployment: {group: apps, resource: deployment}
    name:
      matches: ^(.*)$
    metricsQuery: <<.Series>>

次のように動作確認ができる:

kubectl create deploy --image nginx
kubectl get --raw '/apis/custom.metrics.k8s.io/v1beta1'
kubectl get --raw '/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/deployments/nginx/schedule_desired_replicas'

出力は次のようになる:

{"kind":"APIResourceList","apiVersion":"v1","groupVersion":"custom.metrics.k8s.io/v1beta1","resources":[{"name":"deployments.apps/schedule_desired_replicas","singularName":"","namespaced":true,"kind":"MetricValueList","verbs":["get"]},{"name":"namespaces/schedule_desired_replicas","singularName":"","namespaced":false,"kind":"MetricValueList","verbs":["get"]}]}
{"kind":"MetricValueList","apiVersion":"custom.metrics.k8s.io/v1beta1","metadata":{},"items":[{"describedObject":{"kind":"Deployment","namespace":"default","name":"nginx","apiVersion":"apps/v1"},"metricName":"schedule_desired_replicas","timestamp":"2024-04-27T07:44:07Z","value":"89902m","selector":null}]}

このカスタムメトリクスをもとにした HorizontalPodAutoscaler は次のようになる:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: nginx
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx
  minReplicas: 1
  maxReplicas: 20
  metrics:
  - type: Object
    object:
      describedObject:
        apiVersion: apps/v1
        kind: Deployment
        name: nginx
      metric:
        name: schedule_desired_replicas
      target:
        type: AverageValue
        averageValue: 1

schedule_desired_replicas の値を Pod の数で割った値が1になるように調整される設定ということはつまり、 Pod の数が schedule_desired_replicas の値になるように調整される。

今後の検討事項

今回は事前にスケールアウトの計画を立てたい場合を想定していたが、そこで実用するには次の事項を検討しなければならない:

  • Deployment ごとにスケールアウトするタイミングが異なる場合のメトリクスの設定方法
  • スケールアウトさせた後の管理方法
  • スケールインさせる方法:スケジュールでスケールインさせるだけなら、そういうクエリを書けばよいが、アクセス増にそなえてスケールアウトさせたのであれば、アクセスが収まってからスケールインすべきなので、スケジュールでスケールインさせることは少ないと考えられる。

クエリの設定箇所について

今回はカスタムメトリクスのクエリをPrometheusのレコーディングルールとして設定したが、Prometheus Adapterの設定として書くこともできる。

Helmチャートでデプロイ・更新することを想定すると、Prometheus Adapterは変更時に再起動が必要だが、Prometheusは再起動せずにConfig Reloaderでクエリの更新が可能な構成を取れる。
レコーディングルールを用いる場合は、ルール評価タイミングがずれ、Prometheusメトリクスとして保存される分、ストレージを消費することになる。

Discussion