Docker で AWS SES API V2 に対応したローカルサーバーを立ち上げる

はじめに

AWS SES を使用したアプリケーションを開発する場合、テストやローカル環境などでメールを送信できるようにするため、ローカルな SES サーバーを立ち上げて使用することがあると思います。

これは、 SES だけに言えることではなく、S3 など他の AWS ソリューションでもローカルで立ち上げることができます。そして、 AWS をローカルでエミュレートするアプリケーションは多くありますが一番使われているのは LocalStack ではないでしょうか?

www.localstack.cloud

S3 などでは LocalStack を使えば特に問題はないのです。ですが、 AWS SES だけ問題があります。実際に LocalStack のドキュメントを見てみると、

SESv1 is available in LocalStack Community image, while SESv2 and SMTP integration is available in LocalStack Pro. The supported APIs are available on the API coverage page for SESv1 and SESv2.
Simple Email Service (SES) | Docs

はい、なんと、 AWS SES API V2 経由 LocalStack で SES を使いたい場合は LocalStack Pro プランが必要なんですよね*1

しかし、SESのためだけに Pro になるのは…… というケースのために GitHub - domdomegg/aws-ses-v2-local: ☁📬 A local version of Amazon Simple Email Service (AWS SES) supporting the V1 and V2 API を使用したローカル SES サーバーの立ち上げ方について紹介します。

github.com

aws-ses-v2-local とは?

AWS SES をローカルでエミュレートできるツールです。API の受取部として動作します。実際にここからメール送信などは行えず、専用のダッシュボードが用意されていて、そこで API 経由で受け取ったメールが表示される仕組みになっています。地味にダッシュボードはリッチになっていてダークモードとかにも対応しているっぽい、すごい。

特筆するポイントとして、 V1 と V2 どちらの API にも対応しています。

Note

ちなみに、送信されたメールの保存先はインメモリのようなので再起動するとデータはすべて消えてしまうようです。

aws-ses-v2-local/src/store.ts at 5cf711b82753612666dcd69f15636493255f5a9c · domdomegg/aws-ses-v2-local · GitHub

簡単な使い方

このアプリケーションは node.js で作られていて、npm にアップロードされているため、 npm 経由で簡単にインストールが可能です。

npm i -g aws-ses-v2-local
aws-ses-v2-local

これでもいいですが、これだけなら npx を使えば1行で実行することができます。

npx -y aws-ses-v2-local

この状態で http://localhost:8005 にアクセスすることでダッシュボードが確認できるかと思います。

Tip

aws-ses-v2-local はデフォルトで [::1]:8005 を受け付けるらしいです。[::1] は IPv6 のループバックアドレスなので、 IPv4 でしか対応していない環境などではアクセスできません。そのため、明示的に 0.0.0.0 を指定して IPv4 で受け付けるといいです。

npx -y aws-ses-v2-local --host 0.0.0.0

アプリケーション側では、以下のようにエンドポイントを localhost:8005 とすることで aws-ses-v2-local にメールを送信することが可能になります。以下は Go での実装例です。

package main

import (
    "context"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/credentials"
    "github.com/aws/aws-sdk-go-v2/service/sesv2"
)

func main() {
    ctx := context.Background()
    awsConfig := config.LoadDefaultConfig(
        ctx,
        config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider("test", "test", "test")),
        config.WithRegion("ap-northeast-1"),
    )
    // ここを localhost にする
    endpoint := "http://localhost:8005"

    client := sesv2.NewFromConfig(awsConfig, func(o *sesv2.Options) {
        // BaseEndpoint を上書きすることで、APIの送信先を変更することができる
        o.BaseEndpoint = endpoint
    })

    input := &sesv2.SendEmailInput{
        ...
    }

    _, err := client.SendEmail(ctx, input)
    if err != nil {
        panic(err)
    }
}

Dockerfile

さて、これを Docker で利用する方法についても解説しましょう。

FROM node:22

WORKDIR /app
EXPOSE 8005:8005

CMD ["npx", "-y", "aws-ses-v2-local", "--host", "0.0.0.0"]

特にむずかしくないです。 node のベースイメージはとりあえず現時点での最新版を使えばいいと思います。

さらに、compose.yaml で定義する場合は以下のようにします。この例では、inline で image, command, working_dir を指定してしまっていますが、別で Dockerfile を作って build を使って指定することも可能です。

version: '3'
services:
  ses-local:
    container_name: ses-local
    image: node:22
    working_dir: /app
    command: ["npx", "-y", "aws-ses-v2-local", "--host", "0.0.0.0"]
    ports:
      - "8005:8005"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8005/health-check"]

オススメポイントとして、http://localhost:8005/health-check が用意されていて、サーバーの準備が完了すると Response 200 で応答が返ってきます。これを docker compose の healthcheck で確認することで depends_on で簡単に依存を定義することが可能となります。

depends_on で healthcheck を利用する方法は過去にエントリを書いているのでぜひ参考にしてみてください。

blog.cateiru.com

参考文献

*1:Proを使えるのであれば、こちらを使用したほうが絶対良い