Skip to content

Commit a7703e7

Browse files
committed
fix: Added existingSecret support and ingress to Helm chart
Signed-off-by: Aditya Patil <adityapatil7649@gmail.com>
1 parent 0038ad4 commit a7703e7

9 files changed

Lines changed: 251 additions & 48 deletions

File tree

infra/charts/feast/README.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,16 @@ helm repo add feast-charts https://feast-helm-charts.storage.googleapis.com
2121
helm repo update
2222
```
2323

24-
Install Feast
24+
Install Feast (using an existing Secret — recommended for production):
25+
```
26+
kubectl create secret generic my-feast-config \
27+
--from-literal=feature_store_yaml_base64=$(base64 < feature_store.yaml)
28+
29+
helm install feast-release feast-charts/feast \
30+
--set feature-server.existingSecret=my-feast-config
31+
```
32+
33+
Or pass the config inline (the value will be stored in Helm release metadata):
2534
```
2635
helm install feast-release feast-charts/feast \
2736
--set feature-server.feature_store_yaml_base64=$(base64 < feature_store.yaml)
@@ -31,13 +40,18 @@ helm install feast-release feast-charts/feast \
3140

3241
This Feast chart comes with a [values.yaml](values.yaml) that allows for configuration and customization of all sub-charts.
3342

34-
The feature server requires a base64-encoded `feature_store.yaml` to be provided:
43+
The feature server requires a base64-encoded `feature_store.yaml`. You can either reference a pre-existing Kubernetes Secret or provide the value inline:
3544

