CNCFで開発の進むFinOps関連ツールの動向紹介

2024年10月11日(金)
馬庭 尚志(まにわ なおし)
本記事では、FinOpsの概要ならびにCNCFで開発が進められている関連ツールOpenCostについて紹介します。

近年、クラウドコンピューティングの適用範囲は拡大を続け、さまざまな分野で利用されています。クラウドを利用することで、企業はデータセンターへの投資やハードウェアへの投資といった初期投資を抑え、クラウド利用料という形で実際のビジネスに利用した分だけ料金を支払うことができます。これにより、企業はビジネスの拡大に合わせて柔軟にコンピューティングリソースを利用できるようになりました。その一方で、動的に変化するクラウド利用料を適切に管理するという新たな課題に直面することになりました。このような背景の中で、FinanceとDevOpsを組み合わせた「FinOps」の取り組みが進められています。

本記事では、FinOpsの概要とOpenCostというクラウドのコスト可視化に役立つオープンソースソフトウェア(OSS)についてご紹介します。

FinOpsとは

FinOpsは、クラウドのビジネス価値を最大化し、タイムリーかつデータドリブンな意思決定を行うためのフレームワークであり、文化的な実践方法です。Linux Foundationのプロジェクトの1つとしてFinOps Foundationがあり、FinOpsのフレームワークについての最新情報を豊富に発信しています。

前述のFinOps Foundationのフレームワークの中で、FinOpsの活動は次の3つのフェーズに分類されています。

FinOpsの3つのフェーズ

FinOpsのフェーズ説明
Informクラウド上のリソースの利用状況を把握し、コストをリアルタイムで可視化します。タグやアカウントなどの単位で管理し、どこにどれだけのコストがかかっているか、将来的にどのくらいのコストがかかるかを予測できるようにします。ここで得られたデータに基づいて、チームが的確な判断をタイムリーに行えるようにします。
OptimizeInformフェーズで得られたデータに基づいて無駄なリソースを排除し、効率化します。さらに、クラウドサービスプロバイダが提供する最適化オプションの利用を検討します。例えば、Reserved Instances、Saving Plans、Committed Use Discounts(CUD)などの利用を検討し、コストを最適化します。
OperateInformフェーズで得られたデータと、Optimizeフェーズで検討したコスト最適化の方法を運用します。具体的には、クラウド利用を統制するためのポリシーの策定と運用、チームメンバーへの教育などを行います。継続的にクラウド上のコストをモニタリングし、異常への対処、運用の改善を行います。

出典:FinOps Foundation:FinOps Phases(CC BY 4.0)より抜粋

上記の3つのフェーズを反復的に行うことで、クラウド上のコストを継続的に改善することが可能となります。

FinOpsに関するCNCFの取り組み

Linux Foundationのプロジェクトの1つとしてCloud Native Computing Foundation(CNCF)があり、CNCFにおいてもFinOpsに関連する活動が進められています。具体的には、FinOpsの各フェーズで利用可能な以下のソフトウェアの開発が進められています。

CNCFで開発されているFinOps関連のソフトウェア

FinOpsのフェーズソフトウェア主な用途公式サイト
InformOpenCostクラウド上のリソースの利用状況の把握とコストのリアルタイムでの可視化。Kubernetesのリソースの粒度でコストをモニタリング。https://www.opencost.io/
Optimize、OperateCloud Custodianポリシー定義によるクラウド上の不要なリソースの停止、削除を自動化。https://cloudcustodian.io/

この中でもFinOpsの最初のフェーズであるInformフェーズで活用できるOpenCostは、これからFinOpsに力を入れたい方にとっては注目のソフトウェアとなります。本記事ではOpenCostの概要と利用方法についてご紹介します。

OpenCostの概要

クラウドのコストを可視化するツールとしては、クラウドベンダーのネイティブなツールがあり、例えばAWSではCost Explorerを利用することでAWSのサービスの利用状況を簡単に把握、分析することができます。しかし、AWSのCost ExplorerではAWSのサービスの粒度での分析は可能ですが、コンテナ環境においてどのコンテナにどれだけのコストがかかっているか、といった詳細なリソースの粒度でのコスト把握が難しいという課題があります。

