KINTO Tech Blog
AWS

MinIOを用いたS3ローカル開発環境の構築ガイド(RELEASE.2023-10)

Yohei Miyashita
Yohei Miyashita
Cover Image for MinIOを用いたS3ローカル開発環境の構築ガイド(RELEASE.2023-10)

自己紹介・記事要約

こんにちは。KINTOテクノロジーズの共通サービス開発グループ[1][2][3][4]で会員管理のエンジニアを担当している宮下です。
今日は、私たちの開発現場で直面したS3互換のローカルストレージ環境構築の課題をどのように解決したかをお話しします。
具体的には、オープンソースのMinIOを活用してAWS S3の機能をエミュレートする方法について、実践的なアプローチを共有します。
この記事が、同様の課題に直面しているエンジニアの方々にとって、参考になれば幸いです。

MinIOとは?

MinIOはS3互換機能を備えたオープンソースのオブジェクトストレージサーバーツールです。NASのように、ファイルをアップロードやダウンロードすることができます。

この分野にはLocalStackという似たようなサービスもあります。LocalStackはAWSのエミュレーションに特化し、S3をはじめとするLambda, SQS, DynamoDBなどのサービスをローカルでエミュレートできるツールです。

これらは目的が異なる2つのツールですが、ローカル環境でのS3互換環境を設定するための要件はどちらも満たしています。

MinIO website
LocalStack website

MinIO と LocalStack で ツール選定

開発の要件

開発の要件として、docker-composeを実行するだけで、自動で任意のS3バケットを作成し、そのバケットにメールテンプレートやCSVファイルなどが登録されていることが必要でした。
コンテナが起動してからコマンドやGUIでファイル登録するのは面倒くさいですからね。
また、自動化されたローカルでのS3接続テストを行う際も、コンテナが起動すると同時にバケットとファイルが準備済みでなければなりません。

ツールの比較

どちらのツールが簡単に要件を実現できるか比較した結果、LocalStackはaws-cliでバケット作成やファイル操作をするのに対し、MinIOは専用のコマンドラインツールであるmc(MinIO Client)を提供しています。これにより、より簡単にシステムの構築が可能でした。

さらに、GUIによる管理コンソールにおいても、MinIOの方が洗練されていると感じました。Google Trendsでの比較では、MinIOがより人気があることが分かります。これらの理由から、MinIOを採用する事に決定しました。

googleTrends_comp

composeファイル

MinIOのローカル環境をセットアップするために、最初に「compose.yaml」ファイルを用意する必要があります。
以下のステップに従って進めましょう。

  1. 任意のディレクトリを作成します。
  2. そのディレクトリ内にファイル名「compose.yaml」のテキストファイルを作成します。
  3. 下記のcompose.yamlの内容をコピペして保存します。

※docker-compose.ymlは非推奨になりました。composeファイルの仕様はこちら
※docker-compose.ymlでも後方互換性機能で動作します。詳細はこちら

compose.yaml
services:

# MinIOサーバーコンテナの設定
  minio:
    container_name: minio_test
    image: minio/minio:latest

# MinIOサーバーを開始し、管理コンソール(GUI)のアクセスポートを指定
    command: ['server', '/data', '--console-address', ':9001']
    ports:
      - "9000:9000" # APIアクセス用
      - "9001:9001" # 管理コンソール(GUI)用

# USERとPASSWORDを省略する事も可能です。
# その場合は minioadmin | minioadmin に自動で設定されます。
    environment:
      - "MINIO_ROOT_USER=minio"
      - "MINIO_ROOT_PASSWORD=minio123"

# minioが管理する設定ファイルや、アップロードしたファイルを
# ローカルで参照したい場合や、登録したファイルを永続化したい場合は
# ローカルのディレクトリをマウントします。
#    volumes:
#      - ./minio/data:/data

# PC再起動後にminioコンテナが自動で起動してほしい場合など
# 停止していたら自動で起動していてほしい場合は有効化します。
#    restart: unless-stopped


# MinIOクライアント(mc)コンテナの設定
  mc:
    image: minio/mc:latest
    container_name: mc_test
    depends_on:
      - minio
    environment:
      - "MINIO_ROOT_USER=minio" # 上と同じユーザー名
      - "MINIO_ROOT_PASSWORD=minio123" # 上と同じパスワード

