DUICUO

大規模クラスタにおけるkube-state-metricsの最適化

Prometheus を使用して Kubernetes クラスターを監視する場合、kube-state-metrics (KSM) は必須コンポーネントです。KSM は APIServer を監視し、リソースオブジェクトの状態メトリクスを生成します。個々の Kubernetes コンポーネントの健全性ではなく、デプロイメント、ノード、ポッド、イングレス、ジョブ、サービスなど、さまざまなリソースオブジェクトの健全性に焦点を当てています。各リソースオブジェクトには必要なメトリクスが含まれており、公式ドキュメント (https://github.com/kubernetes/kube-state-metrics/tree/main/docs) で確認できます。

KSMのインストールも非常に簡単です。コードリポジトリには対応するリソースマニフェストファイルが含まれています。ただし、インストールバージョンがKubernetesクラスターのバージョンと一致していることを確認してください。

ここでのテスト クラスターはバージョン 1.25 なので、まずそのブランチに切り替えます。

 $ git clone https://github.com/kubernetes/kube-state-metrics && cd kube-state-metrics
` gitチェックアウトv2.7.0`
$ kubectl apply -f/標準

このメソッドは、デプロイメント メソッドを使用して KSM インスタンスをデプロイします。

 $ kubectl get deploy -n kube -system kube -state -metrics
名前準備完了最新利用可能年齢
kube -状態-メトリクス1 / 1 1 1 2分49秒
$ kubectl get pods -n kube -system -l app.kubernetes.io / name = kube -state - metrics
名前準備完了ステータス再起動年齢
kube -状態-メトリクス- 548546f c89 - zgkx5 1/1実行0 2 m51s

後は、Prometheus に KSM インスタンスを検出させるだけです。もちろん、検出方法は様々です。例えば、アノテーションを追加して自動的に検出したり、KSM 用のジョブを別途作成したりすることも可能です。Prometheus Operator を使用している場合は、ServiceMonitor オブジェクトを作成して KSM のメトリックデータを取得することもできます。

このアプローチは、データ量が限られている小規模なクラスターではうまく機能し、本番環境でKSMの高可用性が維持されている限り、通常のサービスを提供します。しかし、大規模クラスターでは非常に困難になります。例えば、私たちのクラスターは約8,000個のPodで構成されていますが、これはそれほど大規模ではありません。

しかし、単一のKSMインスタンスのみを使用してメトリクスを提供することは非常に困難です。多くの場合、メトリクスインターフェースのデータ量が多すぎるため、メトリクスが利用できない可能性があります。

たとえ時折取得できたとしても、`scrape_interval` ごとにこのメトリックインターフェースにアクセスするため、取得に時間がかかる可能性があります。1つのリクエストが完了する前に、次のリクエストが開始される可能性もあります。この問題を解決するには、KSM側で対処する必要があります。KSMの起動パラメータで、不要なメトリックタグを削除するフィルターを設定できます。

 $ kube -state -metrics -h
kube - state - metrics Kubernetes APIサーバーリッスンしオブジェクトの状態に関するメトリック生成するシンプルサービスです

使用法
kube -状態-メトリクス[フラグ]
kube -状態-メトリック[コマンド]

使用可能なコマンド:
完了kube - state - metrics完了スクリプトを生成します
ヘルプ。コマンドに関するヘルプ
versionバージョン情報を出力します

フラグ:
-- add_dir_header true場合、ログメッセージヘッダーファイルディレクトリ追加ます
-- alsologtostderr はファイルだけでなく標準エラーログを記録します( -logtostderr = true場合は効果がありません)
-- apiserver stringマスターとして使用するapiserverURL
-- config string kube - state - metricsオプション設定ファイルパス
--custom - resource - state - config文字列インラインカスタムリソース状態メトリック設定YAML (実験的)
--custom - resource - state - config - file文字列カスタムリソース状態メトリック設定ファイルへのパス実験
-- custom - resource - state - onlyカスタムリソース状態メトリックのみを提供します(試験的)
-- enable - gzip - encodingクライアントから'Accept-Encoding: gzip'ヘッダー経由要求された場合のGzip応答
-h , --helpヘルプテキストを印刷する
-- host stringメトリック公開するホスト (デフォルト: "::" )
-- kubeconfig文字列kubeconfigファイル絶対パス
-- log_backtrace_at traceLocationログがファイルヒットしたとき: Nスタックトレースを出力します(既定値: 0 )
-- log_dir文字列ない場合このディレクトリログファイルを書き込みます( -logtostderr = trueの場合は効果ありません)
-- log_file文字列ない場合はこのログファイルを使用します( -logtostderr = trueの場合は効果ありません)
-- log_file_max_size uintログファイルの最大サイズ定義ます -logtostderr = true場合無効です)。単位メガバイトです0場合最大ファイルサイズ無制限ですデフォルトは1800
-- logtostderrファイルはなく標準エラーログを記録します(デフォルトはtrue )
--metric - allowlist string公開するメトリックカンマ区切りリストこのリストは正確メトリックまたは正規表現パターン構成されますホワイトリストブラックリストは相互排他的です
-- metric - annotations - allowlist stringリソース' labels 'メトリック使用されるKubernetesアノテーションキーコンマ区切りリストデフォルトは、メトリックには名前と名前空間のみが含まれます。追加のアノテーションを含めるには、複数形のリソース名と、それらに許可する Kubernetes アノテーション キーのリストを指定します (例: ' = namespaces = [ kubernetes . io / team ,...], pods = [ kubernetes . io / team ],...) '。代わりに、リソースごとに 1 つの ' * ' を指定してアノテーションを許可することもできますが、パフォーマンスに重大な影響が生じます (例: ' = pods = [ * ] ')。
--metric - denylist string有効ないメトリックコンマ区切りリストこのリストは、メトリックまたは正規表現パターン構成されますホワイトリストブラックリスト相互に排他的です
-- metric - labels - allowlist文字列 リソースのlabelsメトリックで使用される追加Kubernetesラベルキーコンマ区切りリストデフォルトは、メトリックには名前と名前空間ラベルのみが含まれます。追加するには、複数形のリソース名とそれらに許可する Kubernetes ラベルキーのリストを提供します (例: ' = namespaces = [ k8s - label - 1k8s - label - n 、...]、 pods = [ app labels ]、...) '。代わりに、リソースごとに 1 つの ' * ' を指定して任意のラベルを許可できますが、パフォーマンスに重大な影響があります (例: ' = pods = [ * ] ')。また、アスタリスク (*) をキーとして提供することもできます。これはすべてのリソースに解決されます。つまり、 ' -- resources = deploymentspods ' と仮定すると、 ' =*= [ * ] ' は ' = deployments = [ * ]、 pods = [ * ] 'に解決されます
-- metric - opt - in - list stringカンマ区切りのオプトインデフォルトでは有効なっいないメトリックリストこれメトリック許可リスト拒否リスト追加れます
-- namespaces string有効にする名前空間コンマ区切りリストデフォルト" "
-- namespaces - denylist string有効化ない名前空間コンマ区切りリスト。namespacesnamespaces - denylist両方設定されている場合はnamespaces - denylist除外されている名前空間のみが使用されます
-- node string kube - state - metricsポッド含むノード名前おそらく downward API経由で渡されるでしょこれはデーモンセットのシャーディング使用されます。spec.nodeName fieldSelectorサポートするリソースポッドメトリクスのみ利用可能ですこれ実験的なものです
-- one_output trueの場合ログをネイティブ重大度レベルのみ書き込みます(それより低い重大度レベル書き込みません - logtostderr = trueの場合は効果がありません)
--pod string kube - state - metricsコンテナ含むポッド名前設定する場合-- pod--pod - namespace両方設定されている必要あります多くの場合これは下位API経由で渡されるはずですこれは自動検出シャーディング使用されます設定されている場合静的に設定されたシャーディングより優先されますこれ実験的な機能あり予告なく削除される可能性があります
--pod - namespace string --pod指定されたポッドネームスペース設定する場合--pod--pod - namespace両方設定されていることが想定されます多くの場合これは下位API経由渡されるはずですこれは自動検出シャーディング使用されます設定されている場合静的に設定されたシャーディングよりも優先されますこれ実験的な機能あり予告なく削除される可能があります
-- port intメトリック公開するポート (デフォルト8080 )
-- resources string有効にするリソースコンマ区切りリストデフォルト certificatesigningrequests ,configmaps,cronjobs,daemonsets,deployments,endpoints,horizo​​ntalpodautoscalers , ingresses,jobs,leases,limitranges,mutatingwebhookconfigurations,namespaces,networkpolicies,nodes,persistentvolumeclaims,persistentvolumes,poddisruptionbudgets,pods,replicasets,replicationcontrollers,resourcequotas,secrets,services,statefulsets,storageclasses,validatingwebhookconfigurations,volumeattachments」です。
-- shard int32シャード総数うちインスタンスシャード名目値インデックスはゼロ)です。(デフォルトは0
-- skip_headers trueの場合ログメッセージ内のヘッダープレフィックス回避します
-- skip_log_headers trueの場合ログファイルを開くときにヘッダーを回避します( -logtostderr = trueの場合は効果ありません)
--stderrthreshold重大度がこのしきい値以上ログファイルstderr書き込むときstderr送信れます( -logtostderr = trueまたは-alsologtostderr = falseの場合は効果がありません) (デフォルト2 )
-- telemetry - host string kube - state - metricsの自己メトリクス公開するホスト (デフォルト: "::" )
-- telemetry - port int kube - state - metrics の自己メトリクス公開するポート (デフォルト8081 )
-- tls - config文字列TLS設定ファイルパス
-- total - shards intシャード総数シャードの総数1設定されている場合、シャーディングは無効になります。デフォルト: 1
--use - apiserver - cache etcdクォーラム読み取り代わりにapiserverからキャッシュされたリソース使用してListWatchリクエストresourceVersinotallow = 0を設定します
- v , -- vログレベル詳細度レベル番号
-- vmodule moduleSpecカンマ区切りパターンリスト= Nファイルフィルタリングさログ設定

コマンドの詳細について kube-state-metrics [command] --help」使用してください

`--metric-allowlist` または `--metric-denylist` パラメータを使用してメトリクスをフィルタリングできます。しかし、不要なメトリクスやタグをフィルタリングした後でも、メトリクスAPIデータが依然として非常に大きい場合はどうすればよいでしょうか?

実際、どれだけフィルタリングを適用しても、単一のリクエストでメトリクスインターフェースに到達するデータ量は常に膨大になることが想像できます。この時点で、メトリクスデータを分割するしか解決策はないのではないでしょうか?複数のKSMインスタンスをデプロイし、それぞれがインターフェースデータの一部を提供することで、負荷を軽減できます。これは一般的に水平シャーディングと呼ばれています。水平シャーディングのために、kube-state-metricsには既にいくつかの自動シャーディング機能が実装されており、以下のフラグを使用して設定します。

  • --shard (0 から始まる)
  • --合計シャード数

シャーディングは、Kubernetes オブジェクトの UID を MD5 ハッシュ化し、シャードの総数を法として計算することで実現されます。各シャードは、対応する kube-state-metrics インスタンスがオブジェクトを処理するかどうかを決定します。ただし、シャードを持つインスタンスも含め、すべての kube-state-metrics インスタンスは、割り当てられた部分だけでなく、すべてのオブジェクトのネットワークトラフィックとリソース消費を処理することに注意することが重要です。これを最適化するには、Kubernetes API がシャード化されたリスト/ウォッチ機能をサポートする必要があります。理想的には、各シャードのメモリ消費量は、シャード化されていない設定よりも 1/n 少なくなります。通常、kube-state-metrics がメトリクスを Prometheus に迅速に返すことができるように、メモリとレイテンシの最適化が必要です。kube-state-metrics と kube-apiserver 間のレイテンシを短縮する 1 つの方法は、KSM を `--use-apiserver-cache` フラグ付きで実行することです。このオプションはレイテンシを短縮するだけでなく、etcd の負荷も軽減するため、このパラメータを有効にすることをお勧めします。

シャーディングを使用する場合は、シャーディング設定が期待どおりであることを確認するために、シャーディング関連のメトリクスを監視することをお勧めします。アラートには、次の2つのアラートルールを使用できます。

 -アラート: KubeStateMetricsShardingMismatch
注釈:
説明: kube - state - metricsポッドが異なる--total - shards構成実行されているため一部のKubernetesオブジェクトが複数回公開れるまったく公開されない可能性があります
概要: kube - state - metricsシャーディング正しく構成されていません
: |
stdvar ( kube_state_metrics_total_shards {ジョブ= "kube-state-metrics" }) != 0
15メートル
ラベル:
重大度:重大
-アラート: KubeStateMetricsShardsMissing
注釈:
説明: kube - state - metricsシャード欠落しており一部のKubernetesオブジェクト公開されいません
概要: kube - state - metricsシャード見つかりません
: |
2 ^max ( kube_state_metrics_total_shards {ジョブ= "kube-state-metrics" }) - 1
-
合計( 2 ^ max by ( shard_ordinal ) ( kube_state_metrics_shard_ordinal {ジョブ= "kube-state-metrics" }) )
!= 0
15メートル
ラベル:
重大度:重大

シャードを手動で設定するとエラーが発生する可能性があるため、KSM は自動シャーディング機能も提供しています。StatefulSet を使用することで、KSM の複数のレプリカをデプロイできます。自動シャーディングにより、StatefulSet 内にデプロイされた各シャードはインスタンスの場所を検出できるため、シャードの自動設定に非常に役立ちます。したがって、自動シャーディングを有効にするには、StatefulSet を介して kube-state-metrics を実行し、`--pod` および `--pod-namespace` フラグを使用してポッド名と名前空間を kube-state-metrics プロセスに渡す必要があります。以下に例を示します。

 apiバージョン:アプリ/ v1
種類:ステートフルセット
メタデータ
名前: kube -状態-メトリック
名前空間: kube -システム
仕様:
レプリカ10
セレクター:
マッチラベル
app.kubernetes.io / name : kube - state - metrics
サービス名: kube -状態-メトリック
テンプレート
メタデータ
ラベル:
app.kubernetes.io/component :エクスポーター
app.kubernetes.io / name : kube - state - metrics
app.kubernetes.io /バージョン: 2.8.0
仕様:
automountServiceAccountToken : true
コンテナ
-引数:
--pod = $ (ポッド名)
- -- pod -名前空間= $ ( POD_NAMESPACE )
環境:
-名前: POD_NAME
値の開始値:
フィールド参照:
フィールドパス:メタデータ名
-名前: POD_NAMESPACE
値の開始値:
フィールド参照:
fieldPath :メタデータ.名前空間
イメージ: registry.k8s.io/kube-state-metrics/kube-state-metrics : v2.8.0
# ……

このシャードのデプロイ方法は、シャードごとにデプロイメントを用意するのではなく、単一のKubernetesリソース(この場合は単一のStatefulSet)を通じてKSMシャードを管理したい場合に便利です。この利点は、多数のシャードをデプロイする場合に特に顕著です。

もちろん、自動シャーディングによるデプロイメント方法にも欠点はあります。主な欠点は、StatefulSetがサポートするローリングアップグレード戦略です。StatefulSetで管理する場合、ポッドは1つずつ置き換えられます。ポッドが終了すると、再作成されます。このアップグレード速度は遅く、各シャードで短時間のダウンタイムが発生する可能性があります。アップグレード中にPrometheusがクロールされると、kube-state-metricsによってエクスポートされた一部のメトリクスが失われる可能性があります。

自動シャーディング機能の例のリストは、examples/autosharding ディレクトリにあり、次のコマンドを使用して直接デプロイできます。

 $ kubectl apply -k/自動シャーディング

上記のコマンドは、StatefulSet を使用して 2 つの KSM インスタンスをデプロイします。

 $ kubectl get pods -n kube -system -l app.kubernetes.io / name = kube -state - metrics
名前準備完了ステータス再起動年齢
kube -状態-メトリクス- 0 1 / 1実行中0 70 m
kube -状態-メトリクス- 1 1 / 1実行中0 65

任意の Pod のログを表示できます。

 $ kubectlログ- f kube -状態-メトリクス- 1 - nkube -システム
I0216 05 : 53 : 23.151163 1 wrapper.go : 78 ] kube - state - metricsを起動しています
I0216 05 : 53 : 23.154495 1 server . go : 125 ] "デフォルトのリソースを使用しました"
I0216 05 : 53 : 23.154923 1種類. go : 184 ] 「すべての名前空間を使用しています」
I0216 05 : 53 : 23.155556 1 server . go : 166 ] "Metric allow-denylisting" allowDenyStatus = "ブラックリストにあった以下のリストを除外します: "
W0216 05 : 53 : 23.155792 1 client_config.go : 617 ] --kubeconfig--master指定されていませ。inClusterConfig使用していますこれは動作しない可能性があります
I0216 05 : 53 : 23.178553 1 server . go : 311 ] "サーバーとの通信をテストしました"
I0216 05 : 53 : 23.241024 1 server . go : 316 ] "Kubernetesクラスターバージョンで実行" major = "1" minor = "25" gitVersinotallow = "v1.25.3" gitTreeState = "clean" gitCommit = "434bfd82814af038ad94d62ebe59b133fcb50506" platform = "linux/arm64"
I0216 05 : 53 : 23.241169 1 server . go : 317 ] "サーバーとの通信に成功しました"
I0216 05 : 53 : 23.245132 1 server . go : 263 ] "メトリクスサーバーを起動しました" metricsServerAddress = "[::]:8080"
I0216 05 : 53 : 23.246148 1 metrics_handler . go : 103 ] "ポッドで自動シャーディングが有効になりました" pod = "kube-system/kube-state-metrics-1"
I0216 05 : 53 : 23.246233 1 metrics_handler . go : 104 ] "シャーディング設定の自動検出"
I0216 05 : 53 : 23.246267 1 server.go : 252 ] "kube-state-metricsセルフメトリクスサーバーを起動しました" telemetryAddress = " [::]:8081"
I0216 05 : 53 : 23.253477 1サーバー. go : 69 ] levelinfomsgListening onaddress [::]: 8081
I0216 05 : 53 : 23.253477 1サーバー. go : 69 ] levelinfomsgListening onaddress [::]: 8080
I0216 05 : 53 : 23.253944 1サーバー. go : 69 ] levelinfomsgTLS無効です. http2falseaddress [::]: 8080
I0216 05 : 53 : 23.254534 1サーバー. go : 69 ] levelinfomsgTLS無効です. http2falseaddress [::]: 8081
I0216 05 : 53 : 23.297524 1 metrics_handler . go : 80 ] "このインスタンスのシャーディングを、合計シャード数のうちのシャードインデックス(ゼロインデックス)に設定しています" shard = 1 totalShards = 2
I0216 05 : 53 : 23.411710 1 builder.go : 257 ] 「アクティブリソース」 activeStoreNames = 「証明書署名リクエスト、構成マップ、cronジョブ、デーモンセット、デプロイメント、エンドポイント、水平ポッドオートスケーラー、イングレス、ジョブ、リース、制限範囲、変更ウェブフック構成、名前空間、ネットワークポリシー、ノード、永続ボリュームクレーム、永続ボリューム、ポッド中断予算、ポッド、レプリカセット、レプリケーションコントローラー、リソースクォータ、シークレット、サービス、ステートフルセット、ストレージクラス、検証ウェブフック構成、ボリュームアタッチメント」

「このインスタンスのシャーディングを、合計シャード数のうちシャードインデックス(ゼロインデックス)に設定しています」shard=1 totalShards=2 のようなログメッセージが表示されており、自動シャーディングが成功したことを示しています。シャーディングされたメトリックデータのサイズを取得し、シャーディング前のサイズと比較することができます。シャーディング後、メトリックデータが大幅に減少していることがわかります。単一インスタンスのメトリックデータが依然として大きすぎる場合は、StatefulSetのレプリカ数を増やすことができます。

さらに、`--node` と `--resource` を追加するだけで、各ノードのポッドメトリクスを個別にシャーディングすることもできます。この場合、以下のように DaemonSet を直接使用して KSM インスタンスを作成できます。

 apiバージョン:アプリ/ v1
種類: DaemonSet
仕様:
テンプレート
仕様:
コンテナ
-イメージ: registry.k8s.io / kube - state - metrics / kube - state - metrics : IMAGE_TAG
名前: kube -状態-メトリック
引数:
--resource =ポッド
--ノード= $ (ノード名)
環境:
-名前: NODE_NAME
値の開始値:
フィールド参照:
apiバージョン: v1
フィールドパス: spec.nodeName

その他のメトリクスについては、`--resource` を使用して個別にデプロイメントを指定するか、シャーディングを引き続き使用できます。まとめると、大規模クラスタで kube-state-metrics を使用するには、大幅な最適化が必要です。

  • 不要なメトリックとラベルを除外します。
  • シャーディングによるKSMインスタンスのストレス軽減
  • DaemonSet メソッドを使用して、個々のポッドのメトリックをデプロイできます。

もちろん、「自社のビジネスメトリクスも非常に大きい場合はどうするのか?」という疑問も生じるでしょう。その場合、ビジネス側で対応する必要があります。まず、そのような大きなメトリクスデータが正常かどうかを判断する必要があります。もしそれが要件であれば、シャーディングをサポートする方法を見つける必要があります。