Skill 05 · AKS Troubleshooting
Subchapter 5.15
networking.mdMarkdown14 KBView on GitHub
For CNI-specific issues, check CNI pod health and review AKS networking concepts.
First define the failing flow: source pod, destination (Service, pod, or host), port and protocol, and direction. Walk DNS → Service → Endpoints → pod → network policy/CNI → Azure NSG/UDR/egress, and stop at the first layer that blocks. After any fix, re-test the same flow from the same source.
Diagnostics - always start here:
# 1. Verify service exists and has endpoints (read-only)
kubectl get svc <service-name> -n <ns>
kubectl get endpoints <service-name> -n <ns>
kubectl get endpointslice -n <ns> \
-l kubernetes.io/service-name=<service-name> -o wide
kubectl get pods -n <ns> -l <service-selector> -o wideDecision tree:
| Observation | Cause | Fix |
|---|---|---|
Endpoints shows <none> | Label selector mismatch | Align selector with pod labels; check for typos |
| Endpoints has IPs but unreachable | Port mismatch or app not listening | Confirm targetPort = actual container port |
| Works from some pods, fails from others | Network policy blocking | See Network Policy section |
| Works inside cluster, fails externally | Load balancer issue | See Load Balancer section |
ECONNREFUSED immediately | App not listening on that port | Check listening ports in the pod |
Pods that are running but not Ready are removed from Endpoints. Check kubectl get pod <pod> -n <ns>.
Deep diagnostics with Inspektor Gadget (when the above checks are inconclusive):
Use the IG base command pattern with --k8s-namespace <ns> --k8s-podname <pod-name> and these gadgets:
snapshot_socket (timeout 5) — check what ports the pod is listening ontrace_tcp (timeout 30) — trace connect/accept/close eventstrace_tcpretrans (timeout 30) — packet retransmissionsUse the affected pod; do not create a test pod or change DNS, NetworkPolicy, NSG, route, or firewall configuration without explicit approval.
Inspect the affected pod’s resolver inputs and determine whether its node pool uses LocalDNS before assuming every query goes directly through kube-dns and CoreDNS. AKS Automatic preconfigures LocalDNS; AKS Standard can enable it per node pool.
# 1. Preserve and explicitly inspect every nameserver, search suffix, and option
kubectl exec <affected-pod> -n <ns> -- cat /etc/resolv.conf
# 2. Inspect the affected node pool and resolver path
kubectl get pod <affected-pod> -n <ns> -o wide
az aks nodepool show \
--resource-group <cluster-resource-group> \
--cluster-name <cluster-name> \
--name <affected-node-pool> -o jsonIf LocalDNS is enabled, scope the failure before changing anything: all pools or one pool, all nodes or one node, UDP or TCP, and cluster zones or external zones. Inspect the effective LocalDNS forwarding configuration and logs on the affected node using an approved node-access method. LocalDNS forwards different zones to CoreDNS or VNet DNS, so a node-, pool-, protocol-, or zone-specific failure can be local even when CoreDNS is healthy. Query logging has resource cost and requires approval. Updating a node pool’s LocalDNS configuration can reimage nodes; disclose that impact before proposing it.
If LocalDNS is disabled, or once its forwarding path reaches CoreDNS, continue with the CoreDNS branch:
# 3. Check the kube-dns Service, backends, CoreDNS pods, logs, and config
kubectl get service kube-dns -n kube-system -o wide
kubectl get endpoints kube-dns -n kube-system -o wide
kubectl get endpointslice -n kube-system \
-l kubernetes.io/service-name=kube-dns -o wide
kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide
kubectl logs -n
Repeat these checks for each node hosting a CoreDNS pod. For Azure CNI Pod Subnet, COREDNS_SOURCE_SUBNET_ID is the pod subnet; otherwise it is the CoreDNS node NIC subnet. Record an NSG or route table as absent only when its association lookup succeeds with an empty ID; record a failed lookup as inaccessible instead of running its dependent show command. Evaluate outbound rules and routes from the CoreDNS pod/node source to every configured upstream on both UDP and TCP destination port 53.
If the selected route has a VirtualAppliance next hop, run the exact policy or classic network-rule collection commands in Mandatory Firewall or NVA branch when traversed, then query the matching DNS decisions:
az monitor log-analytics query \
--workspace <log-analytics-workspace-id> \
--timespan <incident-start-utc>/<incident-end-utc> \
--analytics-query "union isfuzzy=true AZFWNetworkRule, AzureDiagnostics | where _ResourceId =~ '<firewall-resource-id>' | where (SourceIp in ('<coredns-pod-ip>','<coredns-node-ip>') and DestinationIp == '<custom-forwarder-ip>' and DestinationPort == 53 and Protocol in ('UDP','TCP')) or (Category == 'AzureFirewallNetworkRule' and (msg_s has '<coredns-pod-ip>' or msg_s has '<coredns-node-ip>') and msg_s has '<custom-forwarder-ip>' and msg_s has '53' and (msg_s has 'UDP' or msg_s has 'TCP')) | project TimeGenerated,Category,Action,Protocol,SourceIp,DestinationIp,DestinationPort,RuleCollection,Rule,msg_s"For a custom NVA, collect equivalent network-rule and log evidence filtered to the same CoreDNS source, upstream destination, incident window, and UDP/TCP 53.
If CoreDNS imports a custom ConfigMap, retrieve the named ConfigMap shown by the inventory before evaluating its forwarding rules. A successful direct query to a custom forwarder with a failed query through kube-dns points to CoreDNS configuration or service-path evidence; failure to reach the forwarder points to routing, NSG, firewall, or NetworkPolicy evidence.
Do not name UDP source-port reuse as the cause of CoreDNS imbalance until logs show the same client source IP and port repeatedly reaching one replica.
DNS failure patterns:
| Symptom | Evidence boundary |
|---|---|
NXDOMAIN for svc.cluster.local | Confirm search domains, kube-dns endpoints, and the CoreDNS kubernetes plugin before changing CoreDNS |
Internal names resolve; external names return NXDOMAIN | Compare CoreDNS forwarding configuration and direct queries to each configured upstream |
SERVFAIL | Correlate CoreDNS logs with upstream reachability and forwarder responses |
| Private endpoint FQDN resolves publicly | Inspect the privatelink.* private DNS zone, record set, virtual network links, and conditional forwarders |
i/o timeout | Inspect NetworkPolicy plus effective node-NIC NSG, route, and present firewall rules for both UDP and TCP 53 |
Changing CoreDNS replicas/configuration, custom forwarders, NetworkPolicy, NSGs, routes, or firewall rules is remediation and requires explicit approval.
Sources: LocalDNS troubleshooting (opens in a new tab), LocalDNS configuration (opens in a new tab), and CoreDNS troubleshooting (opens in a new tab).
Deep diagnostics with Inspektor Gadget (when the above checks are inconclusive):
Use the IG base command pattern with --k8s-namespace <ns> --k8s-podname <pod-name> and trace_dns (timeout 30). Key signals: rcode=3 (NXDOMAIN), rcode=2 (SERVFAIL), high latency values, queries going to unexpected destinations.
Use references/azure-network-path.md to
derive the source node/VMSS NIC, prove the effective NSG and route, and inspect
the selected UDR, NAT gateway, load-balancer outbound rule, firewall/NVA,
private endpoint, service endpoint, or peering path. It includes current
Network Watcher show-next-hop and test-ip-flow syntax plus their AKS VMSS
limitations.
Before concluding, require:
Record a skipped branch and why. A successful empty association means absent; a failed lookup means inaccessible, not absent. Do not change DNS, endpoint policy, NSGs, UDRs, peering, NetworkPolicy, or firewall/NVA rules until the first blocking layer is proven and remediation is explicitly approved.