BM: Baca dalam Bahasa Melayu
World O4: The Diary & One Order
Capstone mission 9: Watch Kedai Kancil
You will learn: to run your own control room for Kedai Kancil: Prometheus, Grafana, node-exporter, one alert and one dashboard.
Checked with Prometheus 3 on 1 Nov 2026
The problem
Mission 9: watch Kedai Kancil! You will build a small control room in a monitoring/ folder. First, put each file in its place.
Drag a card into a box. Or tap the card, then tap the box.
Watch
node-exporter has no ports line. How does Prometheus reach it?
Pick one answer.
services:
prometheus:
image: quay.io/prometheus/prometheus:v3.13.5
volumes:
- ./prometheus:/etc/prometheus:ro
ports: ["9090:9090"]
node-exporter:
image: quay.io/prometheus/node-exporter:v1.12.1
grafana:
image: grafana/grafana:13.2.5
volumes:
- ./grafana/provisioning:/etc/grafana/provisioning:ro
- ./grafana/dashboards:/var/lib/grafana/dashboards:ro
ports: ["3000:3000"]Watch
Test the “Targets up” panel’s query first. Make it one number.
Type a PromQL query and press Run. The table shows the answer at the eval time. The graph shows it over time.
{
"uid": "kedai",
"title": "Kedai Kancil",
"panels": [
{ "type": "stat", "title": "Targets up",
"gridPos": { "x": 0, "y": 0, "w": 6, "h": 6 },
"targets": [{ "refId": "A", "expr": "sum(up)" }] },
{ "type": "timeseries", "title": "CPU busy (%)",
"gridPos": { "x": 6, "y": 0, "w": 18, "h": 6 },
"targets": [{ "refId": "A", "expr": "100 * (1 - avg(rate(node_cpu_seconds_total{mode=\"idle\"}[$__rate_interval])))" }] }
]
}Watch
Grafana runs in its own container. Why does the data source say http://prometheus:9090, not localhost?
Pick one answer.
# grafana/provisioning/datasources/prometheus.yaml
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
uid: prometheus
url: http://prometheus:9090
access: proxy
isDefault: true# grafana/provisioning/dashboards/kedai.yaml
apiVersion: 1
providers:
- name: kedai
folder: Kedai Kancil
type: file
options:
path: /var/lib/grafana/dashboardsTry it
This prometheus.yml scrapes localhost:9100. Inside the Prometheus container, nothing listens there. Point the node job at node-exporter.
Change the YAML. The box below shows how a computer reads it.
How a computer reads it
- globalmap · 2 keys
- scrape_interval15stext
- evaluation_interval15stext
- rule_fileslist · 1 items
- item 1rules.ymltext
- scrape_configslist · 2 items
- item 1map · 2 keys
- job_nameprometheustext
- static_configslist · 1 items
- item 1map · 1 keys
- targetslist · 1 items
- item 1localhost:9090text
- targetslist · 1 items
- item 1map · 1 keys
- item 2map · 2 keys
- job_namenodetext
- static_configslist · 1 items
- item 1map · 1 keys
- targetslist · 1 items
- item 1localhost:9100text
- targetslist · 1 items
- item 1map · 1 keys
- item 1map · 2 keys
Hint
For Prometheus itself, localhost is right. For node-exporter, use its service name.Try it
Now the alert. Page when any target is down for a minute, and name the job and instance.
Build an alert rule from the blocks. The timeline shows when it would wait and when it would fire.
groups: - name: kedai rules: - alert: KedaiTargetDown expr: up == 0 annotations: summary: Target down
Graph as a table
| Time | up{instance="db-1", job="db"} | up{instance="shop-1", job="shop"} | up{instance="shop-2", job="shop"} |
|---|---|---|---|
| 0s | 1 | 1 | 1 |
| 1m | 1 | 1 | 1 |
| 2m | 1 | 1 | 1 |
| 3m | 1 | 1 | 1 |
| 4m | 1 | 1 | 1 |
| 5m | 1 | 1 | 1 |
| 6m | 0 | 1 | 1 |
| 7m | 0 | 1 | 1 |
| 8m | 0 | 1 | 1 |
| 9m | 1 | 1 | 1 |
| 10m | 1 | 1 | 1 |
| 11m | 1 | 0 | 1 |
| 12m | 1 | 0 | 1 |
| 13m | 1 | 0 | 1 |
| 14m | 1 | 0 | 1 |
| 15m | 1 | 0 | 1 |
| 16m | 1 | 0 | 1 |
| 17m | 1 | 0 | 1 |
| 18m | 1 | 0 | 1 |
| 19m | 1 | 0 | 1 |
| 20m | 1 | 0 | 1 |
| 21m | 1 | 0 | 1 |
| 22m | 1 | 0 | 1 |
| 23m | 1 | 0 | 1 |
| 24m | 1 | 0 | 1 |
| 25m | 1 | 0 | 1 |
| 26m | 1 | 0 | 1 |
| 27m | 1 | 0 | 1 |
| 28m | 1 | 0 | 1 |
| 29m | 1 | 0 | 1 |
| 30m | 1 | 0 | 1 |
| 31m | 1 | 0 | 1 |
What the alert does over time
Grey: quiet. Amber: pending (true, but waiting). Red: firing.
{instance="db-1", job="db"}🔔 firing 6m to 8m
{instance="shop-1", job="shop"}🔔 firing 11m to 31m
At 12m:
- FIRING
KedaiTargetDowninstance="shop-1", job="shop"Target down
Timeline as a table
| Time | {instance="db-1", job="db"} | {instance="shop-1", job="shop"} |
|---|---|---|
| 0s | quiet | quiet |
| 1m | quiet | quiet |
| 2m | quiet | quiet |
| 3m | quiet | quiet |
| 4m | quiet | quiet |
| 5m | quiet | quiet |
| 6m | 🔔 firing | quiet |
| 7m | 🔔 firing | quiet |
| 8m | 🔔 firing | quiet |
| 9m | quiet | quiet |
| 10m | quiet | quiet |
| 11m | quiet | 🔔 firing |
| 12m | quiet | 🔔 firing |
| 13m | quiet | 🔔 firing |
| 14m | quiet | 🔔 firing |
| 15m | quiet | 🔔 firing |
| 16m | quiet | 🔔 firing |
| 17m | quiet | 🔔 firing |
| 18m | quiet | 🔔 firing |
| 19m | quiet | 🔔 firing |
| 20m | quiet | 🔔 firing |
| 21m | quiet | 🔔 firing |
| 22m | quiet | 🔔 firing |
| 23m | quiet | 🔔 firing |
| 24m | quiet | 🔔 firing |
| 25m | quiet | 🔔 firing |
| 26m | quiet | 🔔 firing |
| 27m | quiet | 🔔 firing |
| 28m | quiet | 🔔 firing |
| 29m | quiet | 🔔 firing |
| 30m | quiet | 🔔 firing |
| 31m | quiet | 🔔 firing |
Hint
Wait 1 minute, so a quick restart does not page anyone.Try it
Now build it for real, step by step. Your ticks are saved.
Real mission
mkdir -p monitoring && cd monitoring && mkdir -p prometheus grafana/dashboards grafana/provisioning/datasources grafana/provisioning/dashboardsdocker compose config --quietdocker run --rm --entrypoint promtool -v "$PWD/prometheus:/etc/prometheus:ro" quay.io/prometheus/prometheus:v3.13.5 check config /etc/prometheus/prometheus.ymldocker compose up -dhttp://localhost:9090/targetshttp://localhost:3000docker compose stop node-exporterhttp://localhost:9090/alertsdocker compose start node-exportergit add monitoring && git commit -m "Watch the shop" && git pushdocker compose down
0 of 12 steps done · Your ticks are saved.
Check
GitHub can see your files. Type your username and press Check now.
Check it on GitHub
- Your kedai-kancil repo is publicNot checked yet
- It has monitoring/compose.yamlNot checked yet
- It has the Prometheus configNot checked yet
- It has the alert ruleNot checked yet
- It has the dashboardNot checked yet
Check
The rest only you can see. Tick what is true.
Self-check
0 of 5 steps done · Your ticks are saved.
You did it!
You can now run a small control room: metrics, a dashboard and an alert.
Go deeper (optional)
Where the story breaks. A real control room watches from outside. Ours runs on the machine it watches. If that machine dies, the bell dies too. In a container, node-exporter sees the container’s own disks and network. Its README shows the host mounts for real machines. Next: add Alertmanager, so the bell reaches a phone.
Bahasa Melayu
Misi 9: pantau Kedai Kancil! Anda akan bina bilik kawalan kecil dalam folder monitoring/. Mula-mula, letak setiap fail di tempatnya.
node-exporter tiada baris ports. Macam mana Prometheus sampai kepadanya?
services:
prometheus:
image: quay.io/prometheus/prometheus:v3.13.5
volumes:
- ./prometheus:/etc/prometheus:ro
ports: ["9090:9090"]
node-exporter:
image: quay.io/prometheus/node-exporter:v1.12.1
grafana:
image: grafana/grafana:13.2.5
volumes:
- ./grafana/provisioning:/etc/grafana/provisioning:ro
- ./grafana/dashboards:/var/lib/grafana/dashboards:ro
ports: ["3000:3000"]
Uji query panel “Targets up” dulu. Jadikan ia satu nombor.
{
"uid": "kedai",
"title": "Kedai Kancil",
"panels": [
{ "type": "stat", "title": "Targets up",
"gridPos": { "x": 0, "y": 0, "w": 6, "h": 6 },
"targets": [{ "refId": "A", "expr": "sum(up)" }] },
{ "type": "timeseries", "title": "CPU busy (%)",
"gridPos": { "x": 6, "y": 0, "w": 18, "h": 6 },
"targets": [{ "refId": "A", "expr": "100 * (1 - avg(rate(node_cpu_seconds_total{mode=\"idle\"}[$__rate_interval])))" }] }
]
}
Grafana berjalan dalam container sendiri. Kenapa sumber data tulis http://prometheus:9090, bukan localhost?
# grafana/provisioning/datasources/prometheus.yaml
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
uid: prometheus
url: http://prometheus:9090
access: proxy
isDefault: true# grafana/provisioning/dashboards/kedai.yaml
apiVersion: 1
providers:
- name: kedai
folder: Kedai Kancil
type: file
options:
path: /var/lib/grafana/dashboards
prometheus.yml ini scrape localhost:9100. Dalam container Prometheus, tiada apa mendengar di situ. Halakan job node ke node-exporter.
Sekarang amaran. Panggil bila mana-mana target mati selama seminit, dan sebut job serta instance.
Sekarang bina betul-betul, langkah demi langkah. Tanda anda disimpan.
GitHub boleh nampak fail anda. Taip nama pengguna anda dan tekan Semak sekarang.
Selebihnya hanya anda boleh nampak. Tanda yang benar.
Kini anda boleh jalankan bilik kawalan kecil: metrik, dashboard dan amaran.
You finished this before. Practice is always welcome!
Official docs: Prometheus: getting started · Prometheus configuration · Node exporter guide · Grafana provisioning
You've learned for 25 minutes. A short break helps your brain.
Sprint done! Great focus. Stretch for a minute?