後ろを向いて後退します

これって前に進んでいることになりませんか?

Amazon SES ベストプラクティス&アンチパターン

Recruit Engineers Advent Calendar 2019

本記事は Recruit Engineers Advent Calendar 2019 6日目の記事です。

adventar.org

昨日の記事はPoohSunnyさんの、

poohsunny.hatenablog.com

でした。

もくじ

Amazon SESとは

Amazon Simple Email Service aws.amazon.com

Amazon Web Services, Inc. (AWS)が提供する高可用性のクラウドEメール送信サービスです。

AWS SDKAmazon SES API)、SMTPAWSマネジメントコンソールなどのインターフェースを通して、普段使用しているようなEメールと同じように自由に宛先・本文・添付ファイルなどを指定してEメールを送ることができます。

ただし、GmailOutlookなどのメーラーに見られるようなリッチなUIや送信済みメール一覧、受信箱のような機能は提供されていません。システマティックにEメールを送り、場合によっては受信することに主眼を置いた、例えばシステムによって自動化されたEメールの配信システムやメールマガジンの配信などでの使用を想定して設計されています。

オレゴンリージョンでの料金体系例は以下のようになっていますが、よほど大きなシステムでない限りは各月の請求においてAmazon SESの利用料金が大部分を占めるようなことにはならないでしょう。 f:id:mic_psm:20191203025850p:plain

Amazon SESでできること / できないこと

できること

  • Eメール(plain text / HTML / multi part)の送信
  • 固定IPアドレスからのEメール送信
  • 任意のサブドメインのMAIL FROMドメイン指定
  • 開封・クリックなどのイベントトラッキング(HTMLメールのみ)
  • Eメール(Plain Text / HTML / Multi Part)の受信
  • フィルタリングルールを使ったEメール受信のハンドリング
  • 送信ステータスなどのメトリクス監視
  • 各機能とSNSとの組み合わせによるログ取得

できないこと

  • SMTPリレーによるEメール転送(擬似的に再現は可能)
  • 送信したEメールの本文などのログ取得(ヘッダーのみ可能)
  • 送信済みメール一覧の表示
  • 受信メール一覧の表示
  • HTMLメール作成(Raw Emailを送信すること自体は可能)
  • Plain Text Eメールの開封・クリックのトラッキング

ベストプラクティス&アンチパターン

メールを送るとき

上限値・サンドボックス制限

まず、SES自体で許容される各数値の上限について知っておいたほうが良いでしょう。

docs.aws.amazon.com

また、各リージョンで初めてSESを使い始めたときはサンドボックス制限状態となっており、送信者(From)・受信者(To、Cc、Bcc)に設定されるEメールアドレスのドメインは全て検証されている必要がある状態になっています。任意の宛先にSESからEメールを送りたい場合はAWSサポートセンターからサンドボックス制限の解除申請を行うことができます。

docs.aws.amazon.com

送信方法

システムでの利用においては、公式から提供されているSDKSMTPを利用しEメールを送信できます。

aws.amazon.com

SDKは、バックエンドのSES APIに準拠しSendEmailSendRawEmailの2つのインターフェースを備えています。

SendEmailはヒューマンリーダブルなテキストを用いて簡単にEメール送信のリクエストを送れる一方で、HTMLタグの使用や添付ファイル付きなどの複雑なEメールを取り扱うことができません。

SendRawEmailはリクエスペイロードとしてRaw Emailを受け取るので、送信可能サイズの最大値が許容する限りはMulti Part Emailを含めてどのようなEメールでも送ることができ、ヘッダーの内容も自在に設定することができます。ただし、Raw Emailを組み立てるのは少し面倒であることと、ヘッダーに設定する値によってはSESによって付与されるヘッダーとキーが重複してリクエスト送信時にエラーになる可能性があることだけ忘れないようにしましょう。

