K3s monitoring

Nadat de Kubernetes Cluster ‘up-and-running’ is en er pods met applicaties op draaien, willen we de status van het cluster gaan monitoren. Datgene dat gemonitored kan worden dient ‘gescraped‘ te worden en voor Kubernetes is Prometheus daar de uitgelezen tool voor. Scrapen van de systeem-eigenschappen van de node(s) zelf kan heel goed met ‘node-exporter‘.

NameSpace

De monitoring functies zetten we in een nameSpace genaamd ‘monitoring’ Deze wordt aangemaakt met:

$ kubectl create namespace monitoring

Node Exporter

De nodes worden voorzien van een DaemonSet. Dit is een pod die door Kubernetes op alle nodes geplaatst en uitgevoerd wordt. In dit geval dus met een node-exporter als applicatie zodat de metrics van de nodes uitgelezen kunnen worden. De manifest ziet er alsvolgt uit in node-exporter-daemonset.yml:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
  namespace: monitoring
  labels:
    k8s-app: node-exporter
spec:
  selector:
    matchLabels:
      name: node-exporter
  template:
    metadata:
      labels:
        name: node-exporter
    spec:
      tolerations:
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      containers:
      - name: node-exporter
        image: nokkie/node-exporter:arm64
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi

De tolerations zorgt ervoor dat deze DaemonSet ook op de master-node(s) geïnstalleerd wordt.

Het starten van de DaemonSet gaat met het volgende commando:

$ kubectl apply -f node-exporter-daemonset.yml

Node Exporter Service

Om de metrics van de node-exporters te kunnen benaderen wordt op elke node een service aangemaakt die de metrics ‘exposed’ op TCP-port 9100. Dit is het voordeel van een standaard K3s installatie waarbij traefik de loadbalancer service verzorgt op de nodes. De node-exporter-svc.yml:

---
apiVersion: v1
kind: Service
metadata:
  name: node-exporter-service
  namespace: monitoring
spec:
  selector: 
    name: node-exporter
  type: LoadBalancer
  ports:
    - port: 9100
      targetPort: 9100
$ kubectl apply -f node-exporter-svc.yml

Ter controle kunnen we nu de metrics op elke node opvragen met:

$ curl http://[node-name]:9100/metrics

Prometheus roles

Kubernetes heeft serviceAccounts waarmee ‘rollen’ ingesteld kunnen worden die uitgevoerd kunnen worden met deze serviceAccounts. De rollen worden dan via ‘RoleBindings’ aan de serviceAccount gekoppeld. Een manifest om serviceAccount, Roles en RoleBindings te configureren ziet er alsvolgt uit in prometheus-roles.yml:

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: prometheus
rules:
- apiGroups: [""]
  resources:
  - nodes
  - nodes/proxy
  - services
  - endpoints
  - pods
  verbs: ["get", "list", "watch"]
- apiGroups:
  - extensions
  resources:
  - ingresses
  verbs: ["get", "list", "watch"]