# mcコマンドでバケット作成と、作成したバケットにファイルを配置します。
# まずは aliasを設定して、それ以降のコマンドで簡単にminio本体を
# 指定できるようにします。
# 今回は myminio というエイリアス名にしました。
# mbは、バケットの新規作成を行う。make bucketの略
# cpは、ローカルのファイルをminioにコピーします。
    entrypoint: >
      /bin/sh -c "
      mc alias set myminio http://minio:9000 minio minio123;

      mc mb myminio/mail-template;
      mc mb myminio/image;
      mc mb myminio/csv;

      mc cp init_data/mail-template/* myminio/mail-template/;
      mc cp init_data/image/* myminio/image/;
      mc cp init_data/csv/* myminio/csv/;
      "

# minioにアップロードしたいファイルが入っているディレクトリをマウントします。
    volumes:
      - ./myData/init_data:/init_data

ディレクトリとファイル構成

適当なダミーファイルを作成し、以下のディレクトリとファイル構成で起動してみます。

tree command
minio_test# tree .
.
├── compose.yaml
└── myData
    └── init_data
        ├── csv
        │   └── example.csv
        ├── image
        │   ├── slide_01.jpg
        │   └── slide_04.jpg
        └── mail-template
            └── mail.vm

起動と動作確認

MinIOとそのクライアントをDocker上で稼働させて、その後の動作を確認する流れを紹介します。
Dockerコンテナは、以下のコマンドでバックグラウンドで起動させます(-d フラグを使用)。
Docker Desktop(for Windows)をインストールした場合は、コマンドプロンプトや
PowerShellといったコマンドラインで、コンテナの作成が可能です。
Docker Desktopのダウンロードはこちら

docker compose up -d

※docker-compose の真ん中のハイフンは付けなくなりました。詳細はこちら

Docker Desktop

Docker Desktopを開いて、コンテナの状態をチェックします。
minio_test コンテナは起動していますが、mc_test コンテナが停止していることが確認できます。
mc_test コンテナの実行ログを確認してみましょう。
doker-desktop

mcの実行ログ

MinIOクライアント(mc)が実行され、すべてのコマンドが正常に終了したことがログから分かります。
mc_test_log

管理コンソール

次に、MinIOのGUI管理コンソールを見てみます。ブラウザで localhost の 9001 ポートにアクセスします。
http://127.0.0.1:9001

ログイン画面が表示されたら、compose.yaml で設定したユーザー名とパスワード
(この例では minio と minio123)を入力します。
gui_console

バケット一覧

左側のメニューから「Object Browser」を選択すると、
作成したバケットとそこに保存されているファイルの数が一覧で表示されます。
bucket_list

ファイル一覧

例として「image」バケットを選んで中を見てみます。
予めアップロードされているファイルが見えます。
ファイルの隣にあるアクションメニューから「プレビュー」を選ぶと、ファイルを直接確認できます。
image

ファイルプレビュー機能

弊社のマスコットキャラクター 謎の生き物K がプレビューされました。
MinIOの管理コンソールで画像を直接プレビューできる機能は非常に便利です。
k_preview

mc(MinIO Client)のインストール

GUIよりコマンドラインを使った方が、大量のファイル操作が効率的な場合があります。
また、開発時にソースコードからMinIOにアクセスしてエラーが出たとき、
ファイルパスの確認を行うにはコマンドラインが非常に便利です。
ここでは、MinIOクライアントのインストール方法と基本的な操作を説明します。
※GUIの管理コンソールで必要十分という方はこのセクションをスキップしてください。

# 以下のコマンドを使用して、mcをダウンロードしてください。実行ファイルは任意のディレクトリに保存されます。
minio_test/mc# curl https://dl.min.io/client/mc/release/linux-amd64/mc \
  --create-dirs \
  -o ./minio-binaries/mc

# 動作確認
# インストールしたmcが最新版か確認し、バージョンを表示させて正しくインストールされたかをチェックします。
# mcコマンドをPathに通すかはお好みで。今回は通さずにいきます。
minio_test/mc# ./minio-binaries/mc update
> You are already running the most recent version of ‘mc’.
minio_test/mc# ./minio-binaries/mc -version
> mc version RELEASE.2023-10-30T18-43-32Z (commit-id=9f2fb2b6a9f86684cbea0628c5926dafcff7de28)
> Runtime: go1.21.3 linux/amd64
> Copyright (c) 2015-2023 MinIO, Inc.
> License GNU AGPLv3 <https://www.gnu.org/licenses/agpl-3.0.html>

# エイリアスの設定
# MinIOサーバへのアクセスに必要なエイリアスを設定します。
minio_test/mc# ./minio-binaries/mc alias set myminio http://localhost:9000 minio minio123;
> Added `myminio` successfully.

# ファイル操作の例
# バケット内のファイル一覧を表示
minio_test/mc# ./minio-binaries/mc ls myminio/image
> [2023-11-07 21:18:54 JST]  11KiB STANDARD slide_01.jpg
> [2023-11-07 21:18:54 JST]  18KiB STANDARD slide_04.jpg
minio_test/mc# ./minio-binaries/mc ls myminio/csv
> [2023-11-07 21:18:54 JST]    71B STANDARD example.csv

# ファイルの中身を画面出力
minio_test/mc# ./minio-binaries/mc cat myminio/csv/example.csv
> name,age,job
> tanaka,30,engineer
> suzuki,25,designer
> satou,,40,manager

# ファイルの一括アップロード
minio_test/mc# ./minio-binaries/mc cp ../myData/init_data/image/* myminio/image/;
> ...t_data/image/slide_04.jpg: 28.62 KiB / 28.62 KiB 

# ファイルの削除
minio_test/mc# ./minio-binaries/mc ls myminio/mail-template
> [2023-11-15 11:46:25 JST]   340B STANDARD mail.txt
minio_test/mc# ./minio-binaries/mc rm myminio/mail-template/mail.txt
> Removed `myminio/mail-template/mail.txt`.

mcコマンド一覧

MinIOクライアントに関する詳細なドキュメントが必要な場合は、公式マニュアルをご覧ください。
MinIO Client 公式マニュアルはこちら

最後にJavaのソースコードからアクセス

ローカルでMinIOを使ってS3互換の開発環境を構築した後、実際のJavaアプリケーションからMinIOにアクセスする方法を紹介します。

まず、Gradleの設定を行います。

build.gradle
plugins {
    id 'java'
}

java {
    sourceCompatibility = '17'
}

repositories {
    mavenCentral()
}

dependencies {
// https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-s3
    implementation 'com.amazonaws:aws-java-sdk-s3:1.12.582'
}

次に、MinIOにアクセスするためのJavaクラスを作成します。

Main.java
package com.example.miniotest;

import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.auth.EC2ContainerCredentialsProviderWrapper;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import com.amazonaws.services.s3.model.ListObjectsV2Result;
import com.amazonaws.regions.Regions;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;

public class Main {

  public static void main(String... args) {
    new Main().execute();
  }

  /**
   * MinIOのS3互換性テスト
   * バケット内のファイル一覧を取得し、内容を表示する
   */
  private void execute() {
    System.out.println("--- Start ---");

    // ローカルのMinIOに接続する場合と、
    // AWS S3に接続する場合で切り替える。
    // spring bootのプロファイルで切り替える事を想定。
    boolean isLocal = true;

    // MinIOはAWS S3と互換性があるので
    // AWSのライブラリから接続する事ができる。
    AmazonS3 s3Client = null;
    if (isLocal) {
      s3Client = getAmazonS3ClientForLocal();
    } else {
      s3Client = getAmazonS3ClientForAwsS3();
    }

    // バケット名
    final String bucketName = "csv";
    // バケット内のすべてのオブジェクトをリストする
    ListObjectsV2Result result = s3Client.listObjectsV2(bucketName);
    List<S3ObjectSummary> objects = result.getObjectSummaries();

    // 取得できたファイル名の数だけループする
    for (S3ObjectSummary os : objects) {
      System.out.println("バケットから取得したファイル名 : " + os.getKey());

      // ストリームでファイルの内容を取得する。
      // もちろんファイルをダウンロードする事もできる。
      try (S3Object s3object = 
             s3Client.getObject(
               new GetObjectRequest(bucketName, os.getKey()));

           BufferedReader reader = 
             new BufferedReader(
               new InputStreamReader(s3object.getObjectContent()))) {

        String line;
        while ((line = reader.readLine()) != null) {
          // 1行ずつファイルの中身を画面出力
          System.out.println(line);
        }

      } catch (IOException e) {
        e.printStackTrace();
      }
      // ファイルの切り替わりで空行を入れておく
      System.out.println();
    }

    System.out.println("--- End ---");
  }

  /**
   * ローカルのMinioに接続する場合はこちら
   * @return AmazonS3インターフェースの実装であるAmazonS3クライアントインスタンス。
   */
  private AmazonS3 getAmazonS3ClientForLocal() {
    final String id = "minio";
    final String pass = "minio123";
    final String endpoint = "http://127.0.0.1:9000";

    return AmazonS3ClientBuilder.standard()
        .withCredentials(
            new AWSStaticCredentialsProvider(
              new BasicAWSCredentials(id, pass)))
        .withEndpointConfiguration(
            new AwsClientBuilder.EndpointConfiguration(
              endpoint, Regions.AP_NORTHEAST_1.getName()))
        .build();
  }

  /**
   * Amazon S3クライアントを取得し、AWS S3サービスへの接続を設定します。
   * このメソッドは、Amazon EC2インスタンス上での実行時にIAMロールを用いて自動的に
   * 認証情報を取得し、S3との接続を確立します。IAMロールにはS3へのアクセスを許可する
   * ポリシーが関連付けられている必要があります。
   *
   * クライアントは以下のように構成されます:
   * - リージョン:Regions.AP_NORTHEAST_1(アジアパシフィック(東京))
   * - 最大接続数:500
   * - 接続タイムアウト:120秒
   * - エラーリトライ回数:最大15回
   *
   * 注意:このメソッドは、EC2インスタンス上で実行されることを想定しています。
   * EC2以外で実行する場合は、AWS認証情報を別途提供する必要があります。
   *
   * @return AmazonS3インターフェースの実装であるAmazonS3クライアントインスタンス。
   * @see com.amazonaws.auth.EC2ContainerCredentialsProviderWrapper
   * @see com.amazonaws.services.s3.AmazonS3
   * @see com.amazonaws.services.s3.AmazonS3ClientBuilder
   * @see com.amazonaws.regions.Regions
   */
  private AmazonS3 getAmazonS3ClientForAwsS3() {
    return AmazonS3ClientBuilder.standard()
        .withCredentials(new EC2ContainerCredentialsProviderWrapper())
        .withRegion(Regions.AP_NORTHEAST_1)
        .withClientConfiguration(
            new ClientConfiguration()
                .withMaxConnections(500)
                .withConnectionTimeout(120 * 1000)
                .withMaxErrorRetry(15))
        .build();
  }
}

