k3s上にWeaviateを導入する

男もすなる生成AIといふものを、自分もしてみむとてするなり。で、生成AIを会社の中で使用するとしたら、社内のデータを如何に活用するかという事になりRAGが必要となり、RAGが必要となればそれに適したベクトルデータベースが必要になる訳ですね。
ベクトルデータベースも百花繚乱ですが、以前[Dify](https://dify.ai/jp)を試しに触ってみたことがあり、そのDifyの内部で使われている[Weaviate](https://weaviate.io/ja)を、aws上に作ったk3s上に入れてみることにしました。Difyもいいのですが、DB内のデータの持ち方をいろいろ工夫したいとなると、お仕着せのDifyでなくLlamaindex+Weaviateを試したくなったという経緯があったので。

# 導入
実際の導入は、[公式](https://weaviate.io/developers/weaviate/installation/kubernetes)に書かれているので慣れてる方にとっては難しくないと思います。英語に慣れてない私には時間がかかりましたが。
### helmを使う
helmが用意されているので、それを使います。
```bash
helm repo add weaviate https://weaviate.github.io/weaviate-helm
helm show values weaviate/weaviate > weaviate_values.yaml
```
### weaviate_value.yamlのカスタマイズ
自分の環境に合わせてweaviate_values.yamlをカスタマイズします。私の場合は以下のようにしました。

・自分のk3sクラスタにはLonghornが入っているので、storageClassNameには"longhorn"を指定しておきます。
```yaml:weaviate_values.yaml
# The Persistent Volume Claim settings for Weaviate. If there's a
# storage.fullnameOverride field set, then the default pvc will not be
# created, instead the one defined in fullnameOverride will be used
storage:
  size: 32Gi
  storageClassName: "longhorn"
```

・今回、AWS Bedrockのcohere.embed-multilingual-v3でembeddingをすることにしました。weaviateデータベースにデータが挿入されるとき、自動的にembeddingできるよう以下設定しました。
```yaml:weaviate_values.yaml
modules:
  text2vec-aws:

    # enable if you want to use AWS module
    enabled: true

    # For Weaviate to be able to use AWS Bedrock service it needs a user credentials to authenticate to AWS.
    # The User must have permissions to use AWS Bedrock service AI.
    # You can pass the User credentials (access-key id and access-secret-key) in 2 ways:
    # 1. by setting the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY plain values in the `secrets` section below
    #     this chart will create a kubernetes secret for you with these key-values pairs
    # 2. create Kubernetes secret/s with AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY keys and their respective values
    #     Set the Key and the secret where it is set in `envSecrets` section below
    secrets: {AWS_ACCESS_KEY_ID: 'ここにAWSアクセスキーを入れる' ,
              AWS_SECRET_ACCESS_KEY: 'ここにAWSシークレットアクセスキーを入れる'}

    # If one has already defined secrets with AWS credentials one can pass them using
    # this setting:
    envSecrets: {}
    #   AWS_ACCESS_KEY_ID: name-of-the-k8s-secret-containing-the-key-id
    #   AWS_SECRET_ACCESS_KEY: name-of-the-k8s-secret-containing-the-key
```
本来はkubenetes secretを使って設定してあげるのが正式だと思いますが。

・なお、私はk3sクラスタにVPNを使ってアクセスするので特にWeaviateにユーザーを設定などはせず、anonymous_accessを有効にしたまま使っていますが、環境、使途によっては注意が必要です。

```yaml:weaviate_values.yaml
authentication:
  anonymous_access:
    enabled: true
```
### デプロイ
ここまで来ると、あとはデプロイします。
```bash
# Create a Weaviate namespace
kubectl create namespace weaviate

# Deploy
helm upgrade --install "weaviate" \ 
  weaviate/weaviate \
  --namespace "weaviate" \
  --values ./values.yaml
```
### ポートの確認
私のk3sクラスタの場合、ロードバランサーが無い（そもそもロードバランサーの設定が分からない）ので、このままではクラスタ外部から一切Weaviateにアクセスできません。まず、httpとgRPCがポートの何番で走ることになっているのか確認しましょう。

```yaml:weaviate_values.yaml
command: ["/bin/weaviate"]
args:
  - '--host'
  - '0.0.0.0'
  - '--port'
  - '8080'
  - '--scheme'
  - 'http'
  - '--config-file'
  - '/weaviate-config/conf.yaml'
  - --read-timeout=60s 
  - --write-timeout=60s
```
```yaml:weaviate_values.yaml
grpcService:
  enabled: true
  name: weaviate-grpc
  ports:
    - name: grpc
      protocol: TCP
      port: 50051
      # Target port is going to be the same for every port
  type: LoadBalancer
  loadBalancerSourceRanges: []
  # optionally set cluster IP if you want to set a static IP
  clusterIP:
  annotations: {}
```
ここからhttp(httpsに非ず)が8080、gRPCが50051で動いてるらしいことが分かります。
### Nodeportの設定
先程ポート番号を調べたので、クラスタ外部からアクセスできるように比較的簡単なNodePortの設定をしてあげるようにしました。これも、私が単純にVPNで繋げるだけだからこうしたのであって、目的、使途で変わる部分だと思います。
```yaml:weaviate_nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: weaviate-http-nodeport
  namespace: weaviate
spec:
  selector:
    app: weaviate
  type: NodePort
  ports:
  - name: weaviate-http
    protocol: TCP
    port: 8080
    targetPort: 8080
    nodePort: 30020  # 30000-32767の範囲で任意のポート番号を指定できます

---
apiVersion: v1
kind: Service
metadata:
  name: weaviate-grpc-nodeport
  namespace: weaviate
spec:
  selector:
    app: weaviate
  type: NodePort
  ports:
  - name: weaviate-grpc
    protocol: TCP
    port: 50051
    targetPort: 50051
    nodePort: 30021  # 30000-32767の範囲で任意のポート番号を指定できます
```
あとは`kubectl apply`するだけです。
```bash
kubectl apply -f nginx-dep.yml
```
# 疎通確認
ここまで来たら、接続確認したいところです。簡単なPythonスクリプトで確認を取りたいと思います。まずは、Pythonクライエントのインストール。
```bash
pip install weaviate-client
```
k3sクラスタまで疎通がとれている状態にして（＝VPN接続を行って）以下のスクリプトを走らせます。
```python:connect_test.py
import weaviate

client = weaviate.connect_to_custom(
    http_host="172.31.42.76",  # URL only, no http prefix
    http_port=30020,
    http_secure=False,   # Set to True if https
    grpc_host="172.31.42.76",
    grpc_port=30021,      # Default is 50051, WCD uses 443
    grpc_secure=False,   # Edit as needed
)

print(client.is_ready())
client.close()
```
うまくいけば単に`True`と返ってくるだけです。