- nonResourceURLs: ["/metrics"]
  verbs: ["get"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: default
  namespace: monitoring
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: prometheus
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: prometheus
subjects:
- kind: ServiceAccount
  name: default
  namespace: monitoring

Om deze in te lezen en te configureren dient het volgende commando:

$ kubectl apply -f prometheus-roles.yml

Prometheus ConfigMap

Prometheus verzamelt de metrics van de aangesloten targets. De targets zijn de nodes waarop de node-exporter draait (met de ingestelde ports), dus die gaan we vervolgens in een configuratie-bestand zetten. We geven binnen Kubernetes de instructie aan Prometheus om dit configuratiebestand in te lezen via een ‘ConfigMap’. De inhoud van prometheus-config.yml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-config
  namespace: monitoring
data:
  prometheus.yml: |
    global:
      scrape_interval:     15s 
      evaluation_interval: 15s 

    # Alertmanager configuration
    alerting:
      alertmanagers:
      - static_configs:
        - targets:
          # - alertmanager:9093

    scrape_configs:

      - job_name: 'prometheus'
        static_configs:
        - targets: ['localhost:9090']

      - job_name: 'rpi-1'
        static_configs:
        - targets: ['rpi-1:9100']

      - job_name: 'rpi-2'
        static_configs:
        - targets: ['rpi-2:9100']

      - job_name: 'rpi-3'
        static_configs:
        - targets: ['rpi-3:9100']

Het ‘scrapen‘ (ophalen van de metrics) laten we elke 15 seconden uitvoeren, de ‘alertManager‘ stellen we een andere keer in en de ‘targets‘ zijn de RaspBerry Pi Nodes van het cluster. De TCP-port is ingesteld via de loadbalancer van de node-exporter services. De commando’s om deze configMap in te stellen:

$ kubectl apply f prometheus-config.yml

Prometheus Deployment

De deployment van Prometheus leest de configuratie van de configMap in en zet deze in een volume van het type emptyDir. De prometheus-deployment.yml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: prometheus-deployment
  namespace: monitoring
  labels:
    app: prometheus-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: prometheus-server
  template:
    metadata:
      labels:
        app: prometheus-server
    spec:
      containers:
        - name: prometheus
          image: prom/prometheus
          args:
            - "--config.file=/etc/prometheus/prometheus.yml"
            - "--storage.tsdb.path=/prometheus/"
          ports:
            - containerPort: 9090
          volumeMounts:
            - name: prometheus-config-volume
              mountPath: /etc/prometheus/
            - name: prometheus-storage-volume
              mountPath: /prometheus/
      volumes:
        - name: prometheus-config-volume
          configMap:
            defaultMode: 420
            name: prometheus-config
  
        - name: prometheus-storage-volume
          emptyDir: {}

Prometheus Service

Om Prometheus te ‘exposen‘ maken we wederom dankbaar gebruik van traefik en wordt een Service aangemaakt van het type loadBalancer die luistert op TCP poort 9090 en de aanvraag doorzet naar dezelfde TCP poort 9090 van Prometheus. De manifest voor de Service is prometheus-service.yml:

---
apiVersion: v1
kind: Service
metadata:
  name: prometheus-service
  namespace: monitoring
spec:
  selector: 
    app: prometheus-server
  type: LoadBalancer
  ports:
    - port: 9090
      targetPort: 9090 

en wordt met de volgende commando’s geconfigureerd en gecontroleerd:

$ kubectl apply -f prometheus-service.yml
$ kubectl get services -n monitoring
NAME                    TYPE           CLUSTER-IP     EXTERNAL-IP                                    PORT(S)          AGE
node-exporter-service   LoadBalancer   10.43.236.32   192.168.178.50,192.168.178.51,192.168.178.52   9100:32280/TCP   9m17s
prometheus-service      LoadBalancer   10.43.74.141   192.168.178.50,192.168.178.51,192.168.178.52   9090:32244/TCP   12m

De Prometheus-service kan nu in een browser aangeroepen worden op de TCP poort 9090, http://node-naam:9090

Via de ‘Status’ pagina kunnen de ‘Targets‘ bekeken worden en die zouden dan allen de status ‘UP’ moeten hebben.

In Prometheus kunnen metrics opgevraagd worden met de zogenaamde PromQL (Prometheus Query Language) en met behulp van ‘Grafana‘ kunnen de resultaten van de queries grafisch weergegeven worden.


Grafana

Om Grafana uit te rollen als deployment, de service in te stellen en om met behulp van een traefik loadBalancer de web-pagina te kunnen benaderen, zetten we dit in een manifest dat er zo uit ziet in grafana-deployment.yml:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: grafana
  namespace: monitoring
  labels:
    app: grafana
spec:
  replicas: 1
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  selector:
    matchLabels:
      app: grafana
  template:
    metadata:
      labels:
        app: grafana
    spec:
      containers:
      - image: grafana/grafana
        imagePullPolicy: Always
        name: grafana
---
apiVersion: v1
kind: Service
metadata:
  name: grafana-svc
  namespace: monitoring
spec:
  selector: 
    app: grafana
  type: LoadBalancer
  ports:
    - port: 3000
      targetPort: 3000 
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: grafana-ingress
  namespace: monitoring
spec:
  rules:
  - host: grafana-rpi.digitalinfo.local
    http:
      paths:
      - backend:
          serviceName: grafana-svc
          servicePort: 3000
        path: /
        pathType: ImplementationSpecific

Het bijbehorende commando:

$ kubectl apply -f grafana-deployment.yml

Er is nu een DNS verwijzing nodig naar de hostname die in de ingress is gedefinieerd: grafana-rpi.digitalinfo.local Dit kan in een DNS-server zijn of in een lokale hosts-file. Daarna kan met de browser de Grafana site bekeken worden:

Inloggen kan met de standaard credentials: admin/admin

Als eerste dient nu de Data-Source aangemaakt te worden waarmee de metrics vanuit Prometheus ingelezen kunnen worden. Via het tandwiel-icon (Configuration) en ‘Data Sources’ kan Prometheus toegevoegd worden.

De URL wordt ‘http://prometheus:9090’ en dit gaat werken omdat de Kubernetes service genaamd ‘prometheus’ ingesteld is op poort 9090.

Via ‘Save & Test‘ wordt de data-source opgeslagen en de connectie getest.

Een voorbeeld van een Grafana Dashboard voor Kubernetes kan ge-importeerd worden met nummer 1860 en dat Dashboard ziet er zo uit: