diff --git a/README.md b/README.md index 32a4f952..eacc6193 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ helm install policy-reporter policy-reporter/policy-reporter --set loki=http://l ``` You can also customize the `./charts/policy-reporter/values.yaml` to change the default configurations. -### Configure policyPriorities +### Configure Policy Priorities By default kyverno PolicyReports has no priority or severity for policies. So every passed rule validation will be processed as notice, a failed validation is processed as error. To customize this you can configure a mapping from policies to fail priorities. So you can send them as warnings instead of errors. To configure the priorities create a ConfigMap in the `policy-reporter` namespace with the name `policy-reporter-config`. This ConfigMap have to have a property `config.yaml` with the map as YAML content. See the Example for Detailes. @@ -33,6 +33,26 @@ policy_priorities: kubectl create configmap policy-reporter-config --from-file=config.yaml -n policy-reporter ``` +## Monitoring + +The Helm Chart includes optional Manifests for the [MonitoringStack](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack). The provided Dashboard works without Loki + +* Enable a ServiceMonitor by setting `metrics.serviceMonitor` to `true`. +* Enable a basic Dashboard as ConfigMap by setting `metrics.dashboard.enabled` to `true`. + * Change the namespace to your required monitoring namespace by changing `metrics.dashboard.namespace` (default: cattle-dashboards) + + +If you are not using the MonitoringStack you can get the dashboard configuration from this [Gist](https://gist.github.com/fjogeleit/bf540421fd28989fc92841177be972bc) + +Example Installation +```bash +helm install policy-reporter policy-reporter/policy-reporter --set metrics.serviceMonitor=true --set metrics.dashboard.enabled=true -n policy-reporter --create-namespace +``` + +#### Dashboard Preview + +![PolicyReporter Grafana Dashboard](https://github.com/fjogeleit/policy-reporter/blob/main/docs/images/policy-reports-dashboard.png?raw=true) + ## Example Outputs ![Grafana Loki](https://github.com/fjogeleit/policy-reporter/blob/main/docs/images/grafana-loki.png?raw=true) diff --git a/charts/policy-reporter/Chart.yaml b/charts/policy-reporter/Chart.yaml index c4bb2f54..56032ae5 100644 --- a/charts/policy-reporter/Chart.yaml +++ b/charts/policy-reporter/Chart.yaml @@ -3,5 +3,5 @@ name: policy-reporter description: K8s PolicyReporter watches for wgpolicyk8s.io/v1alpha1.PolicyReport resources. It creates Prometheus Metrics and can send rule validation events to Loki type: application -version: 0.2.0 +version: 0.3.0 appVersion: 0.2.0 diff --git a/charts/policy-reporter/templates/dashboard.yaml b/charts/policy-reporter/templates/dashboard.yaml new file mode 100644 index 00000000..b4e35313 --- /dev/null +++ b/charts/policy-reporter/templates/dashboard.yaml @@ -0,0 +1,465 @@ +{{- if .Values.metrics.dashboard.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "policyreporter.fullname" . }}-dashboard + namespace: {{ .Values.metrics.dashboard.namespace }} + labels: + grafana_dashboard: "1" +data: + policy-reporter-dashboard.json: | + { + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.1.5" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 35, + "links": [], + "panels": [ + { + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": { + "align": null + }, + "decimals": 0, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "orange", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 15, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "displayMode": "gradient", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showUnfilled": true + }, + "pluginVersion": "7.1.5", + "targets": [ + { + "expr": "sum(policy_report_summary{status=\"Fail\"} > 0) by (exported_namespace)", + "instant": false, + "interval": "", + "legendFormat": "{{`{{ exported_namespace }}`}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Failing Policies by Namespace", + "type": "bargauge" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {}, + "decimals": 0, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 3 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 9, + "x": 15, + "y": 0 + }, + "id": 5, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "vertical", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.1.5", + "targets": [ + { + "expr": "sum(cluster_policy_report_summary{status=~\"Fail|Error\"} > 0) by (status)", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{`{{ status }}`}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Failing ClusterPolicies", + "type": "stat" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 10 + }, + "hiddenSeries": false, + "id": 11, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(cluster_policy_report_result{status=~\"fail|error\"} > 0) by (policy)", + "interval": "", + "legendFormat": "{{` {{ rule }}` }}", + "refId": "A" + }, + { + "expr": "sum(policy_report_result{status=~\"fail|error\"} > 0) by (policy)", + "interval": "", + "legendFormat": "{{` {{ rule }}` }}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Failing Policies Graph", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": { + "align": null + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 18 + }, + "id": 7, + "options": { + "showHeader": true + }, + "pluginVersion": "7.1.5", + "targets": [ + { + "expr": "sum(policy_report_result{status=~\"fail|error\"}) by (exported_namespace,policy,rule,kind,name,status)", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{`{{exported_namespace}}`}}: {{`{{ policy }}`}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Failing PolicyRules", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "Value": true + }, + "indexByName": { + "Time": 0, + "Value": 6, + "exported_namespace": 3, + "kind": 4, + "name": 5, + "policy": 1, + "rule": 2 + }, + "renameByName": { + "exported_namespace": "namespace" + } + } + } + ], + "type": "table" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": { + "align": null + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 28 + }, + "id": 9, + "options": { + "showHeader": true + }, + "pluginVersion": "7.1.5", + "targets": [ + { + "expr": "cluster_policy_report_result > 0", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{`{{ kind }}`}}: {{`{{ name }}`}} - {{`{{ policy }}`}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Failing ClusterPolicyRules", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "Value": true, + "__name__": true, + "endpoint": true, + "instance": true, + "job": true, + "namespace": true, + "pod": true, + "report": true, + "service": true + }, + "indexByName": { + "Time": 0, + "Value": 14, + "__name__": 1, + "endpoint": 2, + "instance": 3, + "job": 4, + "kind": 11, + "name": 12, + "namespace": 5, + "pod": 6, + "policy": 9, + "report": 7, + "rule": 10, + "service": 8, + "status": 13 + }, + "renameByName": { + "__name__": "" + } + } + } + ], + "type": "table" + } + ], + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "PolicyReports", + "uid": "5ivy3MyGz", + "version": 2 + } +{{- end }} \ No newline at end of file diff --git a/charts/policy-reporter/templates/servicemonitor.yaml b/charts/policy-reporter/templates/servicemonitor.yaml new file mode 100644 index 00000000..2eb22d06 --- /dev/null +++ b/charts/policy-reporter/templates/servicemonitor.yaml @@ -0,0 +1,13 @@ +{{- if .Values.metrics.serviceMonitor }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "policyreporter.fullname" . }} + namespace: policy-reporter +spec: + selector: + matchLabels: + {{- include "policyreporter.selectorLabels" . | nindent 8 }} + endpoints: + - port: http +{{- end }} diff --git a/charts/policy-reporter/values.yaml b/charts/policy-reporter/values.yaml index 73a037bb..6960b8e5 100644 --- a/charts/policy-reporter/values.yaml +++ b/charts/policy-reporter/values.yaml @@ -1,13 +1,23 @@ loki: host: http://loki.loki-stack.svc.cluster.local:3100 + +metrics: + serviceMonitor: false + dashboard: + enabled: false + namespace: cattle-dashboards + image: repository: fjogeleit/policy-reporter pullPolicy: IfNotPresent tag: 0.2.0 + imagePullSecrets: [] + service: type: ClusterIP port: 2112 + resources: requests: memory: 50Mi diff --git a/docs/images/policy-reports-dashboard.png b/docs/images/policy-reports-dashboard.png new file mode 100644 index 00000000..278cf30c Binary files /dev/null and b/docs/images/policy-reports-dashboard.png differ