omuronの備忘録

個人的な備忘録

Lambda で Go の S3 イベント駆動を試す

Lambda で Go のめっちゃ基礎 の続きです。
S3 イベントでファイル取得して、別 S3 に出力してみます。

GitHub

github.com

この README のサンプルを参考に実装。

開発環境

$ sw_vers 
ProductName:    Mac OS X
ProductVersion: 10.15.7
BuildVersion:   19H1419

$ go version
go version go1.17 darwin/amd64

Lambda にトリガーを追加

Lambda 関数の「関数の概要」で S3 イベント駆動で動かすためにトリガーを追加。

f:id:omron:20211025102128p:plain

トリガー元のバケットを指定して保存。

f:id:omron:20211025102239p:plain

トリガー元バケットに Lambda からの参照を許可

トリガー元のバケットの「アクセス許可」から

f:id:omron:20211025102453p:plain

バケットポリシーを設定。

f:id:omron:20211025103156p:plain

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::AWSアカウントID:role/service-role/Lambdaの実行ロール"
            },
            "Action": [
                "s3:GetObject",
                "s3:GetObjectAcl"
            ],
            "Resource": "arn:aws:s3:::インプットバケット名/*"
        }
    ]
}

Lambda の実行ロールは、Lambda 関数の「設定」から参照できるので、この ARN を設定する。

f:id:omron:20211025103417p:plain

出力先バケットに Lambda からの保存を許可

出力先バケットにも同様にバケットポリシーを設定。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::AWSアカウントID:role/service-role/Lambdaの実行ロール"
            },
            "Action": [
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::アウトプットバケット名/*"
        }
    ]
}

これで、AWS 側の準備は整いました。

Lambda の実装

エラー処理などは考えずに、取りあえず動くだけの実装。

// main.go
package main

import (
    "bytes"
    "context"
    "io/ioutil"
    "log"

    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/s3"
)

// 出力バケット
const S3Output = "出力バケット名"

// https://github.com/aws/aws-lambda-go/blob/main/events/README_S3.md を参考
func handler(ctx context.Context, s3Event events.S3Event) {
    // セッション獲得
    sess := session.Must(session.NewSession())
    // S3 clientを作成
    svc := s3.New(sess)

    for _, record := range s3Event.Records {
        s3rec := record.S3
        log.Printf("[%s - %s] Bucket = %s, Key = %s \n", record.EventSource, record.EventTime, s3rec.Bucket.Name, s3rec.Object.Key)

        // オブジェクト取得
        obj, err := svc.GetObject(&s3.GetObjectInput{
            Bucket: aws.String(s3rec.Bucket.Name),
            Key:    aws.String(s3rec.Object.Key),
        })
        if err != nil {
            log.Fatal(err)
        }

        // オブジェクト読み込み
        rc := obj.Body
        defer rc.Close()
        content, err := ioutil.ReadAll(rc)
        if err != nil {
            log.Fatal(err)
        }

        // オブジェクト書き込み
        _, err = svc.PutObject(&s3.PutObjectInput{
            Body:   bytes.NewReader(content),
            Bucket: aws.String(S3Output), // バケットは適宜変更
            Key:    aws.String(s3rec.Object.Key),
        })
        if err != nil {
            log.Fatal(err)
        }
    }

}

func main() {
    // Make the handler available for Remote Procedure Call by AWS Lambda
    lambda.Start(handler)
}

試す

ビルドして Lambda にアップロードして、

$ go mod init sample
$ go get
$ GOOS=linux GOARCH=amd64 go build -o main main.go
$ zip main.zip main

インプット側の S3 に何かファイルを置けば、コピーがアウトプット側のS3 に出力されるはず。

ソース

github.com

さいごに

Lambda で Go は、後から追加されたランタイムなので、Python や Node.js と比べてサンプルが少ないですね。
サクッとコピペで試そうとしたけど、断片的にしか見つけられなかったので、自分の試したかったことだけをまとめました。

参考にしたサイト

s3 - Amazon Web Services - Go SDK
s3manager package - github.com/aws/aws-sdk-go/service/s3/s3manager - pkg.go.dev
session package - github.com/aws/aws-sdk-go/aws/session - pkg.go.dev
Go言語(golang)でS3へファイルをアップロードする | DevelopersIO
Go言語(golang)でS3からファイルを取得する | DevelopersIO
3分で作る、S3イベントで実行するLambda | DevelopersIO
クレデンシャルの適切な扱い方 ー AWS SDK for Goの場合 | DevelopersIO
【Golang】AWSLambdaからS3にアップロードする - 怠慢プログラマーの備忘録
【Go】AWS SDK GoのGetObjectの返り値が16進数になった...!