DjangoからAmazon SES経由でメール送信する

Djangoでは標準機能としてメール送信の処理が用意されていますが、デフォルトの設定ではローカル環境からメールが送信されるようになっています。本番運用するためには、SMTPサーバーなどを用意する必要がありますが、AWSのマネージドなメール送信サービスであるAmazon SESを利用することで構築・運用が簡単になります。そこで、DjangoからAmazon SESを経由してメール送信を処理するための設定方法をまとめてみました。
2021.02.25

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

コンサル部のtobachi(@toda_kk)です。

Djangoでは標準機能としてメール送信の処理が用意されていますが、デフォルトの設定ではローカル環境からメールが送信されるようになっています。

動作確認や検証の際はそのままでも問題ないかもしれませんが、本番稼働してメール送信をする際には送信元ドメインが正しく認証されている必要があります。

1つのサーバー内にDjangoとSMTPサーバーを同居させて起動させることで解決することもできなくはないかと思いますが、AWSのメール送信サービスであるAmazon SES(Simple Email Service)を利用することで簡単に設定することができます。

そこで今回は、DjangoからAmazon SESを経由してメール送信を処理するための設定方法をまとめてみました。

注意事項

本記事の内容は、執筆時点(2021年2月)での最新バージョンに基づいて記載しています。 後続のバージョンでは記載の通りに動作しない可能性がありますので、ご注意ください。

  • Django: 3.1.7
  • Django-SES: 1.0.3

Amazon SESとは?

ごく簡単に言えば、Amazon SESはメール送信の機能をマネージドで提供してくれるサービスです。詳細についてはAWS公式のドキュメントをご参照ください。

自前でSMTPサーバーを構築・管理せずに済むため、例えばWebアプリケーションからメール送信する機能を実装する際に必要なインフラリソースに関するコストを減らすことができます。

今回は、Amazon SESを利用開始する際の手順や設定項目については省略します。実際に構築・運用する際は、下記の記事が参考になるかと思います。

Djangoのメール送信機能

上述の通り、Djangoではメール送信機能が標準で用意されています。具体的な利用方法については、Django公式ドキュメントの通りです。

ただし、デフォルトだとSMTPサーバーの向き先がlocalhostで設定されており、Djangoが起動している環境から直接メールが送信されることになります。開発や検証に利用する環境ではそれでも問題ないケースもありますが、本番稼働させるとなるとドメイン認証など冒頭に述べたように課題が発生します。

そこで、Amazon SESを経由してメール送信できるように設定してみます。

DjangoからAmazon SESを経由してメール送信する

DjangoからAmazon SESに接続する方法として、今回はデファクトスタンダードとなっている Django-SES を利用します。

具体的な利用方法や設定内容については、公式GitHubリポジトリにあるドキュメントの通りではありますが、アプリケーションの設定ファイルとなるsettings.pyに下記の項目が必要となります。

  • EMAIL_BACKENDdjango_ses.SESBackendを指定する。
  • Amazon SESを利用するリージョンに合わせて、AWS REGIONおよびAWS_SES_REGION_ENDPOINTを設定する。

また、オプションの項目としてAWSアクセスキーおよびシークレットキーを指定できるようになっています。しかし、AWSのベストプラクティスに則るのであれば、IAMユーザーのアクセスキーといった永続的な認証情報をアプリケーションの設定に直接書き込むのではなく、環境変数やIAMロールなどを用いて利用するべきでしょう。

例えば、単にEC2インスタンス上でDjangoアプリケーションを起動してる場合であれば、Amazon SESへのアクセスを許可したIAMロールをインスタンスプロファイルとして付与するのが良いでしょう。メールを送信するだけであれば、最低限ses:SendRawEmailを許可しておけば良さそうです。

Djangoのメール送信の仕組み

以上でやりたいことは実現できました。ここからは、Djangoで提供されているメール送信機能の仕組みについて、少しだけ深堀りしてみます。

上記の手順では、settings.pyファイルのEMAIL_BACKENDパラメーターの値を指定していました。このパラメーターは、メール送信を処理する際に利用するPythonクラスを指定する項目です。

デフォルトの設定ではバックエンドとしてdjango.core.mail.backends.smtp.EmailBackendクラスが指定されています。またEMAIL_HOSTにはlocalhostが指定されているため、起動環境のSMTPポートを参照するようになっています。

django/global_settings.py at 7785e03ba89aafbd949191f126361fb9103cb980 · django/django

ここで、django.core.mail.backends.smtp.EmailBackendクラスの実装を確認してみます。settings.pyで指定した値からSMTPのホスト名やユーザー・パスワードといった認証情報を取得し、SMTPサーバーとコネクションを開始しメッセージを送信する処理となっています。

django/smtp.py at 7785e03ba89aafbd949191f126361fb9103cb980 · django/django

例えば、SaaSのメール配信サービスであるSendGridをDjangoから利用する場合は、上記のクラスを利用したまま認証情報などを設定することで実現できます。

要するにDjangoでは、設定した接続および認証情報に基づいてSMTPのコネクションを開始してメッセージを送信する処理が提供されているのです。

サードパーティライブラリは不要?

ということは、わざわざDjango-SESといったサードパーティのライブラリを使わなくても、SMTPの接続および認証情報を指定すればAmazon SESを経由したメール送信が実現できるのでは? と考えられます。

では、実際に試してみましょう。今回は東京リージョン(ap-northeast-1)でAmazon SESを利用する想定です。

まずはsettings.pyに下記の設定を追加します。EMAIL_BACKENDはデフォルトのまま、SMTP-AUTHの認証および接続情報を記載します。

# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'email-smtp.ap-northeast-1.amazonaws.com'
EMAIL_HOST_USER = '${SMTPユーザー名}'
EMAIL_HOST_PASSWORD = '${SMTPパスワード}'
EMAIL_PORT = 587
EMAIL_USE_TLS = True

ここで、SMTPユーザー名およびSMTPパスワードIAMユーザーの認証情報とは異なります。詳細についてはAWS公式ドキュメントをご参照いただきたいですが、IAMユーザーを利用する形でSMTP認証情報を作成する必要があります。

これで設定が完了しました。続いて、動作確認してみましょう。manage.pyからshellオプションを指定して実行してみます。

$ python manage.py shell --settings=${settingsファイル}

>>> from django.core.mail import send_mail

>>> send_mail(
    'メールタイトル',
    'メール本文',
    '${送信元メールアドレス}', # Amazon SESで設定したドメインを指定する必要があります。
    ['${送信先メールアドレス}'],
    fail_silently=False,
)

1

認証情報に間違いがなければメール送信処理が成功し、指定した送信先アドレスにメールが届きます。これで、サードパーティのライブラリを使うことなくAmazon SESを経由したメール送信の処理を実装することができました。

実際にDjangoアプリケーションを開発する際にどういった方法を取るべきかはケースバイケースではありますが、選択肢としてどちらの方法でも問題ないということが確認できたかと思います。

以上、コンサル部のtobachi(@toda_kk)でした。