Datadogの最適なコミット料金を計算する

Datadogには様々な料金が存在しますが、それぞれ年払い、月払い、従量課金が存在します。

https://www.datadoghq.com/pricing/list/

何もしなければ従量課金で支払いが発生しますが、すでに動いているサービスなど前もって使うと分かっているものは、年払いや月払いにすることで費用の削減を行うことができます。

しかし、ここで問題になるのは利用する量が時間によって変動するプロダクトです。
例としては、日本の社会人が働いてる時に利用するサービスであれば平日の8時～19時くらいの利用料が多く、それ以外の時間帯が少ないなどといったものです。
上記の例の場合、休日や深夜といったピーク外は少ないインスタンス数で動作するけど、ピークタイムはその〇倍インスタンスが必要、というケースもあります。
この場合、ピーク外でも動作している分のインスタンスは月払いもしくは年払いで契約し、ピークタイムに必要な追加分はオンデマンドで対応するのがコスト効率的です。
ただ、ピークタイムとオフピークで明確に分かれているわけではなく1日のりよう波を打つような線になるので、どの程度定額払いにするかは悩みどころになります。

今回は直近の利用状況から、どれくらいの量をコミットすればいいのか計算してみます。

## 直近の利用状況を確認する

ログイン後、`Plan & Usage`のページに行き、`Usage & Cost`タブを開きます。

ページ下にある`Usage Trends`の項目のセレクトボックスをYearlyにして、`Download as CSV`をクリックして直近1年の利用履歴をDLします。
`![スクリーンショット 2024-12-25 16.31.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359721/21380298-cbd8-9b47-83e5-7b6560245dff.png)


## 最適な料金を求めていくスクリプトを作成する

実際に今から計算する簡単なscriptをrubyで作成します。

今回は、例として2024年11月の`APM Fargate Tasks`の最適なコミット量を出してみます。


CSVには`Organization`、`Name`、`Public ID`、`Hour`、`Product Name`、`Aggregation`の6つの情報が入っています。
今回は`Hour`、`Product Name`、から2024年11月の`APM Fargate Tasks`の`Usage`を抽出します。

まず抽出までのスクリプトを組んでみましょう。

```ruby:main.rb
def extract_usages(year, month, product_name)
  row_count = 0
  array = []
  CSV.foreach('csv/sample.csv', headers: true) do |row|
    hour = row['Hour']
    usage = row['Usage']
    next unless hour.include?("#{year}-#{month}-") && row['Product Name'] == product_name
    row_count += 1

    array << usage.to_i
  end
  return array
end

usages = (2024, 12, 'APM Fargate Tasks')
```

これで2024年11月のusageが手に入りました。本格的に計算をしていきます。

`APM Fargate Tasks`の料金は https://www.datadoghq.com/ja/pricing/list/ より、以下のようになっています。

- 年払い: 1taskあたり一月1.25USD
- 月払い: 1taskあたり一月1.50USD
- オンデマンド: 1taskあたり一月1.75USD

今回は以下の料金表から月払いの場合のコミット量を計算することにします。もし個別で契約している場合はPlanページを見ると何円で計算すると良いか書いてあります。

Datadogの料金は1時間ごとに計算され、`APM Fargate Tasks`の場合以下の式で表されます。

```math
時刻tのオンデマンド料金 = max(0, 時刻tのusage-コミット量) *  1時間あたりのオンデマンド料金
```

上記の計算を一月分行って算出したオンデマンド料金にコミット量 x 月払い料金を足すと、特定のコミット量の時の月額利用料を算出することができます。

あとは各コミット量の時の利用料を計算して、一番小さい利用料金になるコミット量を算出します。

ここまで踏まえるとコードは以下のようになります。

```ruby:main.rb
# frozen_string_literal: true

require 'csv'

def extract_usages(year, month, product_name, file_name)
  row_count = 0
  array = []
  CSV.foreach(file_name, headers: true) do |row|
    hour = row['Hour']
    usage = row['Usage']
    next unless hour.include?("#{year}-#{month}-") && row['Product Name'] == product_name
    row_count += 1

    array << usage.to_i
  end
  return array
end

def calculate_commit_quantity(usages, ondemand_price_per_month, commit_cost_per_month)
  min_commit_quantity_candidate = usages.min
  max_commit_quantity_candidate = usages.max
  min_costs = Float::INFINITY
  min_commit_quantity = -1
  hours = usages.size

  (max_commit_quantity_candidate - max_commit_quantity_candidate + 1).times do |i|
    commit_quantity = min_commit_quantity_candidate + i
    ondemand_costs = usages.map { |usage| usage - commit_quantity > 0 ? (usage - commit_quantity) * ondemand_price_per_month / hours : 0 }.sum
    cost = commit_quantity * commit_cost_per_month + ondemand_costs
    if min_costs > cost
      min_costs = cost
      min_commit_quantity = commit_quantity
    end
  end

  puts "Min cost commit quantity: #{min_commit_quantity} commit(#{min_costs})$"
end

usages = extract_usages(2024, 11, 'APM Fargate Tasks', 'csv/sample.csv')
calculate_commit_quantity(usages, 1.75, 1.5)
```

実行すると以下のような出力が得られます。(値はダミーを入れています。)

```ruby
❯ ruby main.rb              
Min cost commit quantity: 43 commit(100.2)$
```

## まとめ

今回は過去の利用料からコミット量を試算するスクリプトを作成してみました。
年間払いにするときなどは可能な限り正確に見積もって、安くするための取り組みが必要になるので、直近の利用料からしっかり計算して必要な分だけコミットするようにしましょう。