SMTP経由でEメールを送信したい場合は、SESのEメール送信アクション権限を持ったIAMユーザーでSMTPユーザー名とパスワードを生成してください。SMTPユーザー名とパスワードは、IAMユーザーのアクセスキーとシークレットキーから不可逆的に算出することができますが、SESのマネジメントコンソールからIAMユーザーを含めて一括生成できます。

f:id:mic_psm:20191206032402p:plain
SMTP Settingsの画面

一つ注意しておくべきなのは、サンドボックス制限を解除している場合は任意の宛先にEメールを送信できるようになってしまっていることです。例えば開発や検品環境でSESを利用している場合、開発中のシステムの情報やセンシティブなデータが意図せず外部に露出しないように、何らかの方法でEメールの宛先として設定できるEメールアドレスを制限すると良いでしょう。

SendRawEmailの挙動

SendRawEmailは、RFC 5322に準拠したRaw Email形式のデータをリクエスペイロードとして受け取る一方で、Sourceというパラメータを設定できます。Raw Email形式のデータ自体はEメールヘッダーとしてFromの値を持つことができますが、同じ意味を持ちそうなこれらのパラメータをSendRawEmailで同時に設定した場合はどのような挙動になるのでしょうか。AWSの公式CLIツールのドキュメントには以下のように書かれています。

https://docs.aws.amazon.com/cli/latest/reference/ses/send-raw-email.html#options

The identity's email address. If you do not provide a value for this parameter, you must specify a "From" address in the raw text of the message. (You can also specify both.) If you specify the Source parameter and have feedback forwarding enabled, then bounces and complaints will be sent to this email address. This takes precedence over any Return-Path header that you might include in the raw text of the message.

まとめると、

  • Sourceを設定しない場合はRaw Email側のヘッダーにFromのEメールアドレスを設定しておく必要がある。
  • Sourceを設定する場合はその必要はない。
  • SourceFromを両方設定することができる。その場合SourceSMTPのMAIL FROM(=Return-Path)のような振る舞いをするため、フィードバック転送を有効にしているとバウンスや苦情などのフィードバック通知はSourceに対して送り返されるようになる。ヘッダーでReturn-Pathを設定している場合は、その値をSourceで設定したEメールアドレスで上書きする。
From Return-Path
Source="source@example.com", From=null source@example.com source@example.com
Source=null, From="from@example.com" from@example.com from@example.com
Source="source@example.com", From="from@example.com" from@example.com source@example.com

フィードバック通知を特定のEメールアドレスでハンドリングしたい場合、Return-PathFromを明確に別のEメールアドレスで設定したい場合などに有効な使い方です。

専用IPアドレス

SESは、最初の状態だと他のAWSユーザーと共有されたIPアドレスのプールからEメールを送信するようになっています。つまり、自分が送信したEメールの出口IPと同じIPからEメールを送信するユーザーが他にも何人かいるということです。

送信元のIPアドレスが共有されていることの何が問題かというと、例えばユーザーのうち誰かが悪意のあるスパムメールを無差別に送ったとします。Eメール受信側のメールプロバイダによっては、スパム報告を受けた送信元IPアドレスを全世界で共有されているEメールのブラックリストに追加することがあるかもしれません。このブラックリストを他のメールプロバイダが使っている場合、もしくはこの同一のメールプロバイダに対して別のAWSユーザーがSESでEメールを送ろうとした場合に「既にブラックリストに登録されているのでEメールを送ることができない」といったような問題が発生します。

たとえ自分が過去に悪意のあるスパムメールを送ったことがなかったとしても、他の悪意のあるユーザーの行動によって共有IPアドレスが汚染されてしまっている場合、本来正常に送信・配達されるべきはずのEメールを送ることができなくなってしまいます。

f:id:mic_psm:20191206035130p:plain

この問題を解決するために、SESは各ユーザーごとに専用のIPアドレスを有料で割り当てるサービスを提供しています。

docs.aws.amazon.com

サンドボックス解除申請と同じように、AWSサポートセンターから専用IPアドレスの発行申請ができます。

