nato243 weblog.n-jitter brand iconweblog.n-jitter
テクノロジー

aws-sdk-go とS3互換APIでCloudflare R2にJSON保存するまで

2024.09.12
GolangCloudflare
aws-sdk-go とS3互換APIでCloudflare R2にJSON保存するまで アイキャッチ

Cloudflare R2

お仕事では巨大な動画ファイルを扱うことも多く、AWS S3 の料金周りの悩みでやりづらい施策も増えてきました。

そこで、Cloudflare R2 のエグレス無料とストレージ料金の効率に関して最近とても注目しております。
とりあえず本記事であらためて調べ、実際に普段サーバサイド書いているGo言語から触ってみました。


Cloudflare R2 とは?


Cloudflare R2 は Cloudflareが提供するクラウドベースのオブジェクトストレージサービスです。
perplexity.aiにサラッと根拠付きでまとめてもらいました。

主な特徴

  • Amazon S3と互換性のあるAPIを採用しており、既存のS3ツールやライブラリを使用可能
  • Cloudflareのグローバルネットワークを活用し、低レイテンシーでデータにアクセス可能
  • Cloudflare Workersと緊密に統合されており、データの処理や変換が容易
  • 強力なセキュリティ機能を提供(暗号化、アクセス制御など)

コスト効率の良さ

  • エグレス料金が無料:データをR2から取り出す際の料金が発生しないため、大幅なコスト削減が可能
  • 柔軟な料金体系:ストレージ容量と操作回数に基づくシンプルな料金設定
  • 無料枠の提供:毎月10GBまでのストレージと一定量のリクエストが無料

利便性

  • 公開バケット機能:認証なしでアクセス可能な公開バケットを作成可能
  • 自動最適化:データアップロード位置に基づき最適なストレージ領域を自動選択

用途の多様性

R2は以下のようなさまざまな用途に適しています

  • ウェブコンテンツのストレージ
  • メディアファイル(画像、音声、動画)の保存
  • クラウドネイティブアプリケーションのストレージ
  • データ分析やビッグデータのためのデータレイク
  • 機械学習モデルや大規模データセットの保存

Cloudflare R2は、特に頻繁にアクセスされるデータや大規模なデータセットの保存に適しており、エグレス料金が無料であることから、マルチクラウド戦略やデータ集中型アプリケーションで大きなコスト削減が期待できます。

Citations:

[1] https://zenn.dev/kazu0617/articles/4a890a4a21f867

[2] https://developers.cloudflare.com/r2/

[3] https://tks2.co.jp/2022/08/06/cloudflare-r2/

[4] https://future-architect.github.io/articles/20240603a/

[5] https://note.com/effectmoe/n/n9026fd23e130

[6] https://semaphoreci.com/blog/cloudflare-r2

[7] https://reffect.co.jp/cloudflare/cloudflare-r2-basic

[8] https://www.cloudflare.com/ja-jp/developer-platform/r2/

バケットの作成とAPI KEY の発行

かなり初歩的な素振りですが、個人ブログだからいいかな。
とりあえずバケットを作成し、API Keyも発行してみます。

特定のバケットのみに対する権限も、いったんGUIからポチポチでできるようです。

さすがS3互換API。ズバリそれっぽいキーが取得できます。



Golangで基本的なObjectの出し入れをしてみる

aws-sdk-go(今回はv1)を用いて、JSONの保存と取り出しをしてみます。

cloudflare R2 や wasabi 、miniIOなどS3互換APIと呼ばれるものは、基本的にエンドポイントを指定することで向き先をAWSから変えることができます。

package main

import (
	"bytes"
	"encoding/json"
	"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"
	"github.com/sanity-io/litter"
	"github.com/wano/contextlog/clog"
	"os"
)

func main() {
	// Cloudflare R2の認証情報とエンドポイント
	accessKeyID := os.Getenv(`AWS_ACCESS_KEY_ID`)
	secretAccessKey := os.Getenv(`AWS_SECRET_ACCESS_KEY`)
	endpoint := os.Getenv("S3_ENDPOINT")
	bucketName := os.Getenv("S3_BUCKET")
	region := "auto"

	// AWSセッションの作成
	sess, err := session.NewSession(&aws.Config{
		Credentials: credentials.NewStaticCredentials(accessKeyID, secretAccessKey, ""),
		Endpoint:    aws.String(endpoint),
		Region:      aws.String(region),
	})
	if err != nil {
		fmt.Printf("セッションの作成に失敗しました: %s\n", err)
		return
	}

	// S3クライアントの作成
	svc := s3.New(sess)

	type JsonData struct {
		Message string `json:"message"`
	}

	jsonData := JsonData{
		Message: "Hello, World!",
	}

	bys, err := json.Marshal(jsonData)
	if err != nil {
		clog.Panic(err)
	}

	_, err = svc.PutObject(&s3.PutObjectInput{
		Bucket: aws.String(bucketName),
		Key:    aws.String("hello.json"),
		Body:   bytes.NewReader(bys),
	})
	if err != nil {
		clog.Panic(err)
	}

	fmt.Println("S3にファイルをアップロードしました")

	// ファイルのダウンロード
	result2, err := svc.GetObject(&s3.GetObjectInput{
		Bucket: aws.String(bucketName),
		Key:    aws.String("hello.json"),
	})
	if err != nil {
		clog.Panic(err)
	}

	got := new(JsonData)
	err = json.NewDecoder(result2.Body).Decode(got)
	if err != nil {
		clog.Panic(err)
	}

	fmt.Println("S3からファイルをダウンロードしました")
	litter.Dump(got)
}

実行結果

S3にファイルをアップロードしました
S3からファイルをダウンロードしました
&main.JsonData{
  Message: "Hello, World!",
}

通常のS3にアクセスする感覚で簡単に使えましたね。