ローカルS3環境(minio)を構築する

## 概要
ローカルでS3が使えるように環境を作る。
アプリなどから画像をS3に登録してサイト側で閲覧できるようにしたい。

:::note info
お知らせ
minIOのバージョンがアップされています。
この記事のminIOは１つ前のもの (RELEASE.2021-06-17T00-10-46Z) です。
※ 最新のminIOの場合、異なる部分があると思うのでご注意ください
:::
**<参考サイト>**
※ [MinIOを使ってローカルで外部ストレージをAmazonS3から置き換える](https://www.ritolab.com/entry/232)

## ファイル構成

```
├ docker
│ └ minio
│   ├ config               ... minio設定 (git管理対象外)
│   ├ data                 ... minioデータ (git管理対象外) 
│   ├ export
│   │ └ static             ... 起動時に取り込むファイル
│   │   ├ 150x150.png
│   │   └ 640x480.png
│   └ policies
│     └ buckets
│        └ static
│           └ policy.json  ... staticバケットのポリシー定義
│
│.gitignore
├ docker-compose.yml
└ REAMDE.md
```
※ github: https://github.com/reflet/docker-s3

## docker設定
`minio` を `9090` ポートで起動する設定となっています。
※ 起動時に `docker/minio/export/...` を取り込むように設定しています
※ 最新版は仕様が変わっているのでバージョン指定（ `RELEASE.2021-06-17T00-10-46Z` ）しています

```yaml:docker-compose.yml
version: '3'
services:
  minio:
    image: minio/minio:RELEASE.2021-06-17T00-10-46Z
    container_name: 'minio'
    ports:
      - 9090:9000
    environment:
      - MINIO_ACCESS_KEY=minio
      - MINIO_SECRET_KEY=minio123
    entrypoint: sh
    command: -c "
      mkdir -p /data/.minio.sys/buckets;
      cp -r /policies/* /data/.minio.sys/;
      cp -r /export/* /data/;
      /usr/bin/minio server /data;
      "
    volumes:
      - ./docker/minio/data:/data
      - ./docker/minio/export:/export
      - ./docker/minio/config:/root/.minio
      - ./docker/minio/policies:/policies
```

## Bucketのポリシー設定
`static` バケットのポリシー設定を記述します。

```json:policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "*"
        ]
      },
      "Action": [
        "s3:GetBucketLocation",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::static"
      ]
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "*"
        ]
      },
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": [
        "arn:aws:s3:::static/*",
        "arn:aws:s3:::static"
      ]
    }
  ]
}
```

## サーバ起動
下記コマンドにて、dockerコンテナを起動します。

```bash
$ docker-compose up -d
```

## 管理画面
minioの管理画面へアクセスできます。
※ ID、PASSは、 `docker-compose.yml` に記載されています。

```bash
$ open http://127.0.0.1:9090/
```

## S3ファイルへアクセス
下記URLにて `static` バケットのファイルにアクセス可能です。
※ アプリなどからアクセスする際に指定するURLとなります

```bash
$ open http://127.0.0.1:9090/static/hoge.txt
$ open http://127.0.0.1:9090/static/150x150.png
$ open http://127.0.0.1:9090/static/640x480.png
```

![hoge.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142279/3985c378-8cc5-5df4-440b-a1cd5d17a79f.png)

![150.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142279/31bc6492-b8ae-6576-404f-20964a08065a.png)

![640.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142279/4cc08a5d-4629-de00-b787-dfdf6f2b44b6.png)


## AWS SDK for PHP
`AWS SDK for PHP` で使う場合は、以下のようにエンドポイントの指定が必要となります。
※ アクセス元が、docker内のコンテナか、docker外のホストかで「 `endpoint` 」が変わります

```php
date_default_timezone_set('Asia/Tokyo');
require 'vendor/autoload.php';

$s3 = new Aws\S3\S3Client([
        'version' => 'latest',
        'region'  => 'ap-northeast-1',
        'credentials' => [
            'key'    => 'minio',
            'secret' => 'minio123',
        ],
        'endpoint' => 'http://minio:9000',        // ← 追加 (docker内からアクセス)
        // 'endpoint' => 'http://127.0.0.1:9090', // ← 追加 (ホストからアクセス)
        'use_path_style_endpoint' => true,        // ← 追加
]);
```

## minioのファイル操作 (awscliコマンド)
管理画面から操作できますが、まとめて画像ファイルをアップしたいなどの場合、
aws-cliコマンドで操作した方が楽そうです。

```bash
$ aws --version
```

もし、コマンドが無い場合は追加する。

```bash
# MacOS
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
$ brew -v
$ brew install awscli
$ aws --version

# Ubuntu
$ sudo apt-get -y install unzip
$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
$ unzip awscliv2.zip
$ sudo ./aws/install
$ aws --version
```

### Config設定
プロフィールを作成する。 

```bash
$ aws configure --profile minio
AWS Access Key ID [None]: minio
AWS Secret Access Key [None]: minio123
Default region name [None]: ap-northeast-1
Default output format [None]: json
```

プロフィール一覧を確認してみる。

```bash
$ aws configure list-profiles
```

プロフィール(minio)の設定状況を確認してみる。

```bash
$ aws configure list --profile minio
```

### バケット内のファイル一覧を確認する

```bash
# docker内からアクセス: --endpoint-url　http://minio:9000
$ aws --profile minio --endpoint-url http://127.0.0.1:9090/ s3 ls s3://static
2020-10-25 09:33:53          5 hoge.txt
```

### 画像をまとめて転送する
`media` フォルダをまとめて `static` バケットに送る。

```bash
# docker内からアクセス: --endpoint-url　http://minio:9000
$ aws --profile minio --endpoint-url http://127.0.0.1:9090/ s3 sync media s3://static/media
```

### 画像をまとめて削除する
`media` フォルダを削除する

```bash
# docker内からアクセス: --endpoint-url　http://minio:9000
$ aws --profile minio --endpoint-url http://127.0.0.1:9090/ s3 rm s3://static/media/ --recursive
```

### ファイルを追加する

```bash
# docker内からアクセス: --endpoint-url　http://minio:9000
$ aws --profile minio --endpoint-url http://127.0.0.1:9090/ s3 cp fuga.txt s3://static
upload: ./hoge.txt to s3://static/fuga.txt
```

### フォルダを追加する

```bash
# docker内からアクセス: --endpoint-url　http://minio:9000
$ aws --profile minio --endpoint-url http://172.17.100.101:9090/ s3api put-object --bucket static --key hoge/ 
```

### ファイルを削除する

```bash
# docker内からアクセス: --endpoint-url　http://minio:9000
$ aws --profile minio --endpoint-url http://127.0.0.1:9090/ s3 rm s3://static/hoge.txt
delete: s3://static/hoge.txt
```

## minioのファイル操作 (curlコマンド)
Linuxサーバであれば、ほぼ存在する「 `curlコマンド` 」で操作してみる。

### ファイルを取得する
```bash
# docker内からアクセス: 127.0.0.1:9090 → http://minio:9000
$ curl http://127.0.0.1:9090/static/hoge.txt
```

### ファイルを登録する (認証なし)
「 `/tmp/hoge.txt` 」を転送する例です。

```bash
# docker内からアクセス: http://127.0.0.1:9090 → http://minio:9000
$ export FILE='hoge.txt'
$ export URL='http://127.0.0.1:9090/static/${FILE}'
$ export PATH='/tmp/${FILE}'
$ curl -i --request PUT ${URL} \
  --upload-file ${PATH} \
  -H "x-amz-acl: bucket-owner-full-control"
```

### ファイルを登録する (認証あり)
「 `/tmp/hoge.txt` 」を転送する例です。

```bash
# docker内からアクセス: http://127.0.0.1:9090 → http://minio:9000
export S3_KEY="minio"
export S3_SECRET="minio123"
export RESOURCE="/static/hoge.txt"
export CONTENT_TYPE="application/octet-stream"
export DATE=`date -R`
export _SIGNATURE="PUT\n\n${CONTENT_TYPE}\n${DATE}\n${RESOURCE}"
export SIGNATURE=`echo -en ${_SIGNATURE} | openssl sha1 -hmac ${S3_SECRET} -binary | base64`
curl -v -X PUT http://127.0.0.1:9090${RESOURCE} \
  -T /tmp/hoge.txt \
  -H "Host: 127.0.0.1:9090" \
  -H "Date: ${DATE}" \
  -H "Content-Type: ${CONTENT_TYPE}" \
  -H "Authorization: AWS ${S3_KEY}:${SIGNATURE}"
```

### ファイルを登録する (認証なし - PHP)
PHPで `curl_exec()` 関数にて送信を試してみる。

```php
$file = '/tmp/hoge.txt';
$fp   = fopen($file, 'rb');
$size = filesize($file);

// docker内からアクセス: http://127.0.0.1:9090 → http://minio:9000
$url = 'http://127.0.0.1:9090/static/' . basename($file);
$ch  = curl_init($url);
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_INFILE, $fp);
curl_setopt($ch, CURLOPT_INFILESIZE, $size);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 4);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['x-amz-acl: bucket-owner-full-control']);
$response = curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);

// 結果を見てみる
var_dump($info, $response)
```

以上


## 参考サイト
* [minio/minio](https://hub.docker.com/r/minio/minio/)
* [minio-docker-compose](https://docs.min.io/docs/deploy-minio-on-docker-compose.html)
* [
kanolato/Nginx-minio-static.md](https://gist.github.com/kanolato/577d6329ea5107826c7230bab59f1f02)
* [Minio (S3互換) で バージョン管理したデータを含めて永続化する](https://qiita.com/daisukeArk/items/ac97665e5de7b726fa67)
* [How to use AWS SDK for PHP with MinIO Server](https://docs.min.io/docs/how-to-use-aws-sdk-for-php-with-minio-server.html)
* [Upload to minio via curl](https://www.burgundywall.com/post/upload-minio-curl)