Kubernetesのコストはなぜ気づかないうちに膨れ上がるのか
いくつかのマイクロサービスをデプロイし、ステージングクラスターを追加する。そして半年後、気づいたらクラウドの請求額が静かに2倍になっていた。誰もアーキテクチャを意図的に変えていないのに。リソースリクエストを保守的に設定しすぎていて、アイドル状態のネームスペースが気づかれないまま積み重なり、どのワークロードが実際にコストを消費しているのか、チームには見えていなかったのだ。
コストの可観測性は、積極的なDevOpsエンジニアと月次のクラウド請求書に毎回驚かされるエンジニアを分ける、そんなスキルのひとつだ。Grafanaのすべてのパネルでアラートがゼロ件、稼働率100%を維持していても、月$8,000のAWS請求がどこに消えているのか、まったくわかっていないということが普通に起こる。
OpenCostはそのギャップを埋めるツールだ。ベンダーロックインや月$500のSaaSサブスクリプションなしに、ネームスペース単位・ワークロード単位のコストデータを提供するCNCFサンドボックスのオープンソースプロジェクトである。
アプローチ比較:Kubernetesのコスト監視でよく使われる手法
主要なアプローチは3つあり、それぞれに本質的なトレードオフがある。
1. クラウドプロバイダーのネイティブツール
AWS Cost Explorer、GCP Billing Reports、Azure Cost Managementはいずれもクラスター全体の支出を表示する。問題は粒度だ。EC2ノードのコストは報告されるが、それをネームスペース、デプロイメント、ラベルごとに分解することはできない。請求額は見えても、内訳はわからない。
2. 商用FinOpsプラットフォーム
Kubecost(商用版)、CloudHealth、Apptio Cloudabilityといったツールは詳細な配分レポートを提供し、確かによく機能する。ただし落とし穴がある。Kubecost Proは中規模クラスターで月約$500から始まり、CloudHealthは数千ドルに達する。まだ定量化できていない無駄を探している小規模チームには、なかなか導入しにくい金額だ。
3. OpenCost(オープンソース)
OpenCostは2022年にKubecostがオープンソース化し、CNCFに寄贈したプロジェクトだ。クラスター内で動作し、Prometheusのメトリクスをスクレイプして、実際のリソース消費量にクラウドの実際の料金を適用する。クリーンなAPI、軽量なUI、運用は無料。データがインフラの外に出ることはなく、Grafanaとネイティブに統合できる。
OpenCostのメリットとデメリット
メリット
- 完全無料のオープンソース — Apache 2.0ライセンス、使用制限やシート費用なし
- ネームスペース・ワークロードレベルの配分 — デプロイメント、DaemonSet、StatefulSet、ラベル、アノテーション単位でコストを分解
- あらゆるクラウドまたはオンプレで動作 — AWS、GCP、Azure、ベアメタルのカスタム料金設定に対応
- CNCFサンドボックスプロジェクト — 活発なコミュニティ、しっかりメンテナンスされたHelmチャート、充実したドキュメント
- Grafanaダッシュボード付き — Grafana.comで事前構築済みダッシュボードを入手可能
- Prometheusネイティブ — kube-prometheus-stackをすでに運用していれば、統合は約10分で完了
デメリット
- 組み込みの異常アラートなし — コストスパイクに対するPrometheusのアラートルールは自分で記述する必要がある
- クラウド請求の照合には追加設定が必要 — 実際の割引やリザーブドインスタンス料金の照合にはクラウド請求APIへのアクセスが必要
- UIは機能的だが最小限 — 商用のKubecost UIと比べると機能は少なく、OpenCostは基本的なものをカバーするにとどまる
- 履歴保持には永続ストレージを推奨 — なければコスト履歴はPod再起動のたびにリセットされる
推奨構成
ほとんどのチームにとって、この4コンポーネント構成で実際に必要なものの90%をカバーできる。
- OpenCost — コストエンジンとUI
- kube-prometheus-stack — Prometheus + Grafana(未導入の場合)
- OpenCost Grafanaダッシュボード — Grafana.comからインポート
- PrometheusのPersistent Volume — Pod再起動後もコスト履歴を保持するため
クラスターにすでにPrometheusが動いている場合は、ステップ2から始めてください。
導入手順
ステップ1:kube-prometheus-stackのインストール(インストール済みの場合はスキップ)
OpenCostはメトリクスにPrometheusを必要とする。コミュニティのHelmチャートが最も早い導入方法だ。
# Helmリポジトリを追加
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
# monitoringネームスペースにインストール
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
--set prometheus.prometheusSpec.retention=15d \
--set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.resources.requests.storage=20Gi
retention=15dフラグは15日分のメトリクスを保持する。週単位のコストトレンドを把握するには十分な期間だ。ストレージの設定はクラスターの規模に応じて調整してほしい。10〜30ノード構成であれば、20Giが妥当なベースラインだ。
ステップ2:OpenCostのインストール
OpenCostにはコストエンジンと軽量UIを同時にデプロイするHelmチャートが付属している。
# OpenCost Helmリポジトリを追加
helm repo add opencost https://opencost.github.io/opencost-helm-chart
helm repo update
# valuesファイルを作成
cat > opencost-values.yaml << 'EOF'
opencost:
exporter:
cloudProviderApiKey: "" # 割引なしのオンデマンド料金を使用する場合は空のまま
ui:
enabled: true
prometheus:
internal:
enabled: false # インストール済みの外部Prometheusを使用
external:
enabled: true
url: "http://kube-prometheus-stack-prometheus.monitoring.svc.cluster.local:9090"
EOF
# インストール
helm install opencost opencost/opencost \
--namespace opencost \
--create-namespace \
-f opencost-values.yaml
PrometheusのURL形式に注意してほしい。KubernetesのサービスDNSを使っている。PrometheusのサービスドメインやネームスペースがデフォルトのHelmチャートと異なる場合は、インストール前にURLを修正すること。
ステップ3:デプロイの確認
# Podが起動していることを確認
kubectl get pods -n opencost
# 期待される出力:
# NAME READY STATUS RESTARTS AGE
# opencost-xxxxxxxxx-xxxxx 2/2 Running 0 2m
# Prometheusへの接続に問題がないかOpenCostのログを確認
kubectl logs -n opencost deployment/opencost -c opencost
ログ内でPrometheus connection successfulを確認しよう。接続拒否エラーが出ている場合は、ほぼ必ずvaluesファイルのPrometheusのURLを修正する必要がある。
ステップ4:OpenCost UIへのアクセス
# UIをローカルマシンにポートフォワード
kubectl port-forward -n opencost service/opencost-ui 9090:9090
http://localhost:9090を開こう。数分後にはネームスペース別のコストデータが表示される。OpenCostはPrometheusから過去のデータをバックフィルするため、リテンション期間全体にわたる15日分のコスト見積もりをすぐに確認できる。
ステップ5:OpenCost APIでコストをクエリする
OpenCostが真に力を発揮するのはHTTP APIだ。コストレポートのスクリプト化、Slackへのデータ送信、社内ダッシュボードへの連携など、自動化の用途は広い。
# APIをポートフォワード
kubectl port-forward -n opencost service/opencost 9003:9003
# 過去7日間のコストをネームスペース別に集計してクエリ
curl -s "http://localhost:9003/allocation/compute?window=7d&aggregate=namespace" | \
python3 -m json.tool | head -80
# デプロイメントラベル別にコストを集計してクエリ
curl -s "http://localhost:9003/allocation/compute?window=24h&aggregate=label:app" | \
python3 -m json.tool
aggregateパラメータが粒度の細かい分析のカギだ。namespace、controller、pod、node、またはチームが設定した任意のカスタムラベルでグループ化できる。ラベルベースのグループ化により、支出を個々のプロダクトチームに直接紐付けることが可能になる。
ステップ6:Grafanaダッシュボードのインポート
OpenCostは公式のGrafanaダッシュボードを公開している。まずGrafanaをポートフォワードする。
# Grafanaをポートフォワード
kubectl port-forward -n monitoring service/kube-prometheus-stack-grafana 3000:80
http://localhost:3000を開き(デフォルトの認証情報:admin/prom-operator)、Dashboards → Importに移動してダッシュボードID 15714を入力する。プロンプトが表示されたらデータソースにPrometheusインスタンスを設定しよう。
ステップ7:クラウドプロバイダーの料金設定(省略可能だが推奨)
デフォルトでOpenCostはクラウドプロバイダーAPIのオンデマンド料金を使用する。AWSクラスターの場合、請求設定を投入することでより正確な数値を得られる。
cat > aws-config.json << 'EOF'
{
"provider": "AWS",
"description": "AWSプロバイダー設定",
"AWS_ACCESS_KEY_ID": "your-access-key",
"AWS_SECRET_ACCESS_KEY": "your-secret-key",
"awsSpotDataBucket": "",
"awsSpotDataRegion": "ap-northeast-1",
"projectID": "123456789"
}
EOF
# 設定からシークレットを作成
kubectl create secret generic opencost-aws-config \
--from-file=aws-config.json=aws-config.json \
-n opencost
opencost-values.yamlを更新してこのシークレットを参照するように設定し、helm upgradeで適用する。
まず確認すべき指標
ダッシュボードが読み込まれたら、まず確認する価値がある数値が3つある。
- ネームスペース別のアイドルコスト — 4 CPUをリクエストしているのに実際の消費が400mのネームスペースは、コンピュート予算の90%を無駄にしている。これが最も素早くライトサイジングできる機会だ
- コントローラータイプ別コスト — DaemonSetはすべてのノードで動作するため、20ノードのクラスターで500m CPUをリクエストするDaemonSetはクラスター全体で10 CPUコアを消費する。注意深く監査しよう
- コストの時系列トレンド — 緩やかな上昇傾向は通常、リソースリクエストの肥大化を意味する。開発者がスプリントごとに安全マージンを積み上げる一方、古いものを削除しないパターンだ
本番環境での運用から学んだヒント
実際のコスト可視化を実現すると、ほぼすべての本番クラスターで似たようなパターンが見えてくる。
ステージングのネームスペースは、ほぼ常に最大の問題源だ。開発者が本番の設定をそのままコピーし、リクエストを縮小するのを忘れる。2 CPUと4GiのRAMを要求しているのに実際の消費が200m CPUと512Miというステージングのポッドを見つけることは、思っているより頻繁にある。まずそこから始めよう。ステージングのリソースリクエストを修正するだけで、半日でそのネームスペースのコストを15〜30%削減できる。
絶対コストではなく、コストの増加率にアラートを設定しよう。絶対値のしきい値は製品がスケールするにつれて陳腐化する。どのネームスペースでも週次で30%増加していれば、ベースラインに関わらず調査する価値がある。クラスターに月$200使っていても月$20,000使っていても、このシグナルは有効だ。
ラベルベースの集計は、コストに関する会話のダイナミクスをガラリと変える。エンジニアリングマネージャーが自分のチームのインフラ支出を、SLOやエラー率と同じGrafanaインスタンス上で確認できるようになると、予算の議論が見積もりではなくデータに基づくものになる。このコンテキストの変化は、個々の最適化の発見よりも重要なことが多い。

