Skip to content

Commit 08a4e08

Browse files
authored
Merge pull request #119 from AMDResearch/docs-monitoring-guide
docs: Add monitoring deployment guide
2 parents 2d02c1e + eb074c7 commit 08a4e08

1 file changed

Lines changed: 320 additions & 0 deletions

File tree

docs/jupyterhub/monitoring.md

Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
# Monitoring Deployment Guide
2+
3+
This guide shows how to connect AUP Learning Cloud to a Prometheus and Grafana monitoring stack. It uses `kube-prometheus-stack` as the recommended example, then shows how to reuse an existing Prometheus Operator and Grafana deployment.
4+
5+
The AUPlC Helm chart can create the monitoring resources needed for Hub metrics: a `ServiceMonitor`, optional Grafana dashboard ConfigMaps, optional Prometheus alert rules, a metrics `NetworkPolicy`, and an authenticated token secret when authenticated scraping is enabled.
6+
7+
<!-- TODO: Add architecture diagram showing AUPlC Hub metrics scraped by Prometheus Operator and displayed in Grafana. -->
8+
<!-- ![AUPlC Monitoring Architecture](./images/monitoring-1-architecture.png) -->
9+
10+
## Prerequisites
11+
12+
- A Kubernetes cluster with AUP Learning Cloud installed or ready to install.
13+
- `kubectl` access with permission to create resources in the `monitoring` and `jupyterhub` namespaces.
14+
- Helm 3 installed locally.
15+
- Access to the AUP Learning Cloud deployment repository that contains `runtime/values.yaml` and `runtime/chart`.
16+
17+
## Install kube-prometheus-stack
18+
19+
`kube-prometheus-stack` is the recommended reference deployment for Prometheus Operator, Prometheus, Alertmanager, and Grafana.
20+
21+
Artifact Hub page: <https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack>
22+
23+
### 1. Create the monitoring namespace
24+
25+
```bash
26+
kubectl create namespace monitoring
27+
```
28+
29+
If the namespace already exists, this command can return an `AlreadyExists` error. That is safe to ignore.
30+
31+
### 2. Add the Helm repository
32+
33+
```bash
34+
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
35+
helm repo update
36+
```
37+
38+
### 3. Install the stack
39+
40+
Use the Helm release name `monitoring` in the `monitoring` namespace. This matches the default AUPlC `monitoring.releaseLabel: monitoring` value.
41+
42+
```bash
43+
helm upgrade --install monitoring prometheus-community/kube-prometheus-stack \
44+
--namespace monitoring
45+
```
46+
47+
The Prometheus Operator installed by this stack usually selects `ServiceMonitor` and `PrometheusRule` objects with the label `release: monitoring`. If you use a different Helm release name or custom selector, update `monitoring.releaseLabel` in AUPlC to match that selector.
48+
49+
<!-- TODO: Add diagram or screenshot showing the ServiceMonitor label selector relationship, especially release: monitoring. -->
50+
<!-- ![ServiceMonitor Label Selector](./images/monitoring-2-servicemonitor-labels.png) -->
51+
52+
### 4. Check the monitoring pods
53+
54+
```bash
55+
kubectl -n monitoring get pods
56+
kubectl -n monitoring get svc
57+
```
58+
59+
Wait until the Prometheus Operator, Prometheus, and Grafana pods are running.
60+
61+
A working `kube-prometheus-stack` deployment should include pods similar to these:
62+
63+
```text
64+
alertmanager-monitoring-kube-prometheus-alertmanager-0 2/2 Running
65+
monitoring-grafana-... 3/3 Running
66+
monitoring-kube-prometheus-operator-... 1/1 Running
67+
monitoring-kube-state-metrics-... 1/1 Running
68+
prometheus-monitoring-kube-prometheus-prometheus-0 2/2 Running
69+
```
70+
71+
The exact pod names and replica counts depend on the chart version and your cluster configuration.
72+
73+
## Reuse an Existing Prometheus and Grafana Stack
74+
75+
If your cluster already has Prometheus Operator and Grafana, you don't need to install `kube-prometheus-stack` again. Instead, confirm these points with the monitoring owner:
76+
77+
- The Prometheus Operator watches `ServiceMonitor` resources in the `monitoring` namespace.
78+
- Prometheus can scrape services in the `jupyterhub` namespace.
79+
- The operator selector matches the label used by AUPlC. The AUPlC chart creates `ServiceMonitor` and `PrometheusRule` resources with `release: <monitoring.releaseLabel>`.
80+
- Grafana sidecar dashboard discovery reads ConfigMaps from the `monitoring` namespace with `grafana_dashboard: "1"`, if you want the AUPlC dashboards to appear automatically.
81+
82+
For example, if the existing Prometheus stack selects `release: platform-monitoring`, set:
83+
84+
```yaml
85+
monitoring:
86+
releaseLabel: platform-monitoring
87+
```
88+
89+
## Configure AUPlC Monitoring Values
90+
91+
Edit `runtime/values.yaml` and enable the monitoring options you need.
92+
93+
Recommended production configuration:
94+
95+
```yaml
96+
monitoring:
97+
enabled: true
98+
namespace: monitoring
99+
releaseLabel: monitoring
100+
101+
hubMetrics:
102+
enabled: true
103+
allowUnauthenticatedScrape: false
104+
serviceAnnotations:
105+
enabled: false
106+
107+
serviceMonitor:
108+
enabled: true
109+
interval: 15s
110+
authorization:
111+
enabled: true
112+
type: Bearer
113+
hubServiceName: prometheus-metrics
114+
secret:
115+
create: true
116+
name: ""
117+
key: token
118+
119+
grafana:
120+
dashboard:
121+
enabled: true
122+
123+
prometheusRule:
124+
enabled: true
125+
```
126+
127+
### Value Reference
128+
129+
| Value | Description |
130+
|-------|-------------|
131+
| `monitoring.enabled` | Master switch for AUPlC monitoring resources. Keep this `true` when enabling any monitoring feature below. |
132+
| `monitoring.namespace` | Namespace where monitoring objects are created. Use `monitoring` for the stack shown in this guide. |
133+
| `monitoring.releaseLabel` | Value used for the `release` label on `ServiceMonitor` and `PrometheusRule`. It must match the label selected by your Prometheus Operator stack. For a Helm release named `monitoring`, this is commonly `release: monitoring`. |
134+
| `monitoring.hubMetrics.enabled` | Enables Hub metrics integration. The chart also creates a metrics `NetworkPolicy` allowing traffic from the monitoring namespace to the Hub on port `8081`. |
135+
| `monitoring.hubMetrics.allowUnauthenticatedScrape` | Allows `/hub/metrics` scraping without a JupyterHub token when set to `true`. Don't enable this in production unless `/hub/metrics` is guaranteed not to be exposed through a public proxy, NodePort, LoadBalancer, or Ingress. |
136+
| `monitoring.hubMetrics.serviceAnnotations.enabled` | Adds `prometheus.io/scrape`, `prometheus.io/path`, and `prometheus.io/port` annotations to the Hub service. Annotation-based scraping cannot attach the JupyterHub token, so prefer the authenticated `ServiceMonitor` path. |
137+
| `monitoring.serviceMonitor.enabled` | Creates a `ServiceMonitor` named `hub-metrics` in `monitoring.namespace`. It selects the Hub service in the `jupyterhub` namespace by `component: hub`, scrapes target port `8081`, and uses `<hub.baseUrl>/hub/metrics` as the path. |
138+
| `monitoring.serviceMonitor.interval` | Scrape interval for the Hub metrics endpoint, such as `15s`. |
139+
| `monitoring.serviceMonitor.authorization.enabled` | Adds ServiceMonitor authorization settings. Keep this `true` for authenticated scraping. |
140+
| `monitoring.serviceMonitor.authorization.type` | Authorization type passed to the ServiceMonitor. The default is `Bearer`. |
141+
| `monitoring.serviceMonitor.authorization.hubServiceName` | JupyterHub service account used for the metrics token. The default `prometheus-metrics` must match `hub.services.prometheus-metrics` and `hub.loadRoles.prometheus-metrics`, which grants `read:metrics`. |
142+
| `monitoring.serviceMonitor.authorization.secret.create` | Creates a token secret in the monitoring namespace when set to `true`. |
143+
| `monitoring.serviceMonitor.authorization.secret.name` | Optional existing or custom secret name. Leave empty to use the chart-generated `<hub fullname>-metrics-token` name. |
144+
| `monitoring.serviceMonitor.authorization.secret.key` | Secret key that stores the token. The default is `token`. |
145+
| `monitoring.grafana.dashboard.enabled` | Creates Grafana dashboard ConfigMaps in the monitoring namespace with label `grafana_dashboard: "1"`. |
146+
| `monitoring.prometheusRule.enabled` | Creates Prometheus alert rules for `hub_spawn_failed_total` and `hub_pod_failure_total`. |
147+
148+
## Apply the AUPlC Configuration
149+
150+
Run the upgrade from the deployment repository root.
151+
152+
```bash
153+
cd deploy
154+
helm upgrade jupyterhub ../runtime/chart --namespace jupyterhub \
155+
-f ../runtime/values.yaml
156+
```
157+
158+
If your deployment uses an additional local or environment-specific values file, include it in the same command. For example:
159+
160+
```bash
161+
helm upgrade jupyterhub ../runtime/chart --namespace jupyterhub \
162+
-f ../runtime/values.yaml -f ../runtime/values.local.yaml
163+
```
164+
165+
## Verify the Setup
166+
167+
Check that the AUPlC monitoring resources exist:
168+
169+
```bash
170+
kubectl -n monitoring get servicemonitor hub-metrics
171+
kubectl -n monitoring get secret | grep metrics-token
172+
kubectl -n monitoring get configmap grafana-dashboard-aup-hub
173+
kubectl -n jupyterhub get networkpolicy hub-metrics
174+
```
175+
176+
If `monitoring.prometheusRule.enabled: true`, also check the Hub alert rule:
177+
178+
```bash
179+
kubectl -n monitoring get prometheusrule hub-alerts
180+
```
181+
182+
A working cluster with ServiceMonitor, authenticated scraping, Grafana dashboards, and metrics NetworkPolicy enabled should show objects like this:
183+
184+
```text
185+
servicemonitor.monitoring.coreos.com/hub-metrics
186+
secret/hub-metrics-token
187+
configmap/grafana-dashboard-aup-hub
188+
networkpolicy.networking.k8s.io/hub-metrics
189+
```
190+
191+
Check that Prometheus sees the Hub target:
192+
193+
```bash
194+
kubectl -n monitoring port-forward svc/monitoring-kube-prometheus-prometheus 9090:9090
195+
```
196+
197+
Open `http://127.0.0.1:9090/targets` and look for the `hub-metrics` target. It should be `UP`.
198+
199+
You can also verify from the Prometheus API. With the port-forward still running, query the Hub scrape target:
200+
201+
```bash
202+
curl -fsSL 'http://127.0.0.1:9090/api/v1/query?query=up%7Bjob%3D%22hub%22%7D'
203+
```
204+
205+
A healthy result contains `"job":"hub"`, `"namespace":"jupyterhub"`, and a final value of `"1"`:
206+
207+
```json
208+
{
209+
"status": "success",
210+
"data": {
211+
"resultType": "vector",
212+
"result": [
213+
{
214+
"metric": {
215+
"job": "hub",
216+
"namespace": "jupyterhub",
217+
"service": "hub"
218+
},
219+
"value": ["<timestamp>", "1"]
220+
}
221+
]
222+
}
223+
}
224+
```
225+
226+
Check that Grafana can discover the AUPlC dashboards through the dashboard ConfigMap:
227+
228+
```bash
229+
kubectl -n monitoring describe configmap grafana-dashboard-aup-hub
230+
```
231+
232+
The ConfigMap should contain these dashboard files:
233+
234+
```text
235+
aup-hub-operations.json
236+
aup-hub-notebook-resources.json
237+
```
238+
239+
If your Grafana deployment uses the standard sidecar dashboard loader, these ConfigMaps are enough. You do not need to expose Grafana publicly just to validate this step.
240+
241+
Useful AUPlC Hub metrics include:
242+
243+
- `hub_spawn_gpu_total`
244+
- `hub_spawn_failed_total`
245+
- `hub_active_sessions`
246+
- `hub_session_runtime_minutes`
247+
- `hub_spawn_duration_seconds`
248+
- `hub_quota_denied_total`
249+
- `hub_quota_deducted_total`
250+
- `hub_pod_failure_total`
251+
- `hub_repo_clone_failed_total`
252+
253+
## Troubleshooting
254+
255+
### ServiceMonitor Exists but Prometheus Does Not Scrape It
256+
257+
Check the `release` label:
258+
259+
```bash
260+
kubectl -n monitoring get servicemonitor hub-metrics --show-labels
261+
```
262+
263+
If Prometheus expects a different label, update `monitoring.releaseLabel` and run the Helm upgrade again.
264+
265+
### Target Is Down or Returns Unauthorized
266+
267+
Use authenticated ServiceMonitor scraping in production:
268+
269+
```yaml
270+
monitoring:
271+
hubMetrics:
272+
allowUnauthenticatedScrape: false
273+
serviceAnnotations:
274+
enabled: false
275+
serviceMonitor:
276+
enabled: true
277+
authorization:
278+
enabled: true
279+
```
280+
281+
Annotation-based scraping cannot attach the JupyterHub token. It only works when unauthenticated metrics scraping is allowed, which should be limited to isolated development environments.
282+
283+
### Token Secret Is Missing
284+
285+
Confirm these values are enabled:
286+
287+
```yaml
288+
monitoring:
289+
enabled: true
290+
hubMetrics:
291+
enabled: true
292+
serviceMonitor:
293+
enabled: true
294+
authorization:
295+
enabled: true
296+
secret:
297+
create: true
298+
```
299+
300+
The chart also validates that `monitoring.serviceMonitor.authorization.hubServiceName` exists under `hub.services` and has a matching `hub.loadRoles` entry with the `read:metrics` scope.
301+
302+
### Grafana Dashboards Do Not Appear
303+
304+
Check that the dashboard ConfigMap was created:
305+
306+
```bash
307+
kubectl -n monitoring get configmap grafana-dashboard-aup-hub --show-labels
308+
```
309+
310+
The ConfigMap uses `grafana_dashboard: "1"`. Your Grafana sidecar or dashboard loader must watch the `monitoring` namespace and this label.
311+
312+
### Prometheus Alerts Do Not Appear
313+
314+
Check the rule label and namespace:
315+
316+
```bash
317+
kubectl -n monitoring get prometheusrule hub-alerts --show-labels
318+
```
319+
320+
The rule must be in a namespace watched by the Prometheus Operator, and its `release` label must match the operator's rule selector.

0 commit comments

Comments
 (0)