Kubernetes Ingress on AKS — the path that is not a Service
One listener. Many paths. The Service still exists. The path is not a second Service.
<!-- hal:authoritative:yaml -->
One listener. Many paths. The Service still exists. The path is not a second Service.
§I — Frame
Monday named a host. Coin: the host that is not the cluster. Friday named a surge. Coin: the surge that is not a second cluster. Tuesday named leftover API doors on AKS. None of those leftovers is today's.
Today the track is Kubernetes. k8s_day_counter reads 14. Even is CKA-emphasis. 08-28 closed Workloads App Lifecycle: rollingUpdate, maxSurge, maxUnavailable, Q18. 08-16 closed the scheduler half. 08-22 closed Storage. 08-10 closed Troubleshooting first pass. 08-04 closed Cluster Architecture access control. 07-29 closed Services and Networking first pass on ClusterIP, NodePort, LoadBalancer, and kube-proxy. The Cert sibling that day surveyed Ingress as one section inside a 20% domain. Isolation is the leftover, the way 09-02 isolated replace_triggered_by after 08-12 surveyed four lifecycle arguments.
The last three K8s-day clouds in the room are 08-31 GKE, 08-28 EKS, 08-25 AKS. All three vendors sat down. AKS last visited this track as an API door. Today AKS is a path. Leave authorized IP ranges. Leave COS. Leave maxSurge.
The azurecheatsheet Compute row names AKS: managed Kubernetes for containerized applications. The Networking row names Load Balancer: distributes incoming network traffic. It does not write Application Gateway. az-900 does. Application Gateway is a specialized load-balancing service that routes on URL paths and host headers, and can terminate TLS. That second row is the overlay. If you pick the cheatsheet Load Balancer because "load balancer" sounds like Ingress, you just bought an L4 Service. Poulton already told you Ingress is how multiple web applications share a single LoadBalancer Service (Ch. 8, printed p. 93). The cheatsheet stopped one product early. Same class as 08-28: named EKS, stopped before maxSurge. Name the service that exists. Name the object the API actually stores. Do not invent a cheatsheet row for AGIC.
Coin it: the path that is not a Service.
A Service is a stable name in front of Pods. An Ingress is a rule that says which host and which URL path map onto which Service. You can expose echo as NodePort and still fail Q12, because the stem also asked for http://example.org/echo. You can mint a second LoadBalancer Service named echo-ingress and still fail, because the exam wanted an Ingress object, not a second EXTERNAL-IP. 07-29 taught the four Service types. Today teaches the object that sits above them.
§II — Foundations: four facts about the path
Fact one. Ingress is not a Service type.
07-29 named four types. ClusterIP. NodePort. LoadBalancer. ExternalName. Ingress is none of them. Poulton capitalises Ingress because it is a resource in the API (Ch. 8, printed p. 93). It lives on networking.k8s.io/v1. Kind is Ingress. The backend of a rule is a Service name and a port number. The path is not a ClusterIP. The host is not a DNS record the Service owns. CoreDNS still serves echo-service.echo-sound.svc.cluster.local. Ingress serves example.org/echo. Two names. Two objects. One workload.
If/then: if the stem says expose the Deployment and stops, you are in 07-29. If the stem says expose, then create an Ingress for http://example.org/echo, you are here. If the stem says migrate the Ingress to Gateway and HTTPRoute, you are in Q11, which is leftover. Three doors. One word (routing) that people keep using for all three.
Fact two. The object does nothing without a controller.
K8sUR3 is blunt (Ch. 7, PDF p. 112). Ingress is split into a common resource specification and a controller implementation. There is no standard Ingress controller built into Kubernetes. Users create Ingress objects like every other object. By default there is no code running to act on those objects. Poulton says the same split in two constructs: a controller, and an object spec (printed p. 93). A lot of clusters do not ship a built-in controller.
Hosted Kubernetes sometimes does. Poulton's GKE walk installs nginx and then you have two: the built-in GKE controller, and nginx (printed p. 95). That is the trap, not the fleet choice. AKS does not make you SSH to etcd. AKS also does not make kube-apiserver program HTTP paths. Application Gateway is the listener. The controller that writes Gateway rules from Ingress objects is not in the apiserver. If you apply Q12's YAML on a kubeadm cluster with no controller, kubectl get ingress shows the object and ADDRESS stays empty. The exam still grades the object. The fleet grades the listener.
Fact three. One listener. Many Services. Longest prefix wins.
Poulton's summary: Ingress exposes multiple applications and Kubernetes Services via a single cloud load-balancer (printed p. 104). Host-based routing needs DNS (or /etc/hosts) pointed at that listener (printed p. 94). Path-based routing needs the upstream ready to serve the subpath, because the path remains unmodified unless a rewrite annotation the exam did not name is in play (K8sUR3 Ch. 7, PDF p. 120). Longest prefix matches. /echo is more specific than /. Q12's path is /echo with pathType: Prefix. Prefix /echo matches /echo and /echo/foo. It does not match /echobox. Exact would. ImplementationSpecific is a vendor door. On the exam, Prefix is the default you type unless the stem says Exact.
Fact four. The exam is kubeadm nginx. The fleet is AKS plus Application Gateway.
Q12 LabSetUp plants a Deployment in echo-sound. SolutionNotes exposes it as NodePort echo-service on 8080, then applies an Ingress named echo with host example.org and path /echo. Optional test: curl http://<nodeIP>:<nodePort>/echo. That curl proves the Service. It does not prove the Ingress. The Questions file says so: in the exam you may get curl against http://example.org/echo, which requires a controller and an /etc/hosts row. Two curls. Two objects. Grade the exam on the Ingress YAML. Grade the fleet on whether Application Gateway is the listener in front of AKS, not a public Load Balancer Service per app.
az-900 puts Application Gateway in Azure Networking, next to ExpressRoute and Firewall, not next to AKS. That placement is the lesson. The path lives in the network product. The cluster is the backend. 08-25 closed the API door (authorized IP ranges on the AKS control plane). Today closes the data door (which URL lands on which Service). Same cloud. Different product. 08-31 already taught that move on GCP: PGA was a subnet latch, COS was a node image.
§III — Mechanism: the walk the fleet can see
Hold three objects before you touch YAML.
- Deployment. Pods. Labels. 07-29 already spent this. Q12 LabSetUp already planted
echoinecho-sound. - Service. Selector matches those labels. Type may be ClusterIP in production behind Ingress. Q12 asks NodePort so a candidate can curl a node without a controller. NodePort is the Service. It is not the Ingress.
- Ingress. Host. Path. pathType. Backend service name and port. Controller elsewhere.
On AKS the listener you actually wanted is Application Gateway. The cheatsheet Load Balancer is what --type=LoadBalancer on a Service asks the cloud controller for. That is L4. One EXTERNAL-IP per Service. Poulton's whole chapter exists because that bill and that blast radius do not scale. One Gateway. Many rules. The path is how the Gateway multiplexes. The Service is still how kube-proxy finds Pods.
A short HCL sketch names the two Azure objects. It does not install a controller. It does not invent an AGIC block the cheatsheet does not carry.
resource "azurerm_kubernetes_cluster" "app" {
name = "app"
location = azurerm_resource_group.app.location
resource_group_name = azurerm_resource_group.app.name
dns_prefix = "app"
identity { type = "SystemAssigned" }
default_node_pool {
name = "sys"
node_count = 2
vm_size = "Standard_D4s_v5"
}
}
resource "azurerm_application_gateway" "edge" {
name = "edge"
location = azurerm_resource_group.app.location
resource_group_name = azurerm_resource_group.app.name
sku {
name = "Standard_v2"
tier = "Standard_v2"
capacity = 1
}
}
The cluster and the Gateway can exist with zero Ingress objects. That is K8sUR3 p. 112 as an Azure pair: objects you created, no code yet acting on kind: Ingress. Wiring the controller is a second apply. On the exam the controller is often already there and unnamed. On the fleet you name Application Gateway because az-900 named the routing product. You do not name a second AKS cluster because a path was missing. 08-28 already spent the second cluster.
ingressClassName is how you pick a controller when two exist. Poulton's GKE walk creates that problem on purpose. K8sUR3 Advanced Ingress Topics opens with running multiple controllers (PDF p. 120). Q12's SolutionNotes YAML has no class field. If the cluster has one controller watching all Ingress objects, the object still works. If the cluster has two, an Ingress without a class is a coin flip. Do not add a class the stem did not name. Do not omit a class the stem named. Read the stem.
TLS is adjacent. az-900 names offload as a Gateway feature. Q12 does not ask for a Secret. 08-01 already spent Ingress TLS on CKS Cluster Setup. CKS Q25 is that file. Leave the Secret. Leave the annotation. Today's rule is host plus path plus backend.
§IV — Worked example: Q12's two objects
LabSetUp already has the Deployment. You do not recreate it. You expose, then you route.
kubectl expose deployment echo -n echo-sound --name echo-service --type NodePort --port 8080 --target-port 8080
That line is a Service. validate.bash checks name, type NodePort, port 8080, and endpoints. If you stop here you have a green Service and a missing Ingress. Q12 is not over.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: echo
namespace: echo-sound
spec:
rules:
- host: example.org
http:
paths:
- path: /echo
pathType: Prefix
backend:
service:
name: echo-service
port:
number: 8080
validate.bash then checks four Ingress facts: name echo in echo-sound, host example.org, path /echo, backend echo-service port 8080. It does not curl example.org. It does not grep ADDRESS. It does not require ingressClassName. The object is the grade.
The Questions file offers a second curl: NODEIP:NODEPORT/echo. That is the Service path colliding with the app path. echoserver happens to answer on /echo. A candidate who only has NodePort can make that curl green and still fail the Ingress checks. A candidate who only has Ingress and a ClusterIP Service will fail the type=NodePort check even if example.org/echo works on a cluster with a controller. The stem asked for both. Both.
/etc/hosts is how you make example.org land on a node IP when you have no DNS (K8sUR3 Ch. 7, PDF p. 115; Poulton printed p. 104). Q12 mentions it. It is not a Kubernetes object. Do not put a host alias into a ConfigMap and call it Ingress.
Wrong writes, named so you can refuse them:
- A second Service
--type=LoadBalancer --name echo-ingress. 07-29. Extra EXTERNAL-IP. No Ingress object. Fail. path: /because "Prefix will still match /echo". Longest prefix is not an excuse to ignore the stem. validate.bash greps/echo. Fail.- Backend port 80 because "HTTP is 80". The container and the Service are 8080. Fail.
- Ingress in
defaultbecause you forgot-n echo-sound. Fail. - Gateway + HTTPRoute because you studied Q11 last night. Leftover. Fail.
§V — Connection to prior lessons
08-31 named the leftover a GKE node image. AppArmor enabled on describe node, Unconfined on the Pod. The host was not the cluster. Today's leftover is not a kernel. Do not print image_type and call the path routed.
08-28 named the leftover a second EKS cluster next to a Deployment that already had maxSurge. Two ReplicaSets on one API. Today's leftover is not a rollout. An Ingress change does not mint a ReplicaSet. A template change does. If you kubectl rollout status after applying the Ingress YAML you are in the wrong file.
08-25 named leftover API doors on AKS: authorized IP ranges, local accounts. The control plane still answered. Today's AKS visit is the data plane listener. Same subscription. Different object. Do not print api_server_authorized_ip_ranges and call Ingress done.
07-29 named the Service as a stable name over churning Pods. kube-proxy. ClusterIP. NodePort. LoadBalancer. That file's title stopped before Ingress on purpose. The Cert sibling surveyed the L7 layer in one section. Today is that section with a cloud name on it. Do not re-teach iptables versus ipvs.
08-22 and 08-10 and 08-16 are storage, troubleshooting, and scheduling. A Pending backend Pod still breaks Ingress, because endpoints empty is check 8 in validate.bash. Placement remains 08-16. This file does not bind.
§VI — Connection to today's Dev and Cert
Dev is the Python census. NetworkingV1Api.list_ingress_for_all_namespaces. Print host, path, pathType, backend service, backend port, ingressClassName. Join the Service only to report type. Do not patch. 08-31 already taught that a leftover checker that patches securityContext confines the fleet. A leftover checker that patches Ingress rules cuts a path a canary is mid-flight on. Inventory is a timer that prints.
Cert is Q12 by hand. Expose NodePort. Apply the Ingress. Verify the four jsonpaths. Name Q11 as the leftover (Gateway API). Name Q16 as 07-29 (NodePort only). The exam is kubeadm. The fleet is AKS plus Application Gateway. Learn both. Grade this file on the Ingress object. Grade the fleet on one listener, many paths, not a LoadBalancer Service per app.
§VII — Close
The Service is 07-29. The rollout is 08-28. The host is 08-31. The path is this file. Application Gateway is the listener az-900 named. AKS is the cluster the cheatsheet named. The Ingress object is the rule. The controller is not kube-apiserver.
Read the Service first. Then get the Ingress. Then ask whether ADDRESS empty is a missing controller or a missing object. Decide whether you are on the exam or on the fleet. The exam wants example.org/echo as YAML. The fleet wants one Gateway in front of many backends. Neither wants a second cluster because a path was missing. Neither wants a second Service type because the word load balancer appeared.
Examine well. The path is not a Service.
Related
- Prior arc: GKE COS host (2026-08-31)
- Pair hub: Cross-References/domains/01-Earth-DevOps
- Grounding tome: Poulton Ch.8, Ingress, pp. 93-104