専用IPの利用にあたっては、無料の共有IPを利用する場合とでメリットとデメリットを比較すると良いでしょう。公式ドキュメントにも記述がある通り、前提として大量のEメール送信を想定しない場合は共有IPの利用が推奨されています。負荷(送信件数)や送信パターンの観点で言うと、専用IPを利用する場合と共有IPを利用する場合とで特に大きな差はありません。例えば特に理由もなく専用IPを取得してシステムで利用しようとすると、かえって配達可能性が不安定になったり予期せぬ問題が発生する恐れがあります。専用IPを利用することで共有ブラックリスト起因のソフトバウンスの回避や固定されたIPアドレスの取得といった恩恵を受けられるので、システムの重要性や想定されるEメール送信のワークロードを考慮に入れた上で最適な方法を選択することが好ましいです。

送信ログ取得

先述の通り、SESはEメールを送信するときにログを取得する手段を持っていません。送信リクエストや配達イベントをAmazon Simple Notification Service(Amazon SNS)経由で受け取ることはできますが、その内容に本文やRaw Emailなどの情報は含まれていません。システムの要件として送信したEメールの本文ログの取得が含まれている場合、それをSESで担保することはできないので、万が一の事態に備えるため(又は同じ内容でEメールを再送信できるようにするため)に、アプリケーション側で必要な情報をログとして吐いておくことを推奨します。

メールを受け取るとき

受信設定

SESで利用しているドメインのMXレコードがDNSサーバーで検証済みになっている場合、そのドメインではEメールの受信をすることができるようになります。DNSとしてAmazon Route 53を利用する場合は、SESのドメインを作成するときに必要なDNSレコードをRoute 53に自動的に登録して検証するように設定できますが、Route 53以外のDNSサーバーを利用している場合は、手動で必要なレコードを登録しなければならないので注意しましょう。

f:id:mic_psm:20191206040153j:plain
SESドメインのRoute53レコード自動生成画面

受信ルール / 受信ルールセットについて

SESでEメールを受信するにあたって、受信ルールと受信ルールセットの2つをSESに設定する必要があります。

受信ルール

特定のルールにマッチするEメールアドレスからEメールが送られてきたときに、それに対するアクションを定義できます。正常系としてAmazon Simple Notification Service(Amazon SNS)やAWS Lambdaにイベントを発行したり、異常系としてハードバウンスを発生させたり、他にはAmazon WorkMailの連携やヘッダーの追加がアクションとして設定可能です。

基本的に部分文字列一致でルールは評価されますが、何も設定しない場合は全てのEメール受信に対してアクションが実行されることになります。ルールは設定するけれど受信したくない場合は、ルール自体をDeactivateすることによってそのルールでのイベントハンドリングをスキップできるようになります。

f:id:mic_psm:20191206041029p:plain
受信ルールの例

受信ルールセット

ルールに対する複雑なアクションを組み合わせて定義したい場合、1つの受信ルールセットに複数の受信ルールを含めることができます。

受信ルールセットに含まれる受信ルールはそれぞれに優先度をつけることができますが、各受信イベントに対して毎回全ての受信ルールが優先度順に評価されそのアクションが決定されます。同じEメールアドレスのマッチングルールが定義されている受信ルールが存在する場合は、その両方が実行されることになります。

1つの受信ルールセットには複数の受信ルールを設定できる一方で、1つのリージョンで有効にできる受信ルールセットは1個までに制限されています。例えば複数の環境で受信ルールセットを分割してしまった場合、片方の受信ルールセットをActivateするともう一方の受信ルールセットがDeactivateされてしまいイベントのハンドリングができなくなることに注意してください。

Eメール受信イベントのハンドリング・許容サイズ

受信したEメールをハンドリングする際に気をつける必要があるのは、ハンドリング可能なEメールのデータサイズの上限は受信ルールで連携する先のサービスでハンドリング可能なデータサイズの上限に依存するということです。

例えば受信ルールでは連携先としてSNSAmazon Simple Storage Service(Amazon S3)を指定することができますが、SNSではトピックに対してPublish可能なイベントのメッセージのサイズはSNSの上限である256KBよりも小さい150KBに制限されています。この場合、SESの受信ルールで受け取ったEメールのサイズが150KBを超えていると受信ルールではそのEメールをハンドリングできなくなってしまいます。

一方でS3を使う場合はS3のオブジェクトのサイズの上限に縛られることになりますが、SESでは連携先にS3を設定した受信ルールでハンドリングできるEメールメールの最大サイズを30MBと規定しています。この値であれば一般的にEメールの世界でやり取りされるデータは概ね取り扱うことができますし、オブジェクトストレージに保存することによるデータの永続化もできるのでこちらを使うのが良いでしょう。

また、S3に保存されたEメールのデータはRFC 5322に準拠したインターネットメッセージフォーマットのRaw Emailそのものです。拡張子を.emlにしてローカルに保存すれば、macOSのメールアプリやMicrosoft Outlookのような一般的なメーラーで開くこともできます。

SESでのEメール受信は、以下のような構成のコンポーネントでハンドリングをするのが堅実です。

f:id:mic_psm:20191206042132p:plain

通知(Bounce, Complaint, Delivery)の監視

SESでは、Eメール送信リクエストの結果として3種類の通知を受け取ることができます。これらの通知はSESを使ったEメールの運用上最も大切といっても過言ではなく、これらを正しい手順で監視・ハンドリングしてはじめてSESを正しく使えていると言えるでしょう。

サンドボックス制限解除申請の申請フォームにも、どのようにバウンスや苦情のハンドリング機構を組み込んでいるのかを説明するための項目があり、これらのハンドリングは大前提とされています。

いずれに関しても、1回のEメール送信リクエストに対するレスポンスとしての通知なので、ログとして永続化して保存するのが良いでしょう。

Bounce - 不達

最終的にEメールを宛先に届けられなかった場合に返却される通知です。バウンスには、恒久的に配達が不可能なハードバウンスと、一時的に配達が不可能なソフトバウンスの2種類があります。公式の開発者ガイドには以下のように記述されています。

ハードバウンス – 永続的な E メール配信の障害。たとえば、メールボックスが存在しない場合です。Amazon SES は、DNS ルックアップの失敗を除いて、ハードバウンスを再試行しません。ハードバウンスの原因となっている E メールアドレスに対して配信の試行を繰り返さないことを強くお勧めします。

ソフトバウンス – 一時的な E メール配信の障害。たとえば、メールボックスがいっぱいである、接続が多すぎる (スロットリングとも呼ばれる)、または接続がタイムアウトになった場合です。Amazon SES は、ソフトバウンスを何回か再試行します。それでも E メールを配信できない場合、Amazon SES は再試行を停止します。

ハードバウンスは、Eメールのドメインが存在しても指定されたユーザーがそのドメイン上に存在しないケースで発生します。誰かがそのユーザーを作成しない限りはEメールが永遠に届くことはないため、基本的にハードバウンスが発生したユーザーはブラックリストに投入するなどして二度とEメールの送信トライをしないようにすることをAWSも推奨しています。メールが届かない以上はシステム側でどうにかできるわけではないので、発生率などに気を配る必要はあるにしろシステマティックに何か対策をするのは少し難しいでしょう。

ソフトバウンスは、それ以外の何らかの原因でEメールを配達できなかったケースで発生します。例えば以下のような原因です。

  • 受信側SMTPサーバーからの応答が無い。
  • DNSの名前解決に失敗した。
  • 自動応答メールが設定されている。
  • ブラックリストに登録されている。
  • 受信側のメールボックスがパンクしている。
  • Eメールのデータサイズが大きすぎる。
  • 許容できないEメールのコンテンツが存在する。
  • 許容できない添付ファイルが存在する。
  • その他の理由で明示的に受信側SMTPサーバーが拒否している。

