diff --git a/README.md b/README.md index 00747910..382a6a72 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,12 @@ OpenShift to generate project proposals for specific Red Hat products. - Red Hat Openshift cluster running in AWS. Supported regions are : us-east-1 us-east-2 us-west-1 us-west-2 ca-central-1 sa-east-1 eu-west-1 eu-west-2 eu-west-3 eu-central-1 eu-north-1 ap-northeast-1 ap-northeast-2 ap-northeast-3 ap-southeast-1 ap-southeast-2 ap-south-1. - GPU Node to run Hugging Face Text Generation Inference server on Red Hat OpenShift cluster. - Create a fork of the [rag-llm-gitops](https://github.com/validatedpatterns/rag-llm-gitops.git) Git repository. +- **EDB Postgres Operator Credentials** (Required only if you select EDB): The EDB Postgres for Kubernetes operator from the certified-operators catalog requires authentication to pull images from `docker.enterprisedb.com`. You will need to: + 1. Register for a free trial account at [EDB Registration](https://www.enterprisedb.com/accounts/register) + 2. Obtain your subscription token from [EDB Repos Downloads](https://www.enterprisedb.com/repos-downloads) + 3. Add the token to your `values-secret.yaml` file during configuration (see below) + + For more details, see the [EDB Installation Documentation](https://www.enterprisedb.com/docs/postgres_for_kubernetes/latest/installation_upgrade/). ## Demo Description & Architecture @@ -116,6 +122,8 @@ cp values-secret.yaml.template ~/values-secret-rag-llm-gitops.yaml To deploy a model that can requires an Hugging Face token, grab the [Hugging Face token](https://huggingface.co/settings/tokens) and accept the terms and conditions on the model page. Edit ~/values-secret-rag-llm-gitops.yaml to replace the `model Id` and the `Hugging Face` token. +**IMPORTANT**: If you are using EDB Postgres for Kubernetes, you must add your EDB subscription token to the `values-secret.yaml` file: + ```sh secrets: - name: hfmodel @@ -124,6 +132,11 @@ secrets: value: null - name: modelId value: "ibm-granite/granite-3.1-8b-instruct" + - name: edb + fields: + - name: token + value: "YOUR_EDB_TOKEN_HERE" # Replace with your EDB subscription token + description: EDB subscription token for pulling certified operator images - name: minio fields: - name: MINIO_ROOT_USER @@ -133,6 +146,8 @@ secrets: onMissingValue: generate ``` +The EDB token is synced into Vault and then used by External Secrets to create the required pull secret (`postgresql-operator-pull-secret`) in `openshift-operators`. Without this token, the EDB operator will fail to pull its container image and the database will not be created. + ### Provision GPU MachineSet As a pre-requisite to deploy the application using the validated pattern, GPU nodes should be provisioned along with Node Feature Discovery Operator and NVIDIA GPU operator. To provision GPU Nodes @@ -151,7 +166,7 @@ Alternatiely, follow the [instructions](./GPU_provisioning.md) to manually insta ### Deploy application -***Note:**: This pattern supports three types of vector databases, EDB Postgres for Kubernetes, Elasticsearch and Redis. By default the pattern will deploy EDB Postgres for Kubernetes as a vector DB. To deploy Redis, change the global.db.type to REDIS in [values-global.yaml](./values-global.yaml). +***Note:**: This pattern supports four types of vector databases: PGVECTOR (local chart), EDB Postgres for Kubernetes, Elasticsearch, and Redis. By default the pattern will deploy PGVECTOR as a vector DB. To deploy EDB, set `global.db.type` to `EDB` in [values-global.yaml](./values-global.yaml). ```yaml --- @@ -161,10 +176,10 @@ global: useCSV: false syncPolicy: Automatic installPlanApproval: Automatic -# Possible value for db.type = [REDIS, EDB, ELASTIC] +# Possible value for db.type = [REDIS, EDB, ELASTIC, PGVECTOR] db: index: docs - type: EDB # <--- Default is EDB, Change the db type to REDIS for Redis deployment or ELASTIC for Elasticsearch + type: PGVECTOR # <--- Default is PGVECTOR. Use EDB, REDIS, or ELASTIC as needed. main: clusterGroupName: hub multiSourceConfig: diff --git a/charts/all/rag-llm/charts/pgvector/Chart.yaml b/charts/all/rag-llm/charts/pgvector/Chart.yaml new file mode 100644 index 00000000..d3e5923e --- /dev/null +++ b/charts/all/rag-llm/charts/pgvector/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: pgvector +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.5.2 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.5.2" diff --git a/charts/all/rag-llm/charts/pgvector/templates/_helpers.tpl b/charts/all/rag-llm/charts/pgvector/templates/_helpers.tpl new file mode 100644 index 00000000..64934e6d --- /dev/null +++ b/charts/all/rag-llm/charts/pgvector/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "pgvector.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 "pgvector.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 "pgvector.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "pgvector.labels" -}} +helm.sh/chart: {{ include "pgvector.chart" . }} +{{ include "pgvector.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "pgvector.selectorLabels" -}} +app.kubernetes.io/name: {{ include "pgvector.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "pgvector.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "pgvector.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/all/rag-llm/charts/pgvector/templates/configmap.yaml b/charts/all/rag-llm/charts/pgvector/templates/configmap.yaml new file mode 100644 index 00000000..0ebac895 --- /dev/null +++ b/charts/all/rag-llm/charts/pgvector/templates/configmap.yaml @@ -0,0 +1,20 @@ +{{- if eq .Values.global.db.type "PGVECTOR" }} +kind: ConfigMap +apiVersion: v1 +metadata: + name: pgvector-initsql + labels: + {{- include "pgvector.labels" . | nindent 4 }} +data: + init-db.sh: | + #!/bin/bash + set -e + psql -U postgres -c "CREATE DATABASE ${POSTGRES_DBNAME};" + psql -U postgres -d ${POSTGRES_DBNAME} -c "CREATE EXTENSION VECTOR;" + {{- range .Values.extraDatabases }} + psql -U postgres -c "CREATE DATABASE {{ .name }};" + {{- if .vectordb }} + psql -U postgres -d {{ .name }} -c "CREATE EXTENSION VECTOR;" + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/all/rag-llm/charts/pgvector/templates/secret.yaml b/charts/all/rag-llm/charts/pgvector/templates/secret.yaml new file mode 100644 index 00000000..d0a92934 --- /dev/null +++ b/charts/all/rag-llm/charts/pgvector/templates/secret.yaml @@ -0,0 +1,15 @@ +{{- if and (eq .Values.global.db.type "PGVECTOR") .Values.secret.create }} +kind: Secret +apiVersion: v1 +metadata: + name: vectordb-app + labels: + {{- include "pgvector.labels" . | nindent 4 }} +data: + username: {{ .Values.secret.user | b64enc | quote }} + password: {{ .Values.secret.password | b64enc | quote }} + host: {{ default (include "pgvector.fullname" .) .Values.secret.host | b64enc | quote }} + port: {{ .Values.secret.port | b64enc | quote }} + dbname: {{ .Values.secret.dbname | b64enc | quote }} +type: Opaque +{{- end }} diff --git a/charts/all/rag-llm/charts/pgvector/templates/service.yaml b/charts/all/rag-llm/charts/pgvector/templates/service.yaml new file mode 100644 index 00000000..1eed6d7f --- /dev/null +++ b/charts/all/rag-llm/charts/pgvector/templates/service.yaml @@ -0,0 +1,17 @@ +{{- if eq .Values.global.db.type "PGVECTOR" }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "pgvector.fullname" . }} + labels: + {{- include "pgvector.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "pgvector.selectorLabels" . | nindent 4 }} +{{- end }} \ No newline at end of file diff --git a/charts/all/rag-llm/charts/pgvector/templates/statefulset.yaml b/charts/all/rag-llm/charts/pgvector/templates/statefulset.yaml new file mode 100644 index 00000000..07ae7ace --- /dev/null +++ b/charts/all/rag-llm/charts/pgvector/templates/statefulset.yaml @@ -0,0 +1,93 @@ +{{- if eq .Values.global.db.type "PGVECTOR" }} +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "pgvector.fullname" . }} + labels: + {{- include "pgvector.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "pgvector.selectorLabels" . | nindent 6 }} + {{- with .Values.volumeClaimTemplates }} + volumeClaimTemplates: + {{- toYaml . | nindent 4 }} + {{- end }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "pgvector.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "pgvector.serviceAccountName" . }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: {{ .Chart.Name }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- with .Values.args }} + args: + {{- toYaml . | nindent 12 }} + {{- end }} + env: + {{- toYaml .Values.env | nindent 12 }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + {{- with .Values.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + readinessProbe: + exec: + command: + - /bin/sh + - -c + - | + for db in "$POSTGRES_DBNAME" {{- range .Values.extraDatabases }} "{{ .name }}"{{- end }}; do + pg_isready -U "$POSTGRES_USER" -d "$db" -h 127.0.0.1 -p "$POSTGRES_PORT" || exit 1 + done + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: initdb-volume + configMap: + name: pgvector-initsql +{{- end }} \ No newline at end of file diff --git a/charts/all/rag-llm/charts/pgvector/values.yaml b/charts/all/rag-llm/charts/pgvector/values.yaml new file mode 100644 index 00000000..afa168c1 --- /dev/null +++ b/charts/all/rag-llm/charts/pgvector/values.yaml @@ -0,0 +1,91 @@ +# Default values for pgvector. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: docker.io/pgvector/pgvector + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: pg17 + +imagePullSecrets: [] + +nameOverride: "pgvector" +fullnameOverride: "pgvector" + +serviceAccount: + create: false + +podAnnotations: {} +podLabels: {} + +podSecurityContext: {} +securityContext: {} + +service: + type: ClusterIP + port: 5432 + +env: + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + key: username + name: vectordb-app + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + key: password + name: vectordb-app + - name: POSTGRES_PORT + valueFrom: + secretKeyRef: + key: port + name: vectordb-app + - name: POSTGRES_DBNAME + valueFrom: + secretKeyRef: + key: dbname + name: vectordb-app + +resources: {} + +autoscaling: + enabled: false + +volumeClaimTemplates: + - metadata: + name: pg-data + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 5Gi + +volumeMounts: + - mountPath: /docker-entrypoint-initdb.d + name: initdb-volume + - mountPath: /var/lib/postgresql + name: pg-data + +nodeSelector: {} +affinity: {} + +# Secret configuration for pgvector +# These values can be overridden from a parent chart or via --set flags +# Example: helm install ... +# --set pgvector.secret.user=myuser - +# --set pgvector.secret.password=mypass +secret: + create: true + user: postgres + password: rag_password + dbname: rag_blueprint + host: pgvector + port: "5432" + +extraDatabases: [] + #- name: test_db + # vectordb: false diff --git a/charts/all/rag-llm/templates/deployment.yaml b/charts/all/rag-llm/templates/deployment.yaml index 1534476b..adaa158d 100644 --- a/charts/all/rag-llm/templates/deployment.yaml +++ b/charts/all/rag-llm/templates/deployment.yaml @@ -56,7 +56,7 @@ spec: - name: REDIS_SCHEMA value: redis_schema.yaml {{- end }} - {{- if eq .Values.global.db.type "EDB" }} + {{- if or (eq .Values.global.db.type "EDB") (eq .Values.global.db.type "PGVECTOR") }} - name: DB_TYPE value: PGVECTOR - name: DB_USERNAME diff --git a/charts/all/rag-llm/templates/edb-pull-secret.yaml b/charts/all/rag-llm/templates/edb-pull-secret.yaml new file mode 100644 index 00000000..aa96b574 --- /dev/null +++ b/charts/all/rag-llm/templates/edb-pull-secret.yaml @@ -0,0 +1,25 @@ +{{- if eq .Values.global.db.type "EDB" }} +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: edb-operator-pull-secret + namespace: openshift-operators +spec: + refreshInterval: 15s + secretStoreRef: + name: {{ .Values.secretStore.name }} + kind: {{ .Values.secretStore.kind }} + target: + name: postgresql-operator-pull-secret + template: + type: kubernetes.io/dockerconfigjson + engineVersion: v2 + data: + .dockerconfigjson: | + {"auths":{"docker.enterprisedb.com":{"username":"k8s","password":"{{ `{{ .token }}` }}","auth":"{{ `{{ printf "k8s:%s" .token | b64enc }}` }}"}}} + data: + - secretKey: token + remoteRef: + key: {{ .Values.edb.key }} + property: token +{{- end }} diff --git a/charts/all/rag-llm/templates/populate-vectordb-job.yaml b/charts/all/rag-llm/templates/populate-vectordb-job.yaml index 46b0813d..fed9aa0a 100644 --- a/charts/all/rag-llm/templates/populate-vectordb-job.yaml +++ b/charts/all/rag-llm/templates/populate-vectordb-job.yaml @@ -50,7 +50,7 @@ spec: - name: REDIS_SCHEMA value: redis_schema.yaml {{- end }} - {{- if eq .Values.global.db.type "EDB" }} + {{- if or (eq .Values.global.db.type "EDB") (eq .Values.global.db.type "PGVECTOR") }} - name: DB_TYPE value: "PGVECTOR" - name: DB_USERNAME diff --git a/charts/all/rag-llm/values.yaml b/charts/all/rag-llm/values.yaml index e20629ad..75e21035 100644 --- a/charts/all/rag-llm/values.yaml +++ b/charts/all/rag-llm/values.yaml @@ -240,6 +240,9 @@ secretStore: hfmodel: key: secret/data/hub/hfmodel +edb: + key: secret/data/hub/edb + # Create NetworkPolicy to allow traffic from all namespaces to allow monitoring. Set to false if monitoring is not needed customnetworkpolicy: enabled: true diff --git a/values-global.yaml b/values-global.yaml index 8943afab..3f10827b 100644 --- a/values-global.yaml +++ b/values-global.yaml @@ -7,13 +7,14 @@ global: installPlanApproval: Automatic # Possible values for RAG vector DB db.type: # REDIS -> Redis (Local chart deploy) - # EDB -> PGVector (Local chart deploy) + # EDB -> PGVector via EDB operator (Local chart deploy) + # PGVECTOR -> PGVector (Local Postgres chart deploy) # ELASTIC -> Elasticsearch (Local chart deploy) # MSSQL -> MS SQL Server (Local chart deploy) # AZURESQL -> Azure SQL (Pre-existing in Azure) db: index: docs - type: EDB + type: PGVECTOR # Models used by the inference service (should be a HuggingFace model ID) model: vllm: ibm-granite/granite-3.3-8b-instruct diff --git a/values-secret.yaml.template b/values-secret.yaml.template index aa526f1b..0007f3ee 100644 --- a/values-secret.yaml.template +++ b/values-secret.yaml.template @@ -10,11 +10,20 @@ version: "2.0" # provide your token as a value for hftoken # NOTE: you need to add value in values-global.yaml as well +# EDB Postgres Operator requires authentication to pull images from docker.enterprisedb.com +# Register for a free trial at: https://www.enterprisedb.com/accounts/register +# Get your token from: https://www.enterprisedb.com/repos-downloads + secrets: - name: hfmodel fields: - name: hftoken value: null + - name: edb + fields: + - name: token + value: null + description: EDB subscription token for pulling certified operator images - name: mssql fields: - name: sa-pass