読者です 読者をやめる 読者になる 読者になる

AWS SESのBounce率とComplaint率を監視する

AWS内で動かしているアプリケーションで,メールを送りたい時がある.
普通であれば自分でメールサーバーを立ててドメインを使えるようにして……と割とめんどくさい話なんだけど,AWSにはメールを送る専用のSESというサービスがある.

RailsからSESを介してメールを送る話は,このへんで触れたことがある.
http://h3poteto.hatenablog.com/entry/2014/06/26/230359

こんな感じでSESは非常にラクに自分のドメインからのメールを送ることができる.


SESで気をつけなければいけないこと

ただし,注意しなきゃいけないことがある.

SESは簡単にメールを送ってくれる.
だけど,俺が悪意を持って,例えば迷惑メール配信なんかを大量にやろうとした時,こんなに便利なサービスも他にないってことになる.

なにしろドメインは自分で取得しなきゃいけないけど,メール配信自体を行うのはAWS SESなのだから,かなり迷惑なメールも送れそうな気がしてくる.



こういう事態に対応するために,SESにはBounceとComplaintという機能がある.

Bounceはメールが送れなかった時(送信先が存在しなかったときとか),Complaintは送ったメールに対する苦情がきたとき,にそれぞれ発生する.

そして,このBounce率やComplaint率が高い場合には,SESの利用を停止されることがある.



しかも噂によると,ある日突然警告メールが届き,利用を停止されるらしい.
その後,改善報告をすれば再度使えるようになるらしいが…….


怖いので監視する

これがあまりにも怖そうなので,とりあえずBounce率やComplaint率を監視してみます.
一応WebのコンソールからでもBounce率の推移グラフを確認することはできるんだけど,そうじゃなくてアラートを上げたい.

というわけで監視用にt1.microあたりのインスタンスを一個用意しましょう.
そこにスクリプトを突っ込みます.

require 'aws-sdk'

ses = AWS::SimpleEmailService.new

raw_data_array = []
ses.statistics.each do |status|
  raw_data_array.push(status)
end


# timestamp順に並んでいないのでソート
raw_data_array.sort! do |a,b|
  a[:sent] <=> b[:sent]
end

attempts_sum = 0
bounces_sum = 0
complaints_sum = 0
# SESのステータスは15分おきに更新されるので,とりあえず直近1時間分を取り出す
raw_data_array.last(4).each do |data|
  attempts_sum += data[:delivery_attempts].to_i
  bounces_sum += data[:bounces].to_i
  complaints_sum += data[:complaints].to_i
end

cloudwatch = AWS::CloudWatch::Client.new

cloudwatch.put_metric_data(
  :namespace => "SES-Total",
  :metric_data => [
    {:metric_name => 'Bounce', :value => bounces_sum.to_f * 100 / attempts_sum.to_f},
    {:metric_name => 'Complaint', :value => complaints_sum.to_f * 100 / attempts_sum.to_f}
  ]
)


AWS-SDKを使います.
newするところは適宜,access_key_idとsecret_access_keyを入れてあげてください.
sesに関してはregionの指定をすると,上手く動作しない仕様になっているので,regionの指定なしでnewします.

cloudwatchについては特に調べてないのですが……region指定なしだと,us-east-1あたりに勝手に入るので,まぁそれでいいかなと思ってそのまま初期化しました.


SESのステータスを引っ張りだしてみるとわかりますが,15分おきの更新でした.
ただ並び順がぐちゃぐちゃ……だったのでsentというdatetime型の要素でソートしておきました.

で,こいつをcronで実行してやる.

例えば,上記の例だと15分おき更新のステータスを,最新4件,つまり直近1時間分を取得してBounce率等を計算するので,このスクリプトの実行間隔は1時間おき,というのが妥当である.



あとはCloudwatch側に送られてきたLogsから,適当にアラートを自作して,閾値を超えた際にSNSに通知とか作るといいんじゃないでしょうか.


ちなみにAWSのドキュメントによると,Bounce率は5%以下に維持,Complaint率は0.1%以下に維持するのを推奨されています.
その辺りを閾値にしておくと安心ですね.



参考:
http://aws.amazon.com/jp/whitepapers/amazon-ses-best-practices/