こんにちは、ベーシックのDevOpsエンジニア 奥山です。
普段は主に弊社が運営しているサービス ferret One の開発・保守の他、インフラ周りの保守運用をやっています。
今回は私がDevOps業務の一つとして行っているAWSのコスト監視についてご紹介します。
コストを監視する仕組みを導入した背景
AWSのコストは何らかの要因で急激に増加することがあります。それはサービスのインフラ周りの変更や、急激なアクセス増加が要因だったりします。
その時にできるだけ早く異変に気づける仕組みが必要だったのと、コストの無駄を詳細に分析できる体制が必要だったため、ベーシックではコストを監視する仕組みを導入しました。
コストを監視する流れ
AWSにはもともと請求アラート機能やコストを分析できるコストエクスプローラーという機能がありますが、通知や分析を柔軟に行えるようにするためベーシックでは独自にコスト監視の仕組みを実装しています。
全体の構成は下記のようになっています。
まずはコストをサービスや環境ごとに振り分けるため、事前にAWSの各リソースにタグを付与します。
タグエディターを使うと複数リソースのタグを一括で編集できて便利です。タグエディターはAWSコンソールの上部メニューからリソースグループ > タグエディターで開けます。
AWSのコンソール画面にある請求ダッシュボードで請求レポートを受け取るよう設定すると、定期的にS3の指定したバケットに請求データが出力されます。
毎朝cronでバッチを実行してS3のバケットに出力された請求データを集計します。
バッチスクリプトはRubyで実装していて、以下はその抜粋です。
# S3のバケットからから請求データをダウンロード
csv_file = "#{aws_acount_id}-aws-cost-allocation-#{yyyy_mm}.csv";
s3 = AWS::S3.new
bucket = s3.buckets['bucket-name']
o = bucket.objects[csv_file]
File.open(@path+"/input/tmp", "w") do |f|
o.read do |chunk|
f.write chunk
end
end
File.open(@path+"/input/"+csv_file, "w") do |csv|
File.open(@path+"/input/tmp", 'r:utf-8') do |f|
f.each_line do |line|
next if /^"Don't see your tags/ =~ line
next if /^""/ =~ line
next if /InvoiceTotal/ =~ line
csv.write line
end
end
end
# リソースに付与したタグに従ってコストを振り分ける
csv_data = CSV.read(@path+"/input/"+csv_file, headers: true)
file = File.open(@path+"/output/analyze_#{ym}.tsv", 'w')
output = "Service\tProductCode\tUsageType\tCostBeforeTax\tuser:Name\tuser:Service\n"
file.write(output)
csv_data.each do |data|
if data["CostBeforeTax"].to_f == 0 || !data['ProductCode']
next
end
if data['user:Service'].include?("hoge")
service = "hoge"
elsif
# 〜〜省略〜〜
end
output = "#{service}\t#{data['ProductCode']}\t#{data['UsageType']}\t#{data['CostBeforeTax']}\t#{data['user:Name']}\t#{data['user:Service']}\n"
file.write(output)
end
以下は前日比と月間の着地予測を計算するロジックです。
lastday = Date.new(target_year), target_month, -1)
days = (Date.today - Date.parse("#{target_year}-#{target_month}-01"))
# 前日比
if prev_cost
if cost_current > prev_cost
cost_oneday = cost_current - prev_cost
else
cost_oneday = 0
end
else
cost_oneday = cost_current
end
# 着地予測
cost_avg = cost_current / days
cost_prognosis = cost_avg * (lastday.day + AJUST_MONTH_COST_DAYS)
cost_prognosis = cost_current if cost_prognosis < cost_current
バッチで集計した結果は以下の3つに連携しています。
Slack
毎朝、前日にかかったコストをSlackで通知しコストに異常がないかを確認します。突然コストが跳ね上がる時がありヒヤッとしますが、次のスプレットシート等で急増しているコストを分析してすぐ対策するようにしています。また、当月の着地予想も通知して、月間コストが予算内に収まるかを常に監視するようにしています。
以下はSlack通知の例です。
Googleスプレッドシート
コストの詳細な内訳をGoogleスプレッドシートに書き出します。スプレッドシートはコストの分析の他、月ごとにシートを分けて出力しているので、前月データとの比較や、コストレポートの記録としても利用しています。
Prometheus+Grafana
監視ツールのPrometheusとデータ可視化ツールのGrafanaを組み合わせてAWSのコストをグラフ等で可視化しています。Prometheus+Grafanaという組み合わせは非常に強力で、弊社ではAWSのコストだけでなく、サーバ監視など様々なメトリクスの監視に利用しています。
終わりに
コストを監視したりコストの無駄を探す作業はとても地味ですが、それは経費を削減して会社の利益に貢献することにつながります。また私の場合、このコストレポートができたことで、サービスのインフラを考える時や何かを実装する時に常にコストを意識できるようになりました。
コストの分析を自動化したり、日次ではなく時間毎にチェックしたりなど、まだ改善できることがあるので、今後もサービスの成長のためコストの監視周りを改善させていききます!