Skip to content

Commit ff7a4b5

Browse files
authored
feat(observability): add Prometheus to the kind dev stack (agent-substrate#145)
Adds a Prometheus server to the kind dev stack (in `otel-system`, alongside the OpenTelemetry Collector and Jaeger). It scrapes component `/metrics` via the existing `prometheus.io/scrape` annotations on `ate-api-server` and `atelet`, scoped to the `ate-system`/`otel-system` namespaces. Fixes agent-substrate#106 - [x] Tests pass - [x] Appropriate changes to documentation are included in the PR
1 parent e26cfa2 commit ff7a4b5

3 files changed

Lines changed: 223 additions & 0 deletions

File tree

docs/observability.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,24 @@ resource.labels.pod_name="counter-deployment-c995fdf4c-m7d96"
9494

9595
Agent Substrate currently emits foundational OpenTelemetry system and server metrics (such as `rpc.server.call.duration` and `http.server.request.duration`) to monitor the overall health and performance of the `ateapi` and `atelet` control plane services.
9696

97+
### Local Metrics with Prometheus (Kind Cluster)
98+
99+
For local development inside a `kind` cluster, Agent Substrate automatically provisions a Prometheus server in the `otel-system` namespace.
100+
101+
To explore metrics locally:
102+
103+
1. **Expose the Prometheus UI** via port forwarding:
104+
```bash
105+
kubectl port-forward -n otel-system svc/prometheus 9090:9090
106+
```
107+
108+
2. **Open the Prometheus UI** in your web browser:
109+
[http://localhost:9090](http://localhost:9090)
110+
111+
3. **Query metrics**: Run `up` to confirm each component is scraped (one series per target, value `1`), then explore the `rpc_*` series via the expression browser's autocomplete. **Status > Targets** lists the discovered pods.
112+
113+
> **Note:** Storage is ephemeral (`emptyDir`), so metrics are lost when the Prometheus pod restarts.
114+
97115
> **Roadmap Note (Actor-Level Metrics):** A comprehensive metrics roadmap is under active development to support both system operators and workload analysis. Planned OpenTelemetry instrumentation focuses on control plane latency, state snapshot performance, fleet utilization density, and enriching metrics with standardized actor labels for seamless aggregation across pod transitions.
98116
99117
---

manifests/ate-install/kind/kustomization.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ resources:
2525
- ../pod-certificate-controller.yaml
2626
- rustfs.yaml
2727
- ./otel-collector.yaml
28+
- ./prometheus.yaml
2829

2930
patches:
3031
- patch: |-
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
apiVersion: v1
16+
kind: ServiceAccount
17+
metadata:
18+
name: prometheus
19+
namespace: otel-system
20+
---
21+
apiVersion: rbac.authorization.k8s.io/v1
22+
kind: ClusterRole
23+
metadata:
24+
name: ate-prometheus
25+
rules:
26+
- apiGroups: [""]
27+
resources: ["pods"]
28+
verbs: ["get", "list", "watch"]
29+
---
30+
apiVersion: rbac.authorization.k8s.io/v1
31+
kind: RoleBinding
32+
metadata:
33+
name: ate-prometheus
34+
namespace: ate-system
35+
roleRef:
36+
apiGroup: rbac.authorization.k8s.io
37+
kind: ClusterRole
38+
name: ate-prometheus
39+
subjects:
40+
- kind: ServiceAccount
41+
name: prometheus
42+
namespace: otel-system
43+
---
44+
apiVersion: rbac.authorization.k8s.io/v1
45+
kind: RoleBinding
46+
metadata:
47+
name: ate-prometheus
48+
namespace: otel-system
49+
roleRef:
50+
apiGroup: rbac.authorization.k8s.io
51+
kind: ClusterRole
52+
name: ate-prometheus
53+
subjects:
54+
- kind: ServiceAccount
55+
name: prometheus
56+
namespace: otel-system
57+
---
58+
apiVersion: v1
59+
kind: ConfigMap
60+
metadata:
61+
name: prometheus-config
62+
namespace: otel-system
63+
data:
64+
prometheus.yml: |
65+
# The kubernetes-pods scrape job below is adapted from the
66+
# prometheus-community Helm chart's job of the same name:
67+
# https://github.com/prometheus-community/helm-charts/blob/main/charts/prometheus/values.yaml
68+
global:
69+
scrape_interval: 15s
70+
scrape_configs:
71+
- job_name: kubernetes-pods
72+
kubernetes_sd_configs:
73+
- role: pod
74+
namespaces:
75+
names:
76+
- ate-system
77+
- otel-system
78+
relabel_configs:
79+
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
80+
action: keep
81+
regex: "true"
82+
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
83+
action: replace
84+
target_label: __metrics_path__
85+
regex: (.+)
86+
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_port, __meta_kubernetes_pod_ip]
87+
action: replace
88+
regex: (\d+);(([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})
89+
replacement: '[$2]:$1'
90+
target_label: __address__
91+
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_port, __meta_kubernetes_pod_ip]
92+
action: replace
93+
regex: (\d+);((([0-9]+?)(\.|$)){4})
94+
replacement: $2:$1
95+
target_label: __address__
96+
- action: labelmap
97+
regex: __meta_kubernetes_pod_label_(.+)
98+
- source_labels: [__meta_kubernetes_namespace]
99+
action: replace
100+
target_label: namespace
101+
- source_labels: [__meta_kubernetes_pod_name]
102+
action: replace
103+
target_label: pod
104+
- source_labels: [__meta_kubernetes_pod_node_name]
105+
action: replace
106+
target_label: node
107+
- source_labels: [__meta_kubernetes_pod_phase]
108+
action: drop
109+
regex: Pending|Succeeded|Failed
110+
---
111+
apiVersion: apps/v1
112+
kind: Deployment
113+
metadata:
114+
name: prometheus
115+
namespace: otel-system
116+
labels:
117+
app: prometheus
118+
spec:
119+
replicas: 1
120+
selector:
121+
matchLabels:
122+
app: prometheus
123+
template:
124+
metadata:
125+
labels:
126+
app: prometheus
127+
spec:
128+
serviceAccountName: prometheus
129+
securityContext:
130+
runAsNonRoot: true
131+
runAsUser: 65534
132+
runAsGroup: 65534
133+
fsGroup: 65534
134+
seccompProfile:
135+
type: RuntimeDefault
136+
containers:
137+
- name: prometheus
138+
image: prom/prometheus:v3.5.3@sha256:ddc2493835a1509976d5e4e0c94199c4f843ce1f42dd6bcfc8231ba734a93ff7
139+
args:
140+
- --config.file=/etc/prometheus/prometheus.yml
141+
- --storage.tsdb.path=/prometheus
142+
ports:
143+
- name: web
144+
containerPort: 9090
145+
readinessProbe:
146+
httpGet:
147+
path: /-/ready
148+
port: web
149+
initialDelaySeconds: 5
150+
periodSeconds: 5
151+
timeoutSeconds: 3
152+
failureThreshold: 3
153+
livenessProbe:
154+
httpGet:
155+
path: /-/healthy
156+
port: web
157+
initialDelaySeconds: 5
158+
periodSeconds: 15
159+
timeoutSeconds: 3
160+
failureThreshold: 3
161+
resources:
162+
requests:
163+
cpu: 100m
164+
memory: 128Mi
165+
limits:
166+
memory: 512Mi
167+
securityContext:
168+
allowPrivilegeEscalation: false
169+
readOnlyRootFilesystem: true
170+
capabilities:
171+
drop:
172+
- ALL
173+
volumeMounts:
174+
- name: config
175+
mountPath: /etc/prometheus
176+
readOnly: true
177+
- name: storage
178+
mountPath: /prometheus
179+
- name: tmp
180+
mountPath: /tmp
181+
volumes:
182+
- name: config
183+
configMap:
184+
name: prometheus-config
185+
- name: storage
186+
emptyDir: {}
187+
- name: tmp
188+
emptyDir: {}
189+
---
190+
apiVersion: v1
191+
kind: Service
192+
metadata:
193+
name: prometheus
194+
namespace: otel-system
195+
labels:
196+
app: prometheus
197+
spec:
198+
type: ClusterIP
199+
selector:
200+
app: prometheus
201+
ports:
202+
- name: web
203+
port: 9090
204+
targetPort: web

0 commit comments

Comments
 (0)