深夜2時にPagerDutyが鳴り響く原因のトップ3に、「監視サーバー自体のディスク枯渇」が入っている現場は少なくありません。私が担当していたEKS上の大規模なマイクロサービス環境でも、Pod数が数千規模にスケールするにつれ、PrometheusのPVC(EBS)が圧迫され、Retention(保持期間)を15日まで短縮せざるを得ない状況に追い込まれました。さらに最悪なことに、Availability Zoneの障害で単一構成のPrometheusがダウンし、その間のメトリクスが完全に欠落するという「監視システム」としての信頼性を揺るがす事態も発生しました。本稿では、こうしたSPOF(単一障害点)とストレージ問題を解決するために導入した、Thanosサイドカーパターンによるアーキテクチャ刷新の全容を共有します。
Prometheus HAとスケーラビリティの壁
当時の環境はAWS EKS上で動作しており、Prometheus Operatorを使用していました。秒間のIngestionは約15万サンプル。単純なリソース追加では対応しきれない以下の課題に直面していました。
level=error ts=2024-03-10T14:00:00.000Z caller=main.go:800 err="opening storage failed: lock DB directory: resource temporarily unavailable"これは再起動時にWAL(Write-Ahead Log)の破損やロック競合が発生し、復旧に数時間を要した際のログの一部です。
本来であれば、Prometheus公式ドキュメントにあるように、シンプルさを保つことが推奨されます。しかし、エンタープライズレベルの「オブザーバビリティ」を確保するには、過去1年分のデータ参照や、99.9%の可用性が求められます。ローカルディスクに依存するPrometheus単体では、これらをコスト効率よく実現するのは物理的に不可能でした。
失敗談:単純なFederationとEBS拡張の限界
Thanosを導入する前、私たちは「Federation(階層化)」による解決を試みました。各クラスタのPrometheusから、中央集権的なGlobal PrometheusがデータをPullする構成です。しかし、これはすぐに破綻しました。Global Prometheusが全メトリクスをPullする際に巨大なネットワーク帯域を消費し、さらにスクレイピングのタイムアウトが多発したのです。また、単にEBSボリュームを2TBに拡張する案も検討しましたが、Prometheus再起動時のWALリプレイ(メモリへのデータ読み込み)に数十分かかり、その間のダウンタイムが許容できませんでした。「ローカルストレージに依存しない」かつ「Prometheus自体をステートレスに近づける」アプローチが必要だったのです。
解決策:Thanos Sidecarパターンの実装
最終的に採用したのは、Prometheus Pod内にThanos Sidecarコンテナを同居させ、2時間ごとに生成されるTSDBブロックを即座にS3へアップロードする「Thanosアーキテクチャ」です。これにより、Prometheus本体のRetentionを「6時間」程度まで短く設定しても、長期データはS3に永続化されるため問題なくなります。
以下は、kube-prometheus-stack (Helm) を使用した場合の、SidecarとObject Storage設定の重要な抜粋です。
// objstore.yaml (Kubernetes Secretとして登録)
// バケット名は環境に合わせて変更してください
type: S3
config:
bucket: "my-company-metrics-longterm"
endpoint: "s3.ap-northeast-1.amazonaws.com"
region: "ap-northeast-1"
encrypt_sse: true
---
// values.yaml (Prometheus Operator設定)
prometheus:
prometheusSpec:
# Prometheusのローカル保持期間を短縮(ストレージ節約)
retention: 6h
# Sidecarの有効化
thanos:
version: "v0.32.5" # バージョン固定を推奨
objectStorageConfig:
key: objstore.yaml
name: thanos-objstore-secret
# HA構成のためにレプリカを2に設定
replicas: 2
# 異なるレプリカのデータを重複排除するために必要
externalLabels:
cluster: "production-eks-01"
__replica__: "$(POD_NAME)"
ここで重要なのは externalLabels の設定です。Thanos Queryは、同じ cluster ラベルを持ち、異なる __replica__ ラベルを持つデータセットを「同じデータの複製」とみなし、クエリ時に自動的に重複排除(Deduplication)を行います。これにより、Prometheusのレプリカを2台立てて片方が落ちても、欠損のないグラフを描画できる「Prometheus HA」が成立します。
長期ストレージ参照のためのStore Gateway
S3にアップロードされたデータを参照するには、Sidecarだけでは不十分です。Thanos Store Gateway をデプロイし、Querierがここを経由してS3のデータを検索できるようにする必要があります。
// Store Gatewayのデプロイメント設定例
args:
- store
- --data-dir=/var/thanos/store
- --objstore.config-file=/etc/thanos/objstore.yaml
# インデックスキャッシュを適切に設定しないとOOMKillされる可能性がある
- --index-cache-size=500MB
- --chunk-pool-size=500MB
導入効果とベンチマーク
Thanos導入前後での、ストレージコストとクエリパフォーマンスの比較結果です。特に「長期ストレージ」としてのコストパフォーマンスが劇的に改善しました。
| 指標 | 導入前 (Prometheusのみ) | 導入後 (Thanos Sidecar) |
|---|---|---|
| データ保持期間 | 15日 (EBS容量限界) | 無制限 (S3) |
| 月額ストレージコスト | $400 (GP3 4TB) | $80 (S3 Standard + IA) |
| 可用性 | 単一障害点あり (SPOF) | 冗長構成 (HA) |
| 過去データクエリ速度 | 遅い (ローカルIO負荷高) | 高速 (Store Gateway並列処理) |
コストが約1/5に圧縮されただけでなく、EBSのボリューム拡張作業という「Toil(苦役)」から解放されたことが運用チームにとって最大のメリットでした。また、S3のライフサイクルポリシーを活用し、1年経過したデータをGlacierに移行することで、さらにコストを下げることが可能です。
Thanos Sidecar公式ドキュメントを確認する注意点とエッジケース:Compactorのメモリ管理
この構成は強力ですが、運用上の落とし穴があります。特に注意すべきは Thanos Compactor です。これはS3上のブロックを整理し、ダウンサンプリング(解像度を落として容量を削減)を行うコンポーネントですが、非常に多くのメモリを消費します。
大規模なデータセットに対してCompactorを実行する場合、一時的に数十GBのメモリを消費することがあります。KubernetesのLimit設定を厳しくしすぎるとOOMKillが頻発し、ブロックのコンパクション(結合)が失敗し続けることで、クエリ速度が低下します。Compactorは専用のノードプールで実行するか、十分なリソースを割り当てることを推奨します。
また、ネットワーク転送コスト(NAT Gatewayの処理費用など)も見落としがちです。VPCエンドポイントを活用してS3へのトラフィックを内部ネットワークに閉じ込めることで、AWSの転送コストを抑える工夫が必要です。
結論
PrometheusのHA化と長期ストレージ対応は、システムが成長するにつれて避けては通れない課題です。Thanos Sidecarパターンは、既存のPrometheus構成への影響を最小限に抑えつつ、S3という安価で堅牢なバックエンドを活用できる点で、現時点での最適解と言えます。まだローカルディスクの残容量に怯えているのであれば、まずはSidecarの導入から始めてみてください。オブザーバビリティの質が劇的に向上するはずです。
Post a Comment