From a14bdcb77c058146c4355386c52c862f94ebc54b Mon Sep 17 00:00:00 2001 From: Trevor Royer Date: Tue, 20 Jan 2026 10:53:42 -0700 Subject: [PATCH 01/12] llm-d wip --- charts/llm-d-kserve/.helmignore | 23 +++ charts/llm-d-kserve/Chart.yaml | 24 +++ charts/llm-d-kserve/templates/NOTES.txt | 35 ++++ charts/llm-d-kserve/templates/_helpers.tpl | 62 +++++++ charts/llm-d-kserve/templates/deployment.yaml | 78 ++++++++ charts/llm-d-kserve/templates/hpa.yaml | 32 ++++ charts/llm-d-kserve/templates/httproute.yaml | 38 ++++ charts/llm-d-kserve/templates/ingress.yaml | 43 +++++ .../templates/llminferenceservice.yaml | 60 +++++++ charts/llm-d-kserve/templates/service.yaml | 15 ++ .../templates/serviceaccount.yaml | 13 ++ .../templates/tests/test-connection.yaml | 15 ++ charts/llm-d-kserve/values.yaml | 170 ++++++++++++++++++ 13 files changed, 608 insertions(+) create mode 100644 charts/llm-d-kserve/.helmignore create mode 100644 charts/llm-d-kserve/Chart.yaml create mode 100644 charts/llm-d-kserve/templates/NOTES.txt create mode 100644 charts/llm-d-kserve/templates/_helpers.tpl create mode 100644 charts/llm-d-kserve/templates/deployment.yaml create mode 100644 charts/llm-d-kserve/templates/hpa.yaml create mode 100644 charts/llm-d-kserve/templates/httproute.yaml create mode 100644 charts/llm-d-kserve/templates/ingress.yaml create mode 100644 charts/llm-d-kserve/templates/llminferenceservice.yaml create mode 100644 charts/llm-d-kserve/templates/service.yaml create mode 100644 charts/llm-d-kserve/templates/serviceaccount.yaml create mode 100644 charts/llm-d-kserve/templates/tests/test-connection.yaml create mode 100644 charts/llm-d-kserve/values.yaml diff --git a/charts/llm-d-kserve/.helmignore b/charts/llm-d-kserve/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/charts/llm-d-kserve/.helmignore @@ -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/ diff --git a/charts/llm-d-kserve/Chart.yaml b/charts/llm-d-kserve/Chart.yaml new file mode 100644 index 0000000..9508087 --- /dev/null +++ b/charts/llm-d-kserve/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: llm-d-kserve +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.1.0 + +# 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: "1.16.0" diff --git a/charts/llm-d-kserve/templates/NOTES.txt b/charts/llm-d-kserve/templates/NOTES.txt new file mode 100644 index 0000000..d2970e2 --- /dev/null +++ b/charts/llm-d-kserve/templates/NOTES.txt @@ -0,0 +1,35 @@ +1. Get the application URL by running these commands: +{{- if .Values.httpRoute.enabled }} +{{- if .Values.httpRoute.hostnames }} + export APP_HOSTNAME={{ .Values.httpRoute.hostnames | first }} +{{- else }} + export APP_HOSTNAME=$(kubectl get --namespace {{(first .Values.httpRoute.parentRefs).namespace | default .Release.Namespace }} gateway/{{ (first .Values.httpRoute.parentRefs).name }} -o jsonpath="{.spec.listeners[0].hostname}") + {{- end }} +{{- if and .Values.httpRoute.rules (first .Values.httpRoute.rules).matches (first (first .Values.httpRoute.rules).matches).path.value }} + echo "Visit http://$APP_HOSTNAME{{ (first (first .Values.httpRoute.rules).matches).path.value }} to use your application" + + NOTE: Your HTTPRoute depends on the listener configuration of your gateway and your HTTPRoute rules. + The rules can be set for path, method, header and query parameters. + You can check the gateway configuration with 'kubectl get --namespace {{(first .Values.httpRoute.parentRefs).namespace | default .Release.Namespace }} gateway/{{ (first .Values.httpRoute.parentRefs).name }} -o yaml' +{{- end }} +{{- else 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 "llm-d-kserve.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 its status by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "llm-d-kserve.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "llm-d-kserve.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 "llm-d-kserve.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 }} diff --git a/charts/llm-d-kserve/templates/_helpers.tpl b/charts/llm-d-kserve/templates/_helpers.tpl new file mode 100644 index 0000000..a190bdb --- /dev/null +++ b/charts/llm-d-kserve/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "llm-d-kserve.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 "llm-d-kserve.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 "llm-d-kserve.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "llm-d-kserve.labels" -}} +helm.sh/chart: {{ include "llm-d-kserve.chart" . }} +{{ include "llm-d-kserve.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "llm-d-kserve.selectorLabels" -}} +app.kubernetes.io/name: {{ include "llm-d-kserve.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "llm-d-kserve.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "llm-d-kserve.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/llm-d-kserve/templates/deployment.yaml b/charts/llm-d-kserve/templates/deployment.yaml new file mode 100644 index 0000000..1cddb1e --- /dev/null +++ b/charts/llm-d-kserve/templates/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "llm-d-kserve.fullname" . }} + labels: + {{- include "llm-d-kserve.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "llm-d-kserve.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "llm-d-kserve.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "llm-d-kserve.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 }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + {{- with .Values.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- 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 }} diff --git a/charts/llm-d-kserve/templates/hpa.yaml b/charts/llm-d-kserve/templates/hpa.yaml new file mode 100644 index 0000000..c5a66f6 --- /dev/null +++ b/charts/llm-d-kserve/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "llm-d-kserve.fullname" . }} + labels: + {{- include "llm-d-kserve.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "llm-d-kserve.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/charts/llm-d-kserve/templates/httproute.yaml b/charts/llm-d-kserve/templates/httproute.yaml new file mode 100644 index 0000000..0777b66 --- /dev/null +++ b/charts/llm-d-kserve/templates/httproute.yaml @@ -0,0 +1,38 @@ +{{- if .Values.httpRoute.enabled -}} +{{- $fullName := include "llm-d-kserve.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ $fullName }} + labels: + {{- include "llm-d-kserve.labels" . | nindent 4 }} + {{- with .Values.httpRoute.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + parentRefs: + {{- with .Values.httpRoute.parentRefs }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.httpRoute.hostnames }} + hostnames: + {{- toYaml . | nindent 4 }} + {{- end }} + rules: + {{- range .Values.httpRoute.rules }} + {{- with .matches }} + - matches: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .filters }} + filters: + {{- toYaml . | nindent 8 }} + {{- end }} + backendRefs: + - name: {{ $fullName }} + port: {{ $svcPort }} + weight: 1 + {{- end }} +{{- end }} diff --git a/charts/llm-d-kserve/templates/ingress.yaml b/charts/llm-d-kserve/templates/ingress.yaml new file mode 100644 index 0000000..83ecb55 --- /dev/null +++ b/charts/llm-d-kserve/templates/ingress.yaml @@ -0,0 +1,43 @@ +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "llm-d-kserve.fullname" . }} + labels: + {{- include "llm-d-kserve.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with .Values.ingress.className }} + ingressClassName: {{ . }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- with .pathType }} + pathType: {{ . }} + {{- end }} + backend: + service: + name: {{ include "llm-d-kserve.fullname" $ }} + port: + number: {{ $.Values.service.port }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/llm-d-kserve/templates/llminferenceservice.yaml b/charts/llm-d-kserve/templates/llminferenceservice.yaml new file mode 100644 index 0000000..d17b40e --- /dev/null +++ b/charts/llm-d-kserve/templates/llminferenceservice.yaml @@ -0,0 +1,60 @@ +apiVersion: serving.kserve.io/v1alpha1 +kind: LLMInferenceService +metadata: + name: {{ include "llm-d-kserve.fullname" . }} + labels: + {{- include "llm-d-kserve.labels" . | nindent 4 }} +spec: + model: + name: {{ .Values.model.nameOverride | default (include "llm-d-kserve.fullname" .) }} + uri: {{ .Values.model.uri }} + replicas: {{ .Values.replicaCount }} + router: + scheduler: + template: + containers: + - name: main + args: + - -v=4 + - --poolName + - '{{ ChildName .ObjectMeta.Name `-inference-pool` }}' + - --poolNamespace + - '{{ .ObjectMeta.Namespace }}' + - --zap-encoder + - json + - --grpcPort + - "9002" + - --grpcHealthPort + - "9003" + - --secureServing + - --modelServerMetricsScheme + - https + - --modelServerMetricsHttpsInsecureSkipVerify + - --certPath + - /etc/ssl/certs + - --configText + - |2 + + apiVersion: inference.networking.x-k8s.io/v1alpha1 + kind: EndpointPickerConfig + plugins: + - type: single-profile-handler + - type: max-score-picker + - type: queue-scorer + - type: kv-cache-scorer + - type: prefix-cache-scorer + parameters: + hashBlockSize: 64 # block size 16 * 4 chars / token + maxPrefixBlocksToMatch: 256 # this matches up to ~4k tokens + lruCapacityPerServer: 39744 # total blocks (635904) / block size (16) + schedulingProfiles: + - name: default + plugins: + - pluginRef: prefix-cache-scorer + weight: 2.0 + - pluginRef: queue-scorer + weight: 1.0 + - pluginRef: kv-cache-scorer + weight: 1.0 + - pluginRef: max-score-picker + \ No newline at end of file diff --git a/charts/llm-d-kserve/templates/service.yaml b/charts/llm-d-kserve/templates/service.yaml new file mode 100644 index 0000000..0705876 --- /dev/null +++ b/charts/llm-d-kserve/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "llm-d-kserve.fullname" . }} + labels: + {{- include "llm-d-kserve.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "llm-d-kserve.selectorLabels" . | nindent 4 }} diff --git a/charts/llm-d-kserve/templates/serviceaccount.yaml b/charts/llm-d-kserve/templates/serviceaccount.yaml new file mode 100644 index 0000000..39574b5 --- /dev/null +++ b/charts/llm-d-kserve/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "llm-d-kserve.serviceAccountName" . }} + labels: + {{- include "llm-d-kserve.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/charts/llm-d-kserve/templates/tests/test-connection.yaml b/charts/llm-d-kserve/templates/tests/test-connection.yaml new file mode 100644 index 0000000..cd3bc3f --- /dev/null +++ b/charts/llm-d-kserve/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "llm-d-kserve.fullname" . }}-test-connection" + labels: + {{- include "llm-d-kserve.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "llm-d-kserve.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/charts/llm-d-kserve/values.yaml b/charts/llm-d-kserve/values.yaml new file mode 100644 index 0000000..91faf6a --- /dev/null +++ b/charts/llm-d-kserve/values.yaml @@ -0,0 +1,170 @@ +# Default values for llm-d-kserve. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +# This will set the replicaset count more information can be found here: https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/ +replicaCount: 1 + +model: + nameOverride: "meta-llama/llama-3.2-1b-instruct" + uri: oci://quay.io/redhat-ai-services/modelcar-catalog:llama-3.2-1b-instruct + +tolerations: + - key: "nvidia.com/gpu" + operator: "Exists" + effect: "NoSchedule" + +# This sets the container image more information can be found here: https://kubernetes.io/docs/concepts/containers/images/ +image: + repository: nginx + # This sets the pull policy for images. + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +# This is for the secrets for pulling an image from a private repository more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ +imagePullSecrets: [] +# This is to override the chart name. +nameOverride: "" +fullnameOverride: "" + +# This section builds out the service account more information can be found here: https://kubernetes.io/docs/concepts/security/service-accounts/ +serviceAccount: + # Specifies whether a service account should be created + create: true + # Automatically mount a ServiceAccount's API credentials? + automount: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +# This is for setting Kubernetes Annotations to a Pod. +# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ +podAnnotations: {} +# This is for setting Kubernetes Labels to a Pod. +# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ +podLabels: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +# This is for setting up a service more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/ +service: + # This sets the service type more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types + type: ClusterIP + # This sets the ports more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#field-spec-ports + port: 80 + +# This block is for setting up the ingress for more information can be found here: https://kubernetes.io/docs/concepts/services-networking/ingress/ +ingress: + enabled: false + className: "" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +# -- Expose the service via gateway-api HTTPRoute +# Requires Gateway API resources and suitable controller installed within the cluster +# (see: https://gateway-api.sigs.k8s.io/guides/) +httpRoute: + # HTTPRoute enabled. + enabled: false + # HTTPRoute annotations. + annotations: {} + # Which Gateways this Route is attached to. + parentRefs: + - name: gateway + sectionName: http + # namespace: default + # Hostnames matching HTTP header. + hostnames: + - chart-example.local + # List of rules and filters applied. + rules: + - matches: + - path: + type: PathPrefix + value: /headers + # filters: + # - type: RequestHeaderModifier + # requestHeaderModifier: + # set: + # - name: My-Overwrite-Header + # value: this-is-the-only-value + # remove: + # - User-Agent + # - matches: + # - path: + # type: PathPrefix + # value: /echo + # headers: + # - name: version + # value: v2 + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +# This is to setup the liveness and readiness probes more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ +livenessProbe: + httpGet: + path: / + port: http +readinessProbe: + httpGet: + path: / + port: http + +# This section is for setting up autoscaling more information can be found here: https://kubernetes.io/docs/concepts/workloads/autoscaling/ +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +# Additional volumes on the output Deployment definition. +volumes: [] +# - name: foo +# secret: +# secretName: mysecret +# optional: false + +# Additional volumeMounts on the output Deployment definition. +volumeMounts: [] +# - name: foo +# mountPath: "/etc/foo" +# readOnly: true + +nodeSelector: {} + +tolerations: [] + +affinity: {} From 6a5618b165b83e756a68ed7bec24a5482574efe6 Mon Sep 17 00:00:00 2001 From: Trevor Royer Date: Fri, 13 Mar 2026 15:37:15 -0500 Subject: [PATCH 02/12] remove unused files --- charts/llm-d-kserve/templates/deployment.yaml | 78 ------------------- charts/llm-d-kserve/templates/hpa.yaml | 32 -------- charts/llm-d-kserve/templates/httproute.yaml | 38 --------- charts/llm-d-kserve/templates/ingress.yaml | 43 ---------- charts/llm-d-kserve/templates/service.yaml | 15 ---- .../templates/tests/test-connection.yaml | 15 ---- 6 files changed, 221 deletions(-) delete mode 100644 charts/llm-d-kserve/templates/deployment.yaml delete mode 100644 charts/llm-d-kserve/templates/hpa.yaml delete mode 100644 charts/llm-d-kserve/templates/httproute.yaml delete mode 100644 charts/llm-d-kserve/templates/ingress.yaml delete mode 100644 charts/llm-d-kserve/templates/service.yaml delete mode 100644 charts/llm-d-kserve/templates/tests/test-connection.yaml diff --git a/charts/llm-d-kserve/templates/deployment.yaml b/charts/llm-d-kserve/templates/deployment.yaml deleted file mode 100644 index 1cddb1e..0000000 --- a/charts/llm-d-kserve/templates/deployment.yaml +++ /dev/null @@ -1,78 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "llm-d-kserve.fullname" . }} - labels: - {{- include "llm-d-kserve.labels" . | nindent 4 }} -spec: - {{- if not .Values.autoscaling.enabled }} - replicas: {{ .Values.replicaCount }} - {{- end }} - selector: - matchLabels: - {{- include "llm-d-kserve.selectorLabels" . | nindent 6 }} - template: - metadata: - {{- with .Values.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "llm-d-kserve.labels" . | nindent 8 }} - {{- with .Values.podLabels }} - {{- toYaml . | nindent 8 }} - {{- end }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "llm-d-kserve.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 }} - ports: - - name: http - containerPort: {{ .Values.service.port }} - protocol: TCP - {{- with .Values.livenessProbe }} - livenessProbe: - {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.readinessProbe }} - readinessProbe: - {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.resources }} - resources: - {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.volumeMounts }} - volumeMounts: - {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.volumes }} - volumes: - {{- toYaml . | nindent 8 }} - {{- 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 }} diff --git a/charts/llm-d-kserve/templates/hpa.yaml b/charts/llm-d-kserve/templates/hpa.yaml deleted file mode 100644 index c5a66f6..0000000 --- a/charts/llm-d-kserve/templates/hpa.yaml +++ /dev/null @@ -1,32 +0,0 @@ -{{- if .Values.autoscaling.enabled }} -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "llm-d-kserve.fullname" . }} - labels: - {{- include "llm-d-kserve.labels" . | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include "llm-d-kserve.fullname" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} - metrics: - {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} - {{- end }} - {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} - {{- end }} -{{- end }} diff --git a/charts/llm-d-kserve/templates/httproute.yaml b/charts/llm-d-kserve/templates/httproute.yaml deleted file mode 100644 index 0777b66..0000000 --- a/charts/llm-d-kserve/templates/httproute.yaml +++ /dev/null @@ -1,38 +0,0 @@ -{{- if .Values.httpRoute.enabled -}} -{{- $fullName := include "llm-d-kserve.fullname" . -}} -{{- $svcPort := .Values.service.port -}} -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: {{ $fullName }} - labels: - {{- include "llm-d-kserve.labels" . | nindent 4 }} - {{- with .Values.httpRoute.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - parentRefs: - {{- with .Values.httpRoute.parentRefs }} - {{- toYaml . | nindent 4 }} - {{- end }} - {{- with .Values.httpRoute.hostnames }} - hostnames: - {{- toYaml . | nindent 4 }} - {{- end }} - rules: - {{- range .Values.httpRoute.rules }} - {{- with .matches }} - - matches: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .filters }} - filters: - {{- toYaml . | nindent 8 }} - {{- end }} - backendRefs: - - name: {{ $fullName }} - port: {{ $svcPort }} - weight: 1 - {{- end }} -{{- end }} diff --git a/charts/llm-d-kserve/templates/ingress.yaml b/charts/llm-d-kserve/templates/ingress.yaml deleted file mode 100644 index 83ecb55..0000000 --- a/charts/llm-d-kserve/templates/ingress.yaml +++ /dev/null @@ -1,43 +0,0 @@ -{{- if .Values.ingress.enabled -}} -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: {{ include "llm-d-kserve.fullname" . }} - labels: - {{- include "llm-d-kserve.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- with .Values.ingress.className }} - ingressClassName: {{ . }} - {{- end }} - {{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ .path }} - {{- with .pathType }} - pathType: {{ . }} - {{- end }} - backend: - service: - name: {{ include "llm-d-kserve.fullname" $ }} - port: - number: {{ $.Values.service.port }} - {{- end }} - {{- end }} -{{- end }} diff --git a/charts/llm-d-kserve/templates/service.yaml b/charts/llm-d-kserve/templates/service.yaml deleted file mode 100644 index 0705876..0000000 --- a/charts/llm-d-kserve/templates/service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "llm-d-kserve.fullname" . }} - labels: - {{- include "llm-d-kserve.labels" . | nindent 4 }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - {{- include "llm-d-kserve.selectorLabels" . | nindent 4 }} diff --git a/charts/llm-d-kserve/templates/tests/test-connection.yaml b/charts/llm-d-kserve/templates/tests/test-connection.yaml deleted file mode 100644 index cd3bc3f..0000000 --- a/charts/llm-d-kserve/templates/tests/test-connection.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "llm-d-kserve.fullname" . }}-test-connection" - labels: - {{- include "llm-d-kserve.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test -spec: - containers: - - name: wget - image: busybox - command: ['wget'] - args: ['{{ include "llm-d-kserve.fullname" . }}:{{ .Values.service.port }}'] - restartPolicy: Never From 3d3ca3468c7c78b1bbdd0459c0f6916753fd681b Mon Sep 17 00:00:00 2001 From: Trevor Royer Date: Fri, 13 Mar 2026 15:40:26 -0500 Subject: [PATCH 03/12] update helm chart with new values config --- charts/llm-d-kserve/Chart.yaml | 28 +- charts/llm-d-kserve/README.md.gotmpl | 50 ++++ charts/llm-d-kserve/templates/NOTES.txt | 96 ++++--- charts/llm-d-kserve/templates/_helpers.tpl | 8 + .../templates/hardwareprofile.yaml | 21 ++ .../templates/llminferenceservice.yaml | 94 +++---- charts/llm-d-kserve/values.yaml | 259 +++++++----------- 7 files changed, 293 insertions(+), 263 deletions(-) create mode 100644 charts/llm-d-kserve/README.md.gotmpl create mode 100644 charts/llm-d-kserve/templates/hardwareprofile.yaml diff --git a/charts/llm-d-kserve/Chart.yaml b/charts/llm-d-kserve/Chart.yaml index 9508087..2c6e0f7 100644 --- a/charts/llm-d-kserve/Chart.yaml +++ b/charts/llm-d-kserve/Chart.yaml @@ -1,24 +1,14 @@ apiVersion: v2 -name: llm-d-kserve -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/) +name: llm-d-kserve +description: A Helm chart for deploying LLM-D with KServe on OpenShift AI + version: 0.1.0 -# 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: "1.16.0" +# OpenShift AI version +appVersion: "3.3.0" + +icon: https://avatars.githubusercontent.com/u/211385051?s=48&v=4 +sources: + - https://github.com/redhat-ai-services/helm-charts/tree/main/charts/llm-d-kserve diff --git a/charts/llm-d-kserve/README.md.gotmpl b/charts/llm-d-kserve/README.md.gotmpl new file mode 100644 index 0000000..28ca528 --- /dev/null +++ b/charts/llm-d-kserve/README.md.gotmpl @@ -0,0 +1,50 @@ +{{ template "chart.header" . }} +{{ template "chart.description" . }} + +{{ template "chart.versionBadge" . }}{{ template "chart.typeBadge" . }}{{ template "chart.appVersionBadge" . }} + +## Installing the Chart + +To access charts from this from the cli repository add it: + +```sh +helm repo add redhat-ai-services https://redhat-ai-services.github.io/helm-charts/ +helm repo update redhat-ai-services +helm upgrade -i [release-name] redhat-ai-services/{{ template "chart.name" . }} +``` + +To include a chart from this repository in an umbrella chart, include it in your dependencies in your `Chart.yaml` file. + +```yaml +apiVersion: v2 +name: example-chart +description: A Helm chart for Kubernetes +type: application + +version: 0.1.0 + +appVersion: "1.16.0" + +dependencies: + - name: "{{ template "chart.name" . }}" + version: "{{ template "chart.version" . }}" + repository: "https://redhat-ai-services.github.io/helm-charts/" +``` + +## Usage + +### Quick Start + +TODO + +## Configuration + +For a complete list of all configuration options, see the [Values](#values) section below. + +{{ template "chart.sourcesSection" . }} + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/charts/llm-d-kserve/templates/NOTES.txt b/charts/llm-d-kserve/templates/NOTES.txt index d2970e2..3a72982 100644 --- a/charts/llm-d-kserve/templates/NOTES.txt +++ b/charts/llm-d-kserve/templates/NOTES.txt @@ -1,35 +1,65 @@ -1. Get the application URL by running these commands: -{{- if .Values.httpRoute.enabled }} -{{- if .Values.httpRoute.hostnames }} - export APP_HOSTNAME={{ .Values.httpRoute.hostnames | first }} +Successfully deployed LLM-D InferenceService! + +DEPLOYMENT INFORMATION: + Name: {{ include "llm-d-kserve.fullname" . }} + Namespace: {{ .Release.Namespace }} + Model: {{ .Values.model.name }} + Model URI: {{ .Values.model.uri }} + Replicas: {{ .Values.vllm.replicaCount }} +{{- if .Values.hardwareProfile.create }} + + A hardware profile "{{ include "llm-d-kserve.fullname" . }}" has been created in this namespace. {{- else }} - export APP_HOSTNAME=$(kubectl get --namespace {{(first .Values.httpRoute.parentRefs).namespace | default .Release.Namespace }} gateway/{{ (first .Values.httpRoute.parentRefs).name }} -o jsonpath="{.spec.listeners[0].hostname}") - {{- end }} -{{- if and .Values.httpRoute.rules (first .Values.httpRoute.rules).matches (first (first .Values.httpRoute.rules).matches).path.value }} - echo "Visit http://$APP_HOSTNAME{{ (first (first .Values.httpRoute.rules).matches).path.value }} to use your application" - - NOTE: Your HTTPRoute depends on the listener configuration of your gateway and your HTTPRoute rules. - The rules can be set for path, method, header and query parameters. - You can check the gateway configuration with 'kubectl get --namespace {{(first .Values.httpRoute.parentRefs).namespace | default .Release.Namespace }} gateway/{{ (first .Values.httpRoute.parentRefs).name }} -o yaml' -{{- end }} -{{- else 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 "llm-d-kserve.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 its status by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "llm-d-kserve.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "llm-d-kserve.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 "llm-d-kserve.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 + + Hardware Profile: {{ .Values.hardwareProfile.name }} (namespace: {{ .Values.hardwareProfile.namespace }}) {{- end }} + +CHECK STATUS: + oc get llminferenceservices {{ include "llm-d-kserve.fullname" . }} -n {{ .Release.Namespace }} + oc get pods -l serving.kserve.io/llminferenceservice={{ include "llm-d-kserve.fullname" . }} -n {{ .Release.Namespace }} + +INTERNAL ACCESS: + Your model is configured for internal cluster access. + + To get the service URL (if available): + oc get llminferenceservices {{ include "llm-d-kserve.fullname" . }} -n {{ .Release.Namespace }} -o jsonpath='{.status.url}' + + For local access, use port forwarding: + oc port-forward svc/{{ include "llm-d-kserve.fullname" . }}-predictor -n {{ .Release.Namespace }} 8080:80 + + Then test with: + curl -X POST http://localhost:8080/v1/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "{{ .Values.model.name }}", + "messages": [{"role": "user", "content": "Hello, how are you?"}], + "max_tokens": 50 + }' + + Or for completions: + curl -X POST http://localhost:8080/v1/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "{{ .Values.model.name }}", + "prompt": "Hello, how are you?", + "max_tokens": 50 + }' + +USEFUL COMMANDS: + # Check pod logs + oc logs -l serving.kserve.io/llminferenceservice={{ include "llm-d-kserve.fullname" . }} -n {{ .Release.Namespace }} -f + + # List available models + curl http://localhost:8080/v1/models + + # Health check + curl http://localhost:8080/health + +API DOCUMENTATION: + LLM-D exposes an OpenAI-compatible API. Available endpoints: + - GET /v1/models - List available models + - POST /v1/chat/completions - Chat completion + - POST /v1/completions - Text completion + - GET /health - Health check + + Use port-forward (see above) before calling these endpoints from your machine. diff --git a/charts/llm-d-kserve/templates/_helpers.tpl b/charts/llm-d-kserve/templates/_helpers.tpl index a190bdb..db28079 100644 --- a/charts/llm-d-kserve/templates/_helpers.tpl +++ b/charts/llm-d-kserve/templates/_helpers.tpl @@ -60,3 +60,11 @@ Create the name of the service account to use {{- default "default" .Values.serviceAccount.name }} {{- end }} {{- end }} + +{{- define "llm-d-kserve.image" -}} +{{- if hasPrefix "sha256:" (toString .Values.vllm.image.tag) }} +{{- printf "%s@%s" .Values.vllm.image.repository .Values.vllm.image.tag }} +{{- else }} +{{- printf "%s:%s" .Values.vllm.image.repository .Values.vllm.image.tag }} +{{- end }} +{{- end }} diff --git a/charts/llm-d-kserve/templates/hardwareprofile.yaml b/charts/llm-d-kserve/templates/hardwareprofile.yaml new file mode 100644 index 0000000..ba57b5e --- /dev/null +++ b/charts/llm-d-kserve/templates/hardwareprofile.yaml @@ -0,0 +1,21 @@ +{{- if .Values.hardwareProfile.create }} +apiVersion: infrastructure.opendatahub.io/v1 +kind: HardwareProfile +metadata: + annotations: + opendatahub.io/dashboard-feature-visibility: '["model-serving"]' + opendatahub.io/description: "" + opendatahub.io/disabled: "false" + opendatahub.io/display-name: {{ include "llm-d-kserve.fullname" . | quote }} + name: {{ include "llm-d-kserve.fullname" . }} +spec: + identifiers: + {{- toYaml .Values.hardwareProfile.identifiers | nindent 4 }} + scheduling: + {{- if .Values.hardwareProfile.tolerations }} + node: + tolerations: + {{- toYaml .Values.hardwareProfile.tolerations | nindent 8 }} + {{- end }} + type: Node +{{- end }} diff --git a/charts/llm-d-kserve/templates/llminferenceservice.yaml b/charts/llm-d-kserve/templates/llminferenceservice.yaml index d17b40e..bff42be 100644 --- a/charts/llm-d-kserve/templates/llminferenceservice.yaml +++ b/charts/llm-d-kserve/templates/llminferenceservice.yaml @@ -1,60 +1,48 @@ apiVersion: serving.kserve.io/v1alpha1 kind: LLMInferenceService metadata: - name: {{ include "llm-d-kserve.fullname" . }} + annotations: + {{- if .Values.hardwareProfile.create }} + opendatahub.io/hardware-profile-name: {{ include "llm-d-kserve.fullname" . | quote }} + opendatahub.io/hardware-profile-namespace: {{ .Release.Namespace | quote }} + {{- else }} + opendatahub.io/hardware-profile-name: {{ .Values.hardwareProfile.name | quote }} + opendatahub.io/hardware-profile-namespace: {{ .Values.hardwareProfile.namespace | quote }} + {{- end }} + opendatahub.io/model-type: generative + openshift.io/display-name: {{ .Values.model.name | quote }} + security.opendatahub.io/enable-auth: "false" labels: - {{- include "llm-d-kserve.labels" . | nindent 4 }} + opendatahub.io/dashboard: "true" + name: {{ include "llm-d-kserve.fullname" . }} spec: model: - name: {{ .Values.model.nameOverride | default (include "llm-d-kserve.fullname" .) }} - uri: {{ .Values.model.uri }} - replicas: {{ .Values.replicaCount }} + name: {{ .Values.model.name | quote }} + uri: {{ .Values.model.uri | quote }} router: - scheduler: - template: - containers: - - name: main - args: - - -v=4 - - --poolName - - '{{ ChildName .ObjectMeta.Name `-inference-pool` }}' - - --poolNamespace - - '{{ .ObjectMeta.Namespace }}' - - --zap-encoder - - json - - --grpcPort - - "9002" - - --grpcHealthPort - - "9003" - - --secureServing - - --modelServerMetricsScheme - - https - - --modelServerMetricsHttpsInsecureSkipVerify - - --certPath - - /etc/ssl/certs - - --configText - - |2 - - apiVersion: inference.networking.x-k8s.io/v1alpha1 - kind: EndpointPickerConfig - plugins: - - type: single-profile-handler - - type: max-score-picker - - type: queue-scorer - - type: kv-cache-scorer - - type: prefix-cache-scorer - parameters: - hashBlockSize: 64 # block size 16 * 4 chars / token - maxPrefixBlocksToMatch: 256 # this matches up to ~4k tokens - lruCapacityPerServer: 39744 # total blocks (635904) / block size (16) - schedulingProfiles: - - name: default - plugins: - - pluginRef: prefix-cache-scorer - weight: 2.0 - - pluginRef: queue-scorer - weight: 1.0 - - pluginRef: kv-cache-scorer - weight: 1.0 - - pluginRef: max-score-picker - \ No newline at end of file + gateway: {} + route: {} + scheduler: {} + replicas: {{ .Values.vllm.replicaCount }} + template: + {{- if .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml .Values.imagePullSecrets | nindent 6 }} + {{- end }} + containers: + - name: main + env: + - name: VLLM_ADDITIONAL_ARGS + value: {{ .Values.vllm.additionalArguments | quote }} + {{- if .Values.vllm.image.repository }} + image: {{ include "llm-d-kserve.image" . }} + {{- end }} + imagePullPolicy: {{ .Values.vllm.image.pullPolicy | quote }} + {{- if .Values.vllm.resources }} + resources: + {{- toYaml .Values.vllm.resources | nindent 8 }} + {{- end }} + {{- if .Values.vllm.tolerations }} + tolerations: + {{- toYaml .Values.vllm.tolerations | nindent 6 }} + {{- end }} diff --git a/charts/llm-d-kserve/values.yaml b/charts/llm-d-kserve/values.yaml index 91faf6a..ca43bfb 100644 --- a/charts/llm-d-kserve/values.yaml +++ b/charts/llm-d-kserve/values.yaml @@ -1,170 +1,113 @@ -# Default values for llm-d-kserve. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -# This will set the replicaset count more information can be found here: https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/ -replicaCount: 1 +# -- String to partially override fullname template (will maintain the release name) +nameOverride: "" +# -- String to fully override fullname template +fullnameOverride: "" model: - nameOverride: "meta-llama/llama-3.2-1b-instruct" + # -- The name of the model to be advertised in the /v1/models endpoint + name: "meta-llama/llama-3.2-1b-instruct" + # -- The URI of the model to be used by the model server uri: oci://quay.io/redhat-ai-services/modelcar-catalog:llama-3.2-1b-instruct -tolerations: - - key: "nvidia.com/gpu" - operator: "Exists" - effect: "NoSchedule" - -# This sets the container image more information can be found here: https://kubernetes.io/docs/concepts/containers/images/ -image: - repository: nginx - # This sets the pull policy for images. - pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart appVersion. - tag: "" - -# This is for the secrets for pulling an image from a private repository more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ +hardwareProfile: + # -- Creates a hardware profile and adds it to the release namespace. + create: false + # -- The name of the hardware profile to use. + # If create is set to true, this value will be ignored and the hardware profile will be created with the release name. + name: nvidia-gpu + # -- The namespace of the hardware profile to use. + # If create is set to true, this value will be ignored and the hardware profile will be created in the release namespace. + namespace: redhat-ods-applications + # -- The identifiers to add to the hardware profile. + # This value will only be used if create is set to true. + identifiers: [] + # - identifier: cpu + # displayName: CPU + # resourceType: CPU + # defaultCount: "4" + # minCount: "1" + # maxCount: "32" + # - identifier: memory + # displayName: Memory + # resourceType: Memory + # defaultCount: 8Gi + # minCount: 2Gi + # maxCount: 120Gi + # - identifier: nvidia.com/gpu + # displayName: nvidia.com/gpu + # defaultCount: 1 + # minCount: 1 + # maxCount: 8 + # -- The tolerations to apply to the hardware profile. + # This value will only be used if create is set to true. + tolerations: [] + + +vllm: + # -- The number of replicas to run for the model server + replicaCount: 1 + # -- The tolerations to apply to the model server pod. + # If not set, the hardware profile will add the default tolerations. + tolerations: [] + # - key: "nvidia.com/gpu" + # operator: "Exists" + # effect: "NoSchedule" + # -- The resources to apply to the model server pod. + # If not set, the hardware profile will add the default resources. + resources: {} + # requests: + # cpu: 1 + # memory: 4Gi + # nvidia.com/gpu: 1 + # limits: + # cpu: 2 + # memory: 8Gi + # nvidia.com/gpu: 1 + # -- The additional arguments to pass to the model server. + additionalArguments: "" + image: + # -- The repository of the image to use for the model server + # If not set, the default image will be used. + repository: "" + # -- The tag of the image to use for the model server + tag: "" + # -- The pull policy of the image to use for the model server + pullPolicy: IfNotPresent + +scheduler: + endpointPickerConfig: "" #|4 + # apiVersion: inference.networking.x-k8s.io/v1alpha1 + # kind: EndpointPickerConfig + # plugins: + # - type: pd-profile-handler + # parameters: + # threshold: 30000 + # - type: prefill-header-handler + # - type: prefill-filter + # - type: decode-filter + # - type: random-picker + # parameters: + # maxNumOfEndpoints: 1 + # schedulingProfiles: + # - name: prefill + # plugins: + # - pluginRef: prefill-filter + # - pluginRef: random-picker + # - name: decode + # plugins: + # - pluginRef: decode-filter + # - pluginRef: random-picker + +# -- The image pull secrets to use for the model server. imagePullSecrets: [] -# This is to override the chart name. -nameOverride: "" -fullnameOverride: "" -# This section builds out the service account more information can be found here: https://kubernetes.io/docs/concepts/security/service-accounts/ serviceAccount: - # Specifies whether a service account should be created + # -- Specifies whether a service account should be created create: true - # Automatically mount a ServiceAccount's API credentials? + # -- Automatically mount a ServiceAccount's API credentials? automount: true - # Annotations to add to the service account + # -- Annotations to add to the service account annotations: {} - # The name of the service account to use. + # -- The name of the service account to use. # If not set and create is true, a name is generated using the fullname template name: "" - -# This is for setting Kubernetes Annotations to a Pod. -# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ -podAnnotations: {} -# This is for setting Kubernetes Labels to a Pod. -# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ -podLabels: {} - -podSecurityContext: {} - # fsGroup: 2000 - -securityContext: {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 - -# This is for setting up a service more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/ -service: - # This sets the service type more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types - type: ClusterIP - # This sets the ports more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#field-spec-ports - port: 80 - -# This block is for setting up the ingress for more information can be found here: https://kubernetes.io/docs/concepts/services-networking/ingress/ -ingress: - enabled: false - className: "" - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -# -- Expose the service via gateway-api HTTPRoute -# Requires Gateway API resources and suitable controller installed within the cluster -# (see: https://gateway-api.sigs.k8s.io/guides/) -httpRoute: - # HTTPRoute enabled. - enabled: false - # HTTPRoute annotations. - annotations: {} - # Which Gateways this Route is attached to. - parentRefs: - - name: gateway - sectionName: http - # namespace: default - # Hostnames matching HTTP header. - hostnames: - - chart-example.local - # List of rules and filters applied. - rules: - - matches: - - path: - type: PathPrefix - value: /headers - # filters: - # - type: RequestHeaderModifier - # requestHeaderModifier: - # set: - # - name: My-Overwrite-Header - # value: this-is-the-only-value - # remove: - # - User-Agent - # - matches: - # - path: - # type: PathPrefix - # value: /echo - # headers: - # - name: version - # value: v2 - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -# This is to setup the liveness and readiness probes more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ -livenessProbe: - httpGet: - path: / - port: http -readinessProbe: - httpGet: - path: / - port: http - -# This section is for setting up autoscaling more information can be found here: https://kubernetes.io/docs/concepts/workloads/autoscaling/ -autoscaling: - enabled: false - minReplicas: 1 - maxReplicas: 100 - targetCPUUtilizationPercentage: 80 - # targetMemoryUtilizationPercentage: 80 - -# Additional volumes on the output Deployment definition. -volumes: [] -# - name: foo -# secret: -# secretName: mysecret -# optional: false - -# Additional volumeMounts on the output Deployment definition. -volumeMounts: [] -# - name: foo -# mountPath: "/etc/foo" -# readOnly: true - -nodeSelector: {} - -tolerations: [] - -affinity: {} From 579ded69d2559f9fba33687fa81b334c9a5c12a2 Mon Sep 17 00:00:00 2001 From: JeetRH Date: Wed, 18 Mar 2026 15:30:29 -0400 Subject: [PATCH 04/12] Minor changes in HWP and values file to get the chart working --- .../templates/hardwareprofile.yaml | 2 ++ charts/llm-d-kserve/values.yaml | 19 +++++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/charts/llm-d-kserve/templates/hardwareprofile.yaml b/charts/llm-d-kserve/templates/hardwareprofile.yaml index ba57b5e..fe34d12 100644 --- a/charts/llm-d-kserve/templates/hardwareprofile.yaml +++ b/charts/llm-d-kserve/templates/hardwareprofile.yaml @@ -16,6 +16,8 @@ spec: node: tolerations: {{- toYaml .Values.hardwareProfile.tolerations | nindent 8 }} + {{- else }} + node: {} {{- end }} type: Node {{- end }} diff --git a/charts/llm-d-kserve/values.yaml b/charts/llm-d-kserve/values.yaml index ca43bfb..e1c3f4a 100644 --- a/charts/llm-d-kserve/values.yaml +++ b/charts/llm-d-kserve/values.yaml @@ -24,24 +24,27 @@ hardwareProfile: # - identifier: cpu # displayName: CPU # resourceType: CPU - # defaultCount: "4" + # defaultCount: "2" # minCount: "1" - # maxCount: "32" + # maxCount: "4" # - identifier: memory # displayName: Memory # resourceType: Memory - # defaultCount: 8Gi + # defaultCount: 4Gi # minCount: 2Gi - # maxCount: 120Gi + # maxCount: 8Gi # - identifier: nvidia.com/gpu - # displayName: nvidia.com/gpu + # displayName: Nvidia Gpu # defaultCount: 1 # minCount: 1 - # maxCount: 8 + # maxCount: 2 + # resourceType: Accelerator # -- The tolerations to apply to the hardware profile. # This value will only be used if create is set to true. tolerations: [] - + # - key: nvidia.com/gpu + # operator: Exists + # effect: NoSchedule vllm: # -- The number of replicas to run for the model server @@ -64,7 +67,7 @@ vllm: # memory: 8Gi # nvidia.com/gpu: 1 # -- The additional arguments to pass to the model server. - additionalArguments: "" + additionalArguments: "" # "--max-model-len=819" image: # -- The repository of the image to use for the model server # If not set, the default image will be used. From 3e517515a9026a0f8165db1c85b5ab82fc3bdc4f Mon Sep 17 00:00:00 2001 From: Trevor Royer Date: Mon, 30 Mar 2026 14:25:10 -0600 Subject: [PATCH 05/12] generate helm-docs --- charts/llm-d-kserve/README.md | 77 +++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 charts/llm-d-kserve/README.md diff --git a/charts/llm-d-kserve/README.md b/charts/llm-d-kserve/README.md new file mode 100644 index 0000000..99c8a3f --- /dev/null +++ b/charts/llm-d-kserve/README.md @@ -0,0 +1,77 @@ +# llm-d-kserve + +A Helm chart for deploying LLM-D with KServe on OpenShift AI + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 3.3.0](https://img.shields.io/badge/AppVersion-3.3.0-informational?style=flat-square) + +## Installing the Chart + +To access charts from this from the cli repository add it: + +```sh +helm repo add redhat-ai-services https://redhat-ai-services.github.io/helm-charts/ +helm repo update redhat-ai-services +helm upgrade -i [release-name] redhat-ai-services/llm-d-kserve +``` + +To include a chart from this repository in an umbrella chart, include it in your dependencies in your `Chart.yaml` file. + +```yaml +apiVersion: v2 +name: example-chart +description: A Helm chart for Kubernetes +type: application + +version: 0.1.0 + +appVersion: "1.16.0" + +dependencies: + - name: "llm-d-kserve" + version: "0.1.0" + repository: "https://redhat-ai-services.github.io/helm-charts/" +``` + +## Usage + +### Quick Start + +TODO + +## Configuration + +For a complete list of all configuration options, see the [Values](#values) section below. + +## Source Code + +* + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| fullnameOverride | string | `""` | String to fully override fullname template | +| hardwareProfile.create | bool | `false` | Creates a hardware profile and adds it to the release namespace. | +| hardwareProfile.identifiers | list | `[]` | The identifiers to add to the hardware profile. This value will only be used if create is set to true. | +| hardwareProfile.name | string | `"nvidia-gpu"` | The name of the hardware profile to use. If create is set to true, this value will be ignored and the hardware profile will be created with the release name. | +| hardwareProfile.namespace | string | `"redhat-ods-applications"` | The namespace of the hardware profile to use. If create is set to true, this value will be ignored and the hardware profile will be created in the release namespace. | +| hardwareProfile.tolerations | list | `[]` | The tolerations to apply to the hardware profile. This value will only be used if create is set to true. | +| imagePullSecrets | list | `[]` | The image pull secrets to use for the model server. | +| model.name | string | `"meta-llama/llama-3.2-1b-instruct"` | The name of the model to be advertised in the /v1/models endpoint | +| model.uri | string | `"oci://quay.io/redhat-ai-services/modelcar-catalog:llama-3.2-1b-instruct"` | The URI of the model to be used by the model server | +| nameOverride | string | `""` | String to partially override fullname template (will maintain the release name) | +| scheduler.endpointPickerConfig | string | `""` | | +| serviceAccount.annotations | object | `{}` | Annotations to add to the service account | +| serviceAccount.automount | bool | `true` | Automatically mount a ServiceAccount's API credentials? | +| serviceAccount.create | bool | `true` | Specifies whether a service account should be created | +| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template | +| vllm.additionalArguments | string | `""` | The additional arguments to pass to the model server. | +| vllm.image.pullPolicy | string | `"IfNotPresent"` | The pull policy of the image to use for the model server | +| vllm.image.repository | string | `""` | The repository of the image to use for the model server If not set, the default image will be used. | +| vllm.image.tag | string | `""` | The tag of the image to use for the model server | +| vllm.replicaCount | int | `1` | The number of replicas to run for the model server | +| vllm.resources | object | `{}` | The resources to apply to the model server pod. If not set, the hardware profile will add the default resources. | +| vllm.tolerations | list | `[]` | The tolerations to apply to the model server pod. If not set, the hardware profile will add the default tolerations. | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) From 0ab2081a607c70db51385413fe4ef82c4060f760 Mon Sep 17 00:00:00 2001 From: Trevor Royer Date: Wed, 1 Apr 2026 10:59:31 -0600 Subject: [PATCH 06/12] update tabbing --- .../templates/llminferenceservice.yaml | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/charts/llm-d-kserve/templates/llminferenceservice.yaml b/charts/llm-d-kserve/templates/llminferenceservice.yaml index bff42be..f0311aa 100644 --- a/charts/llm-d-kserve/templates/llminferenceservice.yaml +++ b/charts/llm-d-kserve/templates/llminferenceservice.yaml @@ -30,19 +30,19 @@ spec: {{- toYaml .Values.imagePullSecrets | nindent 6 }} {{- end }} containers: - - name: main - env: - - name: VLLM_ADDITIONAL_ARGS - value: {{ .Values.vllm.additionalArguments | quote }} - {{- if .Values.vllm.image.repository }} - image: {{ include "llm-d-kserve.image" . }} + - name: main + env: + - name: VLLM_ADDITIONAL_ARGS + value: {{ .Values.vllm.additionalArguments | quote }} + {{- if .Values.vllm.image.repository }} + image: {{ include "llm-d-kserve.image" . }} + {{- end }} + imagePullPolicy: {{ .Values.vllm.image.pullPolicy | quote }} + {{- if .Values.vllm.resources }} + resources: + {{- toYaml .Values.vllm.resources | nindent 10 }} + {{- end }} + {{- if .Values.vllm.tolerations }} + tolerations: + {{- toYaml .Values.vllm.tolerations | nindent 8 }} {{- end }} - imagePullPolicy: {{ .Values.vllm.image.pullPolicy | quote }} - {{- if .Values.vllm.resources }} - resources: - {{- toYaml .Values.vllm.resources | nindent 8 }} - {{- end }} - {{- if .Values.vllm.tolerations }} - tolerations: - {{- toYaml .Values.vllm.tolerations | nindent 6 }} - {{- end }} From 1cfb28c7ddcff3f9ea35eb1915e03b23d364ffee Mon Sep 17 00:00:00 2001 From: Trevor Royer Date: Mon, 6 Apr 2026 15:27:56 -0600 Subject: [PATCH 07/12] add default topology to screw across multiple nodes --- charts/llm-d-kserve/README.md | 1 + charts/llm-d-kserve/templates/_helpers.tpl | 13 +++++++++++++ .../templates/llminferenceservice.yaml | 16 ++++++++++++++++ charts/llm-d-kserve/values.yaml | 6 ++++++ 4 files changed, 36 insertions(+) diff --git a/charts/llm-d-kserve/README.md b/charts/llm-d-kserve/README.md index 99c8a3f..d55e92c 100644 --- a/charts/llm-d-kserve/README.md +++ b/charts/llm-d-kserve/README.md @@ -72,6 +72,7 @@ For a complete list of all configuration options, see the [Values](#values) sect | vllm.replicaCount | int | `1` | The number of replicas to run for the model server | | vllm.resources | object | `{}` | The resources to apply to the model server pod. If not set, the hardware profile will add the default resources. | | vllm.tolerations | list | `[]` | The tolerations to apply to the model server pod. If not set, the hardware profile will add the default tolerations. | +| vllm.topologySpreadConstraints | list | `[{"maxSkew":1,"topologyKey":"kubernetes.io/hostname","whenUnsatisfiable":"ScheduleAnyway"}]` | Pod topology spread constraints for the model server. If labelSelector is omitted on an item, the chart sets matchLabels app.kubernetes.io/name to llm-d-kserve.fullname. | ---------------------------------------------- Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/charts/llm-d-kserve/templates/_helpers.tpl b/charts/llm-d-kserve/templates/_helpers.tpl index db28079..b0d4d1e 100644 --- a/charts/llm-d-kserve/templates/_helpers.tpl +++ b/charts/llm-d-kserve/templates/_helpers.tpl @@ -61,6 +61,19 @@ Create the name of the service account to use {{- end }} {{- end }} +{{/* +Render a map as a compact YAML list item ("- key: …" instead of "-\n key: …"). +*/}} +{{- define "llm-d-kserve.toYamlListItem" -}} +{{- $lines := splitList "\n" (toYaml .) -}} +- {{ first $lines }} +{{- range rest $lines }} +{{- if . }} + {{ . }} +{{- end }} +{{- end }} +{{- end -}} + {{- define "llm-d-kserve.image" -}} {{- if hasPrefix "sha256:" (toString .Values.vllm.image.tag) }} {{- printf "%s@%s" .Values.vllm.image.repository .Values.vllm.image.tag }} diff --git a/charts/llm-d-kserve/templates/llminferenceservice.yaml b/charts/llm-d-kserve/templates/llminferenceservice.yaml index f0311aa..4c77d26 100644 --- a/charts/llm-d-kserve/templates/llminferenceservice.yaml +++ b/charts/llm-d-kserve/templates/llminferenceservice.yaml @@ -29,6 +29,22 @@ spec: imagePullSecrets: {{- toYaml .Values.imagePullSecrets | nindent 6 }} {{- end }} + {{- if .Values.vllm.topologySpreadConstraints }} + topologySpreadConstraints: + {{- range .Values.vllm.topologySpreadConstraints }} + {{- if hasKey . "labelSelector" }} + {{- /* Caller supplied labelSelector — render unchanged. */}} + {{- include "llm-d-kserve.toYamlListItem" . | nindent 6 }} + {{- else }} + {{- /* No labelSelector — default to matching this chart's pods. */}} + {{- $c := deepCopy . }} + {{- $defaultMatchLabels := dict "app.kubernetes.io/name" (include "llm-d-kserve.fullname" $) }} + {{- $defaultLabelSelector := dict "matchLabels" $defaultMatchLabels }} + {{- $_ := set $c "labelSelector" $defaultLabelSelector }} + {{- include "llm-d-kserve.toYamlListItem" $c | nindent 6 }} + {{- end }} + {{- end }} + {{- end }} containers: - name: main env: diff --git a/charts/llm-d-kserve/values.yaml b/charts/llm-d-kserve/values.yaml index e1c3f4a..850e33f 100644 --- a/charts/llm-d-kserve/values.yaml +++ b/charts/llm-d-kserve/values.yaml @@ -76,6 +76,12 @@ vllm: tag: "" # -- The pull policy of the image to use for the model server pullPolicy: IfNotPresent + # -- Pod topology spread constraints for the model server. + # If labelSelector is omitted on an item, the chart sets matchLabels app.kubernetes.io/name to llm-d-kserve.fullname. + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway scheduler: endpointPickerConfig: "" #|4 From d1aaae58509a21ef8ed4e2256ddc2e990c971588 Mon Sep 17 00:00:00 2001 From: Trevor Royer Date: Mon, 6 Apr 2026 15:33:44 -0600 Subject: [PATCH 08/12] update args logic --- .../llm-d-kserve/templates/llminferenceservice.yaml | 2 +- charts/llm-d-kserve/values.yaml | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/charts/llm-d-kserve/templates/llminferenceservice.yaml b/charts/llm-d-kserve/templates/llminferenceservice.yaml index 4c77d26..72ce8ad 100644 --- a/charts/llm-d-kserve/templates/llminferenceservice.yaml +++ b/charts/llm-d-kserve/templates/llminferenceservice.yaml @@ -49,7 +49,7 @@ spec: - name: main env: - name: VLLM_ADDITIONAL_ARGS - value: {{ .Values.vllm.additionalArguments | quote }} + value: {{ join " " .Values.vllm.additionalArguments | quote }} {{- if .Values.vllm.image.repository }} image: {{ include "llm-d-kserve.image" . }} {{- end }} diff --git a/charts/llm-d-kserve/values.yaml b/charts/llm-d-kserve/values.yaml index 850e33f..a4caf70 100644 --- a/charts/llm-d-kserve/values.yaml +++ b/charts/llm-d-kserve/values.yaml @@ -56,7 +56,8 @@ vllm: # operator: "Exists" # effect: "NoSchedule" # -- The resources to apply to the model server pod. - # If not set, the hardware profile will add the default resources. + # If not set, the hardware profile will add the default resources, + # however it is highly recommended to set the resources explicitly. resources: {} # requests: # cpu: 1 @@ -65,9 +66,13 @@ vllm: # limits: # cpu: 2 # memory: 8Gi - # nvidia.com/gpu: 1 + # nvidia.com/gpu: 1\ + # -- The additional arguments to pass to the model server. - additionalArguments: "" # "--max-model-len=819" + additionalArguments: [] + # - "--max-model-len=8192" + # - "--gpu-memory-utilization=0.90" + image: # -- The repository of the image to use for the model server # If not set, the default image will be used. From cd13c385f4fb64e180679eabfc97db2a02a48c52 Mon Sep 17 00:00:00 2001 From: Trevor Royer Date: Mon, 6 Apr 2026 15:37:26 -0600 Subject: [PATCH 09/12] add helm labels --- charts/llm-d-kserve/templates/hardwareprofile.yaml | 2 ++ charts/llm-d-kserve/templates/llminferenceservice.yaml | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/charts/llm-d-kserve/templates/hardwareprofile.yaml b/charts/llm-d-kserve/templates/hardwareprofile.yaml index fe34d12..7f99892 100644 --- a/charts/llm-d-kserve/templates/hardwareprofile.yaml +++ b/charts/llm-d-kserve/templates/hardwareprofile.yaml @@ -7,6 +7,8 @@ metadata: opendatahub.io/description: "" opendatahub.io/disabled: "false" opendatahub.io/display-name: {{ include "llm-d-kserve.fullname" . | quote }} + labels: + {{- include "llm-d-kserve.labels" . | nindent 4 }} name: {{ include "llm-d-kserve.fullname" . }} spec: identifiers: diff --git a/charts/llm-d-kserve/templates/llminferenceservice.yaml b/charts/llm-d-kserve/templates/llminferenceservice.yaml index 72ce8ad..3fcaf8f 100644 --- a/charts/llm-d-kserve/templates/llminferenceservice.yaml +++ b/charts/llm-d-kserve/templates/llminferenceservice.yaml @@ -11,8 +11,9 @@ metadata: {{- end }} opendatahub.io/model-type: generative openshift.io/display-name: {{ .Values.model.name | quote }} - security.opendatahub.io/enable-auth: "false" + security.opendatahub.io/enable-auth: {{ .Values.auth.enabled | quote }} labels: + {{- include "llm-d-kserve.labels" . | nindent 4 }} opendatahub.io/dashboard: "true" name: {{ include "llm-d-kserve.fullname" . }} spec: From 3dfb54f5eacac02dd38fe0a254419454c6ec6f6d Mon Sep 17 00:00:00 2001 From: Trevor Royer Date: Mon, 6 Apr 2026 15:37:46 -0600 Subject: [PATCH 10/12] add role to enable auth --- charts/llm-d-kserve/templates/role.yaml | 17 +++++++++++++++++ charts/llm-d-kserve/values.yaml | 14 ++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 charts/llm-d-kserve/templates/role.yaml diff --git a/charts/llm-d-kserve/templates/role.yaml b/charts/llm-d-kserve/templates/role.yaml new file mode 100644 index 0000000..5d94d7b --- /dev/null +++ b/charts/llm-d-kserve/templates/role.yaml @@ -0,0 +1,17 @@ +{{- if .Values.auth.enabled }} +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "llm-d-kserve.fullname" . }}-view + labels: + {{- include "llm-d-kserve.labels" $ | nindent 4 }} +rules: + - verbs: + - get + apiGroups: + - serving.kserve.io + resources: + - inferenceservices + resourceNames: + - {{ include "llm-d-kserve.inferenceServiceName" . }} +{{- end }} diff --git a/charts/llm-d-kserve/values.yaml b/charts/llm-d-kserve/values.yaml index a4caf70..e60baab 100644 --- a/charts/llm-d-kserve/values.yaml +++ b/charts/llm-d-kserve/values.yaml @@ -9,6 +9,20 @@ model: # -- The URI of the model to be used by the model server uri: oci://quay.io/redhat-ai-services/modelcar-catalog:llama-3.2-1b-instruct +auth: + # -- Secures the model endpoint and creates a role to grant permissions to service accounts + enabled: false + # -- Creates service accounts with permissions to access the secured endpoint + serviceAccounts: [] + # - name: my-service-account + # - name: existing-service-account + # create: false + # - name: existing-in-another-namespace + # namespace: my-other-namespace + # create: false + # - name: no-token + # createLegacyToken: false + hardwareProfile: # -- Creates a hardware profile and adds it to the release namespace. create: false From 043a873e22531bc7dcd1eaf099f0d85f533f763c Mon Sep 17 00:00:00 2001 From: Trevor Royer Date: Mon, 6 Apr 2026 15:45:21 -0600 Subject: [PATCH 11/12] add ability to provision auth to service accounts --- charts/llm-d-kserve/templates/auth-rbac.yaml | 44 +++++++++++++++++++ .../templates/{role.yaml => auth-role.yaml} | 0 charts/llm-d-kserve/values.yaml | 3 ++ 3 files changed, 47 insertions(+) create mode 100644 charts/llm-d-kserve/templates/auth-rbac.yaml rename charts/llm-d-kserve/templates/{role.yaml => auth-role.yaml} (100%) diff --git a/charts/llm-d-kserve/templates/auth-rbac.yaml b/charts/llm-d-kserve/templates/auth-rbac.yaml new file mode 100644 index 0000000..d6a00eb --- /dev/null +++ b/charts/llm-d-kserve/templates/auth-rbac.yaml @@ -0,0 +1,44 @@ +{{- if .Values.auth.enabled }} +{{- range $serviceAccount := .Values.auth.serviceAccounts }} +{{- if list nil true | has $serviceAccount.create }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ $serviceAccount.name }} + namespace: {{ $serviceAccount.namespace | default $.Release.Namespace }} + labels: + {{- include "llm-d-kserve.labels" $ | nindent 4 }} +{{ if list nil true | has $serviceAccount.createLegacyToken }} +secrets: + - name: {{ $serviceAccount.name }} +{{- end }} +automountServiceAccountToken: true +{{ if list nil true | has $serviceAccount.createLegacyToken }} +--- +apiVersion: v1 +kind: Secret +type: kubernetes.io/service-account-token +metadata: + name: {{ $serviceAccount.name }} + annotations: + kubernetes.io/service-account.name: {{ $serviceAccount.name }} +{{- end }} +{{- end }} +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "llm-d-kserve.fullname" $ }}-{{ $serviceAccount.name }}-view + labels: + {{- include "llm-d-kserve.labels" $ | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ $serviceAccount.name }} + namespace: {{ $serviceAccount.namespace | default $.Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "llm-d-kserve.fullname" $ }}-view +{{- end }} +{{- end }} diff --git a/charts/llm-d-kserve/templates/role.yaml b/charts/llm-d-kserve/templates/auth-role.yaml similarity index 100% rename from charts/llm-d-kserve/templates/role.yaml rename to charts/llm-d-kserve/templates/auth-role.yaml diff --git a/charts/llm-d-kserve/values.yaml b/charts/llm-d-kserve/values.yaml index e60baab..0c0a9e3 100644 --- a/charts/llm-d-kserve/values.yaml +++ b/charts/llm-d-kserve/values.yaml @@ -13,6 +13,9 @@ auth: # -- Secures the model endpoint and creates a role to grant permissions to service accounts enabled: false # -- Creates service accounts with permissions to access the secured endpoint + # If create not set, assume true; if create is set to false, the service account must already exist. + # If createLegacyToken not set, assume true; if createLegacyToken is true, a static token will be created for the service account. + # If namespace not set, assume the release namespace. serviceAccounts: [] # - name: my-service-account # - name: existing-service-account From 344f93f303bf041d15518202cd0359a4825f65bc Mon Sep 17 00:00:00 2001 From: Trevor Royer Date: Sat, 25 Apr 2026 16:47:41 -0600 Subject: [PATCH 12/12] remove trailing space --- charts/llm-d-kserve/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/llm-d-kserve/values.yaml b/charts/llm-d-kserve/values.yaml index 0c0a9e3..e5275ed 100644 --- a/charts/llm-d-kserve/values.yaml +++ b/charts/llm-d-kserve/values.yaml @@ -73,7 +73,7 @@ vllm: # operator: "Exists" # effect: "NoSchedule" # -- The resources to apply to the model server pod. - # If not set, the hardware profile will add the default resources, + # If not set, the hardware profile will add the default resources, # however it is highly recommended to set the resources explicitly. resources: {} # requests: