Observability & Monitoring
Metrics, logs, traces, alerting, dashboards, and diagnosing a degraded host or service.
11 questions
JuniorTheoryVery commonWhat is the purpose of a monitoring system, and name common tools?
What is the purpose of a monitoring system, and name common tools?
A monitoring system continuously collects signals about hosts and services, stores them over time, visualizes them, and raises alerts when something crosses a threshold. Its purpose is to detect failures and degradation early and give data to diagnose them. Common tools include Prometheus, Zabbix, Grafana, and the ELK stack.
Common mistakes
- ✗Reducing monitoring to a single live snapshot rather than time-series history
- ✗Confusing monitoring with deployment or process supervision
- ✗Forgetting that alerting on thresholds is a core function, not optional
Follow-up questions
- →What is the difference between push-based and pull-based metric collection?
- →Why is storing metrics as time series important for detecting degradation?
MiddleTheoryVery commonHow do metrics, logs, and traces differ as the three pillars of observability?
How do metrics, logs, and traces differ as the three pillars of observability?
Metrics are numeric time series — cheap aggregates like request rate or CPU showing a service's state and trends. Logs are timestamped discrete events with detail, used to find what happened around an error. Traces follow one request across services, exposing latency to locate bottlenecks. Metrics answer 'is it healthy?', logs 'what happened?', traces 'where is it slow?'
Common mistakes
- ✗Treating all three as interchangeable rather than answering different questions
- ✗Trying to debug a single request's path from aggregate metrics alone
- ✗Believing logs scale to high cardinality as cheaply as metrics do
Follow-up questions
- →Why are high-cardinality labels expensive in a metrics system like Prometheus?
- →How does a trace propagate its context across service boundaries?
JuniorTheoryCommonThe Prometheus metric types — counter, gauge, histogram, summary — and when do you use each?
The Prometheus metric types — counter, gauge, histogram, summary — and when do you use each?
A counter only ever rises — requests or errors — and you read it with rate(). A gauge moves both ways, like memory or queue depth. A histogram buckets observations so you can derive latency percentiles from the buckets; a summary computes them client-side. Use a histogram for latency — an average hides the tail.
Common mistakes
- ✗Using an average for latency instead of a histogram percentile
- ✗Treating a counter as free to go down, or a gauge as monotonic
- ✗Assuming a summary can be re-aggregated across instances like a histogram
Follow-up questions
- →Why can histogram buckets be re-aggregated across instances while summary quantiles cannot?
- →What makes a histogram the right choice for a latency SLO (service-level objective)?
MiddleTheoryCommontop shows load average 50 20 10 — what does it mean and is it bad?
top shows load average 50 20 10 — what does it mean and is it bad?
Load average is the average number of processes runnable or in uninterruptible wait over the last 1, 5, and 15 minutes. Here 50/20/10 means load is rising fast — recent load far exceeds the older window. Whether it is bad depends on core count: load near the number of cores is healthy, but far above it means tasks are queuing for CPU, disk, or I/O.
Common mistakes
- ✗Reading load average as a CPU percentage rather than a queue length
- ✗Judging the value without dividing by the number of cores
- ✗Forgetting that uninterruptible I/O waits also raise the load
Follow-up questions
- →Why can load average be high while CPU utilization stays low?
- →How does the three-window 1/5/15 reading help distinguish a spike from a trend?
MiddleCodeCommonComplete PromQL queries for a per-second 5xx error rate and 95th-percentile latency.
Complete PromQL queries for a per-second 5xx error rate and 95th-percentile latency.
Error rate is rate() over the http_requests_total counter filtered to 5xx and summed over 5m. For p95 use histogram_quantile(0.95, ...) over the rate of the histogram _bucket series, but you must sum by (le) those buckets first. Never average latency, and never average percentiles across instances — aggregate buckets, then take the quantile.
Common mistakes
- ✗Averaging latency, or averaging each instance's percentile, instead of aggregating buckets
- ✗Feeding histogram_quantile the counter total rather than the rate of _bucket series
- ✗Forgetting
sum by (le)so buckets from many instances are not combined
Follow-up questions
- →Why must you
sum by (le)beforehistogram_quantilewhen scraping many instances? - →Why is averaging two instances' p95 values not the fleet-wide p95?
MiddleTheoryCommonPrometheus pull vs push collection — when do you need a Pushgateway, and how do alerting rules fire?
Prometheus pull vs push collection — when do you need a Pushgateway, and how do alerting rules fire?
Prometheus pulls — it scrapes targets on an interval via service discovery, so a missing scrape signals a target is down. Batch jobs too short-lived to scrape push to a Pushgateway that Prometheus scrapes. Alerting rules re-evaluate a PromQL expression each interval and fire once it holds for the for window — alert on symptoms, not causes.
Common mistakes
- ✗Thinking Prometheus pushes; it scrapes, and a missing scrape is a signal
- ✗Using a Pushgateway for long-running services instead of only batch jobs
- ✗Alerting on every cause, which buries the on-call in fatigue
Follow-up questions
- →Why does a pull model make a target being down easy to detect?
- →Why is a Pushgateway a stale-metric risk if a batch job stops pushing?
SeniorDebuggingCommonUsers report a host is slow — diagnose the bottleneck from the metrics
Users report a host is slow — diagnose the bottleneck from the metrics
Load average 38 on 4 cores is far above capacity, yet CPU is mostly idle and 86.5% is wa (I/O wait), and memory is fine — so the host is disk-bound, not CPU-bound. The bottleneck is disk I/O: processes are blocked waiting on storage. Confirm with iotop or iostat -x to find the busy device and offending process.
Common mistakes
- ✗Reading high load average as CPU saturation while ignoring the wa column
- ✗Misreading I/O wait (wa) as idle CPU rather than blocked-on-disk time
- ✗Stopping at the symptom instead of naming a command to find the busy device
Follow-up questions
- →How would
iostat -xcolumns like%utilandawaitconfirm a disk bottleneck? - →If wait were on the network instead, which tools would you reach for?
MiddleTheoryOccasionalWhat is a configuration-management tool like Ansible, and why is it idempotent?
What is a configuration-management tool like Ansible, and why is it idempotent?
A configuration-management tool such as Ansible or Puppet declares the desired state of many hosts as code and brings each host to that state — packages, files, services. The tool makes only the changes needed. It is idempotent: re-running the same playbook on an already-correct host changes nothing, so runs are safe to repeat and converge to the declared state.
Common mistakes
- ✗Thinking each run reinstalls everything rather than converging to desired state
- ✗Confusing config management with a one-off deployment or build step
- ✗Believing idempotency means it can only run once per host
Follow-up questions
- →How does Ansible's agentless push model differ from Puppet's agent-pull model?
- →Why does idempotency make config-management runs safe to schedule periodically?
SeniorDesignOccasionalYou must roll out a monitoring agent (e.g. Zabbix) with a standard parameter set to roughly 1000 Linux hosts. Describe how you would deploy it at that scale, how you would keep the configuration consistent, and the practical problems you expect along the way — network access, host heterogeneity, weak hardware, and registration of new agents.
You must roll out a monitoring agent (e.g. Zabbix) with a standard parameter set to roughly 1000 Linux hosts. Describe how you would deploy it at that scale, how you would keep the configuration consistent, and the practical problems you expect along the way — network access, host heterogeneity, weak hardware, and registration of new agents.
Do not do it by hand. Use configuration management (Ansible, Puppet, or Salt) to install the package, drop a templated config, and enable the service idempotently across all hosts — one re-runnable source of truth. Drive it in batches to avoid overloading the monitoring server, and use auto-registration gated by an allow-list so only expected hosts join. Expect: missing network access, OS/version heterogeneity, weak hosts, and credential distribution.
Common mistakes
- ✗Deploying by hand instead of with idempotent config management
- ✗Registering all agents at once and overloading the monitoring server
- ✗Assuming every host is reachable and identical (firewall/version gaps)
Follow-up questions
- →How does idempotency let you re-run the rollout safely after a partial failure?
- →Why does auto-registration help, and what risk does it introduce?
SeniorPerformanceOccasionalp99 latency is climbing while CPU stays flat — find the bottleneck with the RED and USE methods.
p99 latency is climbing while CPU stays flat — find the bottleneck with the RED and USE methods.
Flat CPU means the host is not the bottleneck. Apply RED — rate, errors, duration — per route: rising duration with a few errors points to a slow downstream dependency, not compute. USE — utilization, saturation, errors — stays low, ruling out CPU and memory. Read p99 from a histogram, never the mean, which hides the tail.
Common mistakes
- ✗Reading the mean latency, which hides a bad p99 tail
- ✗Slicing latency by a high-cardinality label like user_id, exploding the TSDB (time-series database)
- ✗Assuming flat CPU rules out a problem, ignoring a slow downstream dependency
Follow-up questions
- →Why is averaging each instance's p99 to get a cluster p99 statistically wrong?
- →Why does adding a user_id label to slice latency blow up a Prometheus time-series database?
SeniorTheoryOccasionalHow would you assemble an observability stack for a Kubernetes platform — scrape, store, dashboard, alert?
How would you assemble an observability stack for a Kubernetes platform — scrape, store, dashboard, alert?
Scrape with Prometheus via service discovery so new Pods are found; store long-term in Thanos or Mimir; visualize in Grafana; alert via Alertmanager on SLO (service-level objective) burn, not raw errors. Add logs (Loki/ELK) and traces (Tempo/Jaeger) tied by a correlation id, so a dashboard shows whether the app or a dependency is at fault.
Common mistakes
- ✗Alerting on raw errors or CPU instead of user-facing SLO burn
- ✗Keeping metrics, logs, and traces uncorrelated, so you cannot pivot between them
- ✗Ignoring long-term storage and label cardinality, melting the TSDB (time-series database)
Follow-up questions
- →How does a correlation id let you jump from a slow trace to its logs?
- →Why do you store metrics long-term separately rather than in Prometheus alone?