kubernetes / kube-state-metrics

Add-on agent to generate and expose cluster-level metrics.
https://kubernetes.io/docs/concepts/cluster-administration/kube-state-metrics/
Apache License 2.0
5.32k stars 1.99k forks source link

kube-state-metrics doesn't produce metric for Custom Resources #2453

Open mlubanski opened 1 month ago

mlubanski commented 1 month ago

What happened:

I defined configuration for Custom Resource State Metrics for my three CRD's (ReplicationGroup, ClusterInstance, RDSInstance)

After deploying it to cluster where I have kubernetes objects of all above types kube-state-metric is producing me expected crossplane_engine_version metric

But when I deploy same configuration to the cluster where I have only objects of RDSInstance type, crossplane_engine_version metric is missing

What you expected to happen:

Metric should be produced by kube-state-metric even if there are no real Objects of one type/kind listed in configuration below

How to reproduce it (as minimally and precisely as possible):

apiVersion: v1
kind: ConfigMap
metadata:
  name: kube-state-metrics-crd-config
  namespace: crossplane-system
data:
  crd-config.yaml: |
    kind: CustomResourceStateMetrics
    spec:
      resources:
        - groupVersionKind:
            group: cache.aws.crossplane.io
            version: "*"
            kind: ReplicationGroup
          metricNamePrefix: crossplane
          labelsFromPath:
            managedResourceKind: [kind]
            claimKind: [metadata, ownerReferences, "[controller=true]", kind]
            cllaimName: [metadata, labels, crossplane.io/claim-name]
            claimNamespace: [metadata, labels, crossplane.io/claim-namespace]
          metrics:
            - name: "engine_version"
              help: "Engine Version"
              each:
                type: Info
                info:
                  labelsFromPath:
                    engineVersion: [spec, forProvider, engineVersion]
              commonLabels:
                custom_metric: "yes"
        - groupVersionKind:
            group: rds.aws.upbound.io
            version: "*"
            kind: ClusterInstance
          metricNamePrefix: crossplane
          labelsFromPath:
            managedResourceKind: [kind]
            claimKind: [metadata, ownerReferences, "[controller=true]", kind]
            cllaimName: [metadata, labels, crossplane.io/claim-name]
            claimNamespace: [metadata, labels, crossplane.io/claim-namespace]
          metrics:
            - name: "engine_version"
              help: "Engine Version"
              each:
                type: Info
                info:
                  labelsFromPath:
                    engineVersion: [status, atProvider, engineVersion]
              commonLabels:
                custom_metric: "yes"
        - groupVersionKind:
            group: database.aws.crossplane.io
            version: "*"
            kind: RDSInstance
          metricNamePrefix: crossplane
          labelsFromPath:
            managedResourceKind: [kind]
            claimKind: [metadata, ownerReferences, "[controller=true]", kind]
            cllaimName: [metadata, labels, crossplane.io/claim-name]
            claimNamespace: [metadata, labels, crossplane.io/claim-namespace]
          metrics:
            - name: "engine_version"
              help: "Engine Version"
              each:
                type: Info
                info:
                  labelsFromPath:
                    engineVersion: [spec, forProvider, engineVersion]
              commonLabels:
                custom_metric: "yes"

Environment:

dgrisonnet commented 1 month ago

/assign @rexagod /triage accepted

yiyang-shao commented 3 weeks ago

Hi! We encountered the same issue in version 2.12.0 and 2.13.0. It works with version 2.11.0. After a quick look, we found out that pkg/metrics_store/metrics_writer.go:120(SanitizeHeaders) is causing the problem. In 2.12.0+, it removes the header from the slice.

// Nullify duplicate headers after the sanitization to not miss out on any new candidates.
if header == lastHeader {
    writer.stores[0].headers = append(writer.stores[0].headers[:i], writer.stores[0].headers[i+1:]...)

    // Do not increment the index, as the next header is now at the current index.
    continue
}

In 2.11.0, it is set to an empty string.

 writer.stores[0].headers[i] = ""

I suppose that this change probably further affects the behavior of the below loop in WriteAll function of MetricsWriter, causing that some metrics are missed in writing.

for i, help := range m.stores[0].headers {
      ...
      for _, s := range m.stores {
    for _, metricFamilies := range s.metrics {
        _, err := w.Write(metricFamilies[i])
        ...
    }
      }
}
greenu commented 1 week ago

As suggested in this workaround https://github.com/kubernetes/kube-state-metrics/issues/2366#issuecomment-2058901252, just make help text unique.