Skip to content

Commit dbc056c

Browse files
committed
added helm chart support
1 parent 1b1a5b9 commit dbc056c

14 files changed

+1359
-0
lines changed

deployment/helm-chart/Chart.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
apiVersion: v2
2+
name: wren
3+
description: A Helm chart for Wren AI Service
4+
type: application
5+
version: 1.0.0
6+
appVersion: "0.22.2"
7+
keywords:
8+
- ai
9+
- analytics
10+
- data
11+
home: https://github.com/Canner/WrenAI
12+
sources:
13+
- https://github.com/Canner/WrenAI
14+
maintainers:
15+
- name: Wren AI Team
16+
17+
18+
dependencies:
19+
- name: qdrant
20+
version: "1.14.0"
21+
repository: "https://qdrant.github.io/qdrant-helm"
22+
condition: qdrant.enabled
23+
- name: postgresql
24+
version: "15.5.30"
25+
repository: "https://charts.bitnami.com/bitnami"
26+
condition: postgresql.enabled

deployment/helm-chart/NOTES.txt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
1. Get the application URL by running these commands:
2+
{{- if contains "NodePort" .Values.ui.service.type }}
3+
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "wren-ai.fullname" . }}-ui-svc)
4+
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
5+
echo http://$NODE_IP:$NODE_PORT
6+
{{- else if contains "LoadBalancer" .Values.ui.service.type }}
7+
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
8+
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "wren-ai.fullname" . }}-ui-svc'
9+
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "wren-ai.fullname" . }}-ui-svc --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
10+
echo http://$SERVICE_IP:{{ .Values.ui.service.port }}
11+
{{- else if contains "ClusterIP" .Values.ui.service.type }}
12+
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "wren-ai.name" . }},app.kubernetes.io/instance1. Get the application URL by running these commands:
13+
{{- if contains "NodePort" .Values.aiService.service.type }}
14+
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "wren-ai.fullname" . }}-ai-service-svc)
15+
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
16+
echo http://$NODE_IP:$NODE_PORT
17+
{{- else if contains "LoadBalancer" .Values.aiService.service.type }}
18+
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
19+
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "wren-ai.fullname" . }}-ai-service-svc'
20+
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "wren-ai.fullname" . }}-ai-service-svc --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
21+
echo http://$SERVICE_IP:{{ .Values.aiService.service.port }}
22+
{{- else if contains "ClusterIP" .Values.aiService.service.type }}
23+
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "wren-ai.name" . }},app.kubernetes.io/instance={{ .Release.Name }},app.kubernetes.io/component=ai-service" -o jsonpath="{.items[0].metadata.name}")
24+
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
25+
echo "Visit http://127.0.0.1:8080 to use your application"
26+
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
27+
{{- end }}
28+
29+
Service Details:
30+
- AI Service Port: {{ .Values.config.ports.aiService }}
31+
- Replicas: {{ .Values.aiService.replicaCount }}
32+
- Version: {{ .Values.global.versions.aiService }}
33+
34+
For more information about Wren AI, visit: https://docs.getwren.ai

