Introduction au Service Discovery Kubernetes dans Prometheus
Présentation
Les clusters Kubernetes possèdent des labels et annotations pour ses différents composants (pods, services, etc...) Pour découvrir les cibles, Prometheus a besoin d'utiliser l'API Kubernetes.
Prometheus est livré avec un plugin d'auto-découverte Kubernetes du nom de <kubernetes_sd_config>. Les configurations Service Discovery de Kubernetes permettent la récupération automatique des cibles à scraper de façon à toujours être synchronisés avec l'état du cluster.
Plusieurs types de rôles peuvent être configurés pour la découverte des targets. Selon le type, Prometheus met à disposition différents meta_labels. Voir la documentation officielle
Pour que le service discovery soit opérationnel, les Targets Prometheus sont scrapées avec des labels particuliers attendus : __address__, __scheme__, __metrics_path__.
Les URL cibles qui seront utilisés seront formés ainsi : __scheme__://__address____metrics_path__
Une directive <relabel_config> existe, qui va nous permettre de transformer et créer un ensemble de labels pour correspondre à ce qu'attend Prometheus pour permettre le scraping.
Pour ce faire, nous allons transformer les metalabels vers les labels __ vus plus haut.
L'idée est de construire les labels Prometheus scheme, address et metrics_path avec l'aide des informations transmises dans les annotations des services que l'on souhaite scraper.
Architecture
Pour fonctionner, Prometheus va avoir besoin d'un ServiceAccount, un ClusterRole et un ClusterRoleBinding, le tout pour permettre à ce dernier de pouvoir lire les objets Kubernetes sur l’intégralité du cluster et récupérer les différentes annotations.
Créons le fichier rbac.yaml pour définir le compte de service que nous utiliserons pour Prometheus et lui affecter quelques autorisations sur les objets Kubernetes.
# rbac.yaml
apiVersion: v1
kind: Namespace
metadata:
name: monitoring
---
apiVersion: v1
kind: ServiceAccount
metadata:
namespace: monitoring
name: prometheus
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: discoverer
rules:
- apiGroups: [""]
resources:
- nodes
- services
- endpoints
- pods
verbs: ["get", "list", "watch"]
- apiGroups:
- extensions
resources:
- ingresses
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: prometheus-discoverer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: discoverer
subjects:
- kind: ServiceAccount
name: prometheus
namespace: monitoring
Appliquons les modifications.
$ kubectl apply -f rbac.yaml
kubernetes_sd_config
kubernetes_sd_config permet le scraping de targets depuis l'API REST Kubernetes.
Plusieurs types de roles peuvent être configurés pour la découverte de cibles:
node
service
pod
endpoint
endpointslice
ingress
Chaque rôle met à disposition différents metalabels. La liste de ces metalabels se trouvent sur ce lien.
Configuration
La syntaxe des annotations K8S se présente sous forme de paire clé/valeur. Les clés d'annotation présentent 2 segments. Un préfixe optionnel et un nom, séparé par un
/
.
Partons du principe que nous créons une application sous forme de déploiement ainsi que son service de type ClusterIP associé. L’application utilisée ici pour la démo est RabbitMQ, qui expose un path pour la collecte de métriques au format Prometheus sur le port 15692.
Prometheus
Procédons à la création des manifestes pour l’installation de Prometheus
# prometheus.yaml
apiVersion: v1
kind: Service
metadata:
name: prometheus-service
namespace: monitoring
annotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '9090'
spec:
selector:
app: prometheus
type: NodePort
ports:
- port: 8080
targetPort: 9090
nodePort: 30909
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus
namespace: monitoring
labels:
app: prometheus
spec:
replicas: 1
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
selector:
matchLabels:
app: prometheus
template:
metadata:
labels:
app: prometheus
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
spec:
serviceAccountName: prometheus
containers:
- name: prometheus
image: prom/prometheus
args:
- '--storage.tsdb.retention=6h'
- '--storage.tsdb.path=/prometheus'
- '--config.file=/etc/prometheus/prometheus.yml'
ports:
- name: web
containerPort: 9090
volumeMounts:
- name: prometheus-config-volume
mountPath: /etc/prometheus
volumes:
- name: prometheus-config-volume
configMap:
defaultMode: 420
name: prometheus-config
---
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: monitoring
labels:
name: prometheus-config
data:
prometheus.yml: |-
scrape_configs:
- job_name: 'kubernetes-service-endpoints'
scrape_interval: 10s
scrape_timeout: 5s
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- source_labels: [__meta_kubernetes_service_annotation_io_prometheus_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_service_annotation_io_prometheus_scheme]
action: replace
target_label: __scheme__
regex: (https?)
- source_labels: [__meta_kubernetes_service_annotation_io_prometheus_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels: [__address__, __meta_kubernetes_service_annotation_io_prometheus_port]
action: replace
target_label: __address__
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_service_name]
action: replace
target_label: kubernetes_service
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: kubernetes_pod
- source_labels: [__meta_kubernetes_service_annotation_io_prometheus_app]
action: replace
target_label: app
En consultant la documentation, nous pouvons voir que, pour le role: service
il existe un metalabel appelé __meta_kubernetes_service_annotation_<annotationname>
qui va faire le mapping avec l'annotation correspondante (slug) dans l'objet service.
Un slug est une représentation textuelle simplifiée d'une ressource (ou de son titre) et dont le format lui permet d'être passé en paramètre d'une url tout comme un identifiant. Le format n'est pas normalisé, mais il respecte au minimum ces quelques règles :
Uniquement des caractères minuscules
Pas de caractères accentués
Aucun espace ni aucun caractère spécial à part "-" ou "_"
Arrêtons nous sur l’analyse du ConfigMap:
- source_labels: [__meta_kubernetes_service_annotation_io_prometheus_scrape]
action: keep
regex: true
⬆️ Cette première instruction permet de définir si nous voulons scraper ou pas les métriques. Seule la valeur à true permettra à Prometheus de venir scraper de la data.
- source_labels: [__meta_kubernetes_service_annotation_io_prometheus_scheme]
action: replace
target_label: __scheme__
regex: (https?)
- source_labels: [__meta_kubernetes_service_annotation_io_prometheus_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels: [__address__, __meta_kubernetes_service_annotation_io_prometheus_port]
action: replace
target_label: __address__
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
⬆️ Ces instructions vont définir ce que nous évoquions plus haut, à savoir la construction de l’URL que va scraper Prometheus.
Souvenez vous, Prometheus attend l’URL de scraping avec les labels suivants __scheme__://__address____metrics_path__
source_labels
peut être une liste de labels concaténés en utilisant un séparateur qui peut être personnalisé et qui est ;
par défaut.
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_service_name]
action: replace
target_label: kubernetes_service
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: kubernetes_pod
⬆️ Ici, nous récupérons et transformons certains metalabels mis à disposition nativement par des labels qui pourront nous être utiles lors de requêtes PromQL.
- source_labels: [__meta_kubernetes_service_annotation_io_prometheus_app]
action: replace
target_label: app
⬆️ Il est possible, comme le montre le code au-dessus de créer des labels personnalisés depuis les metalabels __meta_kubernetes_service_annotation_<annotationname>
que nous transformons par un label particulier, ici app.
Après les différents relabels évalués, la cible des métriques de l'application sera scapée sous cette forme : __scheme__://__address____metrics_path__
Installons maintenant Prometheus
$ kubectl apply -f prometheus.yaml
Application cible (RabbitMQ)
Procédons maintenant à la création des manifestes pour notre application RabbitMQ
# rabbitmq.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: rabbitmq
namespace: default
labels:
app: rabbitmq
spec:
replicas: 1
selector:
matchLabels:
app: rabbitmq
template:
metadata:
labels:
app: rabbitmq
spec:
containers:
- name: rabbitmq
image: rabbitmq:3-management
imagePullPolicy: Always
ports:
- containerPort: 15692
---
apiVersion: v1
kind: Service
metadata:
namespace: default
name: rabbitmq
annotations:
io.prometheus/app: "rabbitmq"
io.prometheus/scrape: "true"
io.prometheus/scheme: "http"
io.prometheus/path: "/metrics"
io.prometheus/port: "15692"
spec:
selector:
app: rabbitmq
ports:
- port: 15692
Procédons à l’installation
$ kubectl apply -f rabbitmq.yaml
Il ne nous reste plus qu’à vérifier que Prometheus a bien pris en compte l’arrivée de notre application RabbitMQ et que le scraping est maintenant actif.
Great !
Conclusion
Nous sommes maintenant parfaitement capables de pouvoir scraper n’importe quelle nouvelle solution applicative installée sur notre cluster Kubernetes sans même devoir toucher à la configuration Prometheus mais simplement en y ajoutant certaines annnotations sur l’objet Kubernetes service de notre application.
Références
Subscribe to my newsletter
Read articles from Bruno directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Bruno
Bruno
Depuis août 2024, j'accompagne divers projets sur l'optimisation des processus DevOps. Ces compétences, acquises par plusieurs années d'expérience dans le domaine de l'IT, me permettent de contribuer de manière significative à la réussite et l'évolution des infrastructures de mes clients. Mon but est d'apporter une expertise technique pour soutenir la mission et les valeurs de mes clients, en garantissant la scalabilité et l'efficacité de leurs services IT.