概要
こんにちは。Wano株式会社でエンジニアをしている@kotobuki5991 です。
この記事はWano Group Advent Calendar20223 20日目の記事です。
弊社のプロダクトで新しく実装中の機能の一部に、S3(等のストレージ)に保存されたデータを読み取りメールを送信するという要件があります。
技術選定の中で、ストレージとしてWasabiを、メール配信サービスとしてSendGridを試しに触ってみたので設定・実装などを紹介します。
Wasabiとは?
S3完全互換のクラウドストレージで、現在13のリージョンが使用可能です。
すでにS3を使用しているアプリケーションであれば、修正の必要なく移行することが可能です。
また、S3に対して以下のような利点があります。
- 高速
- 下り料金無料!!!
SendGridとは?
クラウド型のメール配信サービスです。
アカウントを作成するだけで即日メールを送信でき、面倒でコストのかかるメールサーバの構築は不要です。
以下のようなメリットがあります。
- 12000通/月まで無料
- 独自ドメインは無料で使用できる。
- Fromのメールアドレスも自由に設定できる。
ただし、送信元を固定IPアドレスにするにはProプラン以上の選択が必要です。(10,000円〜)
Wasabiの設定からファイルのアップロードまで
まずはWasabiの設定です。
1. バケットの作成
wasabiのダッシュボードにログインし、Create Bucketをクリックします。
バケットの情報を入力し、Create Bucketを再度クリックします。
バケットのが完了しました!
ユーザーの作成
ダッシュボードのUsersから、ユーザー情報を入力してCreate Userをクリックします。
今回は、S3FullAccessのポリシーを付与しておきます。
作成後、アクセスキーのCSVがダウンロードできるので、忘れずにしておきましょう。
ダウンロードしたCSVに記載されているキーを~/.aws/credentialsに登録します。
以下のようになっていたらOKです。
$ cat ~/.aws/credentials
[wasabi] // 任意の名前
aws_access_key_id = XXXXXXXXXXXXXXXXXXX
aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
3. ポリシーの追加
次に先ほど作成したバケットにポリシーを追加します。
今回は以下のように追加しました。ユーザーやバケットは自身のものに修正してください。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAccessToBucketNamedBucket2",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456123456:user/sample-wasabi-user-1" // 作成したユーザーを指定
},
"Action": [
"s3:ListBucket",
"s3:GetObject",
"s3:PutObject",
"s3:GetObjectAcl",
"s3:PutObjectAcl",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::sample-wasabi", // 作成したバケットを指定
"arn:aws:s3:::sample-wasabi/*" // 作成したバケットを指定
]
}
]
}
4. ファイルのアップロード
実際にファイルをアップロードします。
ここで重要なのはエンドポイントのURLです。
ap-northeast-1の場合は、https://s3.ap-northeast-1.wasabisys.com
となります。
上記を指定しないと、AWSを見に行ってしまいます。
$ aws s3 --profile wasabi --endpoint-url https://s3.ap-northeast-1.wasabisys.com cp ./wasabifile1.txt s3://sample-wasabi/hoge.txt
upload: ./wasabifile1.txt to s3://sample-wasabi/hoge.txt
先ほどアップロードしたファイルが確認できました。
Wasabiからファイルリスト取得〜メール送信の実装例
プロジェクトの構成は以下の通りです。
root/
├ aws-util/s3.go
├ sendgrid
│ └ main.go
│ └ sendgrid.env
├ .env
├ go.mod
└ go.sum
Wasabiからデータを読み取る実装
今回は、アップロードされたファイル一覧を確認したいため、ListObjectsメソッドを実装しています。
package awsutil
import (
"fmt"
"log"
"os"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/joho/godotenv"
)
type S3Client interface {
ListObjects(bucketName string) ([]*s3.Object, error)
}
type implS3Client struct {
S3Client *s3.S3
}
func NewS3Client() (i *implS3Client, err error) {
err = godotenv.Load("../.env")
if err != nil {
fmt.Printf("読み込み出来ませんでした: %v", err)
return nil, err
}
profile := os.Getenv("WASABI_AWS_PROFILE")
region := os.Getenv("WASABI_AWS_REGION")
// create a configuration for profile
s3Config := aws.Config{
Endpoint: aws.String("https://s3.ap-northeast-1.wasabisys.com"),
Region: aws.String(region),
S3ForcePathStyle: aws.Bool(true),
}
// create a new session using the config above and profile
goSession, err := session.NewSessionWithOptions(session.Options{
Config: s3Config,
Profile: profile,
})
// check if the session was created correctly.
if err != nil {
fmt.Println(err)
return nil, err
}
// create a s3 client session
s3Client := s3.New(goSession)
i = &implS3Client{
S3Client: s3Client,
}
return i, err
}
func (s *implS3Client) ListObjects() ([]*s3.Object, error) {
bucketName := os.Getenv("WASABI_AWS_BUCKET")
result, err := s.S3Client.ListObjectsV2(&s3.ListObjectsV2Input{
Bucket: aws.String(bucketName),
})
var contents []*s3.Object
if err != nil {
log.Printf("Couldn't list objects in bucket %v. Here's why: %v\n", bucketName, err)
} else {
contents = result.Contents
}
return contents, err
}
メール送信までの実装
次に、S3の情報を読み取ってメールを送信する実装です。
SendGridのアカウントは持っている前提で説明します。
マイページのダッシュボードから、Email API >> Integration Guideへ進みます。
Goを選択します。
画面の指示の通りにコマンドを実行します。
// using SendGrid's Go Library
// https://github.com/sendgrid/sendgrid-go
package main
import (
"fmt"
"log"
"os"
awsutil "github.com/fukuju-t/mail-project/aws-util"
"github.com/sendgrid/sendgrid-go"
"github.com/sendgrid/sendgrid-go/helpers/mail"
)
const confirmMessage = `
ファイル数: %d件
%s
`
func main() {
s3, err := awsutil.NewS3Client()
if err != nil {
log.Fatal(err)
}
objects, err := s3.ListObjects()
if err != nil {
log.Fatal(err)
}
objectsCount := len(objects)
objectNames := ""
for _, v := range objects {
objectNames = "・" + objectNames + *v.Key + "\n"
}
from := mail.NewEmail("Wasabi テストメール", "test@example.com")
subject := "Wasabiのファイルリストです"
to := mail.NewEmail("hoge", "yourmailaddress@test.com") // 送信先(今回は自身の)メールアドレス
plainTextContent := fmt.Sprintf(confirmMessage, objectsCount, objectNames)
htmlContent := ""
message := mail.NewSingleEmail(from, subject, to, plainTextContent, htmlContent)
client := sendgrid.NewSendClient(os.Getenv("SENDGRID_API_KEY"))
response, err := client.Send(message)
if err != nil {
log.Println(err)
} else {
fmt.Println(response.StatusCode)
fmt.Println(response.Body)
fmt.Println(response.Headers)
}
}
メール送信!
成功すると以下のような出力となります。
$ go run main.go
202
map[Access-Control-Allow-Headers:[Authorization, Content-Type, On-behalf-of,
x-sg-elas-acl] Access-Control-Allow-Methods:[POST] Access-Control-Allow-Origin:
// 以下省略
以下のようなメールが届きます。
まとめ
今回はWasabiとSendGridを試してみました。
WasabiはバケットからのDLなど下り通信が多いほどS3よりお得に使えそうです。
また、SendGridは実装が非常に楽で無料なのがいいですね。
現在、Wanoグループ / TuneCore Japan では人材募集をしています。興味のある方は下記を参照してください。
Wano | Wano Group JOBS
TuneCore Japan | TuneCore Japan JOBS