Django で Amazon SES を使う
Amazon さんが、Amazon Web Services の下にメール送信サービスSimple Email Service (SES)を提供している。このサービスは、主に大量なメールでも正しく送信して、メールがちゃんと届くようにサービスを提供している。
Connpass ではメール送信にSESを使っている。その事実がユーザーさんに判明されていたので、Djangoではどう使えばいいかを説明したいなと思った。
実は、SESをDjangoで使うのが簡単過ぎて、あまりネタにならないので、Django の説明とSESのはまりどころの話をしょうと思っている。本来は この話は Djangoよりは、SESの話になるかも。
django-ses
Connpass はdjango-sesを使っている。django-sesはDjangoのEMAIL_BACKEND設定で使えるメールバックエンドを提供している。内部では、botoというAWS APIのためのクライアントライブラリを使っている。
インストールはうつもの pip で簡単:
pip install boto django-ses
そして、Djangoのsettings..pyで4つの設定を加える。以下の適当な値が入っているので、自分のAWSアカウントの該当の設定を入れてください。
NOTE:
AWS_ACCESS_KEY_IDとAWS_SECRET_ACCESS_KEYとアクセス権限について、もうちょっと書くので、下まで読んで下さいね。
AWS_ACCESS_KEY_ID = '<access key>'
AWS_SECRET_ACCESS_KEY = '<access secret>'
EMAIL_BACKEND = 'django_ses.SESBackend'
SERVER_EMAIL = u"noreply <no-reply@example.com>"
この3つの設定で Django アプリの修正は終わり。AWS_ACCESS_KEY_IDとAWS_SECRET_ACCESS_KEYはdjango-storagesと共通なので、S3と簡単に同時に使えます。
それで、アプリケーションで普通にDjangoのsend_mail()を呼び出すことで、SES経由でメール送信できる。
本番アクセス
最初にアカウントをセットアップした時、SESは「sandbox」モードになっている。sandboxモードは特に使い方は特殊ではないのですが、メール送信が大きく制限されている。
- メール送信は24時間で最大200件
- 速度は最大秒間1件
- 確認したメアドからしか送信できない(Fromヘッダー)
- 確認したメアドにしか送れない(Toヘッダー)
SESを本格的に使う場合はProduction Accessを申請する必要がある。本番アクセスの許可が来るまでに数日がかかるので、余裕を持って申請をしてください。
クオータ
設定が非常に簡単ですが、AWS側でいろな設定が必要です。まずは、クオータの話。AWSはメール送信のクオータがあります。最初はクオータが小さくて、信用性が高いメールを送信するにつれて、クオータが上がってくる仕組みになっています。上がってくるタイミングはメールの量で変わってくるけど、少なくとも1段階上がるのに、数日かかります。
最初のクオータは正直小さすぎて、本番アプリには使い物にならないので、クオータが上がるように、何日間でSESの導入テストを行なう必要がある。メールの内容も見ているようなので、アプリケーションが送っているメールと同じような内容で送ればいいです。何かの自前で作ったスクリプトで適当なメールを送っちゃいけない。本番でリリース時にメールのが内容が急にかわったり、そもそもスパムっぽいメールを送ってしまったりするとAPIから怪しく見れて、メールを拒否するかもしれない。
以下はAmazonのドキュメントに書いているクオータが上がるための勧め。
- Send high-quality content (質のいい内容を送る。スパムをそもそも送らない)
- Send real production content (本番と同じような内容を送る)
- Send near your current quota (クオータに近い量を送る)
- Have low bounce and complaint rates (bounce と complaint率を低くする)
1と2と4は常にやればいいことだね。そうしないと、メール送信が拒否される場合があります。
3はクオータが上がって欲しい時にやればいい。つもり、クオータの制限に近づかなければ、SESがそのクオータに問題ないと判定して、クオータを上げてくれない。クオータの制限によく近づく場合は、クオータを上げる必要性を検出して、上げてくれる。
Connpassでやった時は、メール送信量は1日5000件くらいじゃないと厳しいと思って、SESのクオータがリリースギリギリまで、上がって来なかった。今は少し増しになっているかもしれないけど、確かにConnpassの場合は必要なレベルに上がるまでに少なくとも2週間くらいかかったと思います。
最後に、クオータの自動調整と別に制限を上げる要求(Extended Access Request)を出すことが出来る。ただし、すぐアカウントを作った後にはこれは期待できない。
メール受信
SESはメール受信の機能がないですが、自分のサーバーでPostfixなどを立てて、DNSのMXレコードを設定すれば、SMTPで受信することができます。ただし、SMTPサーバーを立てなければ、特に何もしなくともいいですが、SMTPサーバーをたてる場合、SESで送信するメールのFromに入っているアドレスは、SMTPサーバーにも登録されてないといけないらしいです。
わざわざnoreplyのメアドはSMTPサーバーに登録しなくてもいいじゃんって思う人は多いと思うのですが、SESのスパム防止の対策で、SMTPサーバーにメアド存在チェックの為に、見に来ます。実際のPostfixログでAWSからアクセスが来ているが見れます。SMTPサーバーにそのメアドが登録されてないと以下のエラーメッセージでメール送信が拒否される場合があります。これは実際にConnpassで起こった問題です。
BotoServerError: BotoServerError: 400 Bad Request
<ErrorResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
<Error>
<Type>Sender</Type>
<Code>MessageRejected</Code>
<Message>Address blacklisted.</Message>
</Error>
<RequestId>hogehoge</RequestId>
</ErrorResponse>
メールサーバーは具体的に何を返せばいいのか? まず、以下の感じだと そのメールアドレスが登録されていないことが確認できる。
$ telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 connpass.com ESMTP Postfix (Debian/GNU)
helo hi
250 connpass.com
mail from: <hoge@example.com>
250 2.1.0 Ok
rcpt to: <no-reply@connpass.com>
550 5.1.1 <no-reply@connpass.com>: Recipient address rejected: User unknown in local recipient table
登録されている場合、以下の感じになります。
$ telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 connpass.com ESMTP Postfix (Debian/GNU)
helo hi
250 connpass.com
mail from: <hoge@example.com>
250 2.1.0 Ok
rcpt to: <no-reply@connpass.com>
250 2.1.5 Ok
no-reply のユーザーだと、以下コマンドを実行すると、メアドを登録することができます。 こうすると、 no-reply に来るメールはすべて無視します。
# sudo echo "no-reply: /dev/null" >> /etc/aliases
# sudo newaliases
まとめ
SESはセットアップが少し面倒くさくて時間かかるのですが、メールが届かない問題や、スパムとして判定される問題が避けるのが大きなメリットで、Connpassで使ったのが良かったと思います。django-sesでBackend切り替えだけですし、ベンダーロックインなどはそんなに問題なくて安心です。
SES もバウンスメールの処理もできるので、その処理を今 僕のdjango-sesのフォークを実装しようとしていますので、興味ある方は見てみてください。
