🚀

docker-composeで立ち上げているminioをvirtual-hosted styleで接続する

2022/01/19に公開

本番環境のファイルストレージにS3を利用しているプロジェクトで、ローカルでの開発環境としてS3互換のファイルストレージであるminioを利用しています。
今回の記事では、minioのデフォルトの設定を変更しvirtual-hosted styleで接続する方法を解説します。

背景

前提として、S3オブジェクトのリクエスト方法は path-stylevirtual-hosted style の二つがあります。
path-style: https://s3.amazonaws.com/my-bucket/my-file.jpg
virtual-hosted style: https://my-bucket.s3.amazonaws.com/my-file.jpg
このうちpath-styleはdeprecatedであり、削除予定となっています。詳細についてはこちらをご覧ください。
https://aws.amazon.com/jp/blogs/news/amazon-s3-path-deprecation-plan-the-rest-of-the-story/

そしてこのプロジェクトではpath-styleでリクエストを行なっていました。これはminioはデフォルトでpath-styleしか受け付けず、それに合わせてしまっていたためです。

s3-example.go
func (s3 s3Service) Download(f storage.File) (*string, error) {
	sess := session.Must(session.NewSessionWithOptions(session.Options{
		SharedConfigState: session.SharedConfigEnable,
		Config: aws.Config{
			Endpoint:         aws.String(s3.Endpoints),
			S3ForcePathStyle: aws.Bool(true)}, // path-styleを利用する
	}))
	downloader := s3manager.NewDownloader(sess)
	buffer := &aws.WriteAtBuffer{}
	if _, err := downloader.Download(buffer, &s3.GetObjectInput{
		Bucket: aws.String(s3.BucketName),
		Key:    aws.String(f.FileName()),
	}); err != nil {
		return nil, err
	}
	data := base64.StdEncoding.EncodeToString(buffer.Bytes())
	return &data, nil
}

開発環境の問題解決のために本番環境のコードに影響があるのはよろしくないですし、何よりdeprecatedなリクエスト方法を利用しているのは望ましくありません。
そのためminioでもvirtual-hosted styleでのリクエストができるようにしたい、というのが今回のモチベーションです。

環境

ローカルでの環境はdocker-composeで立ち上げており、元々のdocker-compose.ymlはこんな感じです。

docker-compose.yml
version: "3.7"

services:
  api: # GoのAPIサーバー
    build: .
    command: ["air"]
    working_dir: /app
    volumes:
      - .:/app
    ports:
      - 3000:3000
    environment:
      # 省略

  s3:
    image: minio/minio:latest
    ports:
      - 9000:9000
      - 9001:9001
    environment:
      MINIO_ROOT_USER: 'minio'
      MINIO_ROOT_PASSWORD: 'minio123'
    volumes:
      - ./s3/data:/data
    entrypoint: sh
    command: -c "/opt/bin/minio server /data --console-address ":9001";"

# 他にもDBコンテナなどもあるが省略

docker-composeでデフォルトで作られるnetworkでは、コンテナ間の通信をコンテナ名で名前解決します。そのためapiコンテナのAWS SDKからs3コンテナにアクセスするとなると、現状では http://s3:9000/my-bucket/my-file.jpg のようにpath-styleでアクセスすることになります。

解決方法

  1. minioの設定でvirtual-hosted styleを受け付けられるようにします。
  2. docker-composeのapiコンテナからminioを http://バケット名.s3 で名前解決できるようにします。

1. minioでvirtual-hosted styleを受け付ける設定をする

MINIO_DOMAIN 環境変数にドメイン名を指定することで、virtual-hosted styleのリクエストが有効化されます。
docker-compose.ymlに以下のように加筆します。

  s3:
    image: minio/minio:latest
    ports:
      - 9000:9000
      - 9001:9001
    environment:
      MINIO_ROOT_USER: 'minio'
      MINIO_ROOT_PASSWORD: 'minio123'
+     MINIO_DOMAIN: s3
    volumes:
      - ./s3/data:/data
    entrypoint: sh
    command: -c "/opt/bin/minio server /data --console-address ":9001";"

有効化することによって、今回の場合、HTTPリクエストのHostヘッダーが (.+).s3 と一致する場合はそのサブドメインの値がバケットとして解釈されるようになります。

2. apiコンテナからminioを バケット名.s3 で名前解決できるようにする

virtual-hosted styleでのリクエストはバケット名をサブドメインとしてリクエストすることになりますが、docker-composeのdefault networkではコンテナ名で名前解決するので、そのままではサブドメイン込みでの名前解決はできません。
そのため新しくdocker networkを作り、api, s3の二つのコンテナを参加させて解決します。
docker-composeに以下のように加筆します。

  api: # GoのAPIサーバー
    build: .
    command: ["air"]
    working_dir: /app
    volumes:
      - .:/app
    ports:
      - 3000:3000
    environment:
      # 省略
+   networks:
+     - s3_buckets

  s3:
    image: minio/minio:latest
    ports:
      - 9000:9000
      - 9001:9001
    environment:
      MINIO_ROOT_USER: 'minio'
      MINIO_ROOT_PASSWORD: 'minio123'
    volumes:
      - ./s3/data:/data
    entrypoint: sh
    command: -c "/opt/bin/minio server /data --console-address ":9001";"
+   networks:
+     s3_buckets:
+       aliases:
+         - my-bucket.s3

+networks:
+  s3_buckets:

aliasesを利用してサブドメインを受け付けられるようにします。
ワイルドカードは使えないようなので、利用したいバケットが複数存在する場合はそれぞれ記載するしかなさそうです。
とはいえ、これでapiコンテナのAWS SDKからvirtual-hosted styleでリクエストを送ることができるようになりました。

終わり

以上です。なんともニッチな内容ですが、日本語でも英語でも情報がなかったため記事に起こしてみました。
開発環境でのファイルアップロード機能の検証のためにminioを使うのはとてもいいですよ。
リクエストのモックや、開発環境だけtmpディレクトリに保存する条件分岐のような環境差異を減らせるのでオススメです。

Discussion