prometheus-operator / prometheus-operator

Prometheus Operator creates/configures/manages Prometheus clusters atop Kubernetes
https://prometheus-operator.dev
Apache License 2.0
9.16k stars 3.72k forks source link

ServiceMonitor endpoints port and targetPort ambiguity #2515

Open nrvnrvn opened 5 years ago

nrvnrvn commented 5 years ago

What did you do? We spent a huge amount of time trying to understand why we could not scrape a newly added service metrics until we discovered the following:

What did you expect to see? Metrics What did you see instead? Under which circumstances? Instead we discovered that ServiceMonitor's targetPort actually transforms to __meta_kubernetes_pod_container_port_number which means that Prometheus is told to scrape not the Endpoints resources but the Container resources. See https://github.com/coreos/prometheus-operator/blob/v0.28.0/pkg/prometheus/promcfg.go#L378-L399 This creates a lot of confusion... Environment

---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: test
  name: test
spec:
  selector:
    matchLabels:
      app: test
  template:
    metadata:
      labels:
        app: test
    spec:
      containers:
      # please replace this with something that actually serves some metrics in order to reproduce the issue
      - image: k8s.gcr.io/pause-amd64:3.1
        name: pause-amd64
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: test
  name: test
spec:
  ports:
  - port: 8000
  selector:
    app: test
---
apiVersion: v1
kind: Endpoints
metadata:
  labels:
    app: test
  name: test
subsets:
- addresses:
  - ip: 10.20.9.31
    nodeName: somenodename
    targetRef:
      kind: Pod
      name: test-555dbcb98f-7ds92
      namespace: test-prometheus
      resourceVersion: "81073517"
      uid: e6556903-5075-11e9-9584-4201c0a8000b
  ports:
  - port: 8000
    protocol: TCP
---
# ServiceMonitor lives in the same namespace as Prometheus and Prometheus Operator of course
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  labels:
    app: test
  name: test
  namespace: monitoring
spec:
  endpoints:
  - interval: 15s
    path: /metrics
    targetPort: 8000
  namespaceSelector:
    any: true
  selector:
    matchLabels:
      app: test
insert Prometheus Operator logs relevant to the issue here

Proposal

Documentation should be amended to clearly state how the []Endpoints' port and targetPort work.

Since transforming the targetPort to __meta_kubernetes_pod_container_port_number/__meta_kubernetes_pod_container_port_name has been there for a long while a lot of users rely on this and this should not be removed for backwards compatibility.

Instead __meta_kubernetes_endpoint_port_number should be taken into consideration as well.

P.S. While I was writing all the above I haven't yet dug deeply into Prometheus itself but at first glance it seems like this is an upstream issue since I don't see __meta_kubernetes_endpoint_port_number here

Two questions here then:

brancz commented 5 years ago

You're absolutely right, the documentation is faulty here. The targetPort field doesn't refer to the Services fields, it refers to the port name or port number defined on the underlying selected Pods. Clarifying this in the docs is step 1.

Step 2 as you also already mentioned, we should treat port as a IntOrStr just like targetPort and select either the port name or port number, in the same fashion. And in order to do that, Prometheus should expose the __meta_kubernetes_endpoint_port_number meta label. Would you like to go ahead and open this issue? If not I'm happy to do it, but as you discovered it, it's yours to take :slightly_smiling_face: .

what were the initial considerations for using __meta_kubernetes_endpoint_port instead of __meta_kubernetes_serviceport? I assume this was done because endpoints contain actual target information but I still ask this question because ServiceMonitor as we assumed should have grabbed Port number/name from Services.

This is because the Endpoints object is what Prometheus actually uses for discovery purposes, and a Service is not necessarily always present. So going with the Endpoints object whenever possible is the safe bet.

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had any activity in last 60d. Thank you for your contributions.

nrvnrvn commented 5 years ago

/reopen

nrvnrvn commented 5 years ago

/cc @brancz

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had any activity in last 60d. Thank you for your contributions.

brancz commented 5 years ago

