AWS SDK for Go v2 でカスタムエンドポイントを使用 - MinIO へ接続

AWS SDK for Go v2 を使って S3 互換の MinIO へ接続してみました。

今回のサンプルコードは こちら

はじめに

初期化(プロジェクトの作成)を実施します。

> go mod init sample

AWS SDK for Go v2 で S3 を扱うための依存モジュールを追加します。

> go get github.com/aws/aws-sdk-go-v2
・・・
> go get github.com/aws/aws-sdk-go-v2/config
・・・
> go get github.com/aws/aws-sdk-go-v2/service/s3
・・・

カスタムエンドポイント

基本的に、AWS SDK for Go v2 では以下のような実装でカスタムエンドポイントを使用できます。

カスタムエンドポイントの適用例
resolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, opts ...interface{}) (aws.Endpoint, error) {
    return aws.Endpoint{
        URL:               "http://localhost:9000", // カスタムエンドポイント
        HostnameImmutable: true,
    }, nil
})

cfg, err := config.LoadDefaultConfig(
    context.TODO(),
    config.WithEndpointResolverWithOptions(resolver),
)
・・・
svc := s3.NewFromConfig(cfg)

HostnameImmutabletrue にする事で URL で指定したエンドポイントにそのまま接続しますが、デフォルトの false だとホスト名へバケット名を付与するので注意が必要です。

例えば、カスタムエンドポイントが http://localhost:9000バケット名が sample1 の場合、HostnameImmutable が false だと http://sample1.localhost:9000 へ接続する事になります。

また、カスタムエンドポイントの代わりに aws.Endpoint{}, &aws.EndpointNotFoundError{} を返すようにすると、通常のエンドポイントを使うようになります。

下記サンプルでは、S3 への接続時に S3_ENDPOINT 環境変数が設定されていればカスタムエンドポイント(MinIO)へ接続し、そうでなければ通常のエンドポイントを使うようにしてみました。

処理としては、S3(もしくは MinIO)からオブジェクトを取得してローカルファイルへ保存します。

main.go(サンプルコード)
package main

import (
    "context"
    "io"
    "log"
    "os"

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

func main() {
    endpoint := os.Getenv("S3_ENDPOINT")
    bucket := os.Getenv("BUCKET_NAME")

    key := os.Args[1]
    dest := os.Args[2]

    resolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, opts ...interface{}) (aws.Endpoint, error) {

        if service == s3.ServiceID && len(endpoint) > 0 {
            // カスタムエンドポイント使用
            return aws.Endpoint{
                URL:               endpoint,
                HostnameImmutable: true,
            }, nil
        }
        // 通常のエンドポイント使用
        return aws.Endpoint{}, &aws.EndpointNotFoundError{}
    })

    cfg, err := config.LoadDefaultConfig(
        context.TODO(),
        config.WithEndpointResolverWithOptions(resolver),
    )

    if err != nil {
        log.Fatal(err)
    }

    svc := s3.NewFromConfig(cfg)
    // オブジェクト取得
    res, err := svc.GetObject(context.TODO(), &s3.GetObjectInput{
        Bucket: &bucket,
        Key:    &key,
    })

    if err != nil {
        log.Fatal(err)
    }
    // ローカルファイル作成
    fw, err := os.Create(dest)

    if err != nil {
        log.Fatal(err)
    }

    defer fw.Close()
    // ローカルファイルへ書き込み
    _, err = io.Copy(fw, res.Body)

    if err != nil {
        log.Fatal(err)
    }
}

動作確認

MinIO を起動して下記を実施しておきます。

MinIO へ接続するための環境変数を設定します。

環境変数の設定(Windows 環境の場合)
set AWS_ACCESS_KEY_ID=minioadmin
set AWS_SECRET_ACCESS_KEY=minioadmin

set BUCKET_NAME=sample1
set S3_ENDPOINT=http://localhost:9000

実行します。

実行
> go run main.go a01/item-1.jpg output.jpg

MinIO の a01/item-1.jpg を output.jpg として保存できました。

ついでに、S3_ENDPOINT 環境変数の値を空にすると https://sample1.s3.ap-northeast-1.amazonaws.com/a01/item-1.jpg へアクセスするようになりました。