実行結果

--- Start ---
バケットから取得したファイル名 : example.csv
name,age,job
tanaka,30,engineer
suzuki,25,designer
satou,40,manager

--- End ---

ソースコード解説

このコードの注目すべき点は、AWS SDK for JavaがMinIOとAWS S3の両方に対応していることです。
ローカルのMinIOインスタンスに接続する際にはgetAmazonS3ClientForLocalメソッドを、
AWS S3に接続する場合にはgetAmazonS3ClientForAwsS3メソッドを使用してクライアントを初期化します。
このアプローチにより、異なるバックエンド環境間で同一のSDKを利用することが可能となり、
同じインターフェースでの操作を実現します。
このように、追加コストをかけずに実際のAWS環境へのデプロイ前に気軽にアプリケーションをテストできるのはいいですよね。

このガイドが何かのお役に立てば幸いです。
最後までお読みいただき、ありがとうございました🙇‍♂

脚注
  1. 共通サービス開発グループメンバーによる投稿 1
    [ グローバル展開も視野に入れた決済プラットフォームにドメイン駆動設計(DDD)を取り入れた ] ↩︎

  2. 共通サービス開発グループメンバーによる投稿 2
    [入社 1 年未満メンバーだけのチームによる新システム開発をリモートモブプログラミングで成功させた話] ↩︎

  3. 共通サービス開発グループメンバーによる投稿 3
    [JIRA と GitHub Actions を活用した複数環境へのデプロイトレーサビリティ向上の取り組み] ↩︎

  4. 共通サービス開発グループメンバーによる投稿 4
    [ VSCode Dev Container を使った開発環境構築 ] ↩︎

Facebook

関連記事 | Related Posts

We are hiring!

【部長・部長候補】/プラットフォーム開発部/東京

プラットフォーム開発部 について共通サービス開発GWebサービスやモバイルアプリの開発において、必要となる共通機能=会員プラットフォームや決済プラットフォームの開発を手がけるグループです。KINTOの名前が付くサービスやTFS関連のサービスをひとつのアカウントで利用できるよう、様々な共通機能を構築することを目的としています。

【プラットフォームエンジニア】プラットフォームG/東京・大阪

プラットフォームグループについてAWS を中心とするインフラ上で稼働するアプリケーション運用改善のサポートを担当しています。