Skip to content

Commit

Permalink
chore: adding helm chart for librechat
Browse files Browse the repository at this point in the history
Signed-off-by: Noah Ispas (iamNoah1) <noahispas@gmail.com>
  • Loading branch information
iamNoah1 committed Apr 8, 2024
1 parent 6f0eb35 commit 183da62
Show file tree
Hide file tree
Showing 14 changed files with 569 additions and 0 deletions.
35 changes: 35 additions & 0 deletions .github/workflows/helmcharts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Build Helm Charts on Tag

# The workflow is triggered when a tag is pushed
on:
push:
tags:
- "*"

jobs:
release:
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
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:
charts_dir: helmchart
env:
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
91 changes: 91 additions & 0 deletions docs/deployment/helm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
title: 📦 Helm
description: Instructions for deploying LibreChat on Kubernetes using Helm
weight: -1
---
# Deployment as Helm Chart

The following instructions guide you to deploy LibreChat on Kubernetes using Helm. At the moment this installation method only provides running the LibreChat components, without any additional services like MongoDB or Redis. You will need to provide your own MongoDB and Redis instances.

## Prerequisites
* A running Kubernetes cluster
* `kubectl` installed
* Having a MongoDB instance running that can be accessed from the Kubernetes cluster
* Helm installed on your local machine

