GeezerVison is showing github.com — feat: Added support for enabling metrics in Feast Operator (#5317) · jfw-ppi/feast@ce374c6
File treeExpand file treeCollapse file tree Open diff view settings internal/controller/services
Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: infra/feast-operator/api/v1alpha1/featurestore_types.go +2Lines changed: 2 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -504,6 +504,8 @@ type ServerConfigs struct { | 504 | 504 | // Allowed values: "debug", "info", "warning", "error", "critical". | 505 | 505 | // +kubebuilder:validation:Enum=debug;info;warning;error;critical | 506 | 506 | LogLevel *string `json:"logLevel,omitempty"` | | 507 | + // Metrics exposes Prometheus-compatible metrics for the Feast server when enabled. | | 508 | + Metrics *bool `json:"metrics,omitempty"` | 507 | 509 | // VolumeMounts defines the list of volumes that should be mounted into the feast container. | 508 | 510 | // This allows attaching persistent storage, config files, secrets, or other resources | 509 | 511 | // required by the Feast components. Ensure that each volume mount has a corresponding | |
Collapse file Copy file name to clipboardExpand all lines: infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go +5Lines changed: 5 additions & 0 deletions Collapse file Copy file name to clipboardExpand all lines: infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml +32Lines changed: 32 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -1041,6 +1041,10 @@ spec: | 1041 | 1041 | - error | 1042 | 1042 | - critical | 1043 | 1043 | type: string | | 1044 | + metrics: | | 1045 | + description: Metrics exposes Prometheus-compatible metrics | | 1046 | + for the Feast server when enabled. | | 1047 | + type: boolean | 1044 | 1048 | nodeSelector: | 1045 | 1049 | additionalProperties: | 1046 | 1050 | type: string | @@ -1500,6 +1504,10 @@ spec: | 1500 | 1504 | - error | 1501 | 1505 | - critical | 1502 | 1506 | type: string | | 1507 | + metrics: | | 1508 | + description: Metrics exposes Prometheus-compatible metrics | | 1509 | + for the Feast server when enabled. | | 1510 | + type: boolean | 1503 | 1511 | nodeSelector: | 1504 | 1512 | additionalProperties: | 1505 | 1513 | type: string | @@ -1970,6 +1978,10 @@ spec: | 1970 | 1978 | - error | 1971 | 1979 | - critical | 1972 | 1980 | type: string | | 1981 | + metrics: | | 1982 | + description: Metrics exposes Prometheus-compatible | | 1983 | + metrics for the Feast server when enabled. | | 1984 | + type: boolean | 1973 | 1985 | nodeSelector: | 1974 | 1986 | additionalProperties: | 1975 | 1987 | type: string | @@ -2469,6 +2481,10 @@ spec: | 2469 | 2481 | - error | 2470 | 2482 | - critical | 2471 | 2483 | type: string | | 2484 | + metrics: | | 2485 | + description: Metrics exposes Prometheus-compatible metrics | | 2486 | + for the Feast server when enabled. | | 2487 | + type: boolean | 2472 | 2488 | nodeSelector: | 2473 | 2489 | additionalProperties: | 2474 | 2490 | type: string | @@ -5042,6 +5058,10 @@ spec: | 5042 | 5058 | - error | 5043 | 5059 | - critical | 5044 | 5060 | type: string | | 5061 | + metrics: | | 5062 | + description: Metrics exposes Prometheus-compatible | | 5063 | + metrics for the Feast server when enabled. | | 5064 | + type: boolean | 5045 | 5065 | nodeSelector: | 5046 | 5066 | additionalProperties: | 5047 | 5067 | type: string | @@ -5509,6 +5529,10 @@ spec: | 5509 | 5529 | - error | 5510 | 5530 | - critical | 5511 | 5531 | type: string | | 5532 | + metrics: | | 5533 | + description: Metrics exposes Prometheus-compatible | | 5534 | + metrics for the Feast server when enabled. | | 5535 | + type: boolean | 5512 | 5536 | nodeSelector: | 5513 | 5537 | additionalProperties: | 5514 | 5538 | type: string | @@ -5991,6 +6015,10 @@ spec: | 5991 | 6015 | - error | 5992 | 6016 | - critical | 5993 | 6017 | type: string | | 6018 | + metrics: | | 6019 | + description: Metrics exposes Prometheus-compatible | | 6020 | + metrics for the Feast server when enabled. | | 6021 | + type: boolean | 5994 | 6022 | nodeSelector: | 5995 | 6023 | additionalProperties: | 5996 | 6024 | type: string | @@ -6500,6 +6528,10 @@ spec: | 6500 | 6528 | - error | 6501 | 6529 | - critical | 6502 | 6530 | type: string | | 6531 | + metrics: | | 6532 | + description: Metrics exposes Prometheus-compatible metrics | | 6533 | + for the Feast server when enabled. | | 6534 | + type: boolean | 6503 | 6535 | nodeSelector: | 6504 | 6536 | additionalProperties: | 6505 | 6537 | type: string | |
Collapse file Copy file name to clipboardExpand all lines: infra/feast-operator/dist/install.yaml +32Lines changed: 32 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -1049,6 +1049,10 @@ spec: | 1049 | 1049 | - error | 1050 | 1050 | - critical | 1051 | 1051 | type: string | | 1052 | + metrics: | | 1053 | + description: Metrics exposes Prometheus-compatible metrics | | 1054 | + for the Feast server when enabled. | | 1055 | + type: boolean | 1052 | 1056 | nodeSelector: | 1053 | 1057 | additionalProperties: | 1054 | 1058 | type: string | @@ -1508,6 +1512,10 @@ spec: | 1508 | 1512 | - error | 1509 | 1513 | - critical | 1510 | 1514 | type: string | | 1515 | + metrics: | | 1516 | + description: Metrics exposes Prometheus-compatible metrics | | 1517 | + for the Feast server when enabled. | | 1518 | + type: boolean | 1511 | 1519 | nodeSelector: | 1512 | 1520 | additionalProperties: | 1513 | 1521 | type: string | @@ -1978,6 +1986,10 @@ spec: | 1978 | 1986 | - error | 1979 | 1987 | - critical | 1980 | 1988 | type: string | | 1989 | + metrics: | | 1990 | + description: Metrics exposes Prometheus-compatible | | 1991 | + metrics for the Feast server when enabled. | | 1992 | + type: boolean | 1981 | 1993 | nodeSelector: | 1982 | 1994 | additionalProperties: | 1983 | 1995 | type: string | @@ -2477,6 +2489,10 @@ spec: | 2477 | 2489 | - error | 2478 | 2490 | - critical | 2479 | 2491 | type: string | | 2492 | + metrics: | | 2493 | + description: Metrics exposes Prometheus-compatible metrics | | 2494 | + for the Feast server when enabled. | | 2495 | + type: boolean | 2480 | 2496 | nodeSelector: | 2481 | 2497 | additionalProperties: | 2482 | 2498 | type: string | @@ -5050,6 +5066,10 @@ spec: | 5050 | 5066 | - error | 5051 | 5067 | - critical | 5052 | 5068 | type: string | | 5069 | + metrics: | | 5070 | + description: Metrics exposes Prometheus-compatible | | 5071 | + metrics for the Feast server when enabled. | | 5072 | + type: boolean | 5053 | 5073 | nodeSelector: | 5054 | 5074 | additionalProperties: | 5055 | 5075 | type: string | @@ -5517,6 +5537,10 @@ spec: | 5517 | 5537 | - error | 5518 | 5538 | - critical | 5519 | 5539 | type: string | | 5540 | + metrics: | | 5541 | + description: Metrics exposes Prometheus-compatible | | 5542 | + metrics for the Feast server when enabled. | | 5543 | + type: boolean | 5520 | 5544 | nodeSelector: | 5521 | 5545 | additionalProperties: | 5522 | 5546 | type: string | @@ -5999,6 +6023,10 @@ spec: | 5999 | 6023 | - error | 6000 | 6024 | - critical | 6001 | 6025 | type: string | | 6026 | + metrics: | | 6027 | + description: Metrics exposes Prometheus-compatible | | 6028 | + metrics for the Feast server when enabled. | | 6029 | + type: boolean | 6002 | 6030 | nodeSelector: | 6003 | 6031 | additionalProperties: | 6004 | 6032 | type: string | @@ -6508,6 +6536,10 @@ spec: | 6508 | 6536 | - error | 6509 | 6537 | - critical | 6510 | 6538 | type: string | | 6539 | + metrics: | | 6540 | + description: Metrics exposes Prometheus-compatible metrics | | 6541 | + for the Feast server when enabled. | | 6542 | + type: boolean | 6511 | 6543 | nodeSelector: | 6512 | 6544 | additionalProperties: | 6513 | 6545 | type: string | |
Collapse file Copy file name to clipboardExpand all lines: infra/feast-operator/docs/api/markdown/ref.md +1Lines changed: 1 addition & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -762,6 +762,7 @@ _Appears in:_ | 762 | 762 | | `tls` _[TlsConfigs](#tlsconfigs)_ | | | 763 | 763 | | `logLevel` _string_ | LogLevel sets the logging level for the server | 764 | 764 | Allowed values: "debug", "info", "warning", "error", "critical". | | | 765 | +| `metrics` _boolean_ | Metrics exposes Prometheus-compatible metrics for the Feast server when enabled. | | 765 | 766 | | `volumeMounts` _[VolumeMount](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#volumemount-v1-core) array_ | VolumeMounts defines the list of volumes that should be mounted into the feast container. | 766 | 767 | This allows attaching persistent storage, config files, secrets, or other resources | 767 | 768 | required by the Feast components. Ensure that each volume mount has a corresponding | |
Collapse file Copy file name to clipboardExpand all lines: infra/feast-operator/internal/controller/services/services.go +35-1Lines changed: 35 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -470,6 +470,13 @@ func (feast *FeastServices) setContainer(containers *[]corev1.Container, feastTy | 470 | 470 | ContainerPort: getTargetPort(feastType, tls), | 471 | 471 | Protocol: corev1.ProtocolTCP, | 472 | 472 | }) | | 473 | + if feastType == OnlineFeastType && feast.isMetricsEnabled(feastType) { | | 474 | + container.Ports = append(container.Ports, corev1.ContainerPort{ | | 475 | + Name: "metrics", | | 476 | + ContainerPort: MetricsPort, | | 477 | + Protocol: corev1.ProtocolTCP, | | 478 | + }) | | 479 | + } | 473 | 480 | } | 474 | 481 |
| 475 | 482 | container.StartupProbe = &corev1.Probe{ | @@ -565,6 +572,10 @@ func (feast *FeastServices) getContainerCommand(feastType FeastServiceType) []st | 565 | 572 | } | 566 | 573 |
| 567 | 574 | deploySettings := FeastServiceConstants[feastType] | | 575 | + deploySettings.Args = append([]string{}, deploySettings.Args...) | | 576 | + if feastType == OnlineFeastType && feast.isMetricsEnabled(feastType) { | | 577 | + deploySettings.Args = append([]string{deploySettings.Args[0], "--metrics"}, deploySettings.Args[1:]...) | | 578 | + } | 568 | 579 | targetPort := deploySettings.TargetHttpPort | 569 | 580 | tls := feast.getTlsConfigs(feastType) | 570 | 581 |
| @@ -732,6 +743,15 @@ func (feast *FeastServices) setService(svc *corev1.Service, feastType FeastServi | 732 | 743 | }, | 733 | 744 | } | 734 | 745 |
| | 746 | + if feastType == OnlineFeastType && feast.isMetricsEnabled(feastType) { | | 747 | + svc.Spec.Ports = append(svc.Spec.Ports, corev1.ServicePort{ | | 748 | + Name: "metrics", | | 749 | + Port: MetricsPort, | | 750 | + Protocol: corev1.ProtocolTCP, | | 751 | + TargetPort: intstr.FromInt(int(MetricsPort)), | | 752 | + }) | | 753 | + } | | 754 | + | 735 | 755 | return controllerutil.SetControllerReference(feast.Handler.FeatureStore, svc, feast.Handler.Scheme) | 736 | 756 | } | 737 | 757 |
| @@ -800,6 +820,18 @@ func (feast *FeastServices) getLogLevelForType(feastType FeastServiceType) *stri | 800 | 820 | return nil | 801 | 821 | } | 802 | 822 |
| | 823 | +func (feast *FeastServices) isMetricsEnabled(feastType FeastServiceType) bool { | | 824 | + if feastType != OnlineFeastType { | | 825 | + return false | | 826 | + } | | 827 | + | | 828 | + if serviceConfigs := feast.getServerConfigs(feastType); serviceConfigs != nil && serviceConfigs.Metrics != nil { | | 829 | + return *serviceConfigs.Metrics | | 830 | + } | | 831 | + | | 832 | + return false | | 833 | +} | | 834 | + | 803 | 835 | func (feast *FeastServices) getNodeSelectorForType(feastType FeastServiceType) *map[string]string { | 804 | 836 | if serviceConfigs := feast.getServerConfigs(feastType); serviceConfigs != nil { | 805 | 837 | return serviceConfigs.ContainerConfigs.OptionalCtrConfigs.NodeSelector | @@ -1100,7 +1132,9 @@ func (feast *FeastServices) initRoute(feastType FeastServiceType) *routev1.Route | 1100 | 1132 | } | 1101 | 1133 |
| 1102 | 1134 | func applyCtrConfigs(container *corev1.Container, containerConfigs feastdevv1alpha1.ContainerConfigs) { | 1103 | | - container.Image = *containerConfigs.DefaultCtrConfigs.Image | | 1135 | + if containerConfigs.DefaultCtrConfigs.Image != nil { | | 1136 | + container.Image = *containerConfigs.DefaultCtrConfigs.Image | | 1137 | + } | 1104 | 1138 | // apply optional container configs | 1105 | 1139 | if containerConfigs.OptionalCtrConfigs.Env != nil { | 1106 | 1140 | container.Env = envOverride(container.Env, *containerConfigs.OptionalCtrConfigs.Env) | |
Collapse file Copy file name to clipboardExpand all lines: infra/feast-operator/internal/controller/services/services_test.go +44Lines changed: 44 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -23,6 +23,7 @@ import ( | 23 | 23 | "github.com/feast-dev/feast/infra/feast-operator/internal/controller/handler" | 24 | 24 | . "github.com/onsi/ginkgo/v2" | 25 | 25 | . "github.com/onsi/gomega" | | 26 | + corev1 "k8s.io/api/core/v1" | 26 | 27 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 27 | 28 | "k8s.io/apimachinery/pkg/types" | 28 | 29 | "k8s.io/apimachinery/pkg/util/intstr" | @@ -374,6 +375,49 @@ var _ = Describe("Registry Service", func() { | 374 | 375 | Expect(deployment.Spec.Template.Spec.NodeSelector).To(Equal(expectedNodeSelector)) | 375 | 376 | }) | 376 | 377 |
| | 378 | + It("should enable metrics on the online service when configured", func() { | | 379 | + featureStore.Spec.Services.OnlineStore = &feastdevv1alpha1.OnlineStore{ | | 380 | + Server: &feastdevv1alpha1.ServerConfigs{Metrics: ptr(true)}, | | 381 | + } | | 382 | + | | 383 | + Expect(k8sClient.Update(ctx, featureStore)).To(Succeed()) | | 384 | + Expect(feast.ApplyDefaults()).To(Succeed()) | | 385 | + applySpecToStatus(featureStore) | | 386 | + feast.refreshFeatureStore(ctx, typeNamespacedName) | | 387 | + | | 388 | + Expect(feast.deployFeastServiceByType(OnlineFeastType)).To(Succeed()) | | 389 | + | | 390 | + deployment := feast.initFeastDeploy() | | 391 | + Expect(deployment).NotTo(BeNil()) | | 392 | + Expect(feast.setDeployment(deployment)).To(Succeed()) | | 393 | + | | 394 | + onlineContainer := GetOnlineContainer(*deployment) | | 395 | + Expect(onlineContainer).NotTo(BeNil()) | | 396 | + Expect(onlineContainer.Command).To(Equal([]string{"feast", "serve", "--metrics", "-h", "0.0.0.0", "-p", "6566"})) | | 397 | + Expect(onlineContainer.Ports).To(ContainElement(corev1.ContainerPort{ | | 398 | + Name: "metrics", | | 399 | + ContainerPort: MetricsPort, | | 400 | + Protocol: corev1.ProtocolTCP, | | 401 | + })) | | 402 | + metricsPortCount := 0 | | 403 | + for _, port := range onlineContainer.Ports { | | 404 | + if port.Name == "metrics" { | | 405 | + metricsPortCount++ | | 406 | + } | | 407 | + } | | 408 | + Expect(metricsPortCount).To(Equal(1)) | | 409 | + | | 410 | + svc := feast.initFeastSvc(OnlineFeastType) | | 411 | + Expect(svc).NotTo(BeNil()) | | 412 | + Expect(feast.setService(svc, OnlineFeastType, false)).To(Succeed()) | | 413 | + Expect(svc.Spec.Ports).To(ContainElement(corev1.ServicePort{ | | 414 | + Name: "metrics", | | 415 | + Port: MetricsPort, | | 416 | + Protocol: corev1.ProtocolTCP, | | 417 | + TargetPort: intstr.FromInt(int(MetricsPort)), | | 418 | + })) | | 419 | + }) | | 420 | + | 377 | 421 | It("should handle empty NodeSelector gracefully", func() { | 378 | 422 | // Set empty NodeSelector | 379 | 423 | emptyNodeSelector := map[string]string{} | |
Collapse file Copy file name to clipboardExpand all lines: infra/feast-operator/internal/controller/services/services_types.go +4-3Lines changed: 4 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -51,9 +51,10 @@ const ( | 51 | 51 | caBundleAnnotation = "config.openshift.io/inject-trusted-cabundle" | 52 | 52 | caBundleName = "odh-trusted-ca-bundle" | 53 | 53 |
| 54 | | - DefaultOfflineStorageRequest = "20Gi" | 55 | | - DefaultOnlineStorageRequest = "5Gi" | 56 | | - DefaultRegistryStorageRequest = "5Gi" | | 54 | + DefaultOfflineStorageRequest = "20Gi" | | 55 | + DefaultOnlineStorageRequest = "5Gi" | | 56 | + DefaultRegistryStorageRequest = "5Gi" | | 57 | + MetricsPort int32 = 8000 | 57 | 58 |
| 58 | 59 | AuthzFeastType FeastServiceType = "authorization" | 59 | 60 | OfflineFeastType FeastServiceType = "offline" | |
Collapse file Copy file name to clipboardExpand all lines: sdk/python/feast/infra/key_encoding_utils.py +10-3Lines changed: 10 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -142,9 +142,16 @@ def serialize_entity_key( | 142 | 142 | "Serialization of entity key with version < 3 is removed. Please use version 3 by setting entity_key_serialization_version=3." | 143 | 143 | "To reserializa your online store featrues refer - https://github.com/feast-dev/feast/blob/master/docs/how-to-guides/entity-reserialization-of-from-v2-to-v3.md" | 144 | 144 | ) | 145 | | - sorted_keys, sorted_values = zip( | 146 | | - *sorted(zip(entity_key.join_keys, entity_key.entity_values)) | 147 | | - ) | | 145 | + | | 146 | + sorted_keys: List[str] | | 147 | + sorted_values: List[ValueProto] | | 148 | + if not entity_key.join_keys: | | 149 | + sorted_keys = [] | | 150 | + sorted_values = [] | | 151 | + else: | | 152 | + pairs = sorted(zip(entity_key.join_keys, entity_key.entity_values)) | | 153 | + sorted_keys = [k for k, _ in pairs] | | 154 | + sorted_values = [v for _, v in pairs] | 148 | 155 |
| 149 | 156 | output: List[bytes] = [] | 150 | 157 | if entity_key_serialization_version > 2: | |
|