@intentius/chant-lexicon-k8s 0.95.0 → 0.96.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.
Files changed (61) hide show
  1. package/dist/composites/catalog.d.ts.map +1 -1
  2. package/dist/composites/index.d.ts +4 -0
  3. package/dist/composites/index.d.ts.map +1 -1
  4. package/dist/composites/monitored-service.d.ts +19 -0
  5. package/dist/composites/monitored-service.d.ts.map +1 -1
  6. package/dist/composites/otel-collector-agent.d.ts +22 -0
  7. package/dist/composites/otel-collector-agent.d.ts.map +1 -1
  8. package/dist/composites/otel-collector-gateway.d.ts +173 -0
  9. package/dist/composites/otel-collector-gateway.d.ts.map +1 -0
  10. package/dist/composites/otel-collector-shape.d.ts +73 -0
  11. package/dist/composites/otel-collector-shape.d.ts.map +1 -0
  12. package/dist/composites/otel-collector.d.ts +19 -1
  13. package/dist/composites/otel-collector.d.ts.map +1 -1
  14. package/dist/index.d.ts +2 -2
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/integrity.json +7 -3
  17. package/dist/lint/audit-catalog.d.ts.map +1 -1
  18. package/dist/lint/post-synth/index.d.ts.map +1 -1
  19. package/dist/lint/post-synth/otel-placement-helpers.d.ts +103 -0
  20. package/dist/lint/post-synth/otel-placement-helpers.d.ts.map +1 -0
  21. package/dist/lint/post-synth/wk8601.d.ts +17 -0
  22. package/dist/lint/post-synth/wk8601.d.ts.map +1 -0
  23. package/dist/lint/post-synth/wk8602.d.ts +23 -0
  24. package/dist/lint/post-synth/wk8602.d.ts.map +1 -0
  25. package/dist/lint/post-synth/wk8603.d.ts +19 -0
  26. package/dist/lint/post-synth/wk8603.d.ts.map +1 -0
  27. package/dist/manifest.json +1 -1
  28. package/dist/okf/index.md +3 -0
  29. package/dist/okf/rules/WK8601.md +15 -0
  30. package/dist/okf/rules/WK8602.md +16 -0
  31. package/dist/okf/rules/WK8603.md +15 -0
  32. package/dist/okf/types/DaemonSet.md +3 -0
  33. package/dist/okf/types/Service.md +1 -0
  34. package/dist/rules/otel-placement-helpers.ts +277 -0
  35. package/dist/rules/wk8601.ts +44 -0
  36. package/dist/rules/wk8602.ts +133 -0
  37. package/dist/rules/wk8603.ts +66 -0
  38. package/dist/serializer.d.ts.map +1 -1
  39. package/dist/skills/chant-k8s.md +2 -0
  40. package/package.json +4 -3
  41. package/src/composites/__snapshots__/gke-otel-collector.snapshot.test.ts.snap +401 -0
  42. package/src/composites/catalog.ts +109 -0
  43. package/src/composites/gke-otel-collector.snapshot.test.ts +51 -0
  44. package/src/composites/index.ts +9 -0
  45. package/src/composites/monitored-service.ts +40 -16
  46. package/src/composites/otel-collector-agent.ts +62 -26
  47. package/src/composites/otel-collector-gateway.test.ts +260 -0
  48. package/src/composites/otel-collector-gateway.ts +325 -0
  49. package/src/composites/otel-collector-shape.ts +141 -0
  50. package/src/composites/otel-collector.ts +74 -31
  51. package/src/index.ts +3 -0
  52. package/src/lint/audit-catalog.ts +3 -0
  53. package/src/lint/post-synth/index.ts +6 -0
  54. package/src/lint/post-synth/otel-placement-helpers.ts +277 -0
  55. package/src/lint/post-synth/otel-placement.test.ts +401 -0
  56. package/src/lint/post-synth/wk8601.ts +44 -0
  57. package/src/lint/post-synth/wk8602.ts +133 -0
  58. package/src/lint/post-synth/wk8603.ts +66 -0
  59. package/src/prometheus-rule-groups.test.ts +135 -0
  60. package/src/serializer.ts +9 -1
  61. package/src/skills/chant-k8s.md +2 -0
@@ -0,0 +1,133 @@
1
+ /**
2
+ * WK8602: `tail_sampling` on a multi-replica gateway that collectors reach
3
+ * without trace-aware load balancing
4
+ *
5
+ * Tail sampling needs every span of a trace in the same process. Behind a
6
+ * ClusterIP Service, connections are spread across the gateway replicas with
7
+ * no regard for trace ids, so the spans of one trace land on different
8
+ * replicas and each samples a fragment. The fix is a `loadbalancing`
9
+ * exporter in front, routing by `traceID` to the replicas themselves (the
10
+ * `k8s` resolver on any Service that selects them, or the `dns` resolver on
11
+ * a headless one). A single-replica gateway passes.
12
+ *
13
+ * The check fires on evidence, not on absence: another collector in the
14
+ * build whose traces pipeline exports to one of the gateway's Services
15
+ * through `otlp`/`otlphttp`, through a `loadbalancing` exporter routed by
16
+ * something other than `traceID`, or through the `dns` resolver on a
17
+ * ClusterIP Service (one virtual IP, so one backend per connection). When
18
+ * the senders live in another build root, or are applications talking to
19
+ * the gateway directly, it stays silent (chant #1939).
20
+ */
21
+
22
+ import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
23
+ import { docsToManifests } from "./k8s-helpers";
24
+ import {
25
+ collectorPlacements,
26
+ componentType,
27
+ describePlacement,
28
+ hostOfEndpoint,
29
+ pipelinesOf,
30
+ serviceOfHost,
31
+ servicesSelecting,
32
+ tracePipelineProcessors,
33
+ type CollectorPlacement,
34
+ } from "./otel-placement-helpers";
35
+
36
+ const TRACE_ID_KEYS = new Set([undefined, null, "", "traceID"]);
37
+
38
+ function record(v: unknown): Record<string, unknown> {
39
+ return typeof v === "object" && v !== null && !Array.isArray(v) ? (v as Record<string, unknown>) : {};
40
+ }
41
+
42
+ /** Why each traces exporter of `sender` that reaches `services` does so without trace-aware routing. */
43
+ function unroutedExporters(sender: CollectorPlacement, services: Map<string, boolean>): string[] {
44
+ const reasons: string[] = [];
45
+ const seen = new Set<string>();
46
+ for (const [, pipeline] of pipelinesOf(sender.config, "traces")) {
47
+ for (const id of pipeline.exporters) {
48
+ if (seen.has(id)) continue;
49
+ seen.add(id);
50
+ const type = componentType(id);
51
+ const cfg = record(sender.config.exporters?.[id]);
52
+
53
+ if (type === "loadbalancing") {
54
+ const resolver = record(cfg.resolver);
55
+ const k8sService = record(resolver.k8s).service;
56
+ const dnsHost = record(resolver.dns).hostname;
57
+ let target: string | undefined;
58
+ if (typeof k8sService === "string") {
59
+ const svc = serviceOfHost(k8sService, sender.namespace);
60
+ if (svc && services.has(svc)) target = svc;
61
+ } else if (typeof dnsHost === "string") {
62
+ const svc = serviceOfHost(dnsHost, sender.namespace);
63
+ if (svc && services.has(svc)) {
64
+ target = svc;
65
+ if (!services.get(svc)) {
66
+ reasons.push(`${id} resolves ClusterIP Service ${svc} through dns, which returns one virtual IP rather than the replicas`);
67
+ continue;
68
+ }
69
+ }
70
+ }
71
+ if (target && !TRACE_ID_KEYS.has(cfg.routing_key as string | undefined)) {
72
+ reasons.push(`${id} routes by ${String(cfg.routing_key)}, not traceID`);
73
+ }
74
+ continue;
75
+ }
76
+
77
+ if (type === "otlp" || type === "otlphttp") {
78
+ for (const field of ["endpoint", "traces_endpoint"]) {
79
+ const host = hostOfEndpoint(cfg[field]);
80
+ const svc = host ? serviceOfHost(host, sender.namespace) : undefined;
81
+ if (svc && services.has(svc)) {
82
+ reasons.push(`${id} sends to Service ${svc} directly`);
83
+ break;
84
+ }
85
+ }
86
+ }
87
+ }
88
+ }
89
+ return reasons;
90
+ }
91
+
92
+ export const wk8602: PostSynthCheck = {
93
+ id: "WK8602",
94
+ description:
95
+ "OpenTelemetry Collector gateway runs tail_sampling on more than one replica, but collectors send it traces without a loadbalancing exporter routed by traceID. Sees one build root at a time (chant #1939).",
96
+
97
+ check(ctx: PostSynthContext): PostSynthDiagnostic[] {
98
+ const manifests = docsToManifests(ctx);
99
+ const placements = collectorPlacements(manifests);
100
+ const diagnostics: PostSynthDiagnostic[] = [];
101
+
102
+ for (const gateway of placements) {
103
+ if (gateway.kind === "DaemonSet" || gateway.replicas === undefined || gateway.replicas <= 1) continue;
104
+ const tail = tracePipelineProcessors(gateway.config, "tail_sampling");
105
+ if (tail.length === 0) continue;
106
+
107
+ const services = new Map<string, boolean>();
108
+ for (const s of servicesSelecting(manifests, gateway.workload)) services.set(`${s.name}.${s.namespace}`, s.headless);
109
+ if (services.size === 0) continue;
110
+
111
+ const senders = new Set<string>();
112
+ for (const sender of placements) {
113
+ if (sender.workload === gateway.workload) continue;
114
+ const key = `${sender.kind}/${sender.namespace}/${sender.name}/${sender.configMap}/${sender.key}`;
115
+ if (senders.has(key)) continue;
116
+ senders.add(key);
117
+ const reasons = unroutedExporters(sender, services);
118
+ if (reasons.length === 0) continue;
119
+ diagnostics.push({
120
+ checkId: "WK8602",
121
+ severity: "warning",
122
+ message:
123
+ `${describePlacement(gateway)} runs ${tail.join(", ")} on up to ${gateway.replicas} replicas, and ${describePlacement(sender)} sends it traces without trace-aware routing: ${reasons.join("; ")}. ` +
124
+ `Spans of one trace land on different replicas and each samples a fragment. ` +
125
+ `Export traces through a loadbalancing exporter with routing_key traceID (gatewayExporter(gateway, { loadBalance: true })), or run one replica.`,
126
+ entity: gateway.name,
127
+ lexicon: "k8s",
128
+ });
129
+ }
130
+ }
131
+ return diagnostics;
132
+ },
133
+ };
@@ -0,0 +1,66 @@
1
+ /**
2
+ * WK8603: `k8s_cluster` receiver in a collector that runs more than one copy
3
+ * without a leader elector
4
+ *
5
+ * The `k8s_cluster` receiver reports cluster-level metrics and entity events
6
+ * (nodes, deployments, pods, resource quotas) by watching the API server.
7
+ * Every copy of the collector reports every object, so in a DaemonSet the
8
+ * cluster is reported once per node, and in a Deployment once per replica:
9
+ * duplicated series and N watches against the API server. Run it in a
10
+ * single-replica Deployment, or set the receiver's `k8s_leader_elector` to a
11
+ * `k8s_leader_elector` extension enabled in `service.extensions` (both ship in
12
+ * otelcol-contrib and otelcol-k8s at the pinned v0.130.0), so only the
13
+ * leader collects.
14
+ *
15
+ * One build root at a time (chant #1939).
16
+ */
17
+
18
+ import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
19
+ import { docsToManifests } from "./k8s-helpers";
20
+ import { allPipelines, collectorPlacements, componentType, describePlacement, type CollectorConfigShape } from "./otel-placement-helpers";
21
+
22
+ /** Why a `k8s_cluster` receiver is not behind a working leader elector, or undefined when it is. */
23
+ function missingLeader(config: CollectorConfigShape, id: string): string | undefined {
24
+ const cfg = config.receivers?.[id];
25
+ const ref = typeof cfg === "object" && cfg !== null ? (cfg as Record<string, unknown>).k8s_leader_elector : undefined;
26
+ if (typeof ref !== "string" || ref.length === 0) return `${id} has no k8s_leader_elector`;
27
+ if (componentType(ref) !== "k8s_leader_elector" || !(config.extensions && ref in config.extensions)) {
28
+ return `${id} names k8s_leader_elector "${ref}", which is not a declared k8s_leader_elector extension`;
29
+ }
30
+ const enabled = Array.isArray(config.service.extensions) && config.service.extensions.includes(ref);
31
+ if (!enabled) return `${id} names k8s_leader_elector "${ref}", which is not enabled in service.extensions`;
32
+ return undefined;
33
+ }
34
+
35
+ export const wk8603: PostSynthCheck = {
36
+ id: "WK8603",
37
+ description:
38
+ "OpenTelemetry Collector runs the k8s_cluster receiver in a DaemonSet or multi-replica workload without a k8s_leader_elector; every copy reports every cluster object. Sees one build root at a time (chant #1939).",
39
+
40
+ check(ctx: PostSynthContext): PostSynthDiagnostic[] {
41
+ const diagnostics: PostSynthDiagnostic[] = [];
42
+ for (const p of collectorPlacements(docsToManifests(ctx))) {
43
+ const copies = p.kind === "DaemonSet" ? "every node" : p.replicas !== undefined && p.replicas > 1 ? `each of up to ${p.replicas} replicas` : undefined;
44
+ if (!copies) continue;
45
+
46
+ const used = new Set<string>();
47
+ for (const [, pipeline] of allPipelines(p.config)) {
48
+ for (const id of pipeline.receivers) if (componentType(id) === "k8s_cluster") used.add(id);
49
+ }
50
+ const reasons = [...used].map((id) => missingLeader(p.config, id)).filter((r): r is string => r !== undefined);
51
+ if (reasons.length === 0) continue;
52
+
53
+ diagnostics.push({
54
+ checkId: "WK8603",
55
+ severity: "warning",
56
+ message:
57
+ `${describePlacement(p)} runs the k8s_cluster receiver on ${copies} (ConfigMap ${p.configMap}, ${p.key}): ${reasons.join("; ")}. ` +
58
+ `Each copy reports every cluster object, duplicating series and API server watches. ` +
59
+ `Run it in a single-replica Deployment, or reference a k8s_leader_elector extension from the receiver and enable it in service.extensions.`,
60
+ entity: p.name,
61
+ lexicon: "k8s",
62
+ });
63
+ }
64
+ return diagnostics;
65
+ },
66
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAsC,MAAM,6BAA6B,CAAC;AAyRlG;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,UAkO3B,CAAC"}
1
+ {"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAsC,MAAM,6BAA6B,CAAC;AAiSlG;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,UAkO3B,CAAC"}
@@ -171,6 +171,8 @@ Composites are higher-level functions that produce multiple coordinated K8s reso
171
171
  | Multi-host TLS Ingress (cert-manager) | **SecureIngress** | Ingress + optional Certificate |
172
172
  | HPA metrics provider | **MetricsServer** | Deployment + Service + RBAC + APIService |
173
173
  | CockroachDB cluster | **CockroachDbCluster** | StatefulSet + Services + PVCs + RBAC + optional CertificateRequests |
174
+ | OpenTelemetry agent on every node | **OtelCollector** | DaemonSet + node-local Service + RBAC + ConfigMap |
175
+ | OpenTelemetry gateway tier | **OtelCollectorGateway** | Deployment + ClusterIP and headless Services + PDB + ConfigMap; point agents at it with `gatewayExporter(gateway, { loadBalance: true })` |
174
176
 
175
177
  ### Decision Tree — AWS (EKS) Composites
176
178
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-k8s",
3
- "version": "0.95.0",
3
+ "version": "0.96.0",
4
4
  "description": "Kubernetes lexicon for chant — declarative IaC in TypeScript",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -73,11 +73,12 @@
73
73
  },
74
74
  "dependencies": {
75
75
  "@intentius/chant-lexicon-otel": "*",
76
+ "@intentius/chant-lexicon-prometheus": "*",
76
77
  "js-yaml": "^4.3.1",
77
78
  "@types/js-yaml": "^4.0.9"
78
79
  },
79
80
  "optionalDependencies": {
80
- "@intentius/chant-k8s-client": "^0.95.0"
81
+ "@intentius/chant-k8s-client": "^0.96.0"
81
82
  },
82
83
  "devDependencies": {
83
84
  "@intentius/chant": "*",
@@ -85,7 +86,7 @@
85
86
  },
86
87
  "peerDependencies": {
87
88
  "zod": "^4.3.6",
88
- "@intentius/chant": "^0.95.0",
89
+ "@intentius/chant": "^0.96.0",
89
90
  "typescript": "^5.9.3"
90
91
  }
91
92
  }
@@ -0,0 +1,401 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`GkeOtelCollector output > with Workload Identity 1`] = `
4
+ "apiVersion: apps/v1
5
+ kind: DaemonSet
6
+ metadata:
7
+ name: gke-otel-collector
8
+ namespace: gke-monitoring
9
+ labels:
10
+ app.kubernetes.io/name: gke-otel-collector
11
+ app.kubernetes.io/managed-by: chant
12
+ app.kubernetes.io/component: agent
13
+ spec:
14
+ selector:
15
+ matchLabels:
16
+ app.kubernetes.io/name: gke-otel-collector
17
+ template:
18
+ metadata:
19
+ labels:
20
+ app.kubernetes.io/name: gke-otel-collector
21
+ spec:
22
+ serviceAccountName: gke-otel-collector-sa
23
+ containers:
24
+ - name: gke-otel-collector
25
+ image: otel/opentelemetry-collector-contrib:latest
26
+ args:
27
+ - --config=/etc/otel/config.yaml
28
+ ports:
29
+ - containerPort: 4317
30
+ name: otlp-grpc
31
+ - containerPort: 4318
32
+ name: otlp-http
33
+ resources:
34
+ requests:
35
+ cpu: '100m'
36
+ memory: '256Mi'
37
+ limits:
38
+ cpu: '500m'
39
+ memory: '512Mi'
40
+ volumeMounts:
41
+ - name: config
42
+ mountPath: /etc/otel
43
+ readOnly: true
44
+ securityContext:
45
+ runAsNonRoot: true
46
+ runAsUser: 10001
47
+ readOnlyRootFilesystem: true
48
+ allowPrivilegeEscalation: false
49
+ volumes:
50
+ - name: config
51
+ configMap:
52
+ name: gke-otel-collector-config
53
+ tolerations:
54
+ - operator: Exists
55
+
56
+ ---
57
+ apiVersion: v1
58
+ kind: ServiceAccount
59
+ metadata:
60
+ name: gke-otel-collector-sa
61
+ namespace: gke-monitoring
62
+ labels:
63
+ app.kubernetes.io/name: gke-otel-collector
64
+ app.kubernetes.io/managed-by: chant
65
+ app.kubernetes.io/component: agent
66
+ annotations:
67
+ iam.gke.io/gcp-service-account: otel@test-project.iam.gserviceaccount.com
68
+
69
+ ---
70
+ apiVersion: rbac.authorization.k8s.io/v1
71
+ kind: ClusterRole
72
+ metadata:
73
+ name: gke-otel-collector-role
74
+ labels:
75
+ app.kubernetes.io/name: gke-otel-collector
76
+ app.kubernetes.io/managed-by: chant
77
+ app.kubernetes.io/component: rbac
78
+ rules:
79
+ - apiGroups:
80
+ - ''
81
+ resources:
82
+ - pods
83
+ - nodes
84
+ - endpoints
85
+ verbs:
86
+ - get
87
+ - list
88
+ - watch
89
+ - apiGroups:
90
+ - apps
91
+ resources:
92
+ - replicasets
93
+ verbs:
94
+ - get
95
+ - list
96
+ - watch
97
+ - apiGroups:
98
+ - batch
99
+ resources:
100
+ - jobs
101
+ verbs:
102
+ - get
103
+ - list
104
+ - watch
105
+ - apiGroups:
106
+ - ''
107
+ resources:
108
+ - nodes/proxy
109
+ verbs:
110
+ - get
111
+ - apiGroups:
112
+ - ''
113
+ resources:
114
+ - nodes/stats
115
+ - configmaps
116
+ - events
117
+ verbs:
118
+ - create
119
+ - get
120
+ - apiGroups:
121
+ - ''
122
+ resources:
123
+ - configmaps
124
+ verbs:
125
+ - get
126
+ - update
127
+ - create
128
+ resourceNames:
129
+ - otel-container-insight-clusterleader
130
+
131
+ ---
132
+ apiVersion: rbac.authorization.k8s.io/v1
133
+ kind: ClusterRoleBinding
134
+ metadata:
135
+ name: gke-otel-collector-binding
136
+ labels:
137
+ app.kubernetes.io/name: gke-otel-collector
138
+ app.kubernetes.io/managed-by: chant
139
+ app.kubernetes.io/component: rbac
140
+ roleRef:
141
+ apiGroup: rbac.authorization.k8s.io
142
+ kind: ClusterRole
143
+ name: gke-otel-collector-role
144
+ subjects:
145
+ - kind: ServiceAccount
146
+ name: gke-otel-collector-sa
147
+ namespace: gke-monitoring
148
+
149
+ ---
150
+ apiVersion: v1
151
+ kind: ConfigMap
152
+ metadata:
153
+ name: gke-otel-collector-config
154
+ namespace: gke-monitoring
155
+ labels:
156
+ app.kubernetes.io/name: gke-otel-collector
157
+ app.kubernetes.io/managed-by: chant
158
+ app.kubernetes.io/component: config
159
+ data:
160
+ config.yaml: |
161
+ receivers:
162
+ otlp:
163
+ protocols:
164
+ grpc:
165
+ endpoint: 0.0.0.0:4317
166
+ http:
167
+ endpoint: 0.0.0.0:4318
168
+
169
+ processors:
170
+ batch:
171
+ timeout: 30s
172
+ send_batch_size: 8192
173
+ resourcedetection:
174
+ detectors: [gcp]
175
+ timeout: 10s
176
+
177
+ exporters:
178
+ googlecloud:
179
+ project: test-project
180
+ metric:
181
+ prefix: custom.googleapis.com/test-cluster
182
+ trace:
183
+ attribute_mappings:
184
+ - key: service.name
185
+ replacement: g.co/r/service/name
186
+
187
+ service:
188
+ pipelines:
189
+ metrics:
190
+ receivers: [otlp]
191
+ processors: [batch, resourcedetection]
192
+ exporters: [googlecloud]
193
+ traces:
194
+ receivers: [otlp]
195
+ processors: [batch, resourcedetection]
196
+ exporters: [googlecloud]
197
+ "
198
+ `;
199
+
200
+ exports[`GkeOtelCollector output > with every prop set 1`] = `
201
+ "apiVersion: apps/v1
202
+ kind: DaemonSet
203
+ metadata:
204
+ name: otel
205
+ namespace: telemetry
206
+ labels:
207
+ app.kubernetes.io/name: otel
208
+ app.kubernetes.io/managed-by: chant
209
+ team: obs
210
+ app.kubernetes.io/component: agent
211
+ spec:
212
+ selector:
213
+ matchLabels:
214
+ app.kubernetes.io/name: otel
215
+ template:
216
+ metadata:
217
+ labels:
218
+ app.kubernetes.io/name: otel
219
+ team: obs
220
+ spec:
221
+ serviceAccountName: otel-sa
222
+ containers:
223
+ - name: otel
224
+ image: otel/opentelemetry-collector-contrib:0.100.0
225
+ args:
226
+ - --config=/etc/otel/config.yaml
227
+ ports:
228
+ - containerPort: 4317
229
+ name: otlp-grpc
230
+ - containerPort: 4318
231
+ name: otlp-http
232
+ resources:
233
+ requests:
234
+ cpu: '50m'
235
+ memory: '128Mi'
236
+ limits:
237
+ cpu: '1'
238
+ memory: '1Gi'
239
+ volumeMounts:
240
+ - name: config
241
+ mountPath: /etc/otel
242
+ readOnly: true
243
+ securityContext:
244
+ runAsNonRoot: true
245
+ runAsUser: 10001
246
+ readOnlyRootFilesystem: true
247
+ allowPrivilegeEscalation: false
248
+ volumes:
249
+ - name: config
250
+ configMap:
251
+ name: otel-config
252
+ tolerations:
253
+ - operator: Exists
254
+ updateStrategy:
255
+ type: OnDelete
256
+
257
+ ---
258
+ apiVersion: v1
259
+ kind: ServiceAccount
260
+ metadata:
261
+ name: otel-sa
262
+ namespace: telemetry
263
+ labels:
264
+ app.kubernetes.io/name: otel
265
+ app.kubernetes.io/managed-by: chant
266
+ team: obs
267
+ app.kubernetes.io/component: agent
268
+
269
+ ---
270
+ apiVersion: rbac.authorization.k8s.io/v1
271
+ kind: ClusterRole
272
+ metadata:
273
+ name: otel-role
274
+ labels:
275
+ app.kubernetes.io/name: otel
276
+ app.kubernetes.io/managed-by: chant
277
+ team: obs
278
+ app.kubernetes.io/component: rbac
279
+ rules:
280
+ - apiGroups:
281
+ - ''
282
+ resources:
283
+ - pods
284
+ - nodes
285
+ - endpoints
286
+ verbs:
287
+ - get
288
+ - list
289
+ - watch
290
+ - apiGroups:
291
+ - apps
292
+ resources:
293
+ - replicasets
294
+ verbs:
295
+ - get
296
+ - list
297
+ - watch
298
+ - apiGroups:
299
+ - batch
300
+ resources:
301
+ - jobs
302
+ verbs:
303
+ - get
304
+ - list
305
+ - watch
306
+ - apiGroups:
307
+ - ''
308
+ resources:
309
+ - nodes/proxy
310
+ verbs:
311
+ - get
312
+ - apiGroups:
313
+ - ''
314
+ resources:
315
+ - nodes/stats
316
+ - configmaps
317
+ - events
318
+ verbs:
319
+ - create
320
+ - get
321
+ - apiGroups:
322
+ - ''
323
+ resources:
324
+ - configmaps
325
+ verbs:
326
+ - get
327
+ - update
328
+ - create
329
+ resourceNames:
330
+ - otel-container-insight-clusterleader
331
+
332
+ ---
333
+ apiVersion: rbac.authorization.k8s.io/v1
334
+ kind: ClusterRoleBinding
335
+ metadata:
336
+ name: otel-binding
337
+ labels:
338
+ app.kubernetes.io/name: otel
339
+ app.kubernetes.io/managed-by: chant
340
+ team: obs
341
+ app.kubernetes.io/component: rbac
342
+ roleRef:
343
+ apiGroup: rbac.authorization.k8s.io
344
+ kind: ClusterRole
345
+ name: otel-role
346
+ subjects:
347
+ - kind: ServiceAccount
348
+ name: otel-sa
349
+ namespace: telemetry
350
+
351
+ ---
352
+ apiVersion: v1
353
+ kind: ConfigMap
354
+ metadata:
355
+ name: otel-config
356
+ namespace: telemetry
357
+ labels:
358
+ app.kubernetes.io/name: otel
359
+ app.kubernetes.io/managed-by: chant
360
+ team: obs
361
+ app.kubernetes.io/component: config
362
+ data:
363
+ config.yaml: |
364
+ receivers:
365
+ otlp:
366
+ protocols:
367
+ grpc:
368
+ endpoint: 0.0.0.0:4317
369
+ http:
370
+ endpoint: 0.0.0.0:4318
371
+
372
+ processors:
373
+ batch:
374
+ timeout: 30s
375
+ send_batch_size: 8192
376
+ resourcedetection:
377
+ detectors: [gcp]
378
+ timeout: 10s
379
+
380
+ exporters:
381
+ googlecloud:
382
+ project: p-123456
383
+ metric:
384
+ prefix: custom.googleapis.com/c1
385
+ trace:
386
+ attribute_mappings:
387
+ - key: service.name
388
+ replacement: g.co/r/service/name
389
+
390
+ service:
391
+ pipelines:
392
+ metrics:
393
+ receivers: [otlp]
394
+ processors: [batch, resourcedetection]
395
+ exporters: [googlecloud]
396
+ traces:
397
+ receivers: [otlp]
398
+ processors: [batch, resourcedetection]
399
+ exporters: [googlecloud]
400
+ "
401
+ `;