LoginSignup
0
0

S3を使うプロジェクトをローカル環境でテストしたいことないでしょうか?
そんな時には、MinIOが便利です。

MinIOはS3 APIのmockとして使える上に、コンソールまで提供してるので今のバケットの状態をGUIで見れて便利です。
さて、MinIOからpresigned URLを発行しようとした際に詰まったので残しておこうと思います。

MinIOの設定

MinIOは公式のdocker imageがあるのでそれを使用します。

dockercompose.yaml
services:
  minio:
    image: quay.io/minio/minio:RELEASE.2024-05-28T17-19-04Z
    container_name: minio
    environment:
      MINIO_ROOT_USER: ${MINIO_ROOT_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
      # ここがないと後述の問題になる
      MINIO_DOMAIN: ${MINIO_DOMAIN}
    ports:
      - 9000:9000
      - 9001:9001
    restart: unless-stopped
    command: ['server', '/data', '--console-address', ':9001']
    volumes:
      - minio_data:/data
volumes:
  minio_data:

上のcomposeのファイルはどっかから拾ってきたものを編集したものです。
ポートが9000番と9001番二つ空いているのは、minioはAPI用とブラウザのコンソール用で二つ使うからです。
環境変数については後で触れます。

presigned URL

presigned URLとは、S3のオブジェクトをGETやPUTする際に、APIサーバーなどに直接データを経由させることなく、S3とクライアントでデータのやり取りができる仕組みです。
具体的には、GETの場合、APIサーバーがアクセスされた際に、一時的に使えるURLを発行し、
そのURLにリダイレクトさせたりします。
PUTの場合は、PUTする前にpresigned URLを発行してそこに対してPUTさせるようにしたりします。
presigned URLには期限を設けることができて、その期間を過ぎたリクエストを無効化してくれたりします。

presigned URLを取得してみる

さて、presigned URLを発行するAPIを叩いてAPIを発行します。
自分はRustのsdk経由で発行してます。

// clientの作成
let config = aws_config::defaults(BehaviorVersion::v2024_03_28())
    .load()
    .await;
let client = aws_sdk_s3::Client::new(&config);
// urlの取得
let response = client.get_object()
      .bucket(bucket_name)
      .key(object_key)
      // 変更されていなかったらHTTPステータスコードの304を返却してくれる
      .set_if_none_match(get_if_none_match(&headers))
      // presigned Requestを行う。 
      // presigned_configはExpireを設定したコンフィグを返す独自定義の関数
      .presigned(presigned_config()) 
      .await?;

あとは、urlを返却します。axumでサーバーを立てた時のは以下の様に返却します

match response {
    Ok(response) => {
        let uri = response.uri();
        // etagとかをheaderで返しておくと if-none-matchで使える
        Ok((AppendHeaders(out.headers()), Redirect::temporary(uri)))
    }
    Err(e) => {
        Err(e)
    }
} 

presigned URLが無効...?

困ったこととして、presigned URLを発行したときに、正しく取得したはずなのに、
そのURLを使ってもGETできないということがありました。

The specified bucket is not valid.

コンソールなどで確認しても確かにバケットがあることが確認できます。
なんでこのようなエラーになるのでしょうか?

S3のpresigned URLの形式

得られたpresigned_urlのドメインはindex.localhostになってました。
確かに得ようと思ったバケット名はindexです。なぜサブドメインがこのように設定されているのでしょうか。

これはS3のpresigned_urlの形式に由来してます。
S3は2019年以降、仮想ホスト形式というURLの形式に移行していて、
http://<bucket-name>.s3.amazonaws.comという形式になっています。

ただそれ以前はパス形式というhttp://s3.amazonaws.com/<bucket-name>のようにパスにバケット名を入れる方式を利用していたそうです。

Rustのsdk clientにはforce_path_styleという設定があり、これを使うと問題なく取得できました。

ただ記事にある通りこの形式では本番のS3にアクセスできないので、どうにかしたいです。

MinIOの設定

MinIOはデフォルトではパス形式を使うので、仮想ホスト形式に切り替えないといけないです。
解決策は、MinIOにMINIO_DOMAINの設定を渡すだけでした。これで仮想ホスト形式でも、
MinIOがそのURLでアクセスしてObjectの取得ができました。

終わりに

原因がわかりづらくて調査がつらかった…。
そもそも5年前に廃止された方式がなぜ今もデフォルトなのか…。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0