バウンスの理由と種別は受信側SMTPサーバーが決定することなので、これら以外にも多種多様な理由でソフトバウンスが返されることがあります。時にはハードバウンスとして扱われるべき理由(ユーザーが存在しない)に対してもソフトバウンスのステータスコードとメッセージを割り振って返却することもあります。詳しくはRFC 5321SMTP Repliesのチャプターに記述があります。

特にソフトバウンスに関して難しいのは、「あくまで一時的なものなので、あとでリトライすれば配達できる可能性がある」という点です。リトライが成功する可能性がある一方で、例えばDNSの名前解決に失敗しているようなケースだとそもそもDNSサーバーが存在しないため永久にそのEメールが届かないことも有り得ます。そういった点を含めて、リトライによる配達可能性を人間が目で見て吟味する必要があること、更にソフトバウンスの原因となった要素が送信側・受信側のどちらにあるのか、どこを改善すればリトライで成功しそうなのかを判断するには結局は人間が介入し判断する必要があります。

ステータスコードやメッセージがあらゆるSMTPサーバーによってバラバラに定義され返却される以上、自動処理の機構を組み込むのは至難の業です。ソフトバウンスの通知をハンドリングして原因調査と対策にすぐに移れる状態を常に保っておくこと、もしくはその数がハンドリングできるキャパシティを大きく超えたときにはステータスコードが信頼できる前提でトリアージをしてハンドリングする数を絞るなどするしかありません。

バウンスのハンドリングが難しいのはEメールを使ったシステムを構築する上では必ずついてまわる問題で、SESに限った話ではありません。どうしても許容するのが難しい場合は、そもそもEメールを使わないことを視野に入れて検討したほうが良いでしょう。

Complaint - 苦情, スパム報告

スパム報告に関しては、そもそも悪意のあるスパムメールを送っているようなユースケースが無い限りは気にする必要はありません。もちろん件数は最小限に留める必要はありますが、もし発生した場合はハードバウンスと同様にブラックリストに入れるなどして今後Eメールを送信しないようにします。

Delivery - 配達

配達通知はバウンスやスパム報告とは正反対で、受け取ると嬉しい通知です。送信したEメールが、正常に宛先のEメールアドレスに対して配達された場合にこの通知が返却されます。

他の2つとは違って正常系の通知なので、数が膨大になるであろうことを考えるとアラートなどは上げずにS3にJSONCSV形式でログを保存だけしておくのが好ましいでしょう。

メトリクスの監視

CloudWatchで取れるSESのメトリクスのうち、最低限監視するべき値は以下の2つです。

  • Reputation.BounceRate
  • Complaint 又は Reputation.ComplaintRate

バウンスと苦情はSESのサービス利用継続に関わる大切な値です。特にバウンスに関しては、SESの公式ドキュメントに以下のように記されています。

docs.aws.amazon.com

Q.アカウントのレビューや、送信の一時停止の原因になりえるバウンス率は公開されていますか?

A.最良の結果を得るには、バウンス率を 2% 未満に維持する必要があります。これより高いバウンス率は、E メールの配信に影響する可能性があります。 バウンス率が 5% を超えると、アカウントはレビュー対象になります。バウンス率が 10% を超える場合は、高いバウンス率の原因となった問題が解決するまで、以後の E メール送信を一時停止することがあります。

バウンスレートとして計算対象になるのは、バウンスのうち恒久的なハードバウンスが発生したメールのみです。一度ハードバウンスが発生した宛先のEメールアドレスには再送信しないことを前提に、全送信リクエスト数に対するハードバウンスの割合を5%未満、できるなら2%未満に維持することが望ましいです。

この2つのメトリクス以外には、通知や受信EメールをハンドリングするためのSNS、SQS、Lambdaなどのメトリクスも監視しておくと良いでしょう。

f:id:mic_psm:20191206042800p:plain
SESのモニタリングダッシュボードの例

その他気をつけること

専用IPアドレスの自動ウォームアッププロセスについて

専用IPアドレスの利用を開始したとき、SESはそのIPアドレスのEメール送信実績を増やして信頼性を向上させるためにEメール送信リクエストのロードバランシングを行います。具体的には以下のようなプロセスでウォームアップが進められます。

  • SESで送信されるEメールのうち、その時点で専用IPが許容するキャパシティの分を専用IPから送信する。オーバーした分は共有IPから送信する。(例えば、ウォームアップ1日目であれば10通だけを専用IPから送信し、その他の全てのEメールを共有IPから送信する。)
  • ウォームアップの完了にかかる期間には幅があり、最大で1〜2ヶ月かかる。
  • ウォームアップが完了すると、送信されるEメールの全てが専用IP経由になる。

これらのプロセスは全て自動で行われるためユーザーが気にする必要は特にはありませんが、利用するにあたって何点か注意しておくべきことがあります。

1つは、なるべく専用IPに適したワークロードで利用することです。SESでは、専用IPを使ったEメール送信をメールマガジンの大量送信や大規模なユーザーを抱えるシステムで利用されることを想定しています。ウォームアッププロセスによる送信リクエストのロードバランシングもそのワークロードを捌く前提で進むプロセスであるため、実際にシステムで利用されるEメール送信の機能がそんなに大きくなかったりする場合だと、自動ウォームアップの効果が不十分なままウォームアッププロセスが完了してしまう可能性があります。

もう1つは、専用IPプールの振る舞いについてです。専用IPアドレスを利用するにあたっては、IPアドレスを購入した上でSESのマネジメントコンソールからIPプールを作成し、それをConfiguration Setに設定した上でSendEmail又はSendRawEmailで指定することによって専用IPからEメールの送信ができるようになります。しかし、購入したあとIPプールに割り当てがされていない専用IPはses-default-dedicated-poolというIPプールに自動的に所属し、Configuration Setを指定しないEメール送信リクエストにおいて自動的にそれらのIPプールが使われるようになります。自動的に使われることによって直接的な問題が発生するわけではないですが、明らかに直感と反する挙動なので一応注意しておいたほうが良いでしょう。

docs.aws.amazon.com

開封通知・クリック通知について

SESではConfiguration Setを使って、Eメールを受信したユーザーの開封やクリックイベントを通知として受け取ることができます。

docs.aws.amazon.com

SESがこれらのトラッキングを実現する方法として導入しているのは、RFC 8098で定義されているようなDisposition-Notification-Toヘッダーを使った開封確認メール要求とは異なるものです。HTMLメールの本文を一部書き換える形で実現されるこのトラッキングは、Disposition-Notification-Toヘッダーのように開封確認メール要求のダイアログを相手側に表示することなく開封やクリックの通知を受け取ることを可能にしてくれます。

この方法はSES以外であってもEメールのイベントトラッキング機能を提供するサービスでは一般的に使われているものですが、この方法自体HTMLメールの一部を書き換える形で追跡可能なマーカーを埋め込むことによって実現されているため、そもそも書き換えが不可能な形式のEメール、つまりPlain Text形式のEメールではこれらの開封・クリック通知を受け取ることができません。

全てのEメール送信で追跡を有効にしたい場合は、たとえテキストのみのEメールであってもHTML形式かMulti Part形式で送るようにしましょう。

まとめ

Eメール自体、構成している技術の起源はかなり古いものです。拡張や改訂を繰り返しながら現在まで至る中で、機能と仕様が徐々に複雑になっていきました。パブリッククラウドの台頭によってこういった複雑な技術を低コストで利用できるようになった今日では、いかにそれらを正しい方法で使いこなせるか、どうすれば間違った使い方を踏まないかを知ることが重要だと考えます。たとえベースとなっているのが古く複雑な技術であっても、使いこなせた暁には大きな価値をもたらしてくれるはずです。

この記事が今後Amazon SESを利用するユーザーの方にとって少しでも手助けになってくれれば私としても嬉しい限りです。

明日の記事

明日の記事はroronyaさんが執筆予定です。乞うご期待。