MinIO (OSS オブジェクトストレージ)をAzure OpenAIのRAGと共に使う


# minioとは
MinIOとはオープンソースのAmazon S3 互換のオブジェクト・ストレージ・サーバーソフト。
java, Go, Node.js, python, .NETなどにより使用可能であり、特にPythonではBoto3などのライブラリで使用することが可能な点が魅力。
Kubernetesにより、パブリッククラウド、プライベートクラウド問わずシームレスな活用が可能。kubeflowでもアーティファクトストア（学習済みモデルや実行ログなどの格納先）として利用されている。なお、kubeflowに組み込まれているものはバージョンが少し古い。（gcr.io/ml-pipeline/minio:RELEASE.2019-08-14T20-37-41Z-license-compliance）

最新はRELEASE.2023-10-24T04-42-36Z　等であり、GUIも大幅に変わっており操作性も改善しているので、最新版をインストールして利用する手順をまとめた。

# Azure OpenAI (chatgpt) RAG連携での使い方
RAGにてsourceファイルの実体を格納し、そのリンクをVectorStoreにmetadataとして格納すれば、ChatGPTの回答の根拠ソースとして表示できる。ハルシネーション対策として有効。
なおAzure blobやS3、GCSでももちろん良いのだが、Azure blobがいまいちとっつきづらく、またS3、GCSでわざわざマルチクラウド化するのもよくないので、AKS上にMinIOサーバとして構築して使用してみた。
（AKSのPV、Azure上ではブロックデバイス上にファイル保存となるため、可用性面では要インフラレイヤの対策。）

https://min.io/

# MinIOインストール (on kubernetes)
kubernetes上でのインストール手順は以下のとおり。（マニュフェストの適用）
AKS上にk8sクラスタ構築済みの前提として進める。

```bash
curl https://raw.githubusercontent.com/minio/docs/master/source/extra/examples/minio-dev.yaml -O
```

```bash
kubectl apply -f minio-dev.yaml
```

# コンフィグ (yamlの編集)
マニュフェストを適宜修正する。

