@bluedynamics/cdk8s-monitoring 0.3.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +1 -0
- package/README.md +17 -0
- package/lib/constructs/longhorn-monitor.d.ts +24 -0
- package/lib/constructs/longhorn-monitor.js +79 -0
- package/lib/constructs/traefik-monitor.d.ts +20 -0
- package/lib/constructs/traefik-monitor.js +60 -0
- package/lib/dashboards/traefik-dashboard.d.ts +1 -0
- package/lib/dashboards/traefik-dashboard.js +749 -0
- package/lib/default-config.js +21 -1
- package/lib/imports/monitoring.coreos.com.d.ts +4240 -0
- package/lib/imports/monitoring.coreos.com.js +2213 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +3 -1
- package/lib/monitoring-chart.d.ts +9 -3
- package/lib/monitoring-chart.js +29 -4
- package/lib/types.d.ts +21 -0
- package/lib/types.js +1 -1
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -46,6 +46,7 @@ documentation/ # Sphinx/MyST docs (Diataxis, plone-doc-style)
|
|
|
46
46
|
## Design Rules
|
|
47
47
|
|
|
48
48
|
- **App-agnostic**: never add cluster- or application-specific resources (dashboards, alert rules, domains) to this library. Integration charts attach those beside `MonitoringChart`.
|
|
49
|
+
- *Exception — generic infra monitors*: monitors for infrastructure components that are generic across clusters using this stack (Traefik ingress, Longhorn storage) may live here as **opt-in, off-by-default** constructs (`config.traefik`, `config.longhorn`), including their scrape config and — gated by their own sub-toggle — the matching generic dashboard/alert. They must stay disabled unless a consumer explicitly enables them. This is *not* a license for anything cluster- or app-specific.
|
|
49
50
|
- **Defaults in the package, overrides in the chart**: universal values live in `DEFAULT_CONFIG`; cluster-specific values are required input. `mergeConfig` deep-merges.
|
|
50
51
|
- **Generated `imports/` are not hand-edited** and are excluded from lint/style.
|
|
51
52
|
|
package/README.md
CHANGED
|
@@ -71,6 +71,23 @@ new MyAppDashboardConstruct(chart, 'my-app-dashboard', { namespace: config.names
|
|
|
71
71
|
|
|
72
72
|
A dashboard construct is a `ConfigMap` (use the re-exported `KubeConfigMap`) labeled `grafana_dashboard: "1"` so the Grafana sidecar discovers it. Alert rules are plain `PrometheusRule` resources you manage in your integration repo.
|
|
73
73
|
|
|
74
|
+
### Generic infrastructure monitors (opt-in)
|
|
75
|
+
|
|
76
|
+
Monitors for infrastructure components that are generic across clusters running this stack are built in but **disabled by default** — enable them per cluster. They are the exception to the "no dashboards/alerts in the library" rule because they are not app- or cluster-specific.
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
new MonitoringChart(app, 'monitoring', mergeConfig({
|
|
80
|
+
// ...required config...
|
|
81
|
+
traefik: { enabled: true, namespace: 'traefik' }, // PodMonitor + Grafana dashboard
|
|
82
|
+
longhorn: { enabled: true }, // ServiceMonitor + volume-health alerts
|
|
83
|
+
}));
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- **`traefik`** (`TraefikConfig`): a `PodMonitor` (the k3s-bundled Traefik exposes the metrics port on the Pod, not the Service) plus, unless `dashboard: false`, a Grafana dashboard `ConfigMap`. `namespace` is where Traefik runs (default `traefik`).
|
|
87
|
+
- **`longhorn`** (`LonghornConfig`): a `ServiceMonitor` for `longhorn-manager` (port `manager`) plus, unless `alerts: false`, a `PrometheusRule` with `LonghornVolumeFaulted` (critical) / `LonghornVolumeUnhealthy` (warning). `namespace` defaults to `longhorn-system`.
|
|
88
|
+
|
|
89
|
+
Both stay inert unless `enabled: true`, so the base stack and existing consumers are unaffected.
|
|
90
|
+
|
|
74
91
|
## Prerequisites
|
|
75
92
|
|
|
76
93
|
The stack expects these to exist in the cluster:
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Construct } from 'constructs';
|
|
2
|
+
import { LonghornConfig } from '../types';
|
|
3
|
+
export interface LonghornMonitorProps {
|
|
4
|
+
/** Namespace the monitoring stack lives in (where the ServiceMonitor/PrometheusRule go). */
|
|
5
|
+
readonly monitoringNamespace: string;
|
|
6
|
+
/** Longhorn config (namespace it runs in + alerts toggle). */
|
|
7
|
+
readonly longhorn: LonghornConfig;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Generic, opt-in monitor for Longhorn distributed storage.
|
|
11
|
+
*
|
|
12
|
+
* ServiceMonitor scrapes the longhorn-manager metrics (port `manager`, 9500):
|
|
13
|
+
* volume/node/storage-pool health and capacity.
|
|
14
|
+
*
|
|
15
|
+
* Optionally ships volume-health alerts (config.longhorn.alerts). Motivated by a
|
|
16
|
+
* real incident (2026-05-21): a CNPG replica PVC went detached/unknown with no
|
|
17
|
+
* alert; noticed only 2h later via a downstream CNPG error. These rules surface
|
|
18
|
+
* storage health within minutes.
|
|
19
|
+
*
|
|
20
|
+
* longhorn_volume_robustness: 0=unknown, 1=healthy, 2=degraded, 3=faulted.
|
|
21
|
+
*/
|
|
22
|
+
export declare class LonghornMonitorConstruct extends Construct {
|
|
23
|
+
constructor(scope: Construct, id: string, props: LonghornMonitorProps);
|
|
24
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LonghornMonitorConstruct = void 0;
|
|
4
|
+
const constructs_1 = require("constructs");
|
|
5
|
+
const monitoring_coreos_com_1 = require("../imports/monitoring.coreos.com");
|
|
6
|
+
/**
|
|
7
|
+
* Generic, opt-in monitor for Longhorn distributed storage.
|
|
8
|
+
*
|
|
9
|
+
* ServiceMonitor scrapes the longhorn-manager metrics (port `manager`, 9500):
|
|
10
|
+
* volume/node/storage-pool health and capacity.
|
|
11
|
+
*
|
|
12
|
+
* Optionally ships volume-health alerts (config.longhorn.alerts). Motivated by a
|
|
13
|
+
* real incident (2026-05-21): a CNPG replica PVC went detached/unknown with no
|
|
14
|
+
* alert; noticed only 2h later via a downstream CNPG error. These rules surface
|
|
15
|
+
* storage health within minutes.
|
|
16
|
+
*
|
|
17
|
+
* longhorn_volume_robustness: 0=unknown, 1=healthy, 2=degraded, 3=faulted.
|
|
18
|
+
*/
|
|
19
|
+
class LonghornMonitorConstruct extends constructs_1.Construct {
|
|
20
|
+
constructor(scope, id, props) {
|
|
21
|
+
super(scope, id);
|
|
22
|
+
const { monitoringNamespace, longhorn } = props;
|
|
23
|
+
const labels = {
|
|
24
|
+
'app.kubernetes.io/name': 'longhorn',
|
|
25
|
+
'app.kubernetes.io/component': 'storage',
|
|
26
|
+
'app.kubernetes.io/managed-by': 'cdk8s',
|
|
27
|
+
'app.kubernetes.io/part-of': 'monitoring',
|
|
28
|
+
'release': 'kube-prometheus-stack',
|
|
29
|
+
};
|
|
30
|
+
new monitoring_coreos_com_1.ServiceMonitor(this, 'longhorn-metrics', {
|
|
31
|
+
metadata: { name: 'longhorn-metrics', namespace: monitoringNamespace, labels },
|
|
32
|
+
spec: {
|
|
33
|
+
selector: { matchLabels: { app: 'longhorn-manager' } },
|
|
34
|
+
namespaceSelector: { matchNames: [longhorn.namespace] },
|
|
35
|
+
endpoints: [
|
|
36
|
+
{ port: 'manager', path: '/metrics', interval: '30s', scrapeTimeout: '10s' },
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
if (longhorn.alerts) {
|
|
41
|
+
new monitoring_coreos_com_1.PrometheusRule(this, 'longhorn-volume-health-rules', {
|
|
42
|
+
metadata: { name: 'longhorn-volume-health', namespace: monitoringNamespace, labels },
|
|
43
|
+
spec: {
|
|
44
|
+
groups: [
|
|
45
|
+
{
|
|
46
|
+
name: 'longhorn.volume-health',
|
|
47
|
+
rules: [
|
|
48
|
+
{
|
|
49
|
+
alert: 'LonghornVolumeFaulted',
|
|
50
|
+
expr: monitoring_coreos_com_1.PrometheusRuleSpecGroupsRulesExpr.fromString('longhorn_volume_robustness == 3'),
|
|
51
|
+
for: '1m',
|
|
52
|
+
labels: { severity: 'critical' },
|
|
53
|
+
annotations: {
|
|
54
|
+
summary: 'Longhorn volume {{ $labels.volume }} is faulted',
|
|
55
|
+
description: 'Volume {{ $labels.volume }} (node={{ $labels.node }}, pvc={{ $labels.pvc }}) is in faulted state. ' +
|
|
56
|
+
'Data may be lost. Recovery: scale workload down, delete PVC, scale up so the StatefulSet/operator re-creates and re-syncs from a healthy source.',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
alert: 'LonghornVolumeUnhealthy',
|
|
61
|
+
expr: monitoring_coreos_com_1.PrometheusRuleSpecGroupsRulesExpr.fromString('longhorn_volume_robustness == 0 or longhorn_volume_robustness == 2'),
|
|
62
|
+
for: '10m',
|
|
63
|
+
labels: { severity: 'warning' },
|
|
64
|
+
annotations: {
|
|
65
|
+
summary: 'Longhorn volume {{ $labels.volume }} not healthy (robustness={{ $value }})',
|
|
66
|
+
description: 'Volume {{ $labels.volume }} (node={{ $labels.node }}, pvc={{ $labels.pvc }}) has been non-healthy for >10m. ' +
|
|
67
|
+
'robustness values: 0=unknown, 2=degraded. A short degraded window during a replica rebuild is normal; 10m+ means manual investigation.',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.LonghornMonitorConstruct = LonghornMonitorConstruct;
|
|
79
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9uZ2hvcm4tbW9uaXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb25zdHJ1Y3RzL2xvbmdob3JuLW1vbml0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsMkNBQXVDO0FBQ3ZDLDRFQUkwQztBQVUxQzs7Ozs7Ozs7Ozs7O0dBWUc7QUFDSCxNQUFhLHdCQUF5QixTQUFRLHNCQUFTO0lBQ3JELFlBQVksS0FBZ0IsRUFBRSxFQUFVLEVBQUUsS0FBMkI7UUFDbkUsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztRQUNqQixNQUFNLEVBQUUsbUJBQW1CLEVBQUUsUUFBUSxFQUFFLEdBQUcsS0FBSyxDQUFDO1FBRWhELE1BQU0sTUFBTSxHQUFHO1lBQ2Isd0JBQXdCLEVBQUUsVUFBVTtZQUNwQyw2QkFBNkIsRUFBRSxTQUFTO1lBQ3hDLDhCQUE4QixFQUFFLE9BQU87WUFDdkMsMkJBQTJCLEVBQUUsWUFBWTtZQUN6QyxTQUFTLEVBQUUsdUJBQXVCO1NBQ25DLENBQUM7UUFFRixJQUFJLHNDQUFjLENBQUMsSUFBSSxFQUFFLGtCQUFrQixFQUFFO1lBQzNDLFFBQVEsRUFBRSxFQUFFLElBQUksRUFBRSxrQkFBa0IsRUFBRSxTQUFTLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxFQUFFO1lBQzlFLElBQUksRUFBRTtnQkFDSixRQUFRLEVBQUUsRUFBRSxXQUFXLEVBQUUsRUFBRSxHQUFHLEVBQUUsa0JBQWtCLEVBQUUsRUFBRTtnQkFDdEQsaUJBQWlCLEVBQUUsRUFBRSxVQUFVLEVBQUUsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLEVBQUU7Z0JBQ3ZELFNBQVMsRUFBRTtvQkFDVCxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLGFBQWEsRUFBRSxLQUFLLEVBQUU7aUJBQzdFO2FBQ0Y7U0FDRixDQUFDLENBQUM7UUFFSCxJQUFJLFFBQVEsQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNwQixJQUFJLHNDQUFjLENBQUMsSUFBSSxFQUFFLDhCQUE4QixFQUFFO2dCQUN2RCxRQUFRLEVBQUUsRUFBRSxJQUFJLEVBQUUsd0JBQXdCLEVBQUUsU0FBUyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sRUFBRTtnQkFDcEYsSUFBSSxFQUFFO29CQUNKLE1BQU0sRUFBRTt3QkFDTjs0QkFDRSxJQUFJLEVBQUUsd0JBQXdCOzRCQUM5QixLQUFLLEVBQUU7Z0NBQ0w7b0NBQ0UsS0FBSyxFQUFFLHVCQUF1QjtvQ0FDOUIsSUFBSSxFQUFFLHlEQUFpQyxDQUFDLFVBQVUsQ0FBQyxpQ0FBaUMsQ0FBQztvQ0FDckYsR0FBRyxFQUFFLElBQUk7b0NBQ1QsTUFBTSxFQUFFLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBRTtvQ0FDaEMsV0FBVyxFQUFFO3dDQUNYLE9BQU8sRUFBRSxpREFBaUQ7d0NBQzFELFdBQVcsRUFDVCxvR0FBb0c7NENBQ3BHLGtKQUFrSjtxQ0FDcko7aUNBQ0Y7Z0NBQ0Q7b0NBQ0UsS0FBSyxFQUFFLHlCQUF5QjtvQ0FDaEMsSUFBSSxFQUFFLHlEQUFpQyxDQUFDLFVBQVUsQ0FDaEQsb0VBQW9FLENBQ3JFO29DQUNELEdBQUcsRUFBRSxLQUFLO29DQUNWLE1BQU0sRUFBRSxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUU7b0NBQy9CLFdBQVcsRUFBRTt3Q0FDWCxPQUFPLEVBQUUsNEVBQTRFO3dDQUNyRixXQUFXLEVBQ1QsOEdBQThHOzRDQUM5Ryx3SUFBd0k7cUNBQzNJO2lDQUNGOzZCQUNGO3lCQUNGO3FCQUNGO2lCQUNGO2FBQ0YsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztJQUNILENBQUM7Q0FDRjtBQWpFRCw0REFpRUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcbmltcG9ydCB7XG4gIFByb21ldGhldXNSdWxlLFxuICBQcm9tZXRoZXVzUnVsZVNwZWNHcm91cHNSdWxlc0V4cHIsXG4gIFNlcnZpY2VNb25pdG9yLFxufSBmcm9tICcuLi9pbXBvcnRzL21vbml0b3JpbmcuY29yZW9zLmNvbSc7XG5pbXBvcnQgeyBMb25naG9ybkNvbmZpZyB9IGZyb20gJy4uL3R5cGVzJztcblxuZXhwb3J0IGludGVyZmFjZSBMb25naG9ybk1vbml0b3JQcm9wcyB7XG4gIC8qKiBOYW1lc3BhY2UgdGhlIG1vbml0b3Jpbmcgc3RhY2sgbGl2ZXMgaW4gKHdoZXJlIHRoZSBTZXJ2aWNlTW9uaXRvci9Qcm9tZXRoZXVzUnVsZSBnbykuICovXG4gIHJlYWRvbmx5IG1vbml0b3JpbmdOYW1lc3BhY2U6IHN0cmluZztcbiAgLyoqIExvbmdob3JuIGNvbmZpZyAobmFtZXNwYWNlIGl0IHJ1bnMgaW4gKyBhbGVydHMgdG9nZ2xlKS4gKi9cbiAgcmVhZG9ubHkgbG9uZ2hvcm46IExvbmdob3JuQ29uZmlnO1xufVxuXG4vKipcbiAqIEdlbmVyaWMsIG9wdC1pbiBtb25pdG9yIGZvciBMb25naG9ybiBkaXN0cmlidXRlZCBzdG9yYWdlLlxuICpcbiAqIFNlcnZpY2VNb25pdG9yIHNjcmFwZXMgdGhlIGxvbmdob3JuLW1hbmFnZXIgbWV0cmljcyAocG9ydCBgbWFuYWdlcmAsIDk1MDApOlxuICogdm9sdW1lL25vZGUvc3RvcmFnZS1wb29sIGhlYWx0aCBhbmQgY2FwYWNpdHkuXG4gKlxuICogT3B0aW9uYWxseSBzaGlwcyB2b2x1bWUtaGVhbHRoIGFsZXJ0cyAoY29uZmlnLmxvbmdob3JuLmFsZXJ0cykuIE1vdGl2YXRlZCBieSBhXG4gKiByZWFsIGluY2lkZW50ICgyMDI2LTA1LTIxKTogYSBDTlBHIHJlcGxpY2EgUFZDIHdlbnQgZGV0YWNoZWQvdW5rbm93biB3aXRoIG5vXG4gKiBhbGVydDsgbm90aWNlZCBvbmx5IDJoIGxhdGVyIHZpYSBhIGRvd25zdHJlYW0gQ05QRyBlcnJvci4gVGhlc2UgcnVsZXMgc3VyZmFjZVxuICogc3RvcmFnZSBoZWFsdGggd2l0aGluIG1pbnV0ZXMuXG4gKlxuICogbG9uZ2hvcm5fdm9sdW1lX3JvYnVzdG5lc3M6IDA9dW5rbm93biwgMT1oZWFsdGh5LCAyPWRlZ3JhZGVkLCAzPWZhdWx0ZWQuXG4gKi9cbmV4cG9ydCBjbGFzcyBMb25naG9ybk1vbml0b3JDb25zdHJ1Y3QgZXh0ZW5kcyBDb25zdHJ1Y3Qge1xuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wczogTG9uZ2hvcm5Nb25pdG9yUHJvcHMpIHtcbiAgICBzdXBlcihzY29wZSwgaWQpO1xuICAgIGNvbnN0IHsgbW9uaXRvcmluZ05hbWVzcGFjZSwgbG9uZ2hvcm4gfSA9IHByb3BzO1xuXG4gICAgY29uc3QgbGFiZWxzID0ge1xuICAgICAgJ2FwcC5rdWJlcm5ldGVzLmlvL25hbWUnOiAnbG9uZ2hvcm4nLFxuICAgICAgJ2FwcC5rdWJlcm5ldGVzLmlvL2NvbXBvbmVudCc6ICdzdG9yYWdlJyxcbiAgICAgICdhcHAua3ViZXJuZXRlcy5pby9tYW5hZ2VkLWJ5JzogJ2NkazhzJyxcbiAgICAgICdhcHAua3ViZXJuZXRlcy5pby9wYXJ0LW9mJzogJ21vbml0b3JpbmcnLFxuICAgICAgJ3JlbGVhc2UnOiAna3ViZS1wcm9tZXRoZXVzLXN0YWNrJyxcbiAgICB9O1xuXG4gICAgbmV3IFNlcnZpY2VNb25pdG9yKHRoaXMsICdsb25naG9ybi1tZXRyaWNzJywge1xuICAgICAgbWV0YWRhdGE6IHsgbmFtZTogJ2xvbmdob3JuLW1ldHJpY3MnLCBuYW1lc3BhY2U6IG1vbml0b3JpbmdOYW1lc3BhY2UsIGxhYmVscyB9LFxuICAgICAgc3BlYzoge1xuICAgICAgICBzZWxlY3RvcjogeyBtYXRjaExhYmVsczogeyBhcHA6ICdsb25naG9ybi1tYW5hZ2VyJyB9IH0sXG4gICAgICAgIG5hbWVzcGFjZVNlbGVjdG9yOiB7IG1hdGNoTmFtZXM6IFtsb25naG9ybi5uYW1lc3BhY2VdIH0sXG4gICAgICAgIGVuZHBvaW50czogW1xuICAgICAgICAgIHsgcG9ydDogJ21hbmFnZXInLCBwYXRoOiAnL21ldHJpY3MnLCBpbnRlcnZhbDogJzMwcycsIHNjcmFwZVRpbWVvdXQ6ICcxMHMnIH0sXG4gICAgICAgIF0sXG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgaWYgKGxvbmdob3JuLmFsZXJ0cykge1xuICAgICAgbmV3IFByb21ldGhldXNSdWxlKHRoaXMsICdsb25naG9ybi12b2x1bWUtaGVhbHRoLXJ1bGVzJywge1xuICAgICAgICBtZXRhZGF0YTogeyBuYW1lOiAnbG9uZ2hvcm4tdm9sdW1lLWhlYWx0aCcsIG5hbWVzcGFjZTogbW9uaXRvcmluZ05hbWVzcGFjZSwgbGFiZWxzIH0sXG4gICAgICAgIHNwZWM6IHtcbiAgICAgICAgICBncm91cHM6IFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgbmFtZTogJ2xvbmdob3JuLnZvbHVtZS1oZWFsdGgnLFxuICAgICAgICAgICAgICBydWxlczogW1xuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgIGFsZXJ0OiAnTG9uZ2hvcm5Wb2x1bWVGYXVsdGVkJyxcbiAgICAgICAgICAgICAgICAgIGV4cHI6IFByb21ldGhldXNSdWxlU3BlY0dyb3Vwc1J1bGVzRXhwci5mcm9tU3RyaW5nKCdsb25naG9ybl92b2x1bWVfcm9idXN0bmVzcyA9PSAzJyksXG4gICAgICAgICAgICAgICAgICBmb3I6ICcxbScsXG4gICAgICAgICAgICAgICAgICBsYWJlbHM6IHsgc2V2ZXJpdHk6ICdjcml0aWNhbCcgfSxcbiAgICAgICAgICAgICAgICAgIGFubm90YXRpb25zOiB7XG4gICAgICAgICAgICAgICAgICAgIHN1bW1hcnk6ICdMb25naG9ybiB2b2x1bWUge3sgJGxhYmVscy52b2x1bWUgfX0gaXMgZmF1bHRlZCcsXG4gICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAgICAgICAgICAgICAgICdWb2x1bWUge3sgJGxhYmVscy52b2x1bWUgfX0gKG5vZGU9e3sgJGxhYmVscy5ub2RlIH19LCBwdmM9e3sgJGxhYmVscy5wdmMgfX0pIGlzIGluIGZhdWx0ZWQgc3RhdGUuICcgK1xuICAgICAgICAgICAgICAgICAgICAgICdEYXRhIG1heSBiZSBsb3N0LiBSZWNvdmVyeTogc2NhbGUgd29ya2xvYWQgZG93biwgZGVsZXRlIFBWQywgc2NhbGUgdXAgc28gdGhlIFN0YXRlZnVsU2V0L29wZXJhdG9yIHJlLWNyZWF0ZXMgYW5kIHJlLXN5bmNzIGZyb20gYSBoZWFsdGh5IHNvdXJjZS4nLFxuICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgIGFsZXJ0OiAnTG9uZ2hvcm5Wb2x1bWVVbmhlYWx0aHknLFxuICAgICAgICAgICAgICAgICAgZXhwcjogUHJvbWV0aGV1c1J1bGVTcGVjR3JvdXBzUnVsZXNFeHByLmZyb21TdHJpbmcoXG4gICAgICAgICAgICAgICAgICAgICdsb25naG9ybl92b2x1bWVfcm9idXN0bmVzcyA9PSAwIG9yIGxvbmdob3JuX3ZvbHVtZV9yb2J1c3RuZXNzID09IDInLFxuICAgICAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICAgICAgIGZvcjogJzEwbScsXG4gICAgICAgICAgICAgICAgICBsYWJlbHM6IHsgc2V2ZXJpdHk6ICd3YXJuaW5nJyB9LFxuICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbnM6IHtcbiAgICAgICAgICAgICAgICAgICAgc3VtbWFyeTogJ0xvbmdob3JuIHZvbHVtZSB7eyAkbGFiZWxzLnZvbHVtZSB9fSBub3QgaGVhbHRoeSAocm9idXN0bmVzcz17eyAkdmFsdWUgfX0pJyxcbiAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICAgICAgICAgICAgICAgJ1ZvbHVtZSB7eyAkbGFiZWxzLnZvbHVtZSB9fSAobm9kZT17eyAkbGFiZWxzLm5vZGUgfX0sIHB2Yz17eyAkbGFiZWxzLnB2YyB9fSkgaGFzIGJlZW4gbm9uLWhlYWx0aHkgZm9yID4xMG0uICcgK1xuICAgICAgICAgICAgICAgICAgICAgICdyb2J1c3RuZXNzIHZhbHVlczogMD11bmtub3duLCAyPWRlZ3JhZGVkLiBBIHNob3J0IGRlZ3JhZGVkIHdpbmRvdyBkdXJpbmcgYSByZXBsaWNhIHJlYnVpbGQgaXMgbm9ybWFsOyAxMG0rIG1lYW5zIG1hbnVhbCBpbnZlc3RpZ2F0aW9uLicsXG4gICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIF0sXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIF0sXG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgICB9XG4gIH1cbn1cbiJdfQ==
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Construct } from 'constructs';
|
|
2
|
+
import { TraefikConfig } from '../types';
|
|
3
|
+
export interface TraefikMonitorProps {
|
|
4
|
+
/** Namespace the monitoring stack lives in (where the PodMonitor/ConfigMap go). */
|
|
5
|
+
readonly monitoringNamespace: string;
|
|
6
|
+
/** Traefik config (namespace it runs in + dashboard toggle). */
|
|
7
|
+
readonly traefik: TraefikConfig;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Generic, opt-in monitor for a Traefik ingress controller.
|
|
11
|
+
*
|
|
12
|
+
* Uses a PodMonitor (not a ServiceMonitor): the k3s-bundled Traefik Service only
|
|
13
|
+
* exposes web/websecure; the `metrics` port is on the Pod, not the Service.
|
|
14
|
+
*
|
|
15
|
+
* Optionally ships the Traefik Grafana dashboard as a ConfigMap (grafana_dashboard
|
|
16
|
+
* label → sidecar auto-discovery). Enabled via config.traefik in MonitoringChart.
|
|
17
|
+
*/
|
|
18
|
+
export declare class TraefikMonitorConstruct extends Construct {
|
|
19
|
+
constructor(scope: Construct, id: string, props: TraefikMonitorProps);
|
|
20
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TraefikMonitorConstruct = void 0;
|
|
4
|
+
const constructs_1 = require("constructs");
|
|
5
|
+
const traefik_dashboard_1 = require("../dashboards/traefik-dashboard");
|
|
6
|
+
const k8s_1 = require("../imports/k8s");
|
|
7
|
+
const monitoring_coreos_com_1 = require("../imports/monitoring.coreos.com");
|
|
8
|
+
/**
|
|
9
|
+
* Generic, opt-in monitor for a Traefik ingress controller.
|
|
10
|
+
*
|
|
11
|
+
* Uses a PodMonitor (not a ServiceMonitor): the k3s-bundled Traefik Service only
|
|
12
|
+
* exposes web/websecure; the `metrics` port is on the Pod, not the Service.
|
|
13
|
+
*
|
|
14
|
+
* Optionally ships the Traefik Grafana dashboard as a ConfigMap (grafana_dashboard
|
|
15
|
+
* label → sidecar auto-discovery). Enabled via config.traefik in MonitoringChart.
|
|
16
|
+
*/
|
|
17
|
+
class TraefikMonitorConstruct extends constructs_1.Construct {
|
|
18
|
+
constructor(scope, id, props) {
|
|
19
|
+
super(scope, id);
|
|
20
|
+
const { monitoringNamespace, traefik } = props;
|
|
21
|
+
new monitoring_coreos_com_1.PodMonitor(this, 'traefik-metrics', {
|
|
22
|
+
metadata: {
|
|
23
|
+
name: 'traefik-metrics',
|
|
24
|
+
namespace: monitoringNamespace,
|
|
25
|
+
labels: {
|
|
26
|
+
'app.kubernetes.io/name': 'traefik',
|
|
27
|
+
'app.kubernetes.io/component': 'ingress-controller',
|
|
28
|
+
'app.kubernetes.io/managed-by': 'cdk8s',
|
|
29
|
+
'app.kubernetes.io/part-of': 'monitoring',
|
|
30
|
+
'release': 'kube-prometheus-stack',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
spec: {
|
|
34
|
+
selector: { matchLabels: { 'app.kubernetes.io/name': 'traefik' } },
|
|
35
|
+
namespaceSelector: { matchNames: [traefik.namespace] },
|
|
36
|
+
podMetricsEndpoints: [
|
|
37
|
+
{ port: 'metrics', path: '/metrics', interval: '30s', scrapeTimeout: '10s' },
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
if (traefik.dashboard) {
|
|
42
|
+
new k8s_1.KubeConfigMap(this, 'traefik-dashboard', {
|
|
43
|
+
metadata: {
|
|
44
|
+
name: 'traefik-dashboard',
|
|
45
|
+
namespace: monitoringNamespace,
|
|
46
|
+
labels: {
|
|
47
|
+
'grafana_dashboard': '1',
|
|
48
|
+
'app.kubernetes.io/name': 'traefik',
|
|
49
|
+
'app.kubernetes.io/component': 'dashboard',
|
|
50
|
+
'app.kubernetes.io/managed-by': 'cdk8s',
|
|
51
|
+
'app.kubernetes.io/part-of': 'monitoring',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
data: { 'traefik.json': JSON.stringify(traefik_dashboard_1.TRAEFIK_DASHBOARD) },
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.TraefikMonitorConstruct = TraefikMonitorConstruct;
|
|
60
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhZWZpay1tb25pdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbnN0cnVjdHMvdHJhZWZpay1tb25pdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLDJDQUF1QztBQUN2Qyx1RUFBb0U7QUFDcEUsd0NBQStDO0FBQy9DLDRFQUE4RDtBQVU5RDs7Ozs7Ozs7R0FRRztBQUNILE1BQWEsdUJBQXdCLFNBQVEsc0JBQVM7SUFDcEQsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUEwQjtRQUNsRSxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQ2pCLE1BQU0sRUFBRSxtQkFBbUIsRUFBRSxPQUFPLEVBQUUsR0FBRyxLQUFLLENBQUM7UUFFL0MsSUFBSSxrQ0FBVSxDQUFDLElBQUksRUFBRSxpQkFBaUIsRUFBRTtZQUN0QyxRQUFRLEVBQUU7Z0JBQ1IsSUFBSSxFQUFFLGlCQUFpQjtnQkFDdkIsU0FBUyxFQUFFLG1CQUFtQjtnQkFDOUIsTUFBTSxFQUFFO29CQUNOLHdCQUF3QixFQUFFLFNBQVM7b0JBQ25DLDZCQUE2QixFQUFFLG9CQUFvQjtvQkFDbkQsOEJBQThCLEVBQUUsT0FBTztvQkFDdkMsMkJBQTJCLEVBQUUsWUFBWTtvQkFDekMsU0FBUyxFQUFFLHVCQUF1QjtpQkFDbkM7YUFDRjtZQUNELElBQUksRUFBRTtnQkFDSixRQUFRLEVBQUUsRUFBRSxXQUFXLEVBQUUsRUFBRSx3QkFBd0IsRUFBRSxTQUFTLEVBQUUsRUFBRTtnQkFDbEUsaUJBQWlCLEVBQUUsRUFBRSxVQUFVLEVBQUUsQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUU7Z0JBQ3RELG1CQUFtQixFQUFFO29CQUNuQixFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLGFBQWEsRUFBRSxLQUFLLEVBQUU7aUJBQzdFO2FBQ0Y7U0FDRixDQUFDLENBQUM7UUFFSCxJQUFJLE9BQU8sQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUN0QixJQUFJLG1CQUFhLENBQUMsSUFBSSxFQUFFLG1CQUFtQixFQUFFO2dCQUMzQyxRQUFRLEVBQUU7b0JBQ1IsSUFBSSxFQUFFLG1CQUFtQjtvQkFDekIsU0FBUyxFQUFFLG1CQUFtQjtvQkFDOUIsTUFBTSxFQUFFO3dCQUNOLG1CQUFtQixFQUFFLEdBQUc7d0JBQ3hCLHdCQUF3QixFQUFFLFNBQVM7d0JBQ25DLDZCQUE2QixFQUFFLFdBQVc7d0JBQzFDLDhCQUE4QixFQUFFLE9BQU87d0JBQ3ZDLDJCQUEyQixFQUFFLFlBQVk7cUJBQzFDO2lCQUNGO2dCQUNELElBQUksRUFBRSxFQUFFLGNBQWMsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLHFDQUFpQixDQUFDLEVBQUU7YUFDNUQsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztJQUNILENBQUM7Q0FDRjtBQTNDRCwwREEyQ0MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcbmltcG9ydCB7IFRSQUVGSUtfREFTSEJPQVJEIH0gZnJvbSAnLi4vZGFzaGJvYXJkcy90cmFlZmlrLWRhc2hib2FyZCc7XG5pbXBvcnQgeyBLdWJlQ29uZmlnTWFwIH0gZnJvbSAnLi4vaW1wb3J0cy9rOHMnO1xuaW1wb3J0IHsgUG9kTW9uaXRvciB9IGZyb20gJy4uL2ltcG9ydHMvbW9uaXRvcmluZy5jb3Jlb3MuY29tJztcbmltcG9ydCB7IFRyYWVmaWtDb25maWcgfSBmcm9tICcuLi90eXBlcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgVHJhZWZpa01vbml0b3JQcm9wcyB7XG4gIC8qKiBOYW1lc3BhY2UgdGhlIG1vbml0b3Jpbmcgc3RhY2sgbGl2ZXMgaW4gKHdoZXJlIHRoZSBQb2RNb25pdG9yL0NvbmZpZ01hcCBnbykuICovXG4gIHJlYWRvbmx5IG1vbml0b3JpbmdOYW1lc3BhY2U6IHN0cmluZztcbiAgLyoqIFRyYWVmaWsgY29uZmlnIChuYW1lc3BhY2UgaXQgcnVucyBpbiArIGRhc2hib2FyZCB0b2dnbGUpLiAqL1xuICByZWFkb25seSB0cmFlZmlrOiBUcmFlZmlrQ29uZmlnO1xufVxuXG4vKipcbiAqIEdlbmVyaWMsIG9wdC1pbiBtb25pdG9yIGZvciBhIFRyYWVmaWsgaW5ncmVzcyBjb250cm9sbGVyLlxuICpcbiAqIFVzZXMgYSBQb2RNb25pdG9yIChub3QgYSBTZXJ2aWNlTW9uaXRvcik6IHRoZSBrM3MtYnVuZGxlZCBUcmFlZmlrIFNlcnZpY2Ugb25seVxuICogZXhwb3NlcyB3ZWIvd2Vic2VjdXJlOyB0aGUgYG1ldHJpY3NgIHBvcnQgaXMgb24gdGhlIFBvZCwgbm90IHRoZSBTZXJ2aWNlLlxuICpcbiAqIE9wdGlvbmFsbHkgc2hpcHMgdGhlIFRyYWVmaWsgR3JhZmFuYSBkYXNoYm9hcmQgYXMgYSBDb25maWdNYXAgKGdyYWZhbmFfZGFzaGJvYXJkXG4gKiBsYWJlbCDihpIgc2lkZWNhciBhdXRvLWRpc2NvdmVyeSkuIEVuYWJsZWQgdmlhIGNvbmZpZy50cmFlZmlrIGluIE1vbml0b3JpbmdDaGFydC5cbiAqL1xuZXhwb3J0IGNsYXNzIFRyYWVmaWtNb25pdG9yQ29uc3RydWN0IGV4dGVuZHMgQ29uc3RydWN0IHtcbiAgY29uc3RydWN0b3Ioc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZywgcHJvcHM6IFRyYWVmaWtNb25pdG9yUHJvcHMpIHtcbiAgICBzdXBlcihzY29wZSwgaWQpO1xuICAgIGNvbnN0IHsgbW9uaXRvcmluZ05hbWVzcGFjZSwgdHJhZWZpayB9ID0gcHJvcHM7XG5cbiAgICBuZXcgUG9kTW9uaXRvcih0aGlzLCAndHJhZWZpay1tZXRyaWNzJywge1xuICAgICAgbWV0YWRhdGE6IHtcbiAgICAgICAgbmFtZTogJ3RyYWVmaWstbWV0cmljcycsXG4gICAgICAgIG5hbWVzcGFjZTogbW9uaXRvcmluZ05hbWVzcGFjZSxcbiAgICAgICAgbGFiZWxzOiB7XG4gICAgICAgICAgJ2FwcC5rdWJlcm5ldGVzLmlvL25hbWUnOiAndHJhZWZpaycsXG4gICAgICAgICAgJ2FwcC5rdWJlcm5ldGVzLmlvL2NvbXBvbmVudCc6ICdpbmdyZXNzLWNvbnRyb2xsZXInLFxuICAgICAgICAgICdhcHAua3ViZXJuZXRlcy5pby9tYW5hZ2VkLWJ5JzogJ2NkazhzJyxcbiAgICAgICAgICAnYXBwLmt1YmVybmV0ZXMuaW8vcGFydC1vZic6ICdtb25pdG9yaW5nJyxcbiAgICAgICAgICAncmVsZWFzZSc6ICdrdWJlLXByb21ldGhldXMtc3RhY2snLFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICAgIHNwZWM6IHtcbiAgICAgICAgc2VsZWN0b3I6IHsgbWF0Y2hMYWJlbHM6IHsgJ2FwcC5rdWJlcm5ldGVzLmlvL25hbWUnOiAndHJhZWZpaycgfSB9LFxuICAgICAgICBuYW1lc3BhY2VTZWxlY3RvcjogeyBtYXRjaE5hbWVzOiBbdHJhZWZpay5uYW1lc3BhY2VdIH0sXG4gICAgICAgIHBvZE1ldHJpY3NFbmRwb2ludHM6IFtcbiAgICAgICAgICB7IHBvcnQ6ICdtZXRyaWNzJywgcGF0aDogJy9tZXRyaWNzJywgaW50ZXJ2YWw6ICczMHMnLCBzY3JhcGVUaW1lb3V0OiAnMTBzJyB9LFxuICAgICAgICBdLFxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIGlmICh0cmFlZmlrLmRhc2hib2FyZCkge1xuICAgICAgbmV3IEt1YmVDb25maWdNYXAodGhpcywgJ3RyYWVmaWstZGFzaGJvYXJkJywge1xuICAgICAgICBtZXRhZGF0YToge1xuICAgICAgICAgIG5hbWU6ICd0cmFlZmlrLWRhc2hib2FyZCcsXG4gICAgICAgICAgbmFtZXNwYWNlOiBtb25pdG9yaW5nTmFtZXNwYWNlLFxuICAgICAgICAgIGxhYmVsczoge1xuICAgICAgICAgICAgJ2dyYWZhbmFfZGFzaGJvYXJkJzogJzEnLFxuICAgICAgICAgICAgJ2FwcC5rdWJlcm5ldGVzLmlvL25hbWUnOiAndHJhZWZpaycsXG4gICAgICAgICAgICAnYXBwLmt1YmVybmV0ZXMuaW8vY29tcG9uZW50JzogJ2Rhc2hib2FyZCcsXG4gICAgICAgICAgICAnYXBwLmt1YmVybmV0ZXMuaW8vbWFuYWdlZC1ieSc6ICdjZGs4cycsXG4gICAgICAgICAgICAnYXBwLmt1YmVybmV0ZXMuaW8vcGFydC1vZic6ICdtb25pdG9yaW5nJyxcbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgICBkYXRhOiB7ICd0cmFlZmlrLmpzb24nOiBKU09OLnN0cmluZ2lmeShUUkFFRklLX0RBU0hCT0FSRCkgfSxcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxufVxuIl19
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const TRAEFIK_DASHBOARD: Record<string, unknown>;
|