Works by

Ren's blog

@rennnosuke_rk 技術ブログです

【Go】S3互換local storageとしてMinIOを立ち上げてaws-sdk-go-v2から接続する

MinIOドキュメントに aws-sdk-go を使用したサンプルはあるのですが、 aws-sdk-go-v2 のものはないため備忘録を残しておきます。

MinIO

オープンソースのオブジェクトストレージです。 S3互換のため、S3 API経由で接続することができます。

min.io

MinIO の立ち上げ

Docker imageがあるのでこれを利用してserver用コンテナを立ち上げます。

$ docker container run -d --name minio -p 9000:9000 -p 9001:9001 minio/minio server /data --console-address ":9001"

MinIOには管理コンソールがあるので、アプリケーション用だけでなく管理コンソール用portも指定&forwardingします。

管理コンソールのportは --console-address で指定できます。指定しないとephemeral portが毎回ランダムに割り当てられてしまうので、明示的に指定したほうが楽です。

管理画面はブラウザ上でアクセスでき、credentialは↓で確認できます。credentialは環境変数 MINIO_ROOT_USER MINIO_ROOT_PASSWORD で設定できますが、特に指定がなければ minioadmin:minioadmin になります。

$ docker logs minio 
API: http://172.17.0.2:9000  http://127.0.0.1:9000 

Console: http://172.17.0.2:9001 http://127.0.0.1:9001 

Documentation: https://docs.min.io
WARNING: Detected default credentials 'minioadmin:minioadmin', we recommend that you change these values with 'MINIO_ROOT_USER' and 'MINIO_ROOT_PASSWORD' environment variables

aws-sdk-go-v2でMinIOを叩く

import (
    "bytes"
    "context"
    "log"

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

func main() {
    ctx := context.Background()

    accessKey := "<access-key>"
    secretKey := "<secret-key>"
    cred := credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")

    endpoint := aws.EndpointResolverFunc(func(service, region string) (aws.Endpoint, error) {
        return aws.Endpoint{
            URL: "http://localhost:9000",
        }, nil
    })

    cfg, err := config.LoadDefaultConfig(ctx, config.WithCredentialsProvider(cred), config.WithEndpointResolver(endpoint))
    if err != nil {
        log.Fatalln(err)
    }

    // change object address style
    client := s3.NewFromConfig(cfg, func(options *s3.Options) {
        options.UsePathStyle = true
    })

    // get buckets
    lbo, err := client.ListBuckets(ctx, nil)
    if err != nil {
        log.Fatalln(err)
    }
    buckets := make(map[string]struct{}, len(lbo.Buckets))
    for _, b := range lbo.Buckets {
        buckets[*b.Name] = struct{}{}
    }

    // create 'develop' bucket if not exist
    bucketName := "develop"
    if _, ok := buckets[bucketName]; !ok {
        _, err = client.CreateBucket(ctx, &s3.CreateBucketInput{
            Bucket: &bucketName,
        })
        if err != nil {
            log.Fatalln(err)
        }
    }

    // put object
    _, err = client.PutObject(ctx, &s3.PutObjectInput{
        Bucket: &bucketName,
        Key:    aws.String("hogehoge"),
        Body:   bytes.NewReader([]byte("Hello, MinIO!")),
    })
    if err != nil {
        log.Fatalln(err)
    }
}

aws-sdk-go-v2からMinIOを叩く際に考慮する点が1つあります。

オブジェクトアドレスをpath styleにする

S3にはpath style と virtual-hosted styleのアドレスがあります。path styleではバケット名がpathに含まれていましたが、virtual-hosted styleではURLドメインに含まれるようになります。

  • path style: https://s3-us-east-1.amazonaws.com/bucket-name/images/obj.jpeg
  • virtual-hosted style: https://bucket-name.s3.amazonaws.com/images/obj.jpeg

AWS S3だと現時点でvirtual-hosted style のアドレスが使用されていますが、MinIOは対応していないのでpath styleでアクセスする必要があります。

path styleへの変更は s3.NewFromConfigoptions.UsePathStyle=true 書き換えを行う func(options *s3.Options) を渡します。

client := s3.NewFromConfig(cfg, func(options *s3.Options) {
    options.UsePathStyle = true
})

管理コンソール上でuploadされたオブジェクトを確認できます。

f:id:rennnosukesann:20211122111122p:plain

参考文献

hub.docker.com

github.com

docs.min.io

github.com