OpenCostは、Kubernetesのコストのモニタリングに特化しており、KubernetesのPodやコンテナなどのリソースの粒度でコストを可視化することで、この課題を解決できます。Kubernetesのシステムは、どのコンテナにどれだけのリソースを割り当てたか、どのコンテナがどれだけのリソースを消費したかを記録できるため、OpenCostはこのデータを取得し、ユーザーが見たい単位に集約して可視化することができます。リソースの利用状況の集約のイメージを以下に示します。

OpenCost

OpenCost

OpenCostは、Kubernetesの各種リソースの利用状況から次のようにコストを算出します(OpenCostの厳密な計算式ではなく、簡略化したものである点をご了承ください)。

CPUコスト=CPU使用量(コア数)×稼働時間×時間単価(通貨単位/core-hour)
メモリーコスト=メモリー使用量(GB)×稼働時間×時間単価(通貨単位/GB-hour)
ストレージコスト=ストレージ使用量(GB)×稼働時間×時間単価(通貨単位/GB-hour)

この計算をPodの単位で行うか、Namespaceの単位で行うか、あるいはNodeの単位で行うか、というように、集約の粒度を変えることでさまざまな単位でコストの可視化を実現しています。OpenCostがどのようにコストを測定、算出しているかの仕様は以下のページにて公開されています。より詳しく知りたい方はぜひチェックしてみてください。

OpenCost Specification

ところで、上記の計算の中で「時間単価」が登場しますがこの単価情報はどこからやってくるのか? と疑問を持たれる方がいるかもしれません。OpenCostが参照する単価情報については、オンプレミス環境とクラウド環境でそれぞれ以下のようになっています。

OpenCostが参照する単価情報

モニタリング対象の  
Kubernetes 環境
OpenCostが参照する単価情報
オンプレミス環境事前に定義された単価情報を参照します。オンプレミス環境のデフォルトの単価情報は以下であり、カスタマイズが可能となっています(OpenCost v1.111.0のデフォルト値です)。
https://github.com/opencost/opencost/blob/v1.111.0/configs/default.json
クラウド環境OpenCostはクラウド環境のプライスリストを自動的に取得して参照します。例えばAWS環境の場合、OpenCostは以下のURLにアクセスします(リージョンがap-northeast-1の場合)。
https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonEC2/current/ap-northeast-1/index.json

OpenCostは、上記のいずれの場合でも単価情報の中に通貨単位を含むものとみなします。例えば上記のオンプレミス環境の場合で、CPUを1コア、合計100時間利用して、CPUの単価が0.031611という値であるとき、OpenCostは以下の計算を行います。このような仕組みとなっているため、OpenCostを利用する際は参照する単価情報がどの通貨単位で表されたものかを理解しておく必要があります。

[単価情報が日本円を表す場合]CPUコスト=1コア×100時間×0.031611円/時間=3.1611円
[単価情報が米ドルを表す場合]CPUコスト=1コア×100時間×0.031611 USD/時間=3.1611 USD

OpenCostの利用方法

OpenCostの利用方法について説明します。以降で説明する手順は、Windows PC上のMinikube環境にて確認した手順となります。

本記事の説明で使用した環境

利用環境バージョン/種類
PC(OS)Windows 10 Pro 22H2
OpenCostv1.111.0
Minikubev1.33.1
Kubernetesv1.30.0
Container Runtimecontainerd
Helmv3.15.3
Note:

OpenCostは一部のメトリクスをcAdvisorから取得します。今回のMinikube環境の場合、デフォルトのコンテナランタイムではcAdvisorのメトリクスを取得できなかったため、containerdに変更しています。現在どのコンテナランタイムを利用しているかはkubectl get nodes -o wideにより確認できるため、Minikube環境でOpenCostを動かしてみたい方は確認してみてください。

https://github.com/kubernetes/minikube/issues/16742#issuecomment-1866483608

OpenCostはメトリクスの収集のためにPrometheusを利用しています。そのため、まずは前提ソフトウェアとしてPrometheusをインストールします。