Is there something left to be answered?

nrvnrvn commented 5 years ago

The issue is still unresolved if I am not mistaken

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had any activity in last 60d. Thank you for your contributions.

brancz commented 4 years ago

I believe the docs still need to be adapted here.

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had any activity in last 60d. Thank you for your contributions.

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had any activity in last 60d. Thank you for your contributions.

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had any activity in last 60d. Thank you for your contributions.

jecnua commented 4 years ago

Please remove the stale label again.

brancz commented 4 years ago

Contributions on improving the docs would be very welcome! :)

vincent-pli commented 4 years ago

@brancz @nrvnrvn If I don't specify a container port, then use targetPort in serviceMonitor and point to right port of container (not specify a container port but truly existed), could it work?

brancz commented 4 years ago

yes that should work, when you specify the actual port number

vincent-pli commented 4 years ago

@brancz Based on my case, it's not work until I specify container port in my pod, see the details here: https://github.com/coreos/prometheus-operator/issues/3354

galindro commented 4 years ago

Guys, I'm getting an issue to scrape metrics via PodMonitor and I found this issue, so, I would like to know if it has something to do with it.

I'm defining a PodMonitor like this to scrape some openebs pods metrics:

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: openebs-cstor-pool-metrics
  namespace: openebs
spec:
  selector:
    matchLabels:
      app: cstor-pool
  podMetricsEndpoints:
  - port: "9500"
    interval: 10s

The problem is that the generated prometheus.yaml has the bellow content. It isn't working properly. Prometheus isn't being able to scrape any metrics because it can't find any targets.

- job_name: openebs/openebs-cstor-pool-metrics/0
  honor_labels: false
  kubernetes_sd_configs:
  - role: pod
    namespaces:
      names:
      - openebs
  scrape_interval: 10s
  relabel_configs:
  - action: keep
    source_labels:
    - __meta_kubernetes_pod_label_app
    regex: cstor-pool
  - action: keep
    source_labels:
    - __meta_kubernetes_pod_container_port_name
    regex: "9500"
  - source_labels:
    - __meta_kubernetes_namespace
    target_label: namespace
  - source_labels:
    - __meta_kubernetes_pod_container_name
    target_label: container
  - source_labels:
    - __meta_kubernetes_pod_name
    target_label: pod
  - target_label: job
    replacement: openebs/openebs-cstor-pool-metrics
  - target_label: endpoint
    replacement: "9500"

But now, if I change the PodMonitor to the bellow one, it works:


apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: openebs-cstor-pool-metrics
  namespace: openebs
spec:
  selector:
    matchLabels:
      app: cstor-pool
  podMetricsEndpoints:
  - targetPort: 9500
    interval: 10s

Note that I changed from port to targetPort (and I informed an integer in place of a string). This was the generated prometheus.yaml:

- job_name: openebs/openebs-cstor-pool-metrics/0
  honor_labels: false
  kubernetes_sd_configs:
  - role: pod
    namespaces:
      names:
      - openebs
  scrape_interval: 10s
  relabel_configs:
  - action: keep
    source_labels:
    - __meta_kubernetes_pod_label_app
    regex: cstor-pool
  - action: keep
    source_labels:
    - __meta_kubernetes_pod_container_port_number   # THIS IS DIFFERENT
    regex: "9500"
  - source_labels:
    - __meta_kubernetes_namespace
    target_label: namespace
  - source_labels:
    - __meta_kubernetes_pod_container_name
    target_label: container
  - source_labels:
    - __meta_kubernetes_pod_name
    target_label: pod
  - target_label: job
    replacement: openebs/openebs-cstor-pool-metrics
  - target_label: endpoint
    replacement: "9500"

The only difference between the two prometheus.yaml generated is the following:

diff /tmp/prometheus.yaml /tmp/prometheus_2.yaml 
1056c1056
<     - __meta_kubernetes_pod_container_port_name
---
>     - __meta_kubernetes_pod_container_port_number

