S3互換のオブジェクトストレージ minio を動かしてNextcloudのプライマリストレージにする


## 経緯

現在我が家では自宅サーバが一機稼働している[^homeserver]のだが、諸々の理由[^reason]により、将来的にraspberry pi のような省電力で安価なSBCとコンテナを利用したクラスタ構成にしたいと思っている。

その際、バックアップやセキュリティの都合上、オブジェクトストレージやデータベースといった、永続化したいサービスのみファイルサーバで動かし、それ以外のウェブアプリケーションは別のホストに分離したい。

ということで、今回はその第一歩として、[MinIO](https://min.io/)というS3互換のオブジェクトストレージを起動し、Nextcloudのストレージとして使えるようにしてみる。

## 初めに

### 本記事で扱うこと

- minioのインストール、起動、Nextcloud用の各種設定（ユーザ作成、ポリシー、バケットの設定）
  - コンテナの操作はpodmanを、設定はminio公式のCLIクライアントである`mc`（Manjaroでは`mcli`）コマンドを利用する
- Nextcloudのメインのストレージ（プライマリストレージ）としてminioを使うよう設定の変更

### 本記事で扱わないこと

- Nextcloudのインストール
- Nextcloudのオブジェクトストレージへのデータ移行
  - プライマリストレージを変更した場合、元のファイルは無視されるため、Nextcloud上ではファイルが空になってしまう。（[この記事](https://qiita.com/myoshimi/items/953119efc90d8c117fd7)によると設定を戻せば戻るらしい、今回は検証してない。）
  - プライマリストレージとしてオブジェクトストレージを使う場合とそうでない場合でファイル名の形式が異なる（後者はクライアント側とおなじディレクトリ構造のためわかりやすい）ため、`mv`コマンド等で簡単に移動はできない。
  - 今回はユーザーが自分一人であり、当然同期済みのデータがローカルにあるので、サーバー側での対応は行わず、ローカルのファイルを控えて入れなおす方向で対処した。

## バージョン情報

```
$ uname -a                                                                                                                       
Linux fox 5.4.169-1-MANJARO #1 SMP PREEMPT Wed Dec 29 18:08:59 UTC 2021 x86_64 GNU/Linux
$ podman --version                                                                                                               
podman version 3.4.4
$ mcli --version
mcli version RELEASE.2021-12-10T00-14-28Z
```

- **Nextcloud**: Nextcloud Hub II (23.0.0)
- **MinIO server**: RELEASE.2022-01-08T03-11-54Z

## Minioの起動

公式の例ではコンテナを扱うコマンドとして、`docker` ではなく `podman` というのを使っていた。 `podman` は初見ではあったが `docker` もたいして使ったことがあるわけでもないので公式のマニュアル通り無難に `podman` を使う。

```
$ sudo pacman -S podman
```

podmanでminioを起動する。
各引数は`-d`でバックグラウンド化、`-p`でポートを、`-v`でホストのボリュームをマウント、`-e`で環境変数を指定している（基本的にはDockerと同じはず）。

```
$ sudo podman run -d \
  -p 9000:9000 \
  -p 9001:9001 \
  -v /bottle/minio:/data \
  -e "MINIO_ROOT_USER=minioadmin" \
  -e "MINIO_ROOT_PASSWORD=<miniopassword>" \
  quay.io/minio/minio server /data --console-address ":9001"
```

ちなみに、上記の管理ユーザや後で作成する`nextcloud`ユーザもそうだが、MinIOでのユーザ名とパスワードは、S3でいうところのAccess key Id, Secret Access Keyと共用となっている模様。

`9000`ポートでオブジェクトストレージに、`9001`ポートでコンソールにアクセスできる。

## Minioの設定

[MinIO Client (mc) — MinIO Baremetal Documentation](https://docs.min.io/minio/baremetal/reference/minio-mc.html)

Webコンソールも使えるのだが、基本的な設定はCLIでできるようにしたいので、今回は`mc`（`mcli`）というMinIO用のクライアントコマンドを使って設定していく。

```
$ sudo pacman -S minio-client
```

### サーバの登録

`mcli alias set` で、9000ポートを `local` という名前に紐づける。`git` でいうところの`git remote add` のような感じ。

```
$ mcli alias set local http://localhost:9000
Enter Access Key: minioadmin
Enter Secret Key: 
Added `local` successfully.
```

パスワードやIDは `podman run` 時に指定した管理用の物を入力する。

### バケットの作成

`mcli mb` でバケットの作成

```
$ mcli mb local/nextcloud
Bucket created successfully `local/nextcloud`.
```

`mcli ls` で確認できる

```
$ mcli ls local
[2022-01-23 11:34:02 JST]     0B nextcloud/
```

### ポリシーの作成

[Amazon S3公式の例（S3 バケットのオブジェクトへの読み取りおよび書き込みアクセスを許可する](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_examples_s3_rw-bucket.html)）を参考に `nextcloud` バケット用のポリシーを作成する

```json:nextcloud.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ListObjectsInBucket",
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::nextcloud"
      ]
    },
    {
      "Sid": "AllObjectActions",
      "Effect": "Allow",
      "Action": [
        "s3:*Object*"
      ],
      "Resource": [
        "arn:aws:s3:::nextcloud/*"
      ]
    }
  ]
}
```

このjsonを `nextcloud` ポリシーとして読み込む

```
$ mcli admin policy add local nextcloud nextcloud.json
```

### ユーザの作成、ポリシーの割り当て

`nextcloud` ユーザを作成する

```
$ mcli admin user add local nextcloud nextcloudpasswd
Added user `nextcloud` successfully.
```

`nextcloud` ユーザに `nextcloud` ポリシーを紐づける。

```
$ mcli admin policy set local nextcloud user=nextcloud
Policy `nextcloud` is set on user `nextcloud`
```





これでnextcloudユーザがnextcloudバケットの読み書きができるようになったはず。

## Nextcloud の設定

今回はNextcloud内部に全くデータを置かないというのが理想なので、`data`ディレクトリ（マニュアル曰くPrimary Storage)を丸々オブジェクトストレージに差し替える設定をする。[^externalstorage]

### 設定ファイルに `objectstore` の追記

以下のようにNextcloudの設定ファイルの最後に `objectstore` を追加する

```/usr/share/webapps/nextcloud/config/config.php  
<?php
$CONFIG = array (
  /* 中略 */
  'objectstore' => array (
    'class' => 'OC\\Files\\ObjectStore\\S3',
    'arguments' => 
    array (
      'bucket' => 'nextcloud', //MinIOのバケット名
      'autocreate' => true, // 未検証、おそらくバケットがない場合作るものと思われるが、今回は作成済みのため関係ないはず。
      'key' => 'nextcloud', // MinIO上の`nextcloud`用ユーザ
      'secret' => '****************************************', // MinIO上の`nextcloud`ユーザのパスワード
      'hostname' => 'localhost', //MinIOのホスト名
      'port' => 9000, //MinIOのポート
      'use_ssl' => false, //ssl を使うかどうか、今回はローカルなので不要。
      'use_path_style' => true, // バケットをパスで指定するかドメインで指定するか。今回は手っ取り早くlocalhostでアクセスするためパスで指定する。なお、本家AWS S3ではパス指定は非推奨
    ),
  ),
);
```

設定自体はこれだけで完了、MinIOにアクセスできるようになっている。


## 結果

設定直後のNextcloud Filesの様子がこちら、`Nextcloud Notes` アプリが作成した `Documents/Note` フォルダ以外は空っぽになっていることがわかる。

[![空っぽになったNextcloud filesの画像][img-empty-files]][link-img-empty-files]
Minioのコンソールで確認すると、すでに多数のファイルが追加されていることがわかる。

[![バケットの画像][img-bucket-content]][link-img-bucket-content]

ちゃんと読み書きできている模様。

ファイル名はすべてIDらしきものになっており、これだけだとどれがどのファイルなのかはわからない。

参考までに、以下がデフォルト（`objectstore`未設定時）の`data`ディレクトリの中身、上記のMinioとは全く異なっていることがわかる。

```
sudo tree -L 3 /usr/share/webapps/nextcloud/data                                                                                               
/usr/share/webapps/nextcloud/data
├── appdata_************
│   #中略
├── files_external
│   └── rootcerts.crt
├── fluo10
│   ├── cache
│   ├── files
│   │   ├── Documents
│   │   ├── InstantUpload
│   │   ├── Nextcloud intro.mp4
│   │   ├── Nextcloud Manual.pdf
│   │   ├── Nextcloud.png
│   │   ├── Notes
│   │   ├── Pictures
│   │   ├── Reasons to use Nextcloud.pdf
│   │   └── Talk
│   ├── files_trashbin
│   │   ├── files
│   │   ├── keys
│   │   └── versions
│   ├── files_versions
│   │   ├── Documents
│   │   ├── InstantUpload
│   │   └── Pictures
│   └── uploads
├── index.html
├── nextcloud.log
└── nextcloud.log.1
```

パスの構造が全く違うため、[通常のバックアップやリストア](https://docs.nextcloud.com/server/latest/admin_manual/maintenance/restore.html)のように、単にディレクトリをコピペして移行するのは無理そう。今回はユーザーが筆者一人であり、クライアント側にあるデータを同期し直せば済む（タイムスタンプは残らなそうだが）ので、サーバ側での作業はこれでヨシとする。

## その他参考ページ

- [DB Migration to Object Storage as Primary Storage: How? : NextCloud](https://www.reddit.com/r/NextCloud/comments/exeigh/db_migration_to_object_storage_as_primary_storage/)

[^homeserver]: NextcloudやGitea, mpdなど、自分用のサービスをまとめて動かしてVPN経由で屋外からアクセスできるようにしている。自作PCのあまりパーツを流用したもので、無駄にRyzenを積んでおり、力を持て余している感が否めない。

[^reason]: 理由としては省電力化やハードウェアのスケーリングや冗長化、セキュリティ向上のためのホストの分離、とくにファイルサーバとそれ以外の機能はなるべく分離したい。

[^externalstorage]: ちなみにGUIから設定できる外部ストレージ機能もあるのだが、それとは別の機能。単に外部のストレージを使うだけならsmb/cifsが使えるそっちの方が手軽かも知れない。


[img-bucket-content]: https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/400984/9f0e236a-9129-8c57-14f9-6e17641af151.png
[link-img-bucket-content]: https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/400984/9f0e236a-9129-8c57-14f9-6e17641af151.png


[img-empty-files]: https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/400984/eb5dab7e-8c85-f7b5-e978-001407969bd7.png
[link-img-empty-files]: https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/400984/eb5dab7e-8c85-f7b5-e978-001407969bd7.png