helm install prometheus --repo https://prometheus-community.github.io/helm-charts prometheus ^
--namespace prometheus-system --create-namespace ^
--set prometheus-pushgateway.enabled=false ^
--set alertmanager.enabled=false ^
-f https://raw.githubusercontent.com/opencost/opencost/develop/kubernetes/prometheus/extraScrapeConfigs.yaml

次に、OpenCostをインストールするためのnamespaceを作成します。

kubectl create namespace opencost

続いて、OpenCostをインストールします。

helm install opencost --repo https://opencost.github.io/opencost-helm-chart opencost ^
--namespace opencost -f local.yaml

上記コマンドのオプションに指定しているlocal.yamlの中身は以下のとおりです。今回は後で行うテストの際にわかりやすいように、CPUおよびメモリーの1時間当たりの料金に対して、それぞれ100と50の値を指定しています。OpenCostはこのカスタマイズした単価情報を参照して動作します。「OpenCostの概要」で説明したとおり、この単価情報には通貨単位を含むものとして扱われます。

opencost:
  customPricing:
    enabled: true
    costModel:
      description: prices for testing
      CPU: 100
      RAM: 50

OpenCostのインストールが完了したら、ブラウザーからOpenCostのUIにアクセスしてみます。ホストからアクセスのために以下のコマンドによってポートフォワードを行います。これにより、http://localhost:9090からOpenCostのUIにアクセスできるようになります。

kubectl --namespace opencost port-forward service/opencost 9003 9090

正常にアクセスできた場合、次の画面が表示されます。ここまでの手順により、OpenCostの環境構築は完了です。

OpenCostの画面

OpenCostの画面

UIで選択可能な項目について簡単に説明します。それぞれの項目は以下の意味を持ちます。

UIの項目

選択項目説明
Date Range表示対象のデータの期間を指定します。
Breakdown表示対象のデータの集約単位を指定します。設定により、Namespace、Pod、コンテナなどの単位でデータを表示します。
Resolution表示対象のデータの解像度を指定します。OpenCost v1.111.0では以下の2つの解像度を選択できます。
・Daily:日ごとのコストをグラフで表示します
・Entire Window:全期間のコストをグラフで表示します
Currency表示されている数値に「$」や「€」などの記号を付与します。

「Currency」については、為替レートを利用した数値の変換を行うものではなく、単純に表示されている数値に「$」や「€」などの記号を付与するだけのものとなっています。繰り返しになりますが、OpenCostは単価情報に通貨単位を含むという考え方をしており、コストの算出結果である値には通貨単位を含むことになるため、UIから参照する際は単価情報と同じ通貨単位を選択することが適切です。下記Issueで為替レートを利用した変換を望む声があるようなので、もしかしたら今後の機能追加はあるかもしれません。

https://github.com/opencost/opencost-ui/issues/5

さて、試しにBreakdownの項目としてPodを選択してみると、Pod単位のコストに集約されて表示されます。このように、OpenCostを利用することでKubernetes上のリソースの粒度でコストを可視化できます。

Breakdownの項目をPodにしてみる

Breakdownの項目をPodにしてみる

加えて、OpenCostはAPIも提供しています。例えば、今回の手順で構築した環境ではhttp://localhost:9003/allocation?window=7dのAPIによって直近7日分のコストのデータを取得することができます。本記事ではこのAPIを利用してOpenCostのコスト算出方法を確認していきますので、利用方法についてはそちらを参考にしてください。詳細なAPI仕様は以下のページで公開されていますので、詳しく知りたい方はこちらもぜひチェックしてみてください。

https://www.opencost.io/docs/integrations/api

それではOpenCostの機能に一通り触れたところで、実際にPodをデプロイして負荷をかけた際に、どのようにOpenCost上でデータが表示されるかを確認してみたいと思います。stressというnamespaceを作って、そこにテスト用のPodを1つデプロイしてみましょう。

kubectl create namespace stress
kubectl apply -f stress.yaml

