← Back to Projects

Debugging a Managed CoreDNS Regression

Tracing intermittent production timeouts to a provider-managed DNS add-on update, and the probe design that amplified it

Platform: AKS (200+ nodes)
Type: Incident RCA
Tools: APM, logs, kubectl
Role: Lead investigator

Overview

A customer-facing document API on a large, multi-tenant AKS cluster started timing out intermittently. Nobody had deployed anything and no infrastructure had changed. Within a few hours I'd tied the onset to a provider-managed CoreDNS image update rolled out as part of routine add-on maintenance. I also found that a liveness probe in a downstream service was turning short DNS blips into a restart storm.

Why This Was Tricky

Architecture

PodDNS lookupCoreDNS5 replicas, managedVNet resolverchainUpstreamAzure DNS
Resolution path. The managed CoreDNS layer was the one that changed.

A consumer service in another namespace health-checked the API. Its liveness probe included that dependency check, which turned out to matter more than anyone expected.

Investigation

1. Build the timeline before forming theories

The ReplicaSet history in kube-system showed a new CoreDNS ReplicaSet with a patch-level image bump. The first errors appeared within about two minutes of it rolling out.

Check add-on history first
kubectl -n kube-system get rs -l k8s-app=kube-dns \
  -o custom-columns=NAME:.metadata.name,CREATED:.metadata.creationTimestamp,IMAGE:.spec.template.spec.containers[0].image

2. Compare against a baseline

The same daily window had hundreds of errors on the incident day and zero on each of the two days before. That pattern points to a change, not load or slow capacity drift.

3. Read CoreDNS's own telemetry

The new pods logged a steady stream of forward health-check failures and upstream i/o timeouts. Most were AAAA and HTTPS record lookups.

4. Disprove the alternatives with data

TheoryEvidenceVerdict
Single-replica API emptied the endpoint poolEvery 503 was on the API's own readiness endpoint. There were zero customer-facing 503sRuled out
Burstable nodes out of CPU creditsCredits at maximum, node CPU around 20%, errors spread evenly across all CoreDNS podsRuled out
Database pressureWell under the IOPS ceiling, negligible failed connections, healthy p95 latencyRuled out
Managed CoreDNS updateOnset minutes after the image change, zero baseline before itRoot cause

5. Find the amplifier

The consumer's liveness probe called the API's health endpoint, and failures were never cached. Every DNS blip made kubelet restart the consumer, and every probe sent a live request to one of the API's busiest endpoints.

Recommendations

  1. Decouple liveness from dependencies. Liveness answers "is this process alive?". Dependencies belong in readiness.
  2. Escalate to the provider. Rolling back proves causation, but the add-on manager reconciles the image back, so the fix has to come from them.
  3. Add DNS headroom: NodeLocal DNSCache, ndots: 2 for high-volume external lookups, and a higher autoscaler ladder for CoreDNS.
  4. Customise only through coredns-custom. Direct edits to the managed ConfigMap get reverted.
pod spec
# Illustrative: cut search-domain fan-out for workloads making many external calls
spec:
  dnsConfig:
    options:
      - name: ndots
        value: "2"

Key Outcomes

Lessons Learned

Conclusion

Evidence first, theory second. The obvious fix would have cost money and changed nothing. The lasting fixes were in probe design and DNS headroom.

Technologies Used

AKSCoreDNScoredns-autoscalerKubernetes ProbesIstioAPMLog AnalyticsNRQL / KQL

About this case study: this write-up is drawn from my hands-on platform engineering experience. Organisation names, system names, figures and timelines have been changed or generalised, and all code, configuration and queries are illustrative examples written for this page.

← Hardening a Developer Portal on AKSUnblocking Batch Jobs: An Admission Webhook Bottleneck →
← Back to Projects Get in Touch