M1 Mac上でcolimaを使ってベクトルDB  Milvusをインストール

M1 Mac上でcolimaを使ってベクトルデータベース [Milvus](https://milvus.io/)をインストールしてみました。その方法です。

# 1. まずはcolimaとdockerとdocker-composeをサクッとインストール
colimaとdockerとdocker-compose、未導入の場合は導入します。
3つまとめてbrewでインストールします。

```shell
$ brew install colima docker　docker-compose
```
ちなみに私は[「M1 Mac上でcolimaを使ってDb2 Community Edition Docker イメージを動かす](https://qiita.com/nishikyon/items/553b5c4dff473d9b6322)」でcolimaとdockerは導入済みだったので、　docker-composeのみ追加導入しました(`brew install docker-compose`)。

一旦以下のコマンドでcolimaを開始します(既に導入済み一度でも開始している場合は不要)
```
colima start
```
問題なく開始できるのを確認したら、colimaを停止してください。
```
colima stop
```

# 2. colima chown Permission denied エラー回避設定
milvus 導入時そのままだと「chown Permission denied エラー」　が発生するので、[「How to fix chown ‘permission denied’ issue when using colima on Mac OS X?」](https://medium.com/dandelion-tutorials/how-to-fix-chown-permission-denied-issue-when-using-colima-on-mac-os-x-d925e420c875)に書いてある方法で、設定を修正します。
以下上記のリンク先の内容を日本語で説明します。

### 1. `colima stop`の実行
1.で最後に`colima stop`を実行していると思いますが、実行していない場合は`colima stop`を実行してください。

### 2. ターミナルで`/Users/{username}/.lima/_config` フォルダーにcd
`{username}`は自分のidに置き換えてください。例えば`nishito`だったらコマンドは
```
cd /Users/nishito/.lima/_config
```
になります。

### 3. `override.yaml` ファイルを以下の内容で作成(`vi`などで)：
`{username}`は自分のidに置き換えてください
```yaml
mountType: 9p
mounts:
  - location: "/Users/{username}"
    writable: true
    9p:
      securityModel: mapped-xattr
      cache: mmap
  - location: "~"
    writable: true
    9p:
      securityModel: mapped-xattr
      cache: mmap
  - location: /tmp/colima
    writable: true
    9p:
      securityModel: mapped-xattr
      cache: mmap
```
例えば`{username}`が`nishito`だったら ファイルの内容は以下になります
```yaml
mountType: 9p
mounts:
  - location: "/Users/nishito"
    writable: true
    9p:
      securityModel: mapped-xattr
      cache: mmap
  - location: "~"
    writable: true
    9p:
      securityModel: mapped-xattr
      cache: mmap
  - location: /tmp/colima
    writable: true
    9p:
      securityModel: mapped-xattr
      cache: mmap
```
作成したファイルは
`/Users/{username}/.lima/_config` 
に保存してあることを確認してください。`{username}`は自分のidに置き換えてください。`


### 4. `colima delete`の実行
既存のcolimaの設定を全て削除します。
もし既存を残して別環境でcolimaを使いたい場合は、
「[colimaでprofile切り替えで複数VMを使う](https://qiita.com/nishikyon/items/ac3eed100eb118c8af22/)」
を参照し方法を検討してください。ここではキレイにスパッと削除で進めています。

```
colima delete
```

### 5. `--mount-type 9p`を指定して`colima start`の実行
 `--mount-type 9p`を指定して`colima start`を実行します。
cpuとメモリーは[こちら](https://milvus.io/docs/prerequisite-docker.md)を参考に適当な値に設定してください。

実行例:
```
colima start --cpu 2 --memory 8 --mount-type 9p
```

これでcolimaの準備はOKです。

# 3. Milvus Standaloneの導入
「[Install Milvus Standalone with Docker Compose](https://milvus.io/docs/install_standalone-docker-compose.md)」に記載の方法で導入します。
このドキュメントを読む時は右上のVersion指定が、自分の導入したいversionになっているか確認してから読み始めてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294516/fd629187-1739-509c-45f0-3c340f5a1587.png)

この記事では`v2.3.8`を導入します。

### YAMLファイルのダウンロード
どこかにdocker-compose.yml　を置いておくフォルダーを作成してください。
ここでは`~/milvus`を作成しました。
ターミナルで作成したフォルダーにcdしておいてください。
例：
```
mkdir ~/milvus
cd ~/milvus
```

以下のコマンドでmilvus-standalone-docker-compose.ymlをダウンロードし、docker-compose.ymlとして保存します。
```
wget https://github.com/milvus-io/milvus/releases/download/v2.3.8/milvus-standalone-docker-compose.yml -O docker-compose.yml
```

# 4. Milvusを開始
docker-compose.ymlを保存したフォルダーで、以下のコマンドを実行します:
```
docker-compose up -d
```
初回はイメージをダウンロードするので時間がかかります。


以下のコマンドで確認します：
```
docker-compose ps
```
Milvus standaloneが起動すると、Milvus standaloneサービスとその2つの依存関係を含む3つのdockerコンテナが実行されます。

 以下の3つのコンテナが上がっていればOKです。
`milvus-etcd`
`milvus-minio`
`milvus-standalone`

出力例:
```
~/milvus:$ docker-compose ps
NAME                IMAGE                                      COMMAND                   SERVICE      CREATED         STATUS                   PORTS
milvus-etcd         quay.io/coreos/etcd:v3.5.5                 "etcd -advertise-cli…"   etcd         5 minutes ago   Up 5 minutes (healthy)   2379-2380/tcp
milvus-minio        minio/minio:RELEASE.2023-03-20T20-16-18Z   "/usr/bin/docker-ent…"   minio        5 minutes ago   Up 5 minutes (healthy)   0.0.0.0:9000-9001->9000-9001/tcp, :::9000-9001->9000-9001/tcp
milvus-standalone   milvusdb/milvus:v2.3.8                     "/tini -- milvus run…"   standalone   5 minutes ago   Up 5 minutes (healthy)   0.0.0.0:9091->9091/tcp, :::9091->9091/tcp, 0.0.0.0:19530->19530/tcp, :::19530->19530/tcp
~/milvus:$
```
無事あがっているようです！
ちなみに`milvus-standalone`は起動に少し時間がかかります。STATUSが`Up X minutes (healthy)`になるまで待ちましょう。**(healthy)** が重要です。

# 5. Hello Milvusの実行で動作確認
pythonを使って「[Run Milvus using Python](https://milvus.io/docs/example_code.md)」にある`hello_milvus.py`を実行して動作確認してみます。

### 1. 必要なライブラリの導入
pythonのMilvus Python SDKですが、導入したMilvusのバージョンによって動作するバージョンが結構細かく違います。https://milvus.io/docs/v2.1.x/release_notes.md より対応のSDKバージョンを調べて、導入したMilvusのバージョンに対応したSDKのバージョンを導入してください。
今回はMilvus `v2.3.8`を導入したので、対応するSDKのバージョンは`2.3.6`です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294516/181d2b4e-ccc5-2f94-3671-9c9623751c04.png)

「[Install Milvus Python SDK](https://milvus.io/docs/install-pymilvus.md)」でその他の前提を確認しておいてください。

SDK バージョン`2.3.6`を導入:
```
pip install pymilvus==2.3.6
```

### 2. `hello_milvus.py`のダウンロード
以下のコマンドでello_milvus.py`をダウンロードします：
```sh
wget https://raw.githubusercontent.com/milvus-io/pymilvus/master/examples/hello_milvus.py
```
コードの中身は以下で参照できます:
https://github.com/milvus-io/pymilvus/blob/master/examples/hello_milvus.py

### 3. `hello_milvus.py`の実行
実行します。
```
 python hello_milvus.py
```

　実行例:
 ```
 $ python hello_milvus.py

=== start connecting to Milvus     ===

Does collection hello_milvus exist in Milvus: False

=== Create collection `hello_milvus` ===


=== Start inserting entities       ===

Number of entities in Milvus: 3000

=== Start Creating index IVF_FLAT  ===


=== Start loading                  ===


=== Start searching based on vector similarity ===

hit: id: 2998, distance: 0.0, entity: {'random': 0.9728033590489911}, random field: 0.9728033590489911
hit: id: 1262, distance: 0.08883658051490784, entity: {'random': 0.2978858685751561}, random field: 0.2978858685751561
hit: id: 1265, distance: 0.09590047597885132, entity: {'random': 0.3042039939240304}, random field: 0.3042039939240304
hit: id: 2999, distance: 0.0, entity: {'random': 0.02316334456872482}, random field: 0.02316334456872482
hit: id: 1580, distance: 0.05628090724349022, entity: {'random': 0.3855988746044062}, random field: 0.3855988746044062
hit: id: 2377, distance: 0.08096685260534286, entity: {'random': 0.8745922204004368}, random field: 0.8745922204004368
search latency = 0.2290s

=== Start querying with `random > 0.5` ===

query result:
-{'random': 0.6378742006852851, 'embeddings': [0.20963514, 0.39746657, 0.12019053, 0.6947492, 0.9535575, 0.5454552, 0.82360446, 0.21096309], 'pk': '0'}
search latency = 0.4282s
query pagination(limit=4):
	[{'random': 0.6378742006852851, 'pk': '0'}, {'random': 0.5763523024650556, 'pk': '100'}, {'random': 0.9425935891639464, 'pk': '1000'}, {'random': 0.7893211256191387, 'pk': '1001'}]
query pagination(offset=1, limit=3):
	[{'random': 0.5763523024650556, 'pk': '100'}, {'random': 0.9425935891639464, 'pk': '1000'}, {'random': 0.7893211256191387, 'pk': '1001'}]

=== Start hybrid searching with `random > 0.5` ===

hit: id: 2998, distance: 0.0, entity: {'random': 0.9728033590489911}, random field: 0.9728033590489911
hit: id: 747, distance: 0.14606499671936035, entity: {'random': 0.5648774800635661}, random field: 0.5648774800635661
hit: id: 2527, distance: 0.1530652642250061, entity: {'random': 0.8928974315571507}, random field: 0.8928974315571507
hit: id: 2377, distance: 0.08096685260534286, entity: {'random': 0.8745922204004368}, random field: 0.8745922204004368
hit: id: 2034, distance: 0.20354536175727844, entity: {'random': 0.5526117606328499}, random field: 0.5526117606328499
hit: id: 958, distance: 0.21908017992973328, entity: {'random': 0.6647383716417955}, random field: 0.6647383716417955
search latency = 0.4018s

=== Start deleting with expr `pk in ["0" , "1"]` ===

query before delete by expr=`pk in ["0" , "1"]` -> result:
-{'random': 0.6378742006852851, 'embeddings': [0.20963514, 0.39746657, 0.12019053, 0.6947492, 0.9535575, 0.5454552, 0.82360446, 0.21096309], 'pk': '0'}
-{'random': 0.43925103574669633, 'embeddings': [0.52323616, 0.8035404, 0.77824664, 0.80369574, 0.4914803, 0.8265614, 0.6145269, 0.80234545], 'pk': '1'}

query after delete by expr=`pk in ["0" , "1"]` -> result: []


=== Drop collection `hello_milvus` ===

$
 ```
最後までエラーなく実行できればOKです。

参考までにソースのコメントからこのコードは以下をやっています：

1. connect to Milvus (Milvusへの接続)
1. create collection　 (collectionの作成)
1. insert data　（データのインサート）
1. create index　（インデックスの作成）
1. search, query, and hybrid search on entities　(類似検索、照会、類似検索と照会ハイブリッド検索)
1. delete entities by PK　（PKエンティティの削除）
1. drop collection (collectionの削除)


# 6. Milvusの停止
[3. Milvus Standaloneの導入](#3-milvus-standaloneの導入)でダウンロードしたYAMLファイルがあるフォルダーで以下を実行します:
```
docker-compose down
```

もしデータを削除したい場合は以下を実行します:
```
rm -rf  volumes
```

以上です。

# 参考: Milvus関連投稿
-  [Milvusのグラフィカルな管理ツール AttuをMacに導入してみた](https://qiita.com/nishikyon/items/addf2689812a2d9eb8e3)
-  [Docker composeで導入したMilvusにユーザー認証を有効化する](https://qiita.com/nishikyon/items/452d92c144f244ab855a)