3645
```yaml
3746
feature-server:
47+
# Option A: reference an existing Secret (recommended)
48+
existingSecret: my-feast-config
49+
# Option B: provide inline (stored in Helm release metadata)
3850
feature_store_yaml_base64: <base64 encoded feature_store.yaml>
3951
```
4052
53+
> **Upgrading from the Java chart?** See the [feature-server migration guide](charts/feature-server/README.md#migration-from-java-chart) for details on removed gRPC and Java-specific values.
54+
4155
For more details, please see: https://docs.feast.dev/how-to-guides/running-feast-in-production
4256
4357
## Requirements

infra/charts/feast/charts/feature-server/README.md

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,112 @@ Feast Feature Server: Online feature serving service for Feast
66

77
**Homepage:** <https://github.com/feast-dev/feast>
88

9+
## Migration from Java chart
10+
11+
This chart now deploys the Python-based feature server instead of the Java-based one.
12+
13+
### Removed values (no equivalent)
14+
15+
These Java-specific values have been removed. Delete them from your values files:
16+
17+
| Removed value | Reason |
18+
|---|---|
19+
| `javaOpts` | Python server, no JVM |
20+
| `logType`, `logLevel` | Java logging config, not applicable |
21+
| `transformationService.*` | Use the separate `transformation-service` subchart |
22+
| `application.yaml`, `application-generated.yaml` | Java Spring config, not applicable |
23+
| `application-override.yaml` | Replaced by `feature_store_yaml_base64` or `existingSecret` |
24+
| `application-secret.yaml` | Replaced by `feature_store_yaml_base64` or `existingSecret` |
25+
26+
### Changed values
27+
28+
| Old value | New value | Notes |
29+
|---|---|---|
30+
| `service.grpc.port` | `service.port` | Protocol changed from gRPC to HTTP |
31+
| `service.grpc.targetPort` | (removed) | `targetPort` now uses the named port `http` |
32+
| `service.grpc.nodePort` | `service.nodePort` | Flat key |
33+
| `ingress.grpc.*` | (removed) | Python server is HTTP-only |
34+
| `ingress.http.class` | (removed) | Use `ingress.http.ingressClassName` or `annotations` |
35+
| `ingress.http.auth.*` | (removed) | Use `ingress.http.annotations` for controller-specific auth |
36+
| `ingress.http.whitelist` | (removed) | Use `ingress.http.annotations` for controller-specific whitelist |
37+
| `image.repository` | `image.repository` | Changed from `feature-server-java` to `feature-server` |
38+
39+
### New values
40+
41+
| Value | Purpose |
42+
|---|---|
43+
| `feature_store_yaml_base64` | Base64-encoded `feature_store.yaml`, stored in a K8s Secret |
44+
| `existingSecret` | Reference a pre-created Secret instead of providing inline config |
45+
| `ingress.http.ingressClassName` | Support for `networking.k8s.io/v1` IngressClass |
46+
47+
### What still works
48+
49+
- `ingress.http.*` — same value structure, now uses `networking.k8s.io/v1`
50+
- `secrets` — volume mounts for additional Kubernetes secrets
51+
- `envOverrides` — extra environment variables
52+
- `service.type`, `service.loadBalancerIP`, `service.loadBalancerSourceRanges`
53+
- All probe settings (`livenessProbe.*`, `readinessProbe.*`)
54+
55+
## Installation
56+
57+
### Option A: Using an existing Secret (recommended)
58+
59+
Create the Secret outside of Helm so that credentials never pass through Helm values or release metadata:
60+
61+
```bash
62+
kubectl create secret generic my-feast-config \
63+
--from-literal=feature_store_yaml_base64=$(base64 < feature_store.yaml)
64+
```
65+
66+
Then reference it during install:
67+
```bash
68+
helm install feast-feature-server . --set existingSecret=my-feast-config
69+
```
70+
71+
This is the recommended approach when `feature_store.yaml` contains registry or online-store credentials.
72+
73+
### Option B: Using inline config
74+
75+
```bash
76+
helm install feast-feature-server . --set feature_store_yaml_base64=$(base64 < feature_store.yaml)
77+
```
78+
79+
> **Note:** When using `--set`, the base64-encoded config is stored in Helm release metadata and is retrievable via `helm get values`. Use Option A or an external secrets operator (e.g. SealedSecrets, ExternalSecrets) for production deployments with sensitive credentials.
80+
981
## Values
1082

1183
| Key | Type | Default | Description |
1284
|-----|------|---------|-------------|
1385
| envOverrides | object | `{}` | Extra environment variables to set |
14-
| feature_store_yaml_base64 | string | `""` | [required] a base64 encoded version of feature_store.yaml |
86+
| existingSecret | string | `""` | Name of an existing Secret containing key `feature_store_yaml_base64` with base64-encoded config |
87+
| feature_store_yaml_base64 | string | `""` | [required] a base64 encoded version of feature_store.yaml (stored in a K8s Secret) |
1588
| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy |
1689
| image.repository | string | `"quay.io/feastdev/feature-server"` | Docker image for Feature Server repository |
17-
| image.tag | string | `"0.64.0"` | Image tag |
90+
| image.tag | string | `"0.65.0"` | Image tag |
91+
| ingress.http.annotations | object | `{}` | Extra annotations for the ingress (use for controller-specific settings) |
92+
| ingress.http.enabled | bool | `false` | Flag to create an ingress resource for the service |
93+
| ingress.http.hosts | list | `[]` | List of hostnames to match when routing requests |
94+
| ingress.http.https.enabled | bool | `true` | Flag to enable HTTPS |
95+
| ingress.http.https.secretNames | object | `{}` | Map of hostname to TLS secret name |
96+
| ingress.http.ingressClassName | string | `nil` | IngressClass resource name |
1897
| livenessProbe.enabled | bool | `true` | Flag to enabled the probe |
1998
| livenessProbe.failureThreshold | int | `5` | Min consecutive failures for the probe to be considered failed |
20-
| livenessProbe.initialDelaySeconds | int | `30` | Delay before the probe is initiated |
21-
| livenessProbe.periodSeconds | int | `30` | How often to perform the probe |
99+
| livenessProbe.initialDelaySeconds | int | `60` | Delay before the probe is initiated |
100+
| livenessProbe.periodSeconds | int | `10` | How often to perform the probe |
101+
| livenessProbe.successThreshold | int | `1` | Min consecutive success for the probe to be considered successful |
22102
| livenessProbe.timeoutSeconds | int | `5` | When the probe times out |
23103
| nodeSelector | object | `{}` | Node labels for pod assignment |
24-
| podAnnotations | object | `{}` | Annotations to be added to Feast Feature Server pods |
25-
| podLabels | object | `{}` | Labels to be added to Feast Feature Server pods |
104+
| podAnnotations | object | `{}` | Annotations to be added to Feature Server pods |
105+
| podLabels | object | `{}` | Labels to be added to Feature Server pods |
26106
| readinessProbe.enabled | bool | `true` | Flag to enabled the probe |
27107
| readinessProbe.failureThreshold | int | `5` | Min consecutive failures for the probe to be considered failed |
28108
| readinessProbe.initialDelaySeconds | int | `15` | Delay before the probe is initiated |
29109
| readinessProbe.periodSeconds | int | `10` | How often to perform the probe |
110+
| readinessProbe.successThreshold | int | `1` | Min consecutive success for the probe to be considered successful |
30111
| readinessProbe.timeoutSeconds | int | `10` | When the probe times out |
31112
| replicaCount | int | `1` | Number of pods that will be created |
32113
| resources | object | `{}` | CPU/memory [resource requests/limit](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container) |
114+
| secrets | list | `[]` | List of Kubernetes secrets to be mounted on /etc/secrets/\<secret name\> |
33115
| service.loadBalancerIP | string | `nil` | Specify a load balancer IP if service type is LoadBalancer |
34116
| service.loadBalancerSourceRanges | list | `[]` | Optionally restrict load balancer traffic to specified IPs |
35117
| service.nodePort | string | `nil` | Port number that each cluster node will listen to |
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
Feast Feature Server has been deployed.
2+
3+
{{- if and (not .Values.existingSecret) (not .Values.feature_store_yaml_base64) }}
4+
5+
WARNING: Neither "existingSecret" nor "feature_store_yaml_base64" is set.
6+
The feature server will fail to start without a valid feature_store.yaml.
7+
8+
Option A (recommended): create a Secret and reference it:
9+
kubectl create secret generic my-feast-config \
10+
--from-literal=feature_store_yaml_base64=$(base64 < feature_store.yaml)
11+
helm upgrade {{ .Release.Name }} . --set existingSecret=my-feast-config
12+
13+
Option B: provide inline:
14+
helm upgrade {{ .Release.Name }} . \
15+
--set feature_store_yaml_base64=$(base64 < feature_store.yaml)
16+
{{- end }}
17+
18+
{{- if and .Values.feature_store_yaml_base64 (not .Values.existingSecret) }}
19+
20+
NOTE: "feature_store_yaml_base64" is stored in Helm release metadata.
21+
If feature_store.yaml contains credentials, consider using "existingSecret"
22+
to keep sensitive data out of Helm values.
23+
{{- end }}

infra/charts/feast/charts/feature-server/templates/_helpers.tpl

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,21 @@ If release name contains chart name it will be used as a full name.
2525
{{- end -}}
2626

2727
{{/*
28-
Create chart name and version as used by the chart label.
28+
Common labels
2929
*/}}
30-
{{- define "feature-server.chart" -}}
31-
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
30+
{{- define "feature-server.labels" -}}
31+
app: {{ include "feature-server.name" . }}
32+
component: serving
33+
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
34+
release: {{ .Release.Name }}
35+
heritage: {{ .Release.Service }}
3236
{{- end -}}
3337

3438
{{/*
35-
Common labels
39+
Selector labels
3640
*/}}
37-
{{- define "feature-server.labels" -}}
38-
app.kubernetes.io/name: {{ include "feature-server.name" . }}
39-
helm.sh/chart: {{ include "feature-server.chart" . }}
40-
app.kubernetes.io/instance: {{ .Release.Name }}
41-
{{- if .Chart.AppVersion }}
42-
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
43-
{{- end }}
44-
app.kubernetes.io/managed-by: {{ .Release.Service }}
41+
{{- define "feature-server.selectorLabels" -}}
42+
app: {{ include "feature-server.name" . }}
43+
component: serving
44+
release: {{ .Release.Name }}
4545
{{- end -}}

infra/charts/feast/charts/feature-server/templates/deployment.yaml

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,45 +4,54 @@ metadata:
44
name: {{ template "feature-server.fullname" . }}
55
namespace: {{ .Release.Namespace }}
66
labels:
7-
app: {{ template "feature-server.name" . }}
8-
component: serving
9-
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
10-
release: {{ .Release.Name }}
11-
heritage: {{ .Release.Service }}
7+
{{- include "feature-server.labels" . | nindent 4 }}
128
spec:
139
replicas: {{ .Values.replicaCount }}
1410
selector:
1511
matchLabels:
16-
app: {{ template "feature-server.name" . }}
17-
component: serving
18-
release: {{ .Release.Name }}
12+
{{- include "feature-server.selectorLabels" . | nindent 6 }}
1913
template:
2014
metadata:
21-
{{- if .Values.podAnnotations }}
15+
{{- if or (not .Values.existingSecret) .Values.podAnnotations }}
2216
annotations:
23-
{{ toYaml .Values.podAnnotations | nindent 8 }}
17+
{{- if not .Values.existingSecret }}
18+
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
19+
{{- end }}
20+
{{- with .Values.podAnnotations }}
21+
{{- toYaml . | nindent 8 }}
22+
{{- end }}
2423
{{- end }}
2524
labels:
26-
app: {{ template "feature-server.name" . }}
27-
component: serving
28-
release: {{ .Release.Name }}
25+
{{- include "feature-server.selectorLabels" . | nindent 8 }}
2926
{{- if .Values.podLabels }}
30-
{{ toYaml .Values.podLabels | nindent 8 }}
27+
{{- toYaml .Values.podLabels | nindent 8 }}
3128
{{- end }}
3229
spec:
3330
{{- with .Values.nodeSelector }}
3431
nodeSelector:
3532
{{- toYaml . | nindent 8 }}
3633
{{- end }}
3734

35+
{{- if .Values.secrets }}
36+
volumes:
37+
{{- range $secret := .Values.secrets }}
38+
- name: {{ $secret }}
39+
secret:
40+
secretName: {{ $secret }}
41+
{{- end }}
42+
{{- end }}
43+
3844
containers:
3945
- name: {{ .Chart.Name }}
4046
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
4147
imagePullPolicy: {{ .Values.image.pullPolicy }}
4248

4349
env:
4450
- name: FEATURE_STORE_YAML_BASE64
45-
value: {{ .Values.feature_store_yaml_base64 }}
51+
valueFrom:
52+
secretKeyRef:
53+
name: {{ .Values.existingSecret | default (include "feature-server.fullname" .) }}
54+
key: feature_store_yaml_base64
4655

4756
{{- range $key, $value := .Values.envOverrides }}
4857
- name: {{ printf "%s" $key | replace "." "_" | upper | quote }}
@@ -67,13 +76,23 @@ spec:
6776
containerPort: {{ .Values.service.port }}
6877
protocol: TCP
6978

79+
{{- if .Values.secrets }}
80+
volumeMounts:
81+
{{- range $secret := .Values.secrets }}
82+
- name: {{ $secret }}
83+
mountPath: "/etc/secrets/{{ $secret }}"
84+
readOnly: true
85+
{{- end }}
86+
{{- end }}
87+
7088
{{- if .Values.livenessProbe.enabled }}
7189
livenessProbe:
7290
tcpSocket:
7391
port: http
7492
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
7593
periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
7694
timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }}
95+
successThreshold: {{ .Values.livenessProbe.successThreshold }}
7796
failureThreshold: {{ .Values.livenessProbe.failureThreshold }}
7897
{{- end }}
7998