My concern about it is that targetPort was deprecated by https://github.com/prometheus-operator/prometheus-operator/pull/3078. But by using port in place of targetPort doesn't works probably because I'm not using a name for the port, because that openebs pods doesn't has a name for it.

bricef commented 4 years ago

@galindro I have the exact same issue. I can't seem to configure a way to reach an unnamed port from a PodMonitor. That seems like a massive oversight. I'm not always in control of the naming of ports on pods. I should be able to configure scraping at an arbitrary numerical port. A workaround would be useful. I'm not sure what that would look like.

galindro commented 4 years ago

Well, your case is even worse... I have no clues how to help you to workaround it. Sorry

funkypenguin commented 3 years ago

My workaround in this case has been to use the (deprecated) targetPort field, to point to the arbitrary numerical port on the pod.

Note that this works:

  podMetricsEndpoints:
  - targetPort: 8080

While this doesn't:

  podMetricsEndpoints:
  - targetPort: "8080"

.. presumably because by encapsulating the port number in quotes, you're instructing Prometheus to look for a container port named "8080".

funkypenguin commented 3 years ago

Also worth noting that the above workaround doesn't work when using port: 8080, but errors with:

 * spec.podMetricsEndpoints.port: Invalid value: "integer": spec.podMetricsEndpoints.port in body must be of type string: "integer
 *
weibeld commented 3 years ago

For the situation that the Pod does not declare any ports (as mentioned by @bricef), a workaround can be implemented as in the following example:

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: rook-ceph-operator
spec:
  selector:
    matchLabels:
      app: rook-ceph-operator
  namespaceSelector:
    matchNames: 
      - rook-ceph
  podMetricsEndpoints:
    - relabelings:
      - action: replace
        targetLabel: __address__
        sourceLabels:
          - __address__
        replacement: $1:8080

That means, instead of defining the port to scrape with port or targetPort, you manually redefine the __address__ label in the relabelings field to include the desired target port.

funkypenguin commented 3 years ago

That's a neat trick!

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had any activity in the last 60 days. Thank you for your contributions.

nrvnrvn commented 3 years ago

Stay away @stale

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had any activity in the last 60 days. Thank you for your contributions.

jecnua commented 3 years ago

Please remove the stale label again...

washanhanzi commented 2 years ago

For the situation that the Pod does not declare any ports (as mentioned by @bricef), a workaround can be implemented as in the following example:

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: rook-ceph-operator
spec:
  selector:
    matchLabels:
      app: rook-ceph-operator
  namespaceSelector:
    matchNames: 
      - rook-ceph
  podMetricsEndpoints:
    - relabelings:
      - action: replace
        targetLabel: __address__
        sourceLabels:
          - __address__
        replacement: $1:8080

That means, instead of defining the port to scrape with port or targetPort, you manually redefine the __address__ label in the relabelings field to include the desired target port.

If container port is already declared. Use regex to replace only the port.

    - relabelings:
        - action: replace
          targetLabel: __address__
          sourceLabels:
            - __address__
          regex: (.*):.*
          replacement: "${1}:9091"
krichter722 commented 11 months ago

This should all be validated by the Operator. It took me 4 hours to try every combination of namespaces, labels, port and targetPort to figure out a working solution to create a working ServiceMonitor. If it just doesn't work following the official and inofficial tutorial and every question on the internet about the topic, you have to try everything.

So apparently one needs to specify targetPort, but it's mentioned nowhere, I've even seen it deprecated in some docs and it is according to post in this issue. targetPort needs to be an integer. I also specified port which is the name of the port, but a number as a string is accepted as well which is a guarantee for trouble, especially if docs like https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/user-guides/getting-started.md don't mention anything about this very particular combination of port and targetPort. I tried about a hundred combinations in the last hours and I'm pretty sure it's the only way to make this work, but it's really so much easier if it's just documented by the devs or validated with intuitive error messages. It should not be possible to make any mistakes regarding this.

It's neat concept that takes about 20 minutes with intuitive docs and about 2 minutes if the operator or Prometheus would give any feedback at all.