minioを利用してS3のバケット、オブジェクト操作をローカル環境で検証してみた!

どもども、GW早々ブログを更新している僕です。


皆様、Amazon S3は利用されていますでしょうか。バケットを作成しアプリケーションからファイルを保存したり画像を格納してアプリケーション側で表示したりと多種多調な使い方ができるAWS屈指の人気サービスです。


しかし、その利便性故に利用頻度が高く、たった一つのオペレーションミスでオブジェクトを削除なんてしてしまったらアプリケーションにどんな甚大な被害がでるか想像に難くありません。


一番リスクが高いローカル開発環境においては既に稼働しているものではなく、別途S3バケットを用意しておきたい…しかし開発メンバーごとに検証用バケットを用意するなんて…そんな時にとても優秀なものを同僚に教えていただいたので今回は紹介したいと思います。

その名はminio

というわけで今回紹介するのはminioです!minioとはAmazon S3互換のオブジェクトストレージです。特徴としてはクラウドサービスではなくローカルで起動するという点です。


ローカル検証でよくあるパターンとしてはこのコード、本当にS3にオブジェクトをアップロードやダウンロードできるのかな?といったオブジェクトやバケットはなんでもいいんだけど、とにかくちゃんと動作するか確認したい場合などがあります。


そんな時にはminioをローカル環境で立ち上げて操作の対象とすることで既にアプリケーションで使用されているバケットやオブジェクトを一切気にすることなく検証を行うことができます。


それでは実際にどのように操作するのかご紹介いたします。

docker compose による立ち上げ

今回はdocker-composeによるローカル環境の立ち上げを採用しました。立ち上げと削除が簡単なので必要な時だけ使用できますしyamlファイル一つで完結しているのでとても簡単です。

yamlファイルはこちらの記事のものを拝借させていただきました

version: '3.9'

services:
  minio:
    image: quay.io/minio/minio:latest
    container_name: example-minio
    environment:
      MINIO_ROOT_USER: root
      MINIO_ROOT_PASSWORD: password
    command: server --console-address ":9001" /data
    ports:
      - 9000:9000
      - 9001:9001

“docker compose up -d"で立ち上げるとhttp://127.0.0.0:9000ですぐにアクセスすることが可能です。

UIがとてもリッチですね!docker-compose.yamlに記載されているユーザー(root)とパスワード(password)を入力しログインしましょう。

S3と同じようにまずはバケットを作成しましょう。左側メニューからBucketsを選択し、右上のCreate Bucketから簡単に作成することが可能です。今回僕は"test-bucket"という名前でバケットを作成しました。

これでminioを操作する準備は一通り完了です(とても簡単!)。それでは実際にバケット、オブジェクトの操作を行なっていきましょう。

aws cliによる操作

ここまで試して貰えば分かる通りもちろんWeb上でほとんどの操作が可能です。しかし、実際プログラマブルに操作する上ではcliからの操作やコードからの操作が出来ない事には使い用がありません。


まずはaws cliを用いてminioへの接続と操作を行ってみます。事前にアクセスキーとシークレットアクセスキーの発行を済ませておいてください!

クレデンシャルの発行が完了したら環境変数に一時的に入れておきましょう。

$ export AWS_ACCESS_KEY_ID=アクセスキー
$ export AWS_SECRET_ACCESS_KEY=シークレットアクセスキー

それでは実際に操作してみましょう。まずはバケット一覧を表示してみます。普段使用しているコマンドをそのまま流用できますがendpoint urlを使用しないとAWSの方に接続しようとしてしまう点に注意してください。

$ aws --endpoint-url http://127.0.0.1:9000 s3 ls
2023-05-03 16:35:52 test-bucket

おお!先ほどminio上で作成したバケットが表示されていますね!

次にsample.txtという名前のファイルを作成し、minio上のバケットにアップロードしてみます。

$ aws --endpoint-url http://127.0.0.1:9000 s3 cp sample.txt s3://test-bucket/

minioのwebUIで確認すると確かにオブジェクトが増えていることが確認できました!

S3の場合と同様のコマンドでほぼ全ての操作を網羅しているという認識で問題なさそうですね!

コードからアクセスしてみる

次に本題のコードから接続する方法です。minioの利用はやはりローカル開発時が多いでしょうからこの辺りはしっかり検証しておく必要があります。

今回はGoで書いたコードでminio上のバケットやオブジェクトを操作してみましょう。そのための方法は2つあります。

・minio go clientを利用する
・aws-sdk-goを利用する

それぞれ試してみましょう。まずはminio go clientを利用してバケット一覧を取得してみます。コードはhttps://min.io/docs/minio/linux/developers/go/API.html#ListBucketsを参照し作成しました。

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"
)

func main() {
	endpoint := "127.0.0.1:9000"
	accessKeyID := "シークレットキー"
	secretAccessKey := "シークレットアクセスキー"

	// Initialize minio client object.
	minioClient, err := minio.New(endpoint, &minio.Options{
		Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
	})
	if err != nil {
		log.Fatalln(err)
	}

	buckets, err := minioClient.ListBuckets(context.Background())
	if err != nil {
		fmt.Println(err)
		return
	}
	for _, bucket := range buckets {
		fmt.Println(bucket)
	}
}

minio go clientを利用した方がスッキリとしたコードになる印象があります。しかし、本来S3を使用している場合はaws-sdkを利用していることがほとんどだと思いますので実際に稼働するコードと乖離が生まれてしまうのが難点です。


そこで次はaws-sdkを利用した接続方法をためしてみます。今回はオブジェクトを取得してみましょう。こちらはhttps://dev.classmethod.jp/articles/access-minio-using-aws-sdk-for-go/のコードを拝借しています。

package main

import (
	"fmt"

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

func newS3() (*s3.S3, error) {
	s, err := session.NewSession()
	if err != nil {
		return nil, err
	}

	ak := "シークレットキー"
	sk := "シークレットアクセスキー"
	cfg := aws.Config{
		Credentials:      credentials.NewStaticCredentials(ak, sk, ""),
		Region:           aws.String("ap-northeast-1"),
		Endpoint:         aws.String("http://127.0.0.1:9000"),
		S3ForcePathStyle: aws.Bool(true),
	}
	return s3.New(s, &cfg), nil
}

func main() {
	c, err := newS3()
	if err != nil {
		panic(err)
	}

	bucket := "test-bucket"
	var token *string
	for complete := false; !complete; {
		in := s3.ListObjectsV2Input{Bucket: &bucket, ContinuationToken: token}
		out, err := c.ListObjectsV2(&in)
		if err != nil {
			panic(err)
		}

		for i, o := range out.Contents {
			fmt.Printf("[%d] : %s\n", i, *o.Key)
		}

		complete = out.IsTruncated != nil && !*out.IsTruncated
		token = out.NextContinuationToken
	}
}

こちらの方が例えばaws.Configの引数である構造体の内容を書き換えるだけでminioに接続出来たりと汎用性があります。

どちらの方がいいかは状況に応じて判断していただくのがよろしいかとおもいます。

最後に

今回はminioの紹介をさせていただきました。かなり手軽で汎用性が高く便利です。


ローカル開発環境の構築は全サービス開発企業の命題だと思います。ぜひminioを利用してみてはいかがでしょうか。


最後まで読んでいただきありがとうございました!

AWS,Golang

Posted by CY