stress.yamlの中身は以下のとおりです。Ubuntu 22.04のコンテナを起動するだけのものとなっています。コンテナは、CPUを50m(ミリコア)、メモリーを50Mi(メビバイト)だけ起動時に要求する設定となっています。起動後のリソースの消費に上限を設定する場合は、別途limitsを設定する必要がありますが、今回は設定していません。そのため、コンテナの起動後にはrequestsに指定した値以上のリソースを必要に応じて消費する設定となっています。

apiVersion: v1
kind: Pod
metadata:
  name: stress
  namespace: stress
  labels:
    app: stress
spec:
  containers:
  - name: stress
    image: ubuntu:22.04
    imagePullPolicy: IfNotPresent
    command: ["/bin/bash", "-c", "while true; do sleep 3600; done"]
    resources:
      requests:
        cpu: "50m"
        memory: "50Mi"

上記のPodをデプロイした後、OpenCostのUIを更新して確認してみます。するとstressというデータが追加されていることがわかります(データの反映には若干時間がかかるため、表示されない場合は少し待ってみてください)。

新たにデプロイしたPodのデータが表示されている

新たにデプロイしたPodのデータが表示されている

stressのCPUとメモリー(RAM)のコストを見てみるとデプロイしたばかりで小さな値になっています。この値は、stressの中にあるコンテナのリソース使用量×稼働時間×リソースの単価情報に基づいて計算されています。次に、デプロイしたコンテナに負荷をかけるテストをしてみます。以下のコマンドによりコンテナにログインします。

kubectl exec -it stress -n stress -- /bin/bash

続いて、sterss-ngというパッケージをインストールします。stress-ngはCPUやメモリーに任意の負荷をかけることができるツールです。

apt update
apt install stress-ng -y

インストールに成功したらstress-ngを利用して負荷をかけていきます。今回は、CPUは1コアに対して負荷をかけ、メモリーを150MB消費する負荷を2時間(7200秒)かけるテストを行いました。負荷をかける時間はこれより短くても問題ありません。

stress-ng --cpu 1 --vm 1 --vm-bytes 150M --timeout 7200

コマンドの実行が完了したら、コンテナからログアウトしてPodを削除します。

kubectl delete -f stress.yaml

UIを更新して確認してみると、コストの値が増えていることがわかります。

負荷をかけたPodのコストが増えている

負荷をかけたPodのコストが増えている

それでは、コストの値がどのように計算されているのか確認してみましょう。まず、次のAPIを実行して実行結果のJSONを確認してみます。

・http://localhost:9003/allocation?window=7d

結果として得られたJSONのデータから、今回デプロイしたstressのコンテナの部分を抽出してみます。

{
  "name": "default-cluster/minikube/stress/stress/stress",
  // 省略
  "window": {
    "start": "2024-07-30T00:00:00Z",
    "end": "2024-08-06T00:00:00Z"
  },
  "start": "2024-08-05T02:10:00Z",
  "end": "2024-08-05T04:15:00Z",
  "minutes": 125,
  "cpuCores": 1.92813,
  "cpuCoreRequestAverage": 0.05,
  "cpuCoreUsageAverage": 0.16737,
  "cpuCoreHours": 4.01695,
  "cpuCost": 401.69452,                 // UIの表示項目「CPU」に対応
  "cpuCostAdjustment": 0,
  "cpuEfficiency": 3.34748,
  // 省略
  "ramBytes": 229802694.19355,
  "ramByteRequestAverage": 52428800,
  "ramByteUsageAverage": 228262313.984,
  "ramByteHours": 478755612.90323,
  "ramCost": 22.2938,                   // UIの表示項目「RAM」に対応
  "ramCostAdjustment": 0,
  "ramEfficiency": 4.35376,
  // 省略
  "totalCost": 423.98831,               // UIの表示項目「Total cost」に対応
  "totalEfficiency": 3.40039,           // UIの表示項目「Efficiency」に対応
                                        // (百分率にした値)
  // 省略
}

「cpuCost」「ramCost」「totalCost」の値を見てみると、これらはUIに表示されている「CPU」「RAM」「Total cost」の値と一致していることがわかります。それでは、これらのコストの値はどのように算出されているのでしょうか? 確認してみると次の計算によって求められることがわかります。

