Monitoring PVE 8 via Prometheus on Kubernetes: Difference between revisions

From Jwiki
No edit summary
No edit summary
Line 44: Line 44:


<syntaxhighlight lang="yaml">
<syntaxhighlight lang="yaml">
# ===================================================================
# ==        CONFIGURATION FOR PROXMOX HOST: ahsoka                ==
# ===================================================================
# ---
# 1. Secret for "ahsoka" - This holds the unique token components for this host.
apiVersion: v1
apiVersion: v1
kind: Secret
kind: Secret
metadata:
metadata:
   name: jgy-pve-exporter-ahsoka-auth
   name: jgy-pve-exporter-secrets
   namespace: monitoring # Or your preferred namespace
   namespace: monitoring
type: Opaque
stringData:
stringData:
   # The user part of the token
   ahsoka-token: "asd"
   PVE_USER: "pve-exporter@pve"
   thrawn-token: "asd"
  # The name (ID) of the API token
---
  PVE_TOKEN_NAME: "exporter-token" # e.g., exporter-token
apiVersion: v1
  # The secret value of the API token
kind: ConfigMap
  PVE_TOKEN_VALUE: "<token>"
metadata:
  name: jgy-pve-exporter-config-template
  namespace: monitoring
data:
  pve.yml: |
    # --- Module for ahsoka ---
    ahsoka:
      user: pve-exporter@pve
      token_name: ahsoka-token
      token_value: "${PVE_AHSOKA_TOKEN}"
      verify_ssl: false
 
    # --- Module for thrawn ---
    thrawn:
      user: pve-exporter@pve
      token_name: thrawn-token
      token_value: "${PVE_THRAWN_TOKEN}"
      verify_ssl: false
---
---
# 2. Deployment for the "jgy-pve-exporter-ahsoka" instance
apiVersion: apps/v1
apiVersion: apps/v1
kind: Deployment
kind: Deployment
metadata:
metadata:
   name: jgy-pve-exporter-ahsoka
   name: jgy-pve-exporter
   namespace: monitoring
   namespace: monitoring
   labels:
   labels:
     app: jgy-pve-exporter-ahsoka
     app: jgy-pve-exporter
spec:
spec:
   replicas: 1
   replicas: 1
   selector:
   selector:
     matchLabels:
     matchLabels:
       app: jgy-pve-exporter-ahsoka
       app: jgy-pve-exporter
   template:
   template:
     metadata:
     metadata:
       labels:
       labels:
         app: jgy-pve-exporter-ahsoka
         app: jgy-pve-exporter
     spec:
     spec:
      volumes:
      - name: config-template-volume
        configMap:
          name: jgy-pve-exporter-config-template
      - name: processed-config-volume
        emptyDir: {}
      - name: tmp
        emptyDir: {}
      initContainers:
      - name: init-config-secrets
        image: busybox:1.36
        command: ['/bin/sh', '-c']
        args:
        - |
          sed -e "s|\${PVE_AHSOKA_TOKEN}|${PVE_AHSOKA_TOKEN}|g" \
              -e "s|\${PVE_THRAWN_TOKEN}|${PVE_THRAWN_TOKEN}|g" \
              /etc/config-template/pve.yml > /etc/processed-config/pve.yml
        env:
        - name: PVE_AHSOKA_TOKEN
          valueFrom:
            secretKeyRef:
              name: jgy-pve-exporter-secrets
              key: ahsoka-token
        - name: PVE_THRAWN_TOKEN
          valueFrom:
            secretKeyRef:
              name: jgy-pve-exporter-secrets
              key: thrawn-token
        volumeMounts:
        - name: config-template-volume
          mountPath: /etc/config-template
          readOnly: true
        - name: processed-config-volume
          mountPath: /etc/processed-config
       containers:
       containers:
       - name: pve-exporter
       - name: pve-exporter
         image: prompve/prometheus-pve-exporter:3.5.5
         image: prompve/prometheus-pve-exporter:3.5.5
         args:
         args:
        - "--config.file=/etc/prometheus/pve.yml"
         - "--web.listen-address=:9106"
         - "--web.listen-address=:9106"
         ports:
         ports:
         - name: http-metrics
         - name: http-metrics
           containerPort: 9106
           containerPort: 9106
        env:
          protocol: TCP
         - name: PVE_USER
         livenessProbe:
           valueFrom:
           httpGet:
             secretKeyRef:
             path: /
              name: jgy-pve-exporter-ahsoka-auth
            port: http-metrics
              key: PVE_USER
          initialDelaySeconds: 10
         - name: PVE_TOKEN_NAME
          periodSeconds: 15
           valueFrom:
         readinessProbe:
             secretKeyRef:
           httpGet:
              name: jgy-pve-exporter-ahsoka-auth
             path: /
              key: PVE_TOKEN_NAME
            port: http-metrics
         - name: PVE_TOKEN_VALUE
          initialDelaySeconds: 5
           valueFrom:
          periodSeconds: 5
             secretKeyRef:
         securityContext:
              name: jgy-pve-exporter-ahsoka-auth
          runAsNonRoot: true
              key: PVE_TOKEN_VALUE
          runAsUser: 1000
         - name: PVE_VERIFY_SSL
          readOnlyRootFilesystem: true
           value: "false"
          allowPrivilegeEscalation: false
           capabilities:
             drop:
            - ALL
        volumeMounts:
        - name: processed-config-volume
          mountPath: /etc/prometheus
          readOnly: true
         - name: tmp
           mountPath: /tmp
         resources:
         resources:
           requests:
           requests:
             cpu: 50m
             cpu: '0'
             memory: 64Mi
             memory: 128Mi
           limits:
           limits:
             cpu: 100m
             cpu: '0'
             memory: 128Mi
             memory: 256Mi
---
---
# 3. Service for the "jgy-pve-exporter-ahsoka" instance
apiVersion: v1
apiVersion: v1
kind: Service
kind: Service
metadata:
metadata:
   name: jgy-pve-exporter-ahsoka
   name: jgy-pve-exporter
   namespace: monitoring
   namespace: monitoring
   labels:
   labels:
     app: jgy-pve-exporter-ahsoka
     app: jgy-pve-exporter
spec:
spec:
   selector:
   selector:
     app: jgy-pve-exporter-ahsoka
     app: jgy-pve-exporter
   ports:
   ports:
   - name: http-metrics
   - name: http-metrics
     port: 9106
     port: 9106
     targetPort: "http-metrics"
     targetPort: http-metrics
---
# 4. ServiceMonitor for the "jgy-pve-exporter-ahsoka" instance
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: jgy-pve-exporter-ahsoka
  namespace: monitoring
  labels:
    release: prometheus # Label must match your Prometheus Operator's discovery selector
spec:
  selector:
    matchLabels:
      app: jgy-pve-exporter-ahsoka
  endpoints:
  - port: "http-metrics"
    path: /pve
    params:
      target:
      - "ahsoka.tatooine.jgy.local" # <-- Replace with your Proxmox host's FQDN or IP
    relabelings:
    - sourceLabels: [__param_target]
      targetLabel: instance
</syntaxhighlight>
</syntaxhighlight>



Revision as of 16:59, 29 August 2025

Monitor Proxmox with Prometheus Exporter on Kubernetes

This guide outlines a robust and secure method for deploying the `prometheus-pve-exporter` to a Kubernetes cluster. Since it is a security best practice to use a unique API token for each host, this guide details how to deploy a dedicated exporter instance for each host you wish to monitor.

1. Create a Read-Only User and API Token on Each Proxmox Host

This one-time setup must be performed on each Proxmox host you want to monitor. This process ensures a clean permission set and avoids common access control list (ACL) conflicts. Connect to your host via SSH and run the following commands.

The script below will:

  • Create a user named `pve-exporter@pve` for monitoring.
  • Assign the built-in `PVEAuditor` role to the new user.
  • Create an API token named `exporter-token`.
  • Assign the `PVEAuditor` role directly to the API token to override any potential ACL inheritance issues.
# 1. Create the user
pveum useradd pve-exporter@pve

# 2. Assign the standard PVEAuditor role to the USER
pveum aclmod / -user pve-exporter@pve -role PVEAuditor

# 3. Create the API token for the user
pveum user token add pve-exporter@pve exporter-token

# 4. THE CRITICAL STEP: Grant the PVEAuditor role DIRECTLY to the API TOKEN
pveum aclmod / -token 'pve-exporter@pve!exporter-token' -role PVEAuditor

Important: The `pveum user token add` command will output the Token ID (e.g., `pve-exporter@pve!exporter-token`) and the Secret Value. Copy the full secret value immediately, as you will not be able to see it again.

(Optional) Cleanup Script

If you need to re-run the setup on a host, first delete the old resources to ensure a clean state.

pveum aclmod / -delete 1 -token 'pve-exporter@pve!exporter-token'
pveum user token remove pve-exporter@pve exporter-token
pveum userdel pve-exporter@pve

2. Create the Kubernetes Manifest for Each Host

On your local machine, create a single YAML file (e.g., `pve-exporters.yaml`). This file will contain a separate set of Kubernetes resources for each Proxmox host. Below is the complete template for a host named `ahsoka`.

Important: Before saving, replace the placeholder values:

  • `YOUR_TOKEN_NAME`: The name of your token (e.g., `exporter-token`).
  • `YOUR_API_TOKEN_SECRET`: The secret value you just generated.
  • `ahsoka.tatooine.jgy.local`: Update with your Proxmox host's fully qualified domain name or IP address.
