【Docker】Minioでローカル環境でもファイルアップロード環境を構築する

Docker環境でS3代わりにMinioでファイルを保存する

S3などにファイルアップロードしますが、ローカル環境の場合はS3に接続してファイルアップロードするわけにもいきません。

そこでローカルでファイルアップロード環境を構築できるMinioを構築していきましょう!

目次

Docker環境

minioのイメージは下記になります

タグは今の最新のものを取り入れるなど指定しておきましょう!
そうすると、環境の差異を防げます。

それでは、docker-compose.ymlにMinioの情報を記載していきます。

全体像

追加するのは下記になります。

    minio:
        image: minio/minio:latest
        container_name: minio
        ports:
            - "9999:9999"
            - "9002:9002"
        environment:
            MINIO_ROOT_USER: access_key
            MINIO_ROOT_PASSWORD: secret_key
        entrypoint: /bin/bash
        command: -c "/opt/bin/minio server /export --address :9999 --console-address :9002"



    create-bucket:
        container_name: minio-create-bucket
        image: minio/mc:latest
        entrypoint: [ "" ]
        command:
            - /bin/sh
            - -c
            - |
                until (mc config host add minio http://minio:9999 access_key secret_key) do echo 'wait until add host' && sleep 1; done;
                mc mb minio/public
                mc policy set public minio/public
                mc mb minio/private
                mc policy set private minio/private
                mc policy set private minio/private
        environment:
            MINIO_ROOT_USER: access_key
            MINIO_ROOT_PASSWORD: secret_key
        depends_on:
            - minio

Minioの環境

    minio:
        image: minio/minio:latest
        container_name: minio
        ports:
            - "9999:9999"
            - "9002:9002"
        environment:
            MINIO_ROOT_USER: access_key
            MINIO_ROOT_PASSWORD: secret_key
        entrypoint: /bin/bash
        command: -c "/opt/bin/minio server /export --address :9999 --console-address :9002"

まずはMinioそのものを起動します

接続情報は環境変数で定義されています。

        environment:
            MINIO_ROOT_USER: access_key
            MINIO_ROOT_PASSWORD: secret_key

使用するポート番号を指定して起動させます。

command: -c "/opt/bin/minio server /export --address :9999 --console-address :9002"

この設定だと

  • --address :9999はAPIに利用するエンドポイント。http://minio:9999で利用する。ホストマシンで実行されているWebブラウザ。
  • Minioのコンソール画面はlocalhost:9002に設定。

http://localhost:9002/loginを開くと、Minioのログイン画面が表示される。

ここでMINIO_ROOT_USERMINIO_ROOT_PASSWORDを入力すればログインができる。

            MINIO_ROOT_USER: access_key
            MINIO_ROOT_PASSWORD: secret_key

Minio Clientを使用して、初期バケットを作成する

Minioのログイン画面が表示されて、バケット作成など可能ですが、はじめはバケットがあります。

Dockerはコマンド一つで開発環境を構築できるので、Minioのアップロード先のバケットも一緒に構築したいですよね

そこで登場しているのがMinio Clientになります。

    create-bucket:
        container_name: minio-create-bucket
        image: minio/mc:latest
        entrypoint: [ "" ]
        command:
            - /bin/sh
            - -c
            - |
                until (mc config host add minio http://minio:9999 access_key secret_key) do echo 'wait until add host' && sleep 1; done;
                mc mb minio/public
                mc policy set public minio/public
                mc mb minio/private
                mc policy set private minio/private
                mc policy set private minio/private
        environment:
            MINIO_ROOT_USER: access_key
            MINIO_ROOT_PASSWORD: secret_key
        depends_on:
            - minio

Minio ClientとMinioを接続

Object Storage を追加するには、以下のコマンドを実行します。

mc config host add <ALIAS> <COS-ENDPOINT> <ACCESS-KEY> <SECRET-KEY>
  • <ALIAS> – コマンドで Object Storage を参照するためのショート・ネーム
  • <COS-ENDPOINT> – Object Storage インスタンスのエンドポイント。エンドポイントについて詳しくは、エンドポイントおよびストレージ・ロケーションを参照してください。
  • <ACCESS-KEY> – サービス資格情報に割り当てられたアクセス・キー
  • <SECRET-KEY> – サービス資格情報に割り当てられた秘密鍵

今回だと下記のような構成になります

mc config host add minio http://minio:9999 access_key secret_key
  • ALIASminio(dockerコンテナ)
  • COS-ENDPOINThttp://minio:9999
  • ACCESS-KEYaccess_key
  • SECRET-KEYsecret_key

mb – バケットの作成

バケット作成時は、バケットの エイリアスと名前を指定します。

mc mb minio/public

アクセスポリシーの設定

mc policy setコマンドは、バケットの匿名(つまり、認証されていない、または公開されている)アクセスポリシーを設定します。

アクセスポリシーは下記の箇所で設定をしています

mc policy set public minio/public

publicポリシーを設定しています。これによりダウンロードおよびアップロードアクセス可能なディレクトリを指定してます。

パラメーター

許可

指定されたに割り当てるポリシーの必須ALIAS名。次のいずれかの値を指定します。

  • none-への匿名アクセスを無効にしALIASます。
  • download-へのダウンロード専用アクセスを有効にしますALIAS
  • upload-へのアップロード専用アクセスを有効にしALIASます。
  • public-へのダウンロードおよびアップロードアクセスを有効にしますALIAS

エイリアス

必須コマンドが指定されたを適用するバケットまたはバケットプレフィックスへのフルパスPERMISSION

MinIOまたは他のS3互換サービスのエイリアス、バケットまたはバケットプレフィックスへのフルパスを指定します。例えば:

mc set public play/mybucket

バケットプレフィックスを指定して、そのプレフィックスのみにポリシーを設定します。たとえば、次のコマンドは、 mybucket/downloadsmybucket/uploadsプレフィックスに個別の匿名バケットポリシーを設定します。

mc set download play/mybucket/downloads
mc set upload play/mybucket/uploads

まとめ

  • Minioでアップロード先のエイリアスとエンドポイント、コンソール画面を用意
  • Minio Clientでバケット作成し、ポリシーをセットする

設定ファイルを更新

Minioの準備ができたところで、Laravel側で利用できるようにしていきます。

.envに情報を追加

.envにMinioの接続情報を記述します。

AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=

MINIO_ROOT_USER=access_key
MINIO_ROOT_PASSWORD=secret_key
MINIO_DEFAULT_REGION=us-east-1
MINIO_USE_PATH_STYLE_ENDPOINT=true
MINIO_ENDPOINT=http://minio:9999

FILESYSTEM_DRIVER=local
AWS_UPLOADS_PUBLIC_BUCKET=public
AWS_UPLOADS_PRIVATE_BUCKET=secret

filesystems.phpを更新

先ほど.envで定義した接続情報をfilesystems.phpに反映します。

filesystems.phpはLaravelのファイル保存などの設定を管理する場所です。

publicバケットとprivateバケットにdisksをわける場合

<?php
// 共通の接続情報
$s3base = [
    'driver'                  => 's3',
    'key'                     => env('MINIO_ROOT_USER', env('AWS_ACCESS_KEY_ID')),
    'secret'                  => env('MINIO_ROOT_PASSWORD', env('AWS_SECRET_ACCESS_KEY')),
    'region'                  => env('MINIO_DEFAULT_REGION', env('AWS_DEFAULT_REGION')),
    'url'                     => env('MINIO_URL', env('AWS_URL')),
    'endpoint'                => env('MINIO_ENDPOINT', env('AWS_ENDPOINT')),
    'use_path_style_endpoint' => (bool) env("MINIO_USE_PATH_STYLE_ENDPOINT", env('AWS_USE_PATH_STYLE_ENDPOINT', false)),
];

return [

    /*
    |--------------------------------------------------------------------------
    | Default Filesystem Disk
    |--------------------------------------------------------------------------
    |
    | Here you may specify the default filesystem disk that should be used
    | by the framework. The "local" disk, as well as a variety of cloud
    | based disks are available to your application. Just store away!
    |
    */

    'default' => env('FILESYSTEM_DRIVER', 'local'),

    /*
    |--------------------------------------------------------------------------
    | Filesystem Disks
    |--------------------------------------------------------------------------
    |
    | Here you may configure as many filesystem "disks" as you wish, and you
    | may even configure multiple disks of the same driver. Defaults have
    | been setup for each driver as an example of the required options.
    |
    | Supported Drivers: "local", "ftp", "sftp", "s3"
    |
    */

    'disks' => [

        'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),
        ],

        'public' => [
            'driver' => 'local',
            'root' => storage_path('app/public'),
            'url' => env('APP_URL').'/storage',
            'visibility' => 'public',
        ],

        's3_public' => [
            ...$s3base,
            'bucket' => env('AWS_UPLOADS_PUBLIC_BUCKET'),
        ],

        's3_private' => [
            ...$s3base,
            'bucket' => env('AWS_UPLOADS_PRIVATE_BUCKET'),
        ],

    ],

    /*
    |--------------------------------------------------------------------------
    | Symbolic Links
    |--------------------------------------------------------------------------
    |
    | Here you may configure the symbolic links that will be created when the
    | `storage:link` Artisan command is executed. The array keys should be
    | the locations of the links and the values should be their targets.
    |
    */

    'links' => [
        public_path('storage') => storage_path('app/public'),
    ],

];

シンプルにminio用のdisksを用意する場合

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Default Filesystem Disk
    |--------------------------------------------------------------------------
    |
    | Here you may specify the default filesystem disk that should be used
    | by the framework. The "local" disk, as well as a variety of cloud
    | based disks are available to your application. Just store away!
    |
    */

    'default' => env('FILESYSTEM_DRIVER', 'local'),

    /*
    |--------------------------------------------------------------------------
    | Filesystem Disks
    |--------------------------------------------------------------------------
    |
    | Here you may configure as many filesystem "disks" as you wish, and you
    | may even configure multiple disks of the same driver. Defaults have
    | been setup for each driver as an example of the required options.
    |
    | Supported Drivers: "local", "ftp", "sftp", "s3"
    |
    */

    'disks' => [

        'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),
        ],

        'public' => [
            'driver' => 'local',
            'root' => storage_path('app/public'),
            'url' => env('APP_URL').'/storage',
            'visibility' => 'public',
        ],

        's3' => [
            'driver' => 's3',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_BUCKET'),
            'url' => env('AWS_URL'),
            'endpoint' => env('AWS_ENDPOINT'),
            'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
        ],

        'minio' => [
            'driver'                  => 's3',
            'key'                     => env('MINIO_ACCESS_KEY_ID'),
            'secret'                  => env('MINIO_SECRET_ACCESS_KEY'),
            'region'                  => env('MINIO_DEFAULT_REGION'),
            'bucket'                  => env('MINIO_BUCKET'),
            'url'                     => env('MINIO_URL'),
            'endpoint'                => env('MINIO_ENDPOINT'),
            'use_path_style_endpoint' => env('MINIO_PATH_STYLE_ENDPOINT'),
        ]

    ],

    /*
    |--------------------------------------------------------------------------
    | Symbolic Links
    |--------------------------------------------------------------------------
    |
    | Here you may configure the symbolic links that will be created when the
    | `storage:link` Artisan command is executed. The array keys should be
    | the locations of the links and the values should be their targets.
    |
    */

    'links' => [
        public_path('storage') => storage_path('app/public'),
    ],

];