[CPU]cpuCost=cpuCoreHours×local.yamlで定義したCPUコスト(通貨単位/core-hour)=4.01695×100=401.69452
[Memory]ramCost=ramByteHours×local.yamlで定義したメモリーコスト(通貨単位/GB-hour)=(478755612.90323÷1024^3)×50= 22.2938

cpuCoreHoursとramByteHoursがそれぞれ測定期間中に利用したCPUコア数時間とメモリーバイト数時間になっており、それらの値に対してlocal.yamlで定義した単価を掛け合わせた値になっていることがわかります。期待どおりに単価情報を反映した計算結果になっています。

UIには「Efficiency」というデータも表示されています。これはどんな意味を持つのでしょうか?OpenCostのドキュメントには説明が見つかりませんでしたが、実装を確認してみると計算方法は次のようになっています(OpenCost v1.111.0の計算方法となります)。

https://github.com/opencost/opencost/blob/v1.111.0/core/pkg/opencost/allocation.go#L914
https://github.com/opencost/opencost/blob/v1.111.0/core/pkg/opencost/allocation.go#L933

[CPU]cpuEfficiency=cpuCoreUsageAverage÷cpuCoreRequestAverage=0.16737÷0.05=3.3474 => 334.74%
[Memory]ramEfficiency=ramByteUsageAverage÷ramByteRequestAverage=228262313.984÷52428800=4.353758125 => 435.376%

上述のように、使用量(Usage)を要求量(Request)で割った値となっていることがわかります。今回のテスト用のコンテナ(stress)は、stress.yamlの中で、CPUを50m(ミリコア)、メモリーを50Mi(メビバイト)だけ要求する設定になっていたため、この値がそれぞれcpuCoreRequestAverageとramByteRequestAverageに設定されています。cpuCoreUsageAverageとramByteUsageAverageは測定期間中に実際に使用したリソースの量を表しています。計算式からEfficiencyは次の意味を持つことがわかります。

Efficiency=100%:要求したリソースの量と、使用したリソースの量が一致する場合Efficiency<100%:要求したリソースの量より、使用したリソースの量が少ない場合Efficiency>100%:要求したリソースの量より、使用したリソースの量が多い場合

つまり、Efficiencyの値が100%より小さい場合は、実際には使用しない量のリソースを割り当てていることを意味するため、割当量を減らすことでコストを削減できる可能性があります。一方、Efficiencyの値が100%より多い場合は、要求値よりも多くリソースを消費していることを意味するため、想定よりもコストが増大する可能性があり、見直しの対象になります。Efficiencyの値を確認することで、リソース割り当ての最適化に向けたヒントが得られると考えられます。

さて、totalEfficiencyの値についても確認してみましょう。UIではこの値に100をかけて百分率にした値が表示されています。こちらもドキュメントには説明が見つかりませんでしたが、実装を確認してみると計算方法は次のようになっています(OpenCost v1.111.0の計算方法となります)。

https://github.com/opencost/opencost/blob/v1.111.0/core/pkg/opencost/allocation.go#L970

totalEfficiency=(cpuEfficiency×cpuCost+ramEfficiency×ramCost)÷(cpuCost+ramCost)=(3.34748×401.69452+4.35376×22.2938)÷(401.69452+22.2938)=3.40039137516241 => 340%

cpuEfficiencyとramEfficiencyの値に基づいてコストの重みづけをした計算をしており、計算結果はUIの「Efficiency」の値と一致していることを確認できます。この「Efficiency」の値を確認することで大まかにリソース割り当てが適切であるか否かを確認できると考えられます。

ところで、より細かくメトリックを確認したいという方は、Prometheusのメトリックを確認することもできます。Prometheusのメトリックを確認したい場合は、以下のコマンドによってポートフォワードを行います。これにより、http://localhost:9091からPrometheusのUIにアクセスできます。

kubectl --namespace prometheus-system port-forward service/prometheus-server 9091:80

