diff --git a/.github/workflows/helm-release.yml b/.github/workflows/helm-release.yml new file mode 100644 index 0000000..186b840 --- /dev/null +++ b/.github/workflows/helm-release.yml @@ -0,0 +1,36 @@ +name: Release Open WebUI Helm Charts + +on: + push: + branches: + - main + +jobs: + release: + permissions: + contents: write + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Configure Git + run: | + git config user.name "$GITHUB_ACTOR" + git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + + - name: Install Helm + uses: azure/setup-helm@v4 + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + + - name: Run chart-releaser + uses: helm/chart-releaser-action@v1.6.0 + with: + skip_existing: true + packages_with_index: true + charts_dir: charts/ + env: + CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" \ No newline at end of file diff --git a/README.md b/README.md index e8579a8..166c8a8 100644 --- a/README.md +++ b/README.md @@ -1 +1,2 @@ # Open WebUI Helm Charts +Helm charts for the [Open WebUI](https://github.com/open-webui/open-webui) application. \ No newline at end of file diff --git a/charts/open-webui/.helmignore b/charts/open-webui/.helmignore new file mode 100644 index 0000000..e806524 --- /dev/null +++ b/charts/open-webui/.helmignore @@ -0,0 +1 @@ +values-minikube.yaml diff --git a/charts/open-webui/Chart.yaml b/charts/open-webui/Chart.yaml new file mode 100644 index 0000000..ab5b41d --- /dev/null +++ b/charts/open-webui/Chart.yaml @@ -0,0 +1,21 @@ +apiVersion: v2 +name: open-webui +version: 1.0.0 +appVersion: "latest" + +home: https://www.openwebui.com/ +icon: https://raw.githubusercontent.com/open-webui/open-webui/main/static/favicon.png + +description: "Open WebUI: A User-Friendly Web Interface for Chat Interactions 👋" +keywords: +- llm +- chat +- web-ui + +sources: +- https://github.com/open-webui/open-webui/tree/main/kubernetes/helm +- https://hub.docker.com/r/ollama/ollama +- https://github.com/open-webui/open-webui/pkgs/container/open-webui + +annotations: + licenses: MIT diff --git a/charts/open-webui/templates/_helpers.tpl b/charts/open-webui/templates/_helpers.tpl new file mode 100644 index 0000000..6233efa --- /dev/null +++ b/charts/open-webui/templates/_helpers.tpl @@ -0,0 +1,51 @@ +{{- define "open-webui.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end -}} + +{{- define "ollama.name" -}} +ollama +{{- end -}} + +{{- define "ollama.url" -}} +{{- if .Values.ollama.externalHost }} +{{- printf .Values.ollama.externalHost }} +{{- else }} +{{- printf "http://%s.%s.svc.cluster.local:%d" (include "ollama.name" .) (.Release.Namespace) (.Values.ollama.service.port | int) }} +{{- end }} +{{- end }} + +{{- define "chart.name" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "base.labels" -}} +helm.sh/chart: {{ include "chart.name" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{- define "base.selectorLabels" -}} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{- define "open-webui.selectorLabels" -}} +{{ include "base.selectorLabels" . }} +app.kubernetes.io/component: {{ .Chart.Name }} +{{- end }} + +{{- define "open-webui.labels" -}} +{{ include "base.labels" . }} +{{ include "open-webui.selectorLabels" . }} +{{- end }} + +{{- define "ollama.selectorLabels" -}} +{{ include "base.selectorLabels" . }} +app.kubernetes.io/component: {{ include "ollama.name" . }} +{{- end }} + +{{- define "ollama.labels" -}} +{{ include "base.labels" . }} +{{ include "ollama.selectorLabels" . }} +{{- end }} diff --git a/charts/open-webui/templates/ollama-service.yaml b/charts/open-webui/templates/ollama-service.yaml new file mode 100644 index 0000000..32c93ca --- /dev/null +++ b/charts/open-webui/templates/ollama-service.yaml @@ -0,0 +1,23 @@ +{{- if not .Values.ollama.externalHost }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "ollama.name" . }} + labels: + {{- include "ollama.labels" . | nindent 4 }} + {{- with .Values.ollama.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + {{- include "ollama.selectorLabels" . | nindent 4 }} +{{- with .Values.ollama.service }} + type: {{ .type }} + ports: + - protocol: TCP + name: http + port: {{ .port }} + targetPort: http +{{- end }} +{{- end }} diff --git a/charts/open-webui/templates/ollama-statefulset.yaml b/charts/open-webui/templates/ollama-statefulset.yaml new file mode 100644 index 0000000..2750956 --- /dev/null +++ b/charts/open-webui/templates/ollama-statefulset.yaml @@ -0,0 +1,98 @@ +{{- if not .Values.ollama.externalHost }} +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "ollama.name" . }} + labels: + {{- include "ollama.labels" . | nindent 4 }} + {{- with .Values.ollama.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + serviceName: {{ include "ollama.name" . }} + replicas: {{ .Values.ollama.replicaCount }} + selector: + matchLabels: + {{- include "ollama.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "ollama.labels" . | nindent 8 }} + {{- with .Values.ollama.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + enableServiceLinks: false + automountServiceAccountToken: false + {{- with .Values.ollama.runtimeClassName }} + runtimeClassName: {{ . }} + {{- end }} + containers: + - name: {{ include "ollama.name" . }} + {{- with .Values.ollama.image }} + image: {{ .repository }}:{{ .tag }} + imagePullPolicy: {{ .pullPolicy }} + {{- end }} + tty: true + ports: + - name: http + containerPort: {{ .Values.ollama.service.containerPort }} + env: + {{- if .Values.ollama.gpu.enabled }} + - name: PATH + value: /usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + - name: LD_LIBRARY_PATH + value: /usr/local/nvidia/lib:/usr/local/nvidia/lib64 + - name: NVIDIA_DRIVER_CAPABILITIES + value: compute,utility + {{- end }} + {{- with .Values.ollama.resources }} + resources: {{- toYaml . | nindent 10 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /root/.ollama + {{- with .Values.ollama.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.ollama.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + {{- if and .Values.ollama.persistence.enabled .Values.ollama.persistence.existingClaim }} + - name: data + persistentVolumeClaim: + claimName: {{ .Values.ollama.persistence.existingClaim }} + {{- else if not .Values.ollama.persistence.enabled }} + - name: data + emptyDir: {} + {{- else if and .Values.ollama.persistence.enabled (not .Values.ollama.persistence.existingClaim) }} + [] + volumeClaimTemplates: + - metadata: + name: data + labels: + {{- include "ollama.selectorLabels" . | nindent 8 }} + {{- with .Values.ollama.persistence.annotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + accessModes: + {{- range .Values.ollama.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.ollama.persistence.size | quote }} + storageClassName: {{ .Values.ollama.persistence.storageClass }} + {{- with .Values.ollama.persistence.selector }} + selector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/templates/webui-deployment.yaml b/charts/open-webui/templates/webui-deployment.yaml new file mode 100644 index 0000000..bbd5706 --- /dev/null +++ b/charts/open-webui/templates/webui-deployment.yaml @@ -0,0 +1,62 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "open-webui.name" . }} + labels: + {{- include "open-webui.labels" . | nindent 4 }} + {{- with .Values.webui.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.webui.replicaCount }} + selector: + matchLabels: + {{- include "open-webui.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "open-webui.labels" . | nindent 8 }} + {{- with .Values.webui.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + enableServiceLinks: false + automountServiceAccountToken: false + containers: + - name: {{ .Chart.Name }} + {{- with .Values.webui.image }} + image: {{ .repository }}:{{ .tag | default $.Chart.AppVersion }} + imagePullPolicy: {{ .pullPolicy }} + {{- end }} + ports: + - name: http + containerPort: {{ .Values.webui.service.containerPort }} + {{- with .Values.webui.resources }} + resources: {{- toYaml . | nindent 10 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /app/backend/data + env: + - name: OLLAMA_BASE_URL + value: {{ include "ollama.url" . | quote }} + tty: true + {{- with .Values.webui.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + {{- if and .Values.webui.persistence.enabled .Values.webui.persistence.existingClaim }} + - name: data + persistentVolumeClaim: + claimName: {{ .Values.webui.persistence.existingClaim }} + {{- else if not .Values.webui.persistence.enabled }} + - name: data + emptyDir: {} + {{- else if and .Values.webui.persistence.enabled (not .Values.webui.persistence.existingClaim) }} + - name: data + persistentVolumeClaim: + claimName: {{ include "open-webui.name" . }} + {{- end }} diff --git a/charts/open-webui/templates/webui-ingress.yaml b/charts/open-webui/templates/webui-ingress.yaml new file mode 100644 index 0000000..ea9f95e --- /dev/null +++ b/charts/open-webui/templates/webui-ingress.yaml @@ -0,0 +1,33 @@ +{{- if .Values.webui.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "open-webui.name" . }} + labels: + {{- include "open-webui.labels" . | nindent 4 }} + {{- with .Values.webui.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with .Values.webui.ingress.class }} + ingressClassName: {{ . }} + {{- end }} + {{- if .Values.webui.ingress.tls }} + tls: + - hosts: + - {{ .Values.webui.ingress.host | quote }} + secretName: {{ default (printf "%s-tls" .Release.Name) .Values.webui.ingress.existingSecret }} + {{- end }} + rules: + - host: {{ .Values.webui.ingress.host }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ include "open-webui.name" . }} + port: + name: http +{{- end }} diff --git a/charts/open-webui/templates/webui-pvc.yaml b/charts/open-webui/templates/webui-pvc.yaml new file mode 100644 index 0000000..8783324 --- /dev/null +++ b/charts/open-webui/templates/webui-pvc.yaml @@ -0,0 +1,27 @@ +{{- if and .Values.webui.persistence.enabled (not .Values.webui.persistence.existingClaim) }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "open-webui.name" . }} + labels: + {{- include "open-webui.selectorLabels" . | nindent 4 }} + {{- with .Values.webui.persistence.annotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} +spec: + accessModes: + {{- range .Values.webui.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.webui.persistence.size }} + {{- if .Values.webui.persistence.storageClass }} + storageClassName: {{ .Values.webui.persistence.storageClass }} + {{- end }} + {{- with .Values.webui.persistence.selector }} + selector: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/templates/webui-service.yaml b/charts/open-webui/templates/webui-service.yaml new file mode 100644 index 0000000..9ccd9b9 --- /dev/null +++ b/charts/open-webui/templates/webui-service.yaml @@ -0,0 +1,29 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "open-webui.name" . }} + labels: + {{- include "open-webui.labels" . | nindent 4 }} + {{- with .Values.webui.service.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.webui.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + {{- include "open-webui.selectorLabels" . | nindent 4 }} + type: {{ .Values.webui.service.type | default "ClusterIP" }} + ports: + - protocol: TCP + name: http + port: {{ .Values.webui.service.port }} + targetPort: http + {{- if .Values.webui.service.nodePort }} + nodePort: {{ .Values.webui.service.nodePort | int }} + {{- end }} + {{- if .Values.webui.service.loadBalancerClass }} + loadBalancerClass: {{ .Values.webui.service.loadBalancerClass | quote }} + {{- end }} + diff --git a/charts/open-webui/values-minikube.yaml b/charts/open-webui/values-minikube.yaml new file mode 100644 index 0000000..1b67b0b --- /dev/null +++ b/charts/open-webui/values-minikube.yaml @@ -0,0 +1,27 @@ +ollama: + resources: + requests: + cpu: "2000m" + memory: "2Gi" + limits: + cpu: "4000m" + memory: "4Gi" + nvidia.com/gpu: "0" + service: + type: ClusterIP + gpu: + enabled: false + +webui: + resources: + requests: + cpu: "500m" + memory: "500Mi" + limits: + cpu: "1000m" + memory: "1Gi" + ingress: + enabled: true + host: open-webui.minikube.local + service: + type: NodePort diff --git a/charts/open-webui/values.yaml b/charts/open-webui/values.yaml new file mode 100644 index 0000000..4437973 --- /dev/null +++ b/charts/open-webui/values.yaml @@ -0,0 +1,75 @@ +nameOverride: "" + +ollama: + externalHost: "" + annotations: {} + podAnnotations: {} + replicaCount: 1 + image: + repository: ollama/ollama + tag: latest + pullPolicy: Always + resources: {} + persistence: + enabled: true + size: 30Gi + existingClaim: "" + accessModes: + - ReadWriteOnce + storageClass: "" + selector: {} + annotations: {} + nodeSelector: {} + # -- If using a special runtime container such as nvidia, set it here. + runtimeClassName: "" + tolerations: + - key: nvidia.com/gpu + operator: Exists + effect: NoSchedule + service: + type: ClusterIP + annotations: {} + port: 80 + containerPort: 11434 + gpu: + # -- Enable additional ENV values to help Ollama discover GPU usage + enabled: false + +webui: + annotations: {} + podAnnotations: {} + replicaCount: 1 + image: + repository: ghcr.io/open-webui/open-webui + tag: "" + pullPolicy: Always + resources: {} + ingress: + enabled: false + class: "" + # -- Use appropriate annotations for your Ingress controller, e.g., for NGINX: + # nginx.ingress.kubernetes.io/rewrite-target: / + annotations: {} + host: "" + tls: false + existingSecret: "" + persistence: + enabled: true + size: 2Gi + existingClaim: "" + # -- If using multiple replicas, you must update accessModes to ReadWriteMany + accessModes: + - ReadWriteOnce + storageClass: "" + selector: {} + annotations: {} + nodeSelector: {} + tolerations: [] + service: + type: ClusterIP + annotations: {} + port: 80 + containerPort: 8080 + nodePort: "" + labels: {} + loadBalancerClass: ""