## Configuration
Similar to other Helm charts, there exists a [values file](https://github.com/danny-avila/LibreChat/blob/main/helmchart/values.yaml) that serves two primary functions: it outlines the default settings and indicates which configurations are adjustable. Essentially, any setting within this values file can be modified in two main ways. The first method involves creating a separate override file and specifying it when executing the install command. The second method involves directly setting each variable with the installation command itself. If you're planning to change numerous variables, it's advisable to use the override file approach to avoid an overly lengthy command. Conversely, for fewer adjustments, directly setting variables with the installation command might be more convenient.


The very end of the file sets some of environment variables of the application, that should look familiar if you deployed the application before. It is the base configuration without any sensitive data.

```
env:
# Full list of possible values
# https://github.com/danny-avila/LibreChat/blob/main/.env.example
ALLOW_EMAIL_LOGIN: "true"
ALLOW_REGISTRATION: "true"
ALLOW_SOCIAL_LOGIN: "false"
ALLOW_SOCIAL_REGISTRATION: "false"
APP_TITLE: "Librechat"
CUSTOM_FOOTER: "Provided with ❤️"
DEBUG_CONSOLE: "true"
DEBUG_LOGGING: "true"
DEBUG_OPENAI: "true"
DEBUG_PLUGINS: "true"
DOMAIN_CLIENT: ""
DOMAIN_SERVER: ""
ENDPOINTS: "openAI,azureOpenAI,bingAI,chatGPTBrowser,google,gptPlugins,anthropic"
SEARCH: false
```

However, like the comment says you could have a look at which environment variables are generally available to be modified. Because with only these variables set the application won't start correctly. We need to set some more variables, but those contain sensitive data. We will show 2 different ways to make use of Kubernetes features in order to configure those in a secure way.

### Create one Kubernetes Secret with different entries
Assuming you have `kubectl` installed on your machine and you are connected to your Kubernetes cluster you can run the following command to create a respective Kubernetes secret, that can be used by the helm chart.

```
kubectl create secret generic librechat \
--from-literal=CREDS_KEY=0963cc1e5e5df9554c8dd32435d0eb2b1a8b6edde6596178d5c5418ade897673 \
--from-literal=CREDS_IV=46d727a066d5d8c4ebc94305d028fecc \
--from-literal=MONGO_URI=mongodb+srv://<user>:<password>@<mongodb-url> \
--from-literal=JWT_SECRET=83e5c1f0e037e4f027dbdb332d54ca1bd1f12af6798700c207ed817ebd7c544b \ --from-literal=JWT_REFRESH_SECRET=83e5c1f0c037e4f027dbab332d54ca1bd1f12af6798700c207ed817ebd7c544
```

### Create one Kubernetes Secret for each configuration
This one is a bit more complicated but also allows for more fine-grained control over the secrets. For each secret, you would like to create you can run the following command.

```
kubectl create secret generic librechat-creds-key \
--from-literal=CREDS_KEY=0963cc1e5e5df9554c8dd32435d0eb2b1a8b6edde6596178d5c5418ade897673
```
... and so on for each secret.


## Install Helm Chart
In the root directory run:

`helm install <deployment-name> helmchart`

Example: `helm install librechat helmchart --set config.envSecrets.secretRef=librechat` (using one Kubernetes secret for all credentials).

If you used the approach where you created one Kubernetes secret for each credential you will need to do a more extensive configuration which is best placed in a separate file. Create a file with the following content:

```
config:
envSecrets:
secretKeyRef:
- name: CREDS_KEY
secretName: librechat-creds-iv
secretKey: CREDS_KEY
<...>
```

After that you can run the following command: `helm install librechat helmchart --values <values-override-filel>`


## Uninstall Helm Chart

In order to uninstall the Helm Chart simply run: `helm uninstall <deployment-name>`

Example: `helm uninstall librechat`
1 change: 1 addition & 0 deletions docs/deployment/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ weight: 3
- 🏗️ [Hetzner](./hetzner_ubuntu.md)
- 🌈 [Heroku](./heroku.md)
- 🦓 [Zeabur](./zeabur.md)
- 📦 [Helm](./helm.md)

---

Expand Down
23 changes: 23 additions & 0 deletions helmchart/.helmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
4 changes: 4 additions & 0 deletions helmchart/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
apiVersion: v2
name: librechat
type: application
version: 1.0.0
22 changes: 22 additions & 0 deletions helmchart/templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "librechat.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "librechat.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "librechat.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "librechat.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}
74 changes: 74 additions & 0 deletions helmchart/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "librechat.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "librechat.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "librechat.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "librechat.labels" -}}
helm.sh/chart: {{ include "librechat.chart" . }}
{{ include "librechat.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "librechat.selectorLabels" -}}
app.kubernetes.io/name: {{ include "librechat.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Create the name of the service account to use
*/}}
{{- define "librechat.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "librechat.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

{{/*
Print string from list split by ,
*/}}
{{- define "model.list" -}}
{{- range $idx, $val := $.Values.configEndpoint.models -}}
{{- if $idx }}
{{- print ", " -}}
{{- end -}}
{{- $val -}}
{{- end -}}
{{- end -}}
10 changes: 10 additions & 0 deletions helmchart/templates/configmap-env.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "librechat.fullname" . }}-env
labels:
{{- include "librechat.labels" . | nindent 4 }}
data:
{{- range $key, $val := .Values.config.env }}
{{ $key }}: {{ $val | quote }}
{{- end }}
81 changes: 81 additions & 0 deletions helmchart/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "librechat.fullname" . }}
labels:
{{- include "librechat.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "librechat.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "librechat.selectorLabels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "librechat.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
envFrom:
{{ if .Values.config.envSecrets.secretRef }}
- secretRef:
name: {{ .Values.config.envSecrets.secretRef }}
{{- end }}
- configMapRef:
name: {{ include "librechat.fullname" . }}-env
env:
{{- range $secretKeyRef := .Values.config.envSecrets.secretKeyRef }}
- name: {{ $secretKeyRef.name }}
valueFrom:
secretKeyRef:
name: {{ $secretKeyRef.secretName }}
key: {{ $secretKeyRef.secretKey }}
{{- end }}
ports:
- name: http
containerPort: 3080
protocol: TCP
livenessProbe:
initialDelaySeconds: 5
httpGet:
path: /
port: http
readinessProbe:
initialDelaySeconds: 5
httpGet:
path: /
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
28 changes: 28 additions & 0 deletions helmchart/templates/hpa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "librechat.fullname" . }}
labels:
{{- include "librechat.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "librechat.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}

0 comments on commit 183da62

Please sign in to comment.