deployment/helm-chart/README.md

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Deployment of Wren AI to Kubernetes with Helm Chart
2+
1. Ensure you satisfy the dependencies required to deploy Wren AI.
3+
2. Adjust the values accordingly to fit your Kubernetes environment.
4+
3. Secrets vales can be deployed together or separately.
5+
Note: Without authentication, once you publish this on the internet, anyone can access your app, see your data, and modify your settings!
6+
7+
## Dependencies used in this kustomization:
8+
- nginx.ingress
9+
- external-dns
10+
- cert-manager
11+
- kubectl
12+
- helm
13+
14+
## Steps to deploy:
15+
16+
`Suggestion`: Before deploying, check out the Helm values in the `deployment/helm ` file and modify them to suit your Kubernetes environment.
17+
18+
The `deployment/helm` folder contains a `values.yaml` file that will inflate the manifests into a `deployment/helm/template` files used to deploy the app to your Kubernetes cluster.
19+
20+
```shell
21+
# Clone the repository with Helm chart
22+
git clone https://github.com/Canner/WrenAI.git
23+
cd WrenAI
24+
25+
# Create namespace
26+
kubectl create namespace wren
27+
28+
# !!!!!!!!!!!!
29+
# OPENAI_API_KEY or GEMINI_API_KEY is REQUIRED: without a valid key the wren-ai-service pod will not start
30+
# You must update PG_URL, otherwise wren-ui will not work
31+
32+
# MODIFY/GENERATE values of secret and apply kubectl command to create secret (recommended for production)
33+
34+
# Generate secure passwords
35+
OPENAI_API_KEY=UkVRVUlSRUQ6IHNrLXByb2otYWxsLWFjY2Vzcy1wbGFjZWhvbGRlci00LXdyZW4tYWktc2VydmljZS1kZXBsb3ltZW50
36+
PG_USERNAME=wrenai
37+
PG_PASSWORD=$(openssl rand -base64 32)
38+
PG_ADMIN_PASSWORD=$(openssl rand -base64 32)
39+
PG_URL=postgres://wrenai-user:wrenai-pass@wren-postgresql:5432/wrenai
40+
LANGFUSE_PUBLIC_KEY=VUlSYWxsLWFjRUQ6IHNrLtdmljZS1kZXBYWxsLWFktc2VydmljZS13ltZW50
41+
LANGFUSE_SECRET_KEY=UQ6IHNrLsLWFjtYWxsLWFdmljZS1kZXBktVUlSRUQ6IHNrLXByHNYWxsLWFj
42+
POSTHOG_API_KEY=YWxsLWZS1kZXBYWxsLWFkHNrLXByb2oYWxsLWFjtc2VydmljZS1kZXHNrLXByb2o
43+
USER_UUID=$(openssl rand -base64 32)
44+
45+
kubectl create secret generic wrenai-secrets \
46+
--from-literal=OPENAI_API_KEY=$OPENAI_API_KEY \
47+
--from-literal=PG_USERNAME=$PG_USERNAME \
48+
--from-literal=PG_PASSWORD=$PG_PASSWORD \
49+
--from-literal=PG_ADMIN_PASSWORD=$PG_ADMIN_PASSWORD \
50+
--from-literal=PG_URL=$PG_URL \
51+
--from-literal=LANGFUSE_PUBLIC_KEY=$LANGFUSE_PUBLIC_KEY \
52+
--from-literal=LANGFUSE_SECRET_KEY=$LANGFUSE_SECRET_KEY \
53+
--from-literal=POSTHOG_API_KEY=$POSTHOG_API_KEY \
54+
--from-literal=USER_UUID=$USER_UUID \
55+
-n wren
56+
57+
58+
# Download Wren AI dependency charts like Qdrant or postgresql
59+
helm dependency build ./deployment/helm
60+
61+
# Deploy Wren AI with Helm
62+
helm upgrade --install wrenai ./deployment/helm \
63+
--namespace wren \
64+
-f deployment/helm/values.yaml \
65+
66+
kubectl get pods -n wren
67+
```
68+
69+
### Notes on Helm:
70+
- `deployment/helm/values.yaml` is the main file responsible for versions of other apps such as Qdrant and PostgreSQL, version of your Wren AI app. It also combines resourses from the manifest such as ConfigMaps, Deployments, and Services. And example Ingress and Secrets.
71+
- `deployment/helm/template` is the manifests folder that contains the core Wren AI manifest templates, its less likely you need to modify them, but check just in case
72+
- `deployment/helm/charts` is directory contains any dependent Helm charts (subcharts) required by Wren AI, such as PostgreSQL or Qdrant. These dependencies are either added manually or using `helm dependency add`, and they are used to deploy third-party services alongside Wren AI.
73+
- `deployment/helm/Chart.yaml` This file defines the metadata for the Helm chart used to deploy Wren AI. It includes the chart name, version, application version, dependencies and a description. Helm uses this file to identify and manage the chart during installation and upgrades.
74+
75+
#### Wren-UI Database
76+
Starting with wren-ui version 0.6.0 by default the postgres database is used for wren-ui in this helm chart and will be installed in the same namespace as wren-ai.
77+
- `postgres`: Database that will be installed in the same namespace as wren-ai. You *must* update `PG_URL` in the Secret manifest.
78+
79+
Example: `PG_URL: "postgres://wrenai-user:wrenai-pass@wren-postgresql:5432/wrenai"`
80+
- `postgres://` This is the protocol. It tells the system that you’re connecting to a PostgreSQL database.
81+
- `wrenai-user:wrenai-pass` These are the username(first) and password(second) for the database respectively, separated by a colon. In this case, both the username and password are “postgres”.
82+
- `@wren-postgresql` This is the hostname of the database server. "wren-postgresql" means the database server is running in a Kubernetes cluster and it is named "wren-postgresql" in the *same* namespace. If you are using another namespace you must provide the full hostname, example: `wren-postgresql.wrenai.svc.cluster.local`, "wrenai" is the namespace name, "svc.cluster.local" is the default domain name for Kubernetes services no need to change it.
83+
- `:5432` This is the port number. PostgreSQL servers listen on port 5432 by default.
84+
- `/wrenai` This is the name of the database you’re connecting to. In this case, the database name is `wrenai`. It can be found in the helm values file in the auth.database parameter.
85+
86+
# Minikube
87+
Prepare your k8s environment. Then use the `Steps to deploy` section to deploy Wren AI app into your k8s.
88+
```shell
89+
minikube start
90+
minikube addons enable ingress
91+
minikube addons enable metallb
92+
minikube kubectl -- get nodes
93+
minikube kubectl -- get pods -A
94+
95+
minikube update-context
96+
helm repo add bitnami https://charts.bitnami.com/bitnami
97+
helm repo update
98+
helm install external-dns bitnami/external-dns
99+
helm install \
100+
external-dns bitnami/external-dns \
101+
--namespace external-dns \
102+
--version 7.5.2 \
103+
--create-namespace \
104+
--set installCRDs=true
105+
kubectl get pods -n external-dns
106+
107+
helm repo add jetstack https://charts.jetstack.io
108+
helm repo update
109+
helm install \
110+
cert-manager jetstack/cert-manager \
111+
--namespace cert-manager \
112+
--version v1.13.6 \
113+
--create-namespace \
114+
--set installCRDs=true
115+
kubectl get pods -n cert-manager
116+
117+
##########
118+
# Use the `Steps to deploy` section to continue as you would on a production k8s cluster.
119+
```
120+
121+
# GitOps Patches
122+
In the [patches](./patches) folder you can find usefull kustomization examples files if you wish to use existing official kustomization directly from this repo as a base kustomization layer and only customize some values. It can be usefull for you GitOps workflow and can be used in conjunction with FluxCD or ArgoCD.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{{/*
2+
Expand the name of the chart.
3+
*/}}
4+
{{- define "wren-ai.name" -}}
5+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6+
{{- end }}
7+
8+
{{/*
9+
Create a default fully qualified app name.
10+
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
11+
If release name contains chart name it will be used as a full name.
12+
*/}}
13+
{{- define "wren-ai.fullname" -}}
14+
{{- if .Values.fullnameOverride }}
15+
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
16+
{{- else }}
17+
{{- $name := default .Chart.Name .Values.nameOverride }}
18+
{{- if contains $name .Release.Name }}
19+
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
20+
{{- else }}
21+
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
22+
{{- end }}
23+
{{- end }}
24+
{{- end }}
25+
26+
{{/*
27+
Create chart name and version as used by the chart label.
28+
*/}}
29+
{{- define "wren-ai.chart" -}}
30+
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
31+
{{- end }}
32+
33+
{{/*
34+
Common labels
35+
*/}}
36+
{{- define "wren-ai.labels" -}}
37+
helm.sh/chart: {{ include "wren-ai.chart" . }}
38+
{{ include "wren-ai.selectorLabels" . }}
39+
{{- if .Chart.AppVersion }}
40+
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
41+
{{- end }}
42+
app.kubernetes.io/managed-by: {{ .Release.Service }}
43+
{{- end }}
44+
45+
{{/*
46+
Selector labels
47+
*/}}
48+
{{- define "wren-ai.selectorLabels" -}}
49+
app.kubernetes.io/name: {{ include "wren-ai.name" . }}
50+
app.kubernetes.io/instance: {{ .Release.Name }}
51+
{{- end }}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: {{ include "wren-ai.fullname" . }}-ai-service
5+
labels:
6+
{{- include "wren-ai.labels" . | nindent 4 }}
7+
app.kubernetes.io/component: ai-service
8+
spec:
9+
replicas: {{ default 1 .Values.aiService.replicaCount }}
10+
selector:
11+
matchLabels:
12+
{{- include "wren-ai.selectorLabels" . | nindent 6 }}
13+
app.kubernetes.io/component: ai-service
14+
template:
15+
metadata:
16+
labels:
17+
{{- include "wren-ai.selectorLabels" . | nindent 8 }}
18+
app.kubernetes.io/component: ai-service
19+
spec:
20+
{{- with .Values.aiService.nodeSelector }}
21+
nodeSelector:
22+
{{- toYaml . | nindent 8 }}
23+
{{- end }}
24+
{{- with .Values.aiService.affinity }}
25+
affinity:
26+
{{- toYaml . | nindent 8 }}
27+
{{- end }}
28+
{{- with .Values.aiService.tolerations }}
29+
tolerations:
30+
{{- toYaml . | nindent 8 }}
31+
{{- end }}
32+
containers:
33+
- name: wren-ai-service
34+
image: "{{ .Values.aiService.image.repository }}:{{ .Values.aiService.image.tag | default .Values.global.versions.aiService}}"
35+
imagePullPolicy: {{ .Values.aiService.image.pullPolicy }}
36+
volumeMounts:
37+
- name: config-volume
38+
mountPath: /app/data
39+
env:
40+
- name: WREN_AI_SERVICE_PORT
41+
value: {{ .Values.config.ports.aiService | quote }}
42+
{{- if .Values.secrets.createFromValues }}
43+
{{- if .Values.secrets.values.OPENAI_API_KEY }}
44+
- name: OPENAI_API_KEY
45+
valueFrom:
46+
secretKeyRef:
47+
name: {{ include "wren-ai.fullname" . }}-secret
48+
key: OPENAI_API_KEY
49+
{{- else if .Values.secrets.values.GEMINI_API_KEY }}
50+
- name: GEMINI_API_KEY
51+
valueFrom:
52+
secretKeyRef:
53+
name: {{ include "wren-ai.fullname" . }}-secret
54+
key: GEMINI_API_KEY
55+
{{- end }}
56+
{{- else }}
57+
- name: OPENAI_API_KEY
58+
valueFrom:
59+
secretKeyRef:
60+
name: {{ .Values.secrets.existingSecretName }}
61+
key: OPENAI_API_KEY
62+
optional: true
63+
- name: GEMINI_API_KEY
64+
valueFrom:
65+
secretKeyRef:
66+
name: {{ .Values.secrets.existingSecretName }}
67+
key: GEMINI_API_KEY
68+
optional: true
69+
{{- end }}
70+
- name: QDRANT_HOST
71+
value: {{ default (printf "%s-qdrant" .Release.Name) .Values.config.documentStore.qdrantHost | quote }}
72+
- name: LOGGING_LEVEL
73+
value: {{ .Values.config.logging.level | quote }}
74+
- name: WREN_UI_ENDPOINT
75+
value: {{ .Values.config.endpoints.ui | quote }}
76+
- name: PYTHONUNBUFFERED
77+
value: {{ .Values.aiService.env.pythonUnbuffered | quote }}
78+
{{- if .Values.pipeline.settings.langfuseEnable }}
79+
- name: LANGFUSE_PUBLIC_KEY
80+
valueFrom:
81+
secretKeyRef:
82+
name: {{ if .Values.secrets.useExistingSecret }}{{ .Values.secrets.existingSecretName }}{{ else }}{{ include "wren-ai.fullname" . }}-secret{{ end }}
83+
key: LANGFUSE_PUBLIC_KEY
84+
- name: LANGFUSE_SECRET_KEY
85+
valueFrom:
86+
secretKeyRef:
87+
name: {{ if .Values.secrets.useExistingSecret }}{{ .Values.secrets.existingSecretName }}{{ else }}{{ include "wren-ai.fullname" . }}-secret{{ end }}
88+
key: LANGFUSE_SECRET_KEY
89+
- name: LANGFUSE_HOST
90+
value: {{ .Values.pipeline.settings.langfuseHost | quote }}
91+
{{- end }}
92+
- name: CONFIG_PATH
93+
value: {{ .Values.aiService.env.configPath | quote }}
94+
{{- if .Values.config.telemetry.enabled }}
95+
- name: POSTHOG_API_KEY
96+
valueFrom:
97+
secretKeyRef:
98+
name: {{ if .Values.secrets.useExistingSecret }}{{ .Values.secrets.existingSecretName }}{{ else }}{{ include "wren-ai.fullname" . }}-secret{{ end }}
99+
key: POSTHOG_API_KEY
100+
- name: POSTHOG_HOST
101+
value: {{ .Values.config.telemetry.posthogHost | quote }}
102+
{{- end }}
103+
ports:
104+
- name: http
105+
containerPort: {{ .Values.config.ports.aiService }}
106+
protocol: TCP
107+
{{- with .Values.aiService.resources }}
108+
resources:
109+
{{- toYaml . | nindent 12 }}
110+
{{- end }}
111+
volumes:
112+
- name: config-volume
113+
configMap:
114+
name: {{ include "wren-ai.fullname" . }}-ai-service-config
115+
items:
116+
- key: config.yaml
117+
path: config.yaml

0 commit comments

Comments
 (0)