@@ -84,6 +103,7 @@ spec:
84103
initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
85104
periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
86105
timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }}
106+
successThreshold: {{ .Values.readinessProbe.successThreshold }}
87107
failureThreshold: {{ .Values.readinessProbe.failureThreshold }}
88108
{{- end }}
89109

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{{- if .Values.ingress.http.enabled }}
2+
apiVersion: networking.k8s.io/v1
3+
kind: Ingress
4+
metadata:
5+
name: {{ template "feature-server.fullname" . }}
6+
namespace: {{ .Release.Namespace }}
7+
labels:
8+
{{- include "feature-server.labels" . | nindent 4 }}
9+
{{- with .Values.ingress.http.annotations }}
10+
annotations:
11+
{{- toYaml . | nindent 4 }}
12+
{{- end }}
13+
spec:
14+
{{- if .Values.ingress.http.ingressClassName }}
15+
ingressClassName: {{ .Values.ingress.http.ingressClassName }}
16+
{{- end }}
17+
rules:
18+
{{- range $host := .Values.ingress.http.hosts }}
19+
- host: {{ $host }}
20+
http:
21+
paths:
22+
- path: /
23+
pathType: Prefix
24+
backend:
25+
service:
26+
name: {{ include "feature-server.fullname" $ }}
27+
port:
28+
name: http
29+
{{- end }}
30+
{{- if .Values.ingress.http.https.enabled }}
31+
tls:
32+
{{- range $host := .Values.ingress.http.hosts }}
33+
- secretName: {{ index $.Values.ingress.http.https.secretNames $host | default (printf "%s-tls" (splitList "." $host | rest | join "-")) }}
34+
hosts:
35+
- {{ $host }}
36+
{{- end }}
37+
{{- end }}
38+
{{- end }}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{{- if not .Values.existingSecret }}
2+
apiVersion: v1
3+
kind: Secret
4+
metadata:
5+
name: {{ template "feature-server.fullname" . }}
6+
namespace: {{ .Release.Namespace }}
7+
labels:
8+
{{- include "feature-server.labels" . | nindent 4 }}
9+
type: Opaque
10+
stringData:
11+
feature_store_yaml_base64: {{ .Values.feature_store_yaml_base64 | quote }}
12+
{{- end }}

0 commit comments

Comments
 (0)