S3のRepositoryを作成

ファイルアップロードの設定ができたところで、S3でアップロードする用のRepository層を作成します。

インターフェース

<?php

use App\Exceptions\S3Exception;
use Illuminate\Http\UploadedFile;
use Symfony\Component\HttpFoundation\StreamedResponse;

interface S3Repository
{
    /**
     * ファイルをS3にアップロードする
     * @param string $path
     * @param UploadedFile $file アップロードするファイル
     * @return string ファイルの保存先のパス
     * @throws S3Exception
     */
    public function upload(string $path, UploadedFile $file): string;

    /**
     * S3上のファイルを削除する。
     * @param string $path
     * @return bool
     * @throws S3Exception
     */
    public function delete(string $path): bool;

    /**
     * S3上のファイルをダウンロードする
     * @param string $path ファイルパス
     * @param string $file_name ファイル名
     * @throws S3Exception
     * @return StreamedResponse
     */
    public function download(string $path, string $file_name): StreamedResponse;
}

実装クラス

<?php

use App\Exceptions\S3Exception;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\StreamedResponse;

class S3RepositoryImpl implements S3Repository
{
    private const DISK_NAME = 's3_public';

    /**
     * {@inheritDoc}
     */
    public function upload(string $path, UploadedFile $file): string
    {
        try {
            $s3_path = Storage::disk(self::DISK_NAME)->putFile($path, $file);
        } catch (S3Exception $e) {
            throw new S3Exception(S3Exception::CODE_FAILED_TO_CONNECT, null, $e);
        }

        if ($s3_path === false) {
            throw new S3Exception(S3Exception::CODE_UPLOAD_FAILED, trans('message.error.s3.failed_to_upload'));
        }
        return $s3_path;
    }

    /**
     * {@inheritDoc}
     */
    public function delete(string $path): bool
    {
        try {
            return $is_success = Storage::disk(self::DISK_NAME)->delete($path);
        } catch (S3Exception $e) {
            throw new S3Exception(S3Exception::CODE_FAILED_TO_CONNECT, null, $e);
        }

        if ($is_success === false) {
            throw new S3Exception(S3Exception::CODE_DELETE_FAILED, trans('message.error.s3.failed_to_delete'));
        }
    }

    /**
     * {@inheritDoc}
     */
    public function download(string $path, string $file_name): StreamedResponse
    {
        try {
            return Storage::disk(self::DISK_NAME)->download($path, $file_name);
        } catch (S3Exception $e) {
            throw new S3Exception(S3Exception::CODE_FAILED_TO_CONNECT, null, $e);
        }
    }
}

参考記事

ぎゅう
WEBエンジニア
渋谷でWEBエンジニアとして働く。
LaravelとVue.jsをよく取り扱い、誰でも仕様が伝わるコードを書くことを得意とする。
先輩だろうがプルリクにコメントをして、リファクタしまくる仕様伝わるコード書くマン
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次
閉じる