審查 Thanos 部署
來自管理 ~20 個單一租戶 Thanos 堆疊的實戰規則(透過 GKE 上的全域查詢器,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、spot、Pending pods)檢查警報規則。
- 以下方指定的輸出格式進行報告。
各元件檢查清單
並發不變量(查詢前端 + 查詢器 + 儲存閘道)。 --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)。一旦並行度單獨達到閘門上限,一個查詢就會使每個堆疊的閘門飽和。將 --query.max-concurrent-select 大幅調高至 4 以上會移除上限:乘數變成原始的選取器數量。一個為限制儲存閘道 RSS(在 OOMKill 後設定)而調整的閘門,是一個記憶體上限,請保持它。此不變量在寬查詢時生效:測量到的查詢平均使用 1.5 個選取器,且閘門飽和來自於歷史分片上多個並發查詢,而非一個寬查詢。全域查詢器透過 StoreAPI 到達區域查詢器,這會繞過其 PromQL 閘門。請同時調整兩個乘數(包括全域前端),並在整個查詢器池上計算 --query.max-concurrent。為每個乘數加上註解,說明其必須遵守的下游上限。
查詢器。
- 部分回應預設為開啟(
--query.partial-response預設值為true):缺少某個標誌並非安全的預設行為。它會丟棄發生錯誤的儲存端,而非緩慢的儲存端:--store.response-timeout預設值為0ms(停用),因此一個緩慢的儲存端會在--query.timeout(預設 2m)時導致整個查詢失敗,而非被丟棄。 - 在規則器或警報系統使用的查詢器上啟用部分回應,可能會默默地遺漏警報,且部分回應永遠不會被快取。僅在人類使用的 Grafana 資料來源上按請求啟用它。
--selector-label絕不能使用模板預設值:一個匯入了 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 分鐘)。請使用專用 PVC,並配備預配置的 IOPS/吞吐量;在 Hyperdisk Balanced 上,容量不等於效能。 - 將
--sync-block-duration設定在預設的 15m 以下,在快取桶後幾乎不產生額外成本(只有iter呼叫依賴於它,全艦隊測量約 1 USD/月):不值得為此進行專門的部署,請在下一次計劃的重啟時修復它。 - 保留惰性讀取器,絕不要使用惰性索引標頭下載(p99 ~224 秒)。在預設的 5 分鐘空閒逾時下,每次空閒後的第一個查詢都會支付完整的重新載入成本;將隱藏標誌
--store.index-header-lazy-reader-idle-timeout提高到一個工作天(10 小時),在一個堆疊上將載入時間減少了 84%,代價是駐留記憶體。隱藏標誌:升級時請重新檢查。
快取。
- 在 Thanos < v0.35.0 上,跨租戶共用快取桶:金鑰衝突,儲存閘道保持
Ready狀態但不提供歷史資料。Redis 的prefix可在任何版本中隔離。單一租戶的預演測試無法捕捉到此問題。 - 索引快取和快取桶共用同一個 Redis:請使用不同的
db(索引db: 1)。 - 不要根據記憶體內快取的大小來調整共用快取的大小(它們衡量的是 Pod 的運行時間)。請根據 TTL 視窗內的工作集大小來調整(P95 的滾動 >= 24 小時子查詢,具有明確的 step),除以測量到的分片副本之間的重疊,絕不使用 Pod 數量。副本載入相同的區塊但快取不同的塊:2 個副本測量值為 1.4,而非 2。
- 快取桶子範圍存活 24 小時(
chunk_subrange_ttl);Redis 的expiration不控制它們。 - 查詢前端快取檔案解析嚴格:沒有
ttl,沒有enabled_items,TTL 是config:下的expiration。未知鍵:元件拒絕啟動。 - 使用 Sentinel 後端的 Redis:需要 Sentinel 服務 +
master_name,否則故障轉移後寫入會發送給舊的主節點。
Receive。
- 保持按需分配並使用專用優先級類別:這是唯一持有尚未上傳資料的元件。其他所有元件都可以使用 spot。
- 根據峰值活躍序列數調整記憶體限制。
head_series_limit僅在共用多租戶 Receive 或使用複製時才有價值:使用一個副本時,429 會封鎖該租戶的每次寫入,而 OOM 只會花費一次重新啟動和一次 WAL 重播。如果沒有可訪問的meta_monitoring_url,它就不起作用。請求限制回傳 413(Prometheus 丟棄資料),序列限制回傳 429(會重試)。 - 當池大小將會變更時,使用 Ketama 雜湊環,而非
hashmod。 - 限制 Go 堆積:使用 Pod 記憶體限制的
GOMEMLIMIT,或使用--enable-auto-gomemlimit(預設關閉,為容器限制的 0.9)。Go 從 cgroup 設定 GOMAXPROCS(自 1.25 起),但從不設定 GOMEMLIMIT。Receive OOM 意味著在它再次提供服務之前需要進行 WAL 重播。
壓縮器。
- 作為 CronJob:
concurrencyPolicy: Forbid是強制的(一個儲存桶上有 2 個壓縮器會損壞區塊)。 thanos_compact_todo_*指標僅在--wait模式下註冊:在 CronJob 模式下,針對它們的警報永遠不會觸發。標準的ThanosCompactIsDown每天有 23 小時會觸發。請用 kube-state-metrics CronJob 規則和儲存閘道的thanos_blocks_meta_synced滯後代理來替換。任何針對壓縮器指標的警報(thanos_compact_group_compactions_failures_total...)在 CronJob 模式下永遠不會觸發:在執行間隔期間沒有抓取目標。- 絕不要在 Thanos 儲存桶上設定儲存桶生命週期規則或歸檔儲存類別:只有壓縮器知道區塊依賴關係;重試循環會使 API 帳單增加 10 倍於節省的成本。
- 保留層級應與儲存閘道提供的內容一致。
橫切關注。
- 一個堆疊的所有元件應位於同一個可用區域:跨區域流量在資料路徑的每個箭頭上都會產生費用。在一個堆疊上使用跨區域分散約束是一個成本錯誤,除非故意為之並說明權衡。
- 儲存就緒警報應基於 StatefulSet 期望就緒副本數與實際就緒副本數進行比較,並設定
for: 30m:up == 0對於一個被驅逐的 Pending Pod 永遠不會觸發,而for: 5m會在正常的 spot 恢復期間觸發警報。
診斷飽和
使用 references/rules.md 中的程序 D1-D10。D1 的核心:in_flight = throughput x duration(利特爾定律),按 Pod 測量;持續時間通常佔主導地位(在實戰中為 x190 vs x4.5)。使用 max_over_time(...[$__interval:1m]) 讀取閘門面板,延遲使用 p999/p9999,艦隊大小使用 count(group by (pod) (up == 1))。
輸出格式
發現的問題按嚴重性排序:資料遺失或無聲的錯誤結果,然後是中斷風險(元件拒絕啟動的配置視為此類),然後是成本,最後是穩健性。每個發現:
- 欄位或標誌,附上行號
- 問題及其具體後果
- 修復方式,作為確切的值或片段
- wiki URL 及錨點,從
references/rules.md複製(規則 ID 為內部使用,請省略)