apiVersion: v1
kind: Secret
metadata:
  name: jgy-pve-exporter-secrets
  namespace: monitoring
type: Opaque
stringData:
  ahsoka-token: "asd"
  thrawn-token: "asd"
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: jgy-pve-exporter-config-template
  namespace: monitoring
data:
  pve.yml: |
    # --- Module for ahsoka ---
    ahsoka:
      user: pve-exporter@pve
      token_name: ahsoka-token
      token_value: "${PVE_AHSOKA_TOKEN}"
      verify_ssl: false

    # --- Module for thrawn ---
    thrawn:
      user: pve-exporter@pve
      token_name: thrawn-token
      token_value: "${PVE_THRAWN_TOKEN}"
      verify_ssl: false
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jgy-pve-exporter
  namespace: monitoring
  labels:
    app: jgy-pve-exporter
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jgy-pve-exporter
  template:
    metadata:
      labels:
        app: jgy-pve-exporter
    spec:
      volumes:
      - name: config-template-volume
        configMap:
          name: jgy-pve-exporter-config-template
      - name: processed-config-volume
        emptyDir: {}
      - name: tmp
        emptyDir: {}
      initContainers:
      - name: init-config-secrets
        image: busybox:1.36
        command: ['/bin/sh', '-c']
        args:
        - |
          sed -e "s|\${PVE_AHSOKA_TOKEN}|${PVE_AHSOKA_TOKEN}|g" \
              -e "s|\${PVE_THRAWN_TOKEN}|${PVE_THRAWN_TOKEN}|g" \
              /etc/config-template/pve.yml > /etc/processed-config/pve.yml
        env:
        - name: PVE_AHSOKA_TOKEN
          valueFrom:
            secretKeyRef:
              name: jgy-pve-exporter-secrets
              key: ahsoka-token
        - name: PVE_THRAWN_TOKEN
          valueFrom:
            secretKeyRef:
              name: jgy-pve-exporter-secrets
              key: thrawn-token
        volumeMounts:
        - name: config-template-volume
          mountPath: /etc/config-template
          readOnly: true
        - name: processed-config-volume
          mountPath: /etc/processed-config
      containers:
      - name: pve-exporter
        image: prompve/prometheus-pve-exporter:3.5.5
        args:
        - "--config.file=/etc/prometheus/pve.yml"
        - "--web.listen-address=:9106"
        ports:
        - name: http-metrics
          containerPort: 9106
          protocol: TCP
        livenessProbe:
          httpGet:
            path: /
            port: http-metrics
          initialDelaySeconds: 10
          periodSeconds: 15
        readinessProbe:
          httpGet:
            path: /
            port: http-metrics
          initialDelaySeconds: 5
          periodSeconds: 5
        securityContext:
          runAsNonRoot: true
          runAsUser: 1000
          readOnlyRootFilesystem: true
          allowPrivilegeEscalation: false
          capabilities:
            drop:
            - ALL
        volumeMounts:
        - name: processed-config-volume
          mountPath: /etc/prometheus
          readOnly: true
        - name: tmp
          mountPath: /tmp
        resources:
          requests:
            cpu: '0'
            memory: 128Mi
          limits:
            cpu: '0'
            memory: 256Mi
---
apiVersion: v1
kind: Service
metadata:
  name: jgy-pve-exporter
  namespace: monitoring
  labels:
    app: jgy-pve-exporter
spec:
  selector:
    app: jgy-pve-exporter
  ports:
  - name: http-metrics
    port: 9106
    targetPort: http-metrics

To monitor additional hosts, copy and paste this entire four-document block into the same file, then perform a find-and-replace for `ahsoka` with your new host's name (e.g., `thrawn`) and update the new host's unique credentials and target address.

3. Apply the Kubernetes Manifest

Apply the single YAML file to your cluster to deploy all resources.

kubectl apply -f pve-exporters.yaml

4. Verify the Deployment

Check that the pods are running and that Prometheus is successfully scraping the targets.

# Check pod status for all exporters, replacing with your host names
kubectl get pods -n monitoring -l app --selector='app in (jgy-pve-exporter-ahsoka, jgy-pve-exporter-thrawn)'

After a minute, navigate to your Prometheus UI, go to Status -> Targets, and verify that targets for `jgy-pve-exporter-ahsoka` (and any others you deployed) are present and have a state of UP.

Notes:

  • The `PVE_VERIFY_SSL: "false"` setting is used because Proxmox VE defaults to a self-signed SSL certificate. Set to `"true"` if you use a valid, trusted certificate.
  • The `ServiceMonitor` resource is intended for clusters running the Prometheus Operator. If you are not using it, you will need to add the scrape configuration directly to your `prometheus.yml` file.
  • All Kubernetes resources are deployed to the `monitoring` namespace. Adjust if you use a different one.