Thanos デプロイメントのレビュー
GKE上のグローバルクエリアの背後で約20のシングルテナントThanosスタック(Thanos 0.42、キャッシュ0.35+)を運用して得たフィールドルール。数値と理由を含む信頼できる情報源:
- アーキテクチャ、パフォーマンス、FinOps: https://wiki.jdelgado.fr/monitoring/lgtm/thanos/
- キャッシュ: https://wiki.jdelgado.fr/monitoring/lgtm/thanos-cache/
- 読み取り並行性: https://wiki.jdelgado.fr/monitoring/lgtm/thanos-query-concurrency/
完全なルールセット(R1-R102、フラグ、デフォルト値、メトリクス、アンカー)は references/rules.md にあります。変更が影響する各コンポーネントのセクションを結論を出す前に読んでください。
レビュー手順
- Thanosのバージョン(イメージタグ)を確認します。フラグとデフォルト値は変更されます:すべてのフラグをバージョンに対して確認します(ルールR92)。
- ラッパーチャート(bitnami
thanos、ローカルの umbrella chart、手書きのextraFlags/args)を使用する場合、レンダリングされたマニフェスト(スタックごとにhelm template)でフラグを読み取ります。valuesファイルではありません。レイヤーが互いに上書きし合い、一部のフラグはテンプレートにのみ存在します。 - デプロイメント固有の名前(ラベルがプルーニングされるスタック、テナントラベル、クラスタまたはデータセンターラベル)は設定自体から取得します。差分に表示されない場合は、名前を推測せずにGitOpsリポジトリを尋ねてください。
- 存在する各コンポーネントについて、以下のチェックリストを実行し、その後
references/rules.mdの該当セクションを参照します。 - 4つのフラグのいずれかが設定されているか、デフォルトのままになっている場合は、実際の値を使用して並行性不変条件を計算します。
- アラートルールを、コンポーネントが実際にどのように実行されるか(CronJob、スポット、Pendingポッド)に対して確認します。
- 以下の出力形式で報告します。
コンポーネント別チェックリスト
並行性不変条件(クエリフロントエンド + クエリア + ストアゲートウェイ)。 --query-range.max-query-parallelism(デフォルト14) x min(selectors, --query.max-concurrent-select)(デフォルト4)は、min(--store.grpc.series-max-concurrency, --query.max-concurrent)(両方ともデフォルト20)未満に保たれなければなりません。デフォルトですでにこれを破っています(56 vs 20)。並列性だけがゲートキャップに達すると、1つのクエリですべてのスタックのゲートが飽和します。--query.max-concurrent-select を4より大幅に上げるとキャップがなくなります:乗数が生のセレクタ数になります。OOMキル後に設定されたストアゲートウェイRSSをバインドするようにサイジングされたゲートはメモリ境界であり、それを維持します。この不変条件は幅広いクエリにバインドします:測定されたクエリは平均1.5セレクタであり、ゲート飽和は1つの幅広いクエリではなく、履歴シャードに対する複数の同時クエリから発生しました。グローバルクエリアはStoreAPIを介して地域クエリアに到達しますが、これはそれらのPromQLゲートをバイパスします。両方の乗数を一緒に修正し、グローバルフロントエンドも含め、--query.max-concurrent をクエリアプール全体で計算し、各乗数に、それが尊重しなければならないダウンストリームのキャップをコメントします。
クエリア。
- 部分応答はデフォルトでオンです(
--query.partial-responseのデフォルトはtrue):フラグがないことは安全なデフォルトではありません。エラーを起こしたストアをドロップしますが、遅いストアはドロップしません:--store.response-timeoutのデフォルトは0ms(無効)であるため、1つの遅いストアは、ドロップされる代わりに--query.timeout(デフォルト2m)でクエリ全体を失敗させます。 - ルーラーやアラートで使用されるクエリアで部分応答を有効にすると、アラートが黙って失われる可能性があり、部分応答は決してキャッシュされません。人間向けのGrafanaデータソースでのみ、リクエストごとに有効にしてください。
--selector-labelはテンプレートのデフォルトにしてはいけません:ラベルの2つの値を取り込むスタックは、2番目の値に対してEMPTYを返します(データの無音損失)。スタックごとにオプトインし、クエリが実際に運ぶ単一値のラベルを設定します。設定されているすべてのスタックを、そのスタックが実際に取り込むものと照らし合わせて確認してください。--grpc-compressionはv0.43.0以降で非推奨です。後継のclient_config.compressionは--endpoint.sd-config内のエンドポイントごとに設定しますが、そのエンドポイントも独自のtls_config(例:enabled: false)を設定しない限り無視されます:それ以外の場合はグローバルフラグが優先されます。
ストアゲートウェイ。
- 飽和を治すために
--store.grpc.series-max-concurrencyを決して上げないでください:ゲートはキューイングを行い、拒否はしません。飽和はタイムアウトとして現れ、ほとんどの分はキャップをはるかに下回っており、それを上げるとスパイク中の唯一の境界が取り除かれます。クエリフロントエンドのスローログでクライアントを見つけてください。 --store.limits.request-series=2000000はコピーされた値で、決して発動しません(測定された最大呼び出し:200000シリーズ)。最初にthanos_bucket_store_series_data_touched_bucket{data_type="series"}を測定してください。thanos_bucket_store_queries_dropped_totalが0であっても何も証明しません:デフォルトで0(オフ)のリミッターのみをカウントします。- タイムシャードは重複しなければなりません;突き合わせた
--min-time/--max-time境界は、レプリカが欠落している場合にブラインドウィンドウを保証します。--min-timeは最も古い境界です。正の--max-time(例:3h)は上限ではありません:シャードはReceiveの上に、現在までを提供します。 - ノードのブートディスクに
emptyDirを配置しないでください:インデックスヘッダーのダウンロードがそれを飽和させます(復旧に30分)。プロビジョニングされたIOPS/スループットを持つ専用PVCを使用してください;Hyperdisk Balancedでは容量はパフォーマンスを購入しません。 - キャッシングバケットの背後では、デフォルトの15mより低い
--sync-block-durationはほとんどコストがかかりません(それに依存するのはiter呼び出しのみ、測定されたフリート全体で約1 USD/月):専用のロールアウトには価値がなく、次の計画された再起動時に修正します。 - Lazyリーダーは維持し、lazyインデックスヘッダーダウンロードは決して行わないでください(p99 ~224秒)。デフォルトの5mアイドルタイムアウトでは、各ギャップ後の最初のクエリは完全なリロードを支払います;隠しフラグ
--store.index-header-lazy-reader-idle-timeoutを稼働日(10h)に上げると、1つのスタックでロード時間が84%削減されましたが、常駐メモリが犠牲になります。隠しフラグ:アップグレード時に再確認してください。
キャッシュ。
- Thanos < v0.35.0 でのテナント間の共有キャッシングバケット:キー衝突、ストアゲートウェイは
Readyのままで履歴データを提供しません。Redisprefixは任意のバージョンで分離します。シングルテナントのカナリアではそれを捕捉できません。 - 同じRedis上のインデックスキャッシュとキャッシングバケット:別の
db(インデックスdb: 1)。 - インメモリキャッシュサイズから共有キャッシュをサイジングしないでください(ポッドのアップタイムを測定します)。TTLウィンドウ(明示的なステップを持つローリング >= 24 hサブクエリのP95)上のワーキングセットに基づいてサイジングし、シャードのレプリカ間の測定された重複で割り、決してポッド数では割らないでください。レプリカは同じブロックをロードしますが、異なるチャンクをキャッシュします:2つのレプリカで測定されたのは1.4、2ではありません。
- キャッシングバケットのサブレンジは24時間存続します(
chunk_subrange_ttl);Redisexpirationはそれらを管理しません。 - クエリフロントエンドキャッシュファイルは厳密に解析されます:
ttlなし、enabled_itemsなし、TTLはconfig:の下のexpirationです。未知のキー:コンポーネントは起動を拒否します。 - Sentinelの背後にあるRedis:Sentinelサービス +
master_name、そうでなければフェイルオーバー後に書き込みが古いマスターに送られます。
Receive。
- 専用の優先クラスを持つオンデマンドのままにします:まだアップロードされていないデータを保持する唯一のコンポーネントです。他のすべてはスポットに配置します。
- メモリ制限をピークアクティブシリーズにサイジングします。
head_series_limitは共有マルチテナントReceiveまたはレプリケーションがある場合にのみ効果を発揮します:1つのレプリカでは、429はテナントのすべての書き込みをブロックし、OOMは再起動とWALリプレイのみをコストとします。到達可能なmeta_monitoring_urlがなければ無効です。リクエスト制限は413を返し(Prometheusはデータをドロップします)、シリーズ制限は429を返します(リトライされます)。 - プールサイズが変更される場合は、
hashmodではなくKetamaハッシュリング。 - Goヒープをバインドします:ポッドメモリ制限からの
GOMEMLIMIT、または--enable-auto-gomemlimit(デフォルトではオフ、コンテナ制限の0.9)。Goは1.25以降、cgroupからGOMAXPROCSを設定しますが、GOMEMLIMITは決して設定しません。ReceiveのOOMは、再び提供する前のWALリプレイを意味します。
コンパクター。
- CronJobとして:
concurrencyPolicy: Forbidは必須です(1つのバケット上の2つのコンパクターはブロックを破損します)。 thanos_compact_todo_*ゲージは--waitでのみ登録されます:CronJobモードでは、それらのアラートは決して発報しません。標準のThanosCompactIsDownは23時間/24時間発報します。kube-state-metricsのCronJobルールとストアゲートウェイのthanos_blocks_meta_syncedバックログプロキシに置き換えてください。コンパクターメトリクス(thanos_compact_group_compactions_failures_total...)のアラートは、CronJobモードでは決して発報しません:実行間にスクレイプターゲットがありません。- Thanosバケットにバケットライフサイクルルールやアーカイブストレージクラスを決して設定しないでください:コンパクターのみがブロックの依存関係を知っています;リトライループがAPIコストを節約額の10倍に増やしました。
- 保持階層は、ストアゲートウェイが提供するものに合わせます。
横断的。
- スタックのすべてのコンポーネントを1つのゾーンに配置します:ゾーン間トラフィックはデータパスのすべての矢印で課金されます。スタックにゾーン分散制約を設定することはコストバグであり、そうでないと明記する意図的なトレードオフです。
for: 30mを持つStatefulSetの desired vs ready レプリカに関するストア準備完了アラート:up == 0は退避されたPendingポッドに対して決して発報せず、for: 5mは通常のスポット復旧時にページングします。
飽和の診断
references/rules.md の手順D1-D10を使用します。D1の中核:in_flight = throughput x duration(リトルの法則)、ポッドごとに測定;通常はdurationが支配的です(フィールドではx190 vs x4.5)。max_over_time(...[$__interval:1m]) でゲートパネルを読み取り、p999/p9999のレイテンシ、count(group by (pod) (up == 1)) でフリートサイズを確認します。
出力形式
重大度でランク付けされた所見:データ損失または無音の誤った結果、次にダウンタイムリスク(コンポーネントが起動を拒否する設定はここにカウント)、次にコスト、次に衛生状態。各所見:
- フィールドまたはフラグ、該当行
- 問題とその具体的な結果
- 修正、正確な値またはスニペットとして
references/rules.mdからコピーしたアンカー付きのwiki URL(ルールIDは内部的なもので、含めないでください)