Create service discovery with a ServiceMonitor

Updated at:

Managed Service for Prometheus supports service discovery through the ServiceMonitor CRD. A ServiceMonitor defines the namespace scope for pod discovery and selects target Services using matchLabel. This walkthrough uses a Spring Boot application as an example.

Demo

Download the demo project for a complete ServiceMonitor-based service discovery example.

Step 1: Add dependencies

  1. Create a Maven application and add the following dependencies to the pom.xml file.

        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>io.micrometer</groupId>
                <artifactId>micrometer-registry-prometheus</artifactId>
                <version>1.6.6</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
  2. Add the following configuration to the src/resources/applications.properties file in the project.

    management.endpoints.web.exposure.include=prometheus
  3. Start the project and access http://{host}:{port}/actuator/prometheus in a browser.

    JVM monitoring data is displayed. The following is sample output in Prometheus exposition format.

    # HELP jvm_threads_daemon_threads The current number of live daemon threads
    # TYPE jvm_threads_daemon_threads gauge
    jvm_threads_daemon_threads 23.0
    # HELP tomcat_sessions_rejected_sessions_total
    # TYPE tomcat_sessions_rejected_sessions_total counter
    tomcat_sessions_rejected_sessions_total 0.0
    # HELP jvm_memory_committed_bytes The amount of memory in bytes that is committed for the Java virtual machine to use
    # TYPE jvm_memory_committed_bytes gauge
    jvm_memory_committed_bytes{area="heap",id="PSSurvivorSpace",} 1.31072E7
    jvm_memory_committed_bytes{area="heap",id="PSOldGen",} 1.30023424E8
    jvm_memory_committed_bytes{area="heap",id="PSEdenSpace",} 1.56762112E8
    jvm_memory_committed_bytes{area="nonheap",id="Metaspace",} 3.670016E7
    jvm_memory_committed_bytes{area="nonheap",id="CodeCache",} 7143424.0
    jvm_memory_committed_bytes{area="nonheap",id="CompressedClassSpace",} 5242880.0
    # HELP jvm_classes_loaded_classes The number of classes that are currently loaded in the Java virtual machine
    # TYPE jvm_classes_loaded_classes gauge
    jvm_classes_loaded_classes 6877.0
    # HELP jvm_threads_peak_threads The peak live thread count since the Java virtual machine started or peak was reset
    # TYPE jvm_threads_peak_threads gauge
    jvm_threads_peak_threads 28.0
    # HELP system_cpu_count The number of processors available to the Java virtual machine
    # TYPE system_cpu_count gauge
    system_cpu_count 12.0
    # HELP tomcat_sessions_expired_sessions_total
    # TYPE tomcat_sessions_expired_sessions_total counter
    tomcat_sessions_expired_sessions_total 0.0
    # HELP process_files_max_files The maximum file descriptor count
    # TYPE process_files_max_files gauge
    process_files_max_files 10240.0
    # HELP jvm_buffer_total_capacity_bytes An estimate of the total capacity of the buffers in this pool
    # TYPE jvm_buffer_total_capacity_bytes gauge
    jvm_buffer_total_capacity_bytes{id="direct",} 8192.0
    jvm_buffer_total_capacity_bytes{id="mapped",} 0.0
    # HELP jvm_threads_states_threads The current number of threads having NEW state
    # TYPE jvm_threads_states_threads gauge
    jvm_threads_states_threads{state="runnable",} 9.0
    jvm_threads_states_threads{state="blocked",} 0.0
    jvm_threads_states_threads{state="waiting",} 12.0
    jvm_threads_states_threads{state="timed-waiting",} 6.0
    jvm_threads_states_threads{state="new",} 0.0
    jvm_threads_states_threads{state="terminated",} 0.0
    # HELP jvm_buffer_count_buffers An estimate of the number of buffers in the pool
    # TYPE jvm_buffer_count_buffers gauge
    jvm_buffer_count_buffers{id="direct",} 1.0
    jvm_buffer_count_buffers{id="mapped",} 0.0
    # HELP process_files_open_files The open file descriptor count
    # TYPE process_files_open_files gauge
    process_files_open_files 93.0
    # HELP jvm_gc_memory_promoted_bytes_total Count of positive increases in the size of the old generation memory pool before GC to after GC
    # TYPE jvm_gc_memory_promoted_bytes_total counter
    jvm_gc_memory_promoted_bytes_total 5225384.0
    # HELP jvm_memory_max_bytes The maximum amount of memory in bytes that can be used for memory management
    # TYPE jvm_memory_max_bytes gauge
    jvm_memory_max_bytes{area="heap",id="PSSurvivorSpace",} 1.31072E7
    jvm_memory_max_bytes{area="heap",id="PSOldGen",} 2.863661056E9
    jvm_memory_max_bytes{area="heap",id="PSEdenSpace",} 1.40509184E9
    jvm_memory_max_bytes{area="nonheap",id="Metaspace",} -1.0
    jvm_memory_max_bytes{area="nonheap",id="CodeCache",} 2.5165824E8
    jvm_memory_max_bytes{area="nonheap",id="CompressedClassSpace",} 1.073741824E9
    # HELP jvm_gc_memory_allocated_bytes_total Incremented for an increase in the size of the (young) heap memory pool after one GC to before the next
    # TYPE jvm_gc_memory_allocated_bytes_total counter
    jvm_gc_memory_allocated_bytes_total 1.14749824E8
    # HELP jvm_classes_unloaded_classes_total The total number of classes unloaded since the Java virtual machine has started execution
    # TYPE jvm_classes_unloaded_classes_total counter
    jvm_classes_unloaded_classes_total 10.0
    # HELP tomcat_sessions_created_sessions_total
    # TYPE tomcat_sessions_created_sessions_total counter

Step 2: Deploy a Kubernetes cluster

  1. Build an image and upload the image to an image repository. For more information, see Connect to a source code platform.

  2. Create a deployment using the following YAML configuration.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: micrometer-prometheus
      namespace: default
      labels:
        app: demo-prometheus
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: demo-prometheus
      template:
        metadata:
          labels:
            app: demo-prometheus
        spec:
          containers:
            - name: micrometer-prometheus
              image: manjusakalza/micrometer-prometheus:latest
              ports:
                - containerPort: 8080
  3. Create a Service using the following YAML configuration.

    apiVersion: v1
    kind: Service
    metadata:
      name: prometheus-metrics-demo
      namespace: default
      labels:
        micrometer-prometheus-discovery: 'true'
    spec:
      selector:
        app: demo-prometheus
      ports:
        - protocol: TCP
          port: 8080
          targetPort: 8080
          name: metrics

Step 3: Create a ServiceMonitor

  1. Save the YAML file to your computer and run kubectl apply -f {path/to/the/YAML/file} to apply the file.

Sample ServiceMonitor YAML:

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: micrometer-demo
  namespace: default
spec:
  endpoints:
    - interval: 15s
      path: /actuator/prometheus
      port: metrics    # Note: Configure the port name, not the port number.
  namespaceSelector:
    any: true
  selector:
    matchLabels:
      micrometer-prometheus-discovery: 'true'

YAML field descriptions:

  • The metadata fields name and namespace identify the ServiceMonitor.

  • The spec field endpoints defines the scrape targets. Each endpoint in the endpoints array (endpoints supports multiple entries) contains these fields:

    • interval: Scrape interval for this endpoint. Example: 15s.

    • path: Metrics scrape path. Example: /actuator/prometheus.

    • port: Service port name (must match the name defined in your Service from Step 2). Example: metrics.

      Important

      Configure the port name, not the port number.

  • The spec field namespaceSelector sets the Service discovery scope. namespaceSelector supports two mutually exclusive fields:

    • any: Set to true to monitor all matching Services across all namespaces.

    • matchNames: Limits monitoring to specific namespaces. For example, to monitor only the default and arms-prom namespaces, set matchNames as follows:

      namespaceSelector:
        matchNames:
        - default
        - arms-prom
  • The spec field selector filters Services by label.

    The example Service has the label micrometer-prometheus-discovery: 'true', so configure selector as follows:

    selector:
      matchLabels:
        micrometer-prometheus-discovery: 'true'

To enable basic auth, use the following YAML:

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: cloud-open-api-monitor     #  Set a unique name for the ServiceMonitor.
  namespace: default   #  Set the namespace where the ServiceMonitor resides.
spec:
  endpoints:
  - interval: 30s
    # Specify the interval at which Prometheus scrapes the current endpoint.
    port: tcp-8080
    # Enter the path exposed in the Prometheus exporter code.
    path: /api/actuator/prometheus
    basicAuth:
      password:
        name: basic-auth
        key: <password>
      username:
        name: basic-auth
        key: <userName>
    scheme: http
  namespaceSelector:
    any: true
  selector:
    matchLabels:
      # Match Services with the following label.
      edas.oam.acname: cloud-open-api

If you lack permissions for basic auth, create a ClusterRole with the required permissions and bind it to the ServiceAccount arms-prom-operator in the arms-prom namespace by using a ClusterRoleBinding.

  • ClusterRole YAML file

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: prometheus-agent-role
      labels:
        app: prometheus-agent
    rules:
      - apiGroups: [""]
        resources: ["pods", "services", "endpoints", "nodes"]
        verbs: ["get", "list", "watch"]  # Adjust as needed.
      - apiGroups: ["monitoring.coreos.com"]  # Adjust the apiGroups as needed.
        resources: ["*"]
        verbs: ["get", "list", "watch", "create", "update", "delete"]  # Adjust as needed.
    
  • ClusterRoleBinding YAML file

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: prometheus-agent-binding
    subjects:
      - kind: ServiceAccount
        name: arms-prom-operator  # Service account name
        namespace: arms-prom      # The namespace where the service account resides.
    roleRef:
      kind: ClusterRole
      name: prometheus-agent-role  # The name of the ClusterRole defined above.
      apiGroup: rbac.authorization.k8s.io
    

Step 4: Verify the ServiceMonitor

Verify that Prometheus discovers your service:

  1. Log on to the Managed Service for Prometheus console. In the left-side navigation pane, click Integration Management.

  2. On the Integrated Environments tab, click the name of the target container environment in the Container Service list.

  3. Click the Self-Monitoring tab.

    On the Targets tab, check whether a target named {namespace}/{serviceMonitorName}/x exists.

    The page displays the target group default/micrometer-demo/0 (3/3 up), indicating that the ServiceMonitor has been correctly identified by Prometheus. All targets show a green UP state, which confirms that the configuration is valid.

  4. Expand the {namespace}/{serviceMonitorName}/x target row and click the endpoint link.

    Verify that metrics display correctly. The page displays JVM and Tomcat runtime metrics in Prometheus exposition format, confirming that the ServiceMonitor is correctly configured.

    # HELP jvm_buffer_total_capacity_bytes An estimate of the total capacity of the buffers in this pool
    # TYPE jvm_buffer_total_capacity_bytes gauge
    jvm_buffer_total_capacity_bytes{id="mapped",} 0.0
    jvm_buffer_total_capacity_bytes{id="direct",} 81968.0
    # HELP jvm_threads_daemon_threads The current number of live daemon threads
    # TYPE jvm_threads_daemon_threads gauge
    jvm_threads_daemon_threads 29.0
    # HELP jvm_memory_committed_bytes The amount of memory in bytes that is committed for the Java virtual machine to use
    # TYPE jvm_memory_committed_bytes gauge
    jvm_memory_committed_bytes{area="nonheap",id="miscellaneousnon-heapstorage",} 2.3068672E7
    jvm_memory_committed_bytes{area="nonheap",id="classstorage",} 2.8471712E7
    jvm_memory_committed_bytes{area="nonheap",id="JITcodecache",} 2.68435456E8
    jvm_memory_committed_bytes{area="heap",id="tenured-LOA",} 671744.0
    jvm_memory_committed_bytes{area="nonheap",id="JITdatacache",} 2097152.0
    jvm_memory_committed_bytes{area="heap",id="nursery-survivor",} 1638400.0
    jvm_memory_committed_bytes{area="heap",id="nursery-allocate",} 6881280.0
    jvm_memory_committed_bytes{area="heap",id="tenured-SOA",} 1.2763136E7
    # HELP process_start_time_seconds Start time of the process since the unix epoch.
    # TYPE process_start_time_seconds gauge
    process_start_time_seconds 1.622297808811E9
    # HELP tomcat_sessions_expired_sessions_total
    # TYPE tomcat_sessions_expired_sessions_total counter
    tomcat_sessions_expired_sessions_total 0.0
    # HELP jvm_threads_states_threads The current number of threads having NEW state
    # TYPE jvm_threads_states_threads gauge
    jvm_threads_states_threads{state="runnable",} 20.0
    jvm_threads_states_threads{state="blocked",} 0.0
    jvm_threads_states_threads{state="waiting",} 10.0
    jvm_threads_states_threads{state="timed-waiting",} 3.0
    jvm_threads_states_threads{state="new",} 0.0
    jvm_threads_states_threads{state="terminated",} 0.0
    # HELP jvm_classes_unloaded_classes_total The total number of classes unloaded since the Java virtual machine has started execution
    # TYPE jvm_classes_unloaded_classes_total counter
    jvm_classes_unloaded_classes_total 0.0
    # HELP tomcat_sessions_rejected_sessions_total
    # TYPE tomcat_sessions_rejected_sessions_total counter
    tomcat_sessions_rejected_sessions_total 0.0
    # HELP tomcat_sessions_created_sessions_total
    # TYPE tomcat_sessions_created_sessions_total counter
    tomcat_sessions_created_sessions_total 0.0
    # HELP jvm_threads_live_threads The current number of live threads including both daemon and non-daemon threads
    # TYPE jvm_threads_live_threads gauge
    jvm_threads_live_threads 33.0
    # HELP jvm_memory_max_bytes The maximum amount of memory in bytes that can be used for memory management
    # TYPE jvm_memory_max_bytes gauge