Prometheusのメトリックを利用してコンテナのメモリー使用量の平均値を確認してみると、OpenCostのAPIで取得したramByteUsageAverageの値と一致することがわかります。より細かく分析したい場合はこのような方法も利用できます。

Prometheusを利用してもOpenCostと同じ値が得られる

Prometheusを利用してもOpenCostと同じ値が得られる

最後に、今回構築したOpenCostとPrometheusは以下のコマンドによりアンインストールできます。

helm uninstall opencost --namespace opencost
helm uninstall prometheus --namespace prometheus-system

FinOps関連ツールの今後の動向

FinOps関連ツールでは、今後、FinOps Cost and Usage Specification(FOCUS)の利用が進んでいくものと考えられます。FOCUSは、FinOps Foundationがクラウドベンダー各社の協力のもとで策定したクラウド上のコストモデルの仕様であり、これによって従来は差異のあったクラウドベンダーごとの請求項目を標準化する狙いがあります。

出典:<a href="https://focus.finops.org/" class="link">FOCUS: What is FOCUS?</a>(CC BY 4.0)

出典:FOCUS: What is FOCUS?(CC BY 4.0)

AWS、Azure、Google Cloudなどの各ベンダーはFOCUSの仕様に従ったコストデータのエクスポートをサポートし始めており、今後はFOCUSの標準モデルを利用して各種FinOpsツール間での連携が可能になるかもしれません。OpenCostに関しては、前述のFOCUSへの対応に加えて、カーボンコストの可視化やプラグインによる各種クラウドサービスのコスト可視化に取り組むアナウンスが出ています。

https://www.opencost.io/blog/focus
https://www.opencost.io/blog/carbon-costs
https://www.opencost.io/blog/introducing-opencost-plugins

プラグインの第一弾としてDatadogのコスト可視化を行えるプラグインが開発されており、今後は他のサービスのプラグインも出てくるものと考えられます。AWS、Azure、Google Cloudなどのマルチクラウドへの対応に加えて、個別のクラウドサービスへの対応を進めていくことで、将来的にはクラウド上のあらゆるコストをOpenCostのダッシュボードで確認できる日が来るかもしれません。

また、実はAzureではクラウド上のネイティブなコスト分析ツールにOpenCostが取り込まれており、Kubernetesのリソースの粒度でコストを確認できるようになっています。Azureを利用されている方はぜひチェックしてみてください。

https://www.opencost.io/blog/aks-cost-analysis
https://learn.microsoft.com/ja-jp/azure/aks/cost-analysis

おわりに

本記事ではFinOpsの概要とOpenCostの利用方法についてご紹介しました。OpenCostを利用することで、Kubernetesのリソースの粒度でコストを可視化することができ、NamespaceやPodの単位でコストの分析が可能となります。これにより、どのチームが多くのコストを消費しているか、どのアプリケーションのリソース消費量が多いか、などの分析を効率的に行うことができます。また、記事の中で紹介したOpenCostのAPIを利用することで、それぞれの環境に特化したコストのモニタリング自動化やレポート作成も行えるかと思います。DevOpsやDevSecOpsに続いて、FinOpsも今後は盛り上がっていくと思いますので、これからFinOpsをやってみようという方は、本記事でご紹介したOpenCostなどを利用して、まずは可視化から始めてみてはいかがでしょうか。

著者
馬庭 尚志(まにわ なおし)
株式会社日立製作所 アーキテクチャセンタ
ソフトウェアの開発およびコンサルティング業務に従事。 近年ではOSSや生成AI技術の調査/活用検討を主軸に活動中。

連載バックナンバー

クラウド技術解説
第2回

CNCFで開発の進むFinOps関連ツールの動向紹介

2024/10/11
本記事では、FinOpsの概要ならびにCNCFで開発が進められている関連ツールOpenCostについて紹介します。
システム運用イベント
第1回

FinOps Foundation最大のグローバルイベント「FinOps X 2024」開催、最新の技術動向や事例を幅広く紹介

2024/9/27
2024年6月、FinOpsに関する最大規模のイベントFinOps X 2024が米サンディエゴで開催されました。今回の記事では最新の技術動向や事例について紹介します。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています