CLOUD & MANAGED SERVICESCLOUDKUBERNETES
19/03/2021 • Jeroen Hartgers

Hoe auto-discovery van Kubernetes endpoints services opzetten in Prometheus

Het is vanzelfsprekend dat je monitoring nodig hebt wanneer applicaties in Kubernetes (K8s) gebruikt. Naarmate K8s groter en professioneler wordt en jaar na jaar meer wordt toegepast (en dat zal zo blijven), is het niet meer dan logisch dat veel bedrijven professionele online monitoring- en waarschuwingsoplossingen bouwen. Anderen hebben Helm grafieken ontwikkeld die snel en eenvoudig een K8s namespace installeren met kube-state-metrics, Prometheus, Grafana met dashboards en Alert manager - volledig klaar voor gebruik.

K8s dashboard with kube-state-metrics, Prometheus, Grafana

Het probleem hierbij is dat het allemaal nogal 'standaard' is. Als je al een volledig
aangepaste Prometheus/Grafana setup hebt in Rancher 1, zoals wij, is het zonde om dat zomaar weg te gooien. De reis van een Rancher 1 'cattle' Prometheus/Grafana naar Rancher 2 K8s verliep erg vlot en was vrij eenvoudig.

Met Prometheus zou je echter historisch gezien het bestand prometheus.yaml file moeten bewerken zodra je een nieuwe applicatie wil scrapen, tenzij je al je eigen aangepaste discovery tool als een scrape hebt toegevoegd.

Onvolledige data herstellen met auto-discovery

Een probleem dat ik ben tegengekomen bij het rechtstreeks scrapen van een Longhorn en Spring Boot (of een andere) Service in K8s, is dat slechts één van de vele backend pods achter die Service wordt gescrapet. Dus eindig je met onvolledige gegevens in Prometheus en als gevolg daarvan met onvolledige gegevens in je dashboards in Grafana. In Prometheus zie je dat slechts één van de drie bestaande Longhorn endpoints worden gescrapet.

prometheus tool

In Grafana zie je dat er maar één node is goedgekeurd en de andere twee worden gerapporteerd als 'Failed Nodes'. Bovendien wordt er maar één van de zeven delen gerapporteerd als 'Total Number of Volumes'.

grafana dashboard

Dit is waar auto-discovery van Kubernetes endpoint services als redder in nood optreedt. Veel webpagina's beschrijven de verschillende aspecten van het scrapen, maar naar mijn mening zijn geen van allen volledig en in andere omschrijvingen stonden dan weer grote fouten.

In deze blogpost geef ik jou een minimale en simpele configuratie om jouw Prometheus configuratie met auto-discovery van de Kubernetes endpoint services weer op en top
te krijgen!

1. Voeg configMap additions voor Prometheus toe

Voeg dit toe aan het einde van prometheus.yaml in je Prometheus configMap. De jobname is 'kubernetes-service-endpoints', want dat leek me wel toepasselijk

# Scrape config for service endpoints.
#
# The relabeling allows the actual service scrape endpoint to be configured
# via the following annotations:
#
# * `prometheus.io/scrape`: Only scrape services that have a value of `true`
# * `prometheus.io/scheme`: If the metrics endpoint is secured then you will need
# to set this to `https` & most likely set the `tls_config` of the scrape config.
# * `prometheus.io/path`: If the metrics path is not `/metrics` override this.
# * `prometheus.io/port`: If the metrics are exposed on a different port to the
# service then set this appropriately.
- job_name: 'kubernetes-service-endpoints'
 scrape_interval: 5s
 scrape_timeout: 2s
 kubernetes_sd_configs:
   - role: endpoints
 relabel_configs:
   - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
     action: keep
     regex: true
   - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
     action: replace
     target_label: __scheme__
     regex: (https?)
   - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
     action: replace
     target_label: __metrics_path__
     regex: (.+)
   - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
     action: replace
     target_label: __address__
     regex: (.+)(?::\d+);(\d+)
     replacement: $1:$2
   - action: labelmap
     regex: __meta_kubernetes_service_label_(.+)
   - source_labels: [__meta_kubernetes_namespace]
     action: replace
     target_label: kubernetes_namespace
   - source_labels: [__meta_kubernetes_service_name]
     action: replace
     target_label: kubernetes_name

2. Configureer de Services

Zoals ik hierboven al eerder heb vermeld over prometheus.yaml, kun je de volgende
annotaties configureren. De annotatie prometheus.io/scrape: “true” is verplicht, indien je een bepaalde dienst wil scrapen. Alle andere annotaties zijn optioneel en worden hier uitgelegd:

  • prometheus.io/scrape: Enkel scrape services die de waarde `true` hebben
  • prometheus.io/scheme: Als de metrics enpoint beveiligd is, moet je dit instellen
    op `https` en waarschijnlijk de `tls_config` van de scrape config instellen
  • prometheus.io/path: Als het metrics pad niet `/metrics` is, overschrijf deze dan
  • prometheus.io/port: Als de metrics worden weergegeven op een andere poort dan de service, stel dit dan op de juiste manier in

Laten we eerst even kijken naar een voorbeeld van een Longhorn Service. (Longhorn is een goede gerepliceerde opslagoplossing!)

apiVersion: v1
kind: Service
metadata:
 annotations:
   prometheus.io/port: "9500"
   prometheus.io/scrape: "true"
 labels:
   app: longhorn-manager
 name: longhorn-backend
 namespace: longhorn-system
spec:
 ports:
 - name: manager
   port: 9500
   protocol: TCP
   targetPort: manager
 selector:
   app: longhorn-manager
 sessionAffinity: ClientIP
 sessionAffinityConfig:
   clientIP:
     timeoutSeconds: 10800
 type: ClusterIP

Laten we nu kijken naar een voorbeeld van een Spring Boot Application Service. Let op het niet-standaard scrape-path/actuator/prometheus.

apiVersion: v1
kind: Service
metadata:
 name: springbootapp
 namespace: spring
 labels:
   app: gateway
 annotations:
   prometheus.io/path: "/actuator/prometheus"
   prometheus.io/port: "8080"
   prometheus.io/scrape: "true"
spec:
 ports:
   - name: management
     port: 8080
   - name: http
     port: 80
 selector:
   app: gateway
 sessionAffinity: None
 type: ClusterIP

3. Configureren van Prometheus rollen

ClusterRole

Verander indien nodig eerst de namespace. Let op: misschien moet deze clusterRole een beetje strikter zijn dan het nu het geval is.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
 labels:
   app.kubernetes.io/component: controller
   app.kubernetes.io/name: prometheus
 name: prometheus
 namespace: prometheus
rules:
 - apiGroups:
     - apiextensions.k8s.io
   resources:
     - customresourcedefinitions
   verbs:
     - create
 - apiGroups:
     - apiextensions.k8s.io
   resourceNames:
     - alertmanagers.monitoring.coreos.com
     - podmonitors.monitoring.coreos.com
     - prometheuses.monitoring.coreos.com
     - prometheusrules.monitoring.coreos.com
     - servicemonitors.monitoring.coreos.com
     - thanosrulers.monitoring.coreos.com
   resources:
     - customresourcedefinitions
   verbs:
     - get
     - update
 - apiGroups:
     - monitoring.coreos.com
   resources:
     - alertmanagers
     - alertmanagers/finalizers
     - prometheuses
     - prometheuses/finalizers
     - thanosrulers
     - thanosrulers/finalizers
     - servicemonitors
     - podmonitors
     - prometheusrules
   verbs:
     - '*'
 - apiGroups:
     - apps
   resources:
     - statefulsets
   verbs:
     - '*'
 - apiGroups:
     - ""
   resources:
     - configmaps
     - secrets
   verbs:
     - '*'
 - apiGroups:
     - ""
   resources:
     - pods
   verbs:
     - get
     - list
     - watch
 - apiGroups:
     - ""
   resources:
     - services
     - services/finalizers
     - endpoints
   verbs:
     - "*"
 - apiGroups:
     - ""
   resources:
     - nodes
   verbs:
     - list
     - watch
 - apiGroups:
     - ""
   resources:
     - namespaces
   verbs:
     - get
     - list
     - watch
 - apiGroups:
     - extensions
   resources:
     - ingresses
   verbs: ["get", "list", "watch"]
 - nonResourceURLs: ["/metrics"]
   verbs: ["get"]

ClusterRoleBinding

Verander de namespace nogmaals indien nodig.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
 labels:
   app.kubernetes.io/component: controller
   app.kubernetes.io/name: prometheus
 name: prometheus
 namespace: prometheus
roleRef:
 apiGroup: rbac.authorization.k8s.io
 kind: ClusterRole
 name: prometheus
subjects:
 - kind: ServiceAccount
   name: default
   namespace: prometheus

ServiceAccount

Nogmaals, verander de namespace indien nodig. DON'T: verander de naam niet, tenzij je
ook de ClusterRoleBinding subjects.name verandert.

apiVersion: v1
kind: ServiceAccount
metadata:
 name: default
 namespace: prometheus

Pas toe

Pas als eerste de ServiceAccount, ClusterRoleBinding, ClusterRole en Services toe aan jouw K8s cluster. Nadat de Prometheus configMap is geüpdatet, implementeer Prometheus opnieuw om er zeker van te zijn dat de nieuwe configMap geactiveerd/geladen is.

Resultaten in Prometheus

Ga naar de Prometheus GUI en navigeer naar Status -> Targets. Je zal zien dat alle pod endpoints 'op magische wijze' zullen verschijnen in de kubernetes-services-endpoints heading. Alle toekomstige prometheus.io gerelateerde annotatie aanpassingen in k8s Services zullen onmiddellijk in werking treden nadat je ze hebt aangebracht!

prometheus GUI overview

Grafana Longhorn dashboard

Ik heb een generiek Grafana Longhorn dashboard gebruikt, die je hier kan vinden. Dankzij auto-discovery geeft het Grafana Longhorn dashboard nu correct drie nodes en zeven volumes weer, wat klopt!

Grafana Longhorn dashboard

Conclusie

Na het doorlopen van alle stappen in deze blogpost, hoef je in principe nooit meer naar jouw Prometheus configuratie om te kijken. Dankzij auto-discovery van Kubernetes endpoint services is het toevoegen en verwijderen van Prometheus scrapes voor jouw applicaties nu bijna even gemakkelijk dan het ontgrendelen van jouw smartphone! ;-)

Ik hoop dat deze blogpost jou heeft geholpen! Aarzel zeker geen contact met ons op te nemen via hello@aca-it.be voor vragen.

Jeroen Hartgers