- 専用のネームスペース(minio)を作成
- PasswordはSecretにbase64キーを入れる(セキュリティ）

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: minio
---
apiVersion: v1
kind: Secret
metadata:
  namespace: minio
  name: minio
type: Opaque
data:
  MINIO_ROOT_PASSWORD: 【base64-key】
---
```
base64キーは下記コマンドで取得。
（ログインには元のkeyを使用。）
```bash
$ echo "【key】" |base64
【base64-key】
```

ボリュームとデプロイメントの設定
- 環境変数MINIO_ROOT_USER、MINIO_ROOT_PASSWORDにて管理ユーザ、パスワードを設定する。管理ユーザはadmin（パスワードは上記で設定のSecretを参照させる）
- PVC 10GBで確保（要件に合わせて拡張）、/dataにマウント。
- ポートは9000と9001をあける。
- 9001は管理アクセス（ダッシュボード）。/minio/でアクセスする。

```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-minio
  namespace: minio
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: default
  resources:
    requests:
      storage: 10Gi

---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: minio
  name: minio
  labels:
    app: minio
spec:
  replicas: 1
  selector:
    matchLabels:
      app: minio
  template:
    metadata:
      labels:
        app: minio
    spec:
      containers:
        - name: minio
          image: minio/minio:latest
          command:
          - /bin/bash
          - -c
          args:
          - minio server /data  --console-address :9001


          resources: {}
          ports:
            - containerPort: 9000
            - containerPort: 9001
          imagePullPolicy: Always

          env:
            - name: MINIO_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: minio
                  key: MINIO_ROOT_PASSWORD

            - name: MINIO_ROOT_USER
              value: admin

            - name: MINIO_BROWSER_REDIRECT_URL
              value: https://【URL】/minio/

          volumeMounts:
            - name: minio-vol
              mountPath: /data

      volumes:
        - name: minio-vol
          persistentVolumeClaim:
            claimName: pvc-minio
---
```


フロントにNGINXを立てる。これは管理用アクセスとコンテンツ用アクセスを分岐するため。
- /minio/ アクセスは9001に振り分け→管理用アクセス
- /file/　アクセスは9000に振り分け→コンテンツ用アクセス
- configmapにてnginxのパラメータを設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288299/4f872fb4-dc92-8e0c-62e6-2b41d4bb869d.png)


```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
  namespace: minio
data:
  nginx.conf: |-


    server {
        listen       80;
        listen  [::]:80;
        #server_name  localhost;
        server_name  【URL】;

        # To allow special characters in headers
        ignore_invalid_headers off;
        # Allow any size file to be uploaded.
        # Set to a value such as 1000m; to restrict file size to a specific value
        client_max_body_size 0;
        # To disable buffering
        proxy_buffering off;
        proxy_request_buffering off;

        location /minio/ {
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-NginX-Proxy true;

            # This is necessary to pass the correct IP to be hashed
            real_ip_header X-Real-IP;

            proxy_connect_timeout 300;
            
            # To support websocket
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            
            chunked_transfer_encoding off;

            proxy_pass http://minio.minio.svc.cluster.local:9001/;
        }



        location /file/ {
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;

            proxy_connect_timeout 300;
            # Default is HTTP/1, keepalive is only enabled in HTTP/1.1
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            chunked_transfer_encoding off;


            proxy_pass http://minio.minio.svc.cluster.local:9000/;
        }
    }

---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: minio
  name: minio-nginx
  labels:
    app: minio-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: minio-nginx
  template:
    metadata:
      labels:
        app: minio-nginx
    spec:
      containers:
        - name: minio-nginx
          image: nginx:1.19.2-alpine
          ports:
            - containerPort: 80
          volumeMounts:
            - name: conf
              mountPath: /etc/nginx/conf.d/
      volumes:
        - name: conf
          configMap:
            name: nginx-config

---
kind: Service
apiVersion: v1
metadata:
  namespace: minio
  name: minio-nginx
  labels:
    app: minio-nginx
spec:
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 80

  selector:
    app: minio-nginx
  type:  ClusterIP

---
kind: Service
apiVersion: v1
metadata:
  namespace: minio
  name: minio
  labels:
    app: minio
spec:
  ports:
    - name: http
      protocol: TCP
      port: 9001
      targetPort: 9001


    - name: http2
      protocol: TCP
      port: 9000
      targetPort: 9000

  selector:
    app: minio
  type:  ClusterIP
---
```
外部からのアクセス設定。
必要に応じてバーチャルサービス設定。
（下記はkubeflow内に構築する場合。）

```yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: minio
  namespace: minio
spec:
  gateways:
  - kubeflow/kubeflow-gateway
  hosts:
  - '*'
  http:
  - match:
    - uri:
        prefix: /minio
    rewrite:
      uri: /minio
    route:
    - destination:
        host: minio-nginx.minio.svc.cluster.local
        port:
          number: 80

  - match:
    - uri:
        prefix: /file
    rewrite:
      uri: /file
    route:
    - destination:
        host: minio-nginx.minio.svc.cluster.local
        port:
          number: 80
```


# 利用
MinIOへのファイル操作はブラウザによる管理アクセス、もしくはboto3等によるAPI操作にて可能。
以下ブラウウザ操作にて説明。

## 管理アクセス
ブラウザより適宜設定したアドレス（URL/minio）にてアクセスする。（フロントの構築方法により適宜読み替え）
https://【URL】/minio/

設定したユーザ名（admin）、パスワードにてログイン。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288299/e819ad32-666f-84bc-a69b-6ee5b7c4265e.png)


## バケットの作成
Create Bucketよりバケットを作成する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288299/c7e0438b-0038-bff9-c75b-245b1e535246.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288299/14378d5a-6f4a-53b2-e162-aac3d0fbeaf0.png)

後述のファイルへのブラウザアクセスを可能とするため、バケットのAccess Policyはpublicにする。
（→フロントにてNW等でセキュリティを担保されている前提）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288299/94a37e9f-6d00-517a-cdd7-71b52c877a76.png)

## ファイルアップロード
Object Browserより作成したバケットへドラッグアンドドロップにてファイルをアップロードする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288299/3c7e35aa-22ca-e47b-fc57-d6db109188f1.png)



## LLMのRAGでの利用
ファイルへはURL/file/～にてブラウザアクセスすることができる。
Cognitive Searchのメタデータ（source）にリンクとして設定し、RAG問合せ回答の根拠（ソース）として提供する。（グラウンディング）

例）sourceにファイルへのアクセスURLとして設定
https://【URL】/file/【バケット名】/【ファイル名】

```python
# Cognitive Searchへの登録例
documents = [{'@search.action': 'upload',
              'content': t.page_content,
              'id': prefix + str(i),
              'title': t.metadata['title'],
              'source': t.metadata['source'],
             } for i, t in enumerate(splitted_docs)]

result = search_client.upload_documents(documents=documents)
```

プロンプト例
```
Sourcesを元にmarkdown書式で回答してください。
回答の最後に参照した全てのSourceのリンクを[title](source)の書式で箇条書きで示してください。
```

回答例
```
回答
　　xxxx

参照したSources:
　　[title](URL "タイトル")  ※titleの内容の外部リンク
```


# ゲートウェイ機能について
なおKubeflowにて使用する場合、ディストリビューション版でインストールするとファイル実態はMinIOのPVに保管されず、クラウドサービスのマネージドのオブジェクトストレージ（GCS等）を使用するゲートウェイ設定がされている。現在最新版では当該機能は凍結されてしまっており使用できない。（S3 APIへの追従が困難になったらしい）
https://blog.min.io/deprecation-of-the-minio-gateway/

# まとめ
OSSオブジェクトストレージMinIOのインストール、設定、そしてAzure OpenAI等のRAGからの利用例を整理した。オブジェクトストレージはクラウドサービスによる方言が大きく、かつ一番最初に使用する必要性が生じることが多いため、手っ取り早く使えるという点では優れている。（もちろん非機能面ではマネージドサービスを駆使する方が高度なことはできるので、状況に応じて使い分ける。）