Certified Kubernetes Administrator (CKA) #19 Networking 2: Ingress, IngressClass, TLS
In #18 Networking 1 we attached a stable entry point to a group of Pods with a Service. But what happens when you have dozens of services to expose externally? NodePort splits traffic only by port number, and LoadBalancer spins up one cloud load balancer per service, driving up costs. Routing traffic by domain or path is also beyond what a single Service layer can do.
This post’s subject, Ingress, gathers that burden in one place. It’s an object that looks at host and path at the L7 (HTTP/HTTPS) level and splits traffic to the right Service. But Ingress is just a manifest that writes down rules — what actually turns those rules into real traffic is the Ingress Controller. This two-layer model is where people most often trip up, both in the exam and in operations, so that’s where we’ll focus.
The two-layer model: Ingress and Ingress Controller #
The first thing to grasp is that the Ingress object and the Ingress Controller are different things. Confusing the two lands you in the “I created an Ingress, so why is there no response?” trap on exam day.
| Category | What it is | Role |
|---|---|---|
| Ingress (object) | API resource (manifest) | A rule declaration like “this path on this host goes to that Service” |
| Ingress Controller | An actually running Pod (usually Deployment + Service) | The reverse proxy that reads those rules and actually routes the traffic |
An Ingress manifest does nothing on its own. An Ingress Controller must be running in the cluster; it watches Ingress objects and, following the rules, builds proxy configuration to receive external traffic. There are several kinds of controllers.
- ingress-nginx (nginx-based, managed by the Kubernetes project. The standard for the exam and for learning)
- Traefik
- HAProxy
- Cloud-managed (GKE Ingress, AWS Load Balancer Controller, etc.)
In CKA exam clusters, ingress-nginx is often pre-installed, or installation manifests are provided. If you’re building an operations cluster yourself, you have to install the controller first with Helm or the official manifests. If you create only the Ingress without a controller, ADDRESS stays empty and traffic reaches nowhere.
Structure of the Ingress object #
The core field of an Ingress is spec.rules. A single rule consists of a host (optional) and a list of paths, and each path points to which port of which Service to forward to.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-ingress
namespace: default
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80The manifest above sends requests coming into shop.example.com that start with /api to api-service:8080, and everything else under / to web-service:80. This is path-based routing, splitting backends by path under a single domain.
host-based routing #
Splitting host across multiple rules lets you send traffic to a different Service per domain. This is the structure of hosting multiple sites from one entry point.
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-service
port:
number: 80
- host: blog.example.com # add a rule per domain
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: blog-service
port:
number: 80If you omit host, the rule applies to all hostnames.
pathType: Prefix and Exact #
pathType is a required field in v1 Ingress. Leaving it out gets the manifest rejected, so it’s a frequent point of lost marks in the exam.
| pathType | Matching | Example |
|---|---|---|
Prefix | Prefix match on path elements (by / segment) | /api matches /api and /api/v1 |
Exact | Path must be exactly equal | /api matches only /api; /api/v1 doesn’t match |
ImplementationSpecific | Delegated to the controller’s implementation | Interpretation varies by controller |
In most cases you use Prefix, and only use Exact when you must accept exactly one path. ImplementationSpecific depends on controller behavior, so it’s safer to avoid it when you need portability.
defaultBackend #
You can set a default backend to receive requests that match no rule. If you don’t set one, unmatched requests fall through to the controller’s default 404. It goes at the same level as spec.rules.
spec:
ingressClassName: nginx
defaultBackend: # destination for requests that match no rule
service:
name: fallback-service
port:
number: 80
rules:
- host: shop.example.com
# ... (same rules as above)IngressClass: separating multiple controllers #
A single cluster can have several Ingress Controllers running. For example, running an internal nginx and an external cloud controller together. In that case you have to decide which controller each Ingress object belongs to, and that’s the job IngressClass takes on.
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: nginx
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
spec:
controller: k8s.io/ingress-nginxAn Ingress object specifies the IngressClass it follows with spec.ingressClassName. The ingressClassName: nginx in the manifests above points to exactly this IngressClass. The two have to be linked by name so that the ingress-nginx controller recognizes that Ingress as its own and processes it.
If one IngressClass carries the is-default-class: "true" annotation, an Ingress that omits ingressClassName is automatically bound to that default class. In environments with multiple controllers or no default class, you must specify ingressClassName to reach the intended controller.
In the past, the class was specified with the
kubernetes.io/ingress.classannotation. That approach is deprecated, and using thespec.ingressClassNamefield is the standard today. We’ll write it with the field approach for the exam too.
TLS termination #
Ingress can receive HTTPS traffic and terminate TLS. The certificate and key are stored in the cluster as a Secret, and the Ingress’s spec.tls section references that Secret.
First, create a kubernetes.io/tls type Secret.
# create a TLS Secret from the certificate (tls.crt) and key (tls.key)
kubectl create secret tls shop-tls \
--cert=tls.crt \
--key=tls.keyThen add a tls section to the Ingress and bind the relevant host.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-ingress
namespace: default
spec:
ingressClassName: nginx
tls:
- hosts:
- shop.example.com
secretName: shop-tls
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80HTTPS requests arriving at the hosts listed in tls[].hosts are terminated using the certificate in the Secret named by secretName. There are two common mistakes here. First, if the domain in tls[].hosts and the domain in rules[].host don’t match, the certificate isn’t applied. Second, if the Secret is in a different namespace than the Ingress, it won’t be picked up. The Secret must be in the same namespace as the Ingress.
Validation and troubleshooting #
After creating it, check the state right away. Whether the Ingress’s ADDRESS is filled in is the first signal.
# check the Ingress list and ADDRESS
kubectl get ingress
# see rules, TLS, and events in detail
kubectl describe ingress shop-ingressThe typical causes of getting stuck in operations and the exam are as follows.
- ADDRESS is empty: The Ingress Controller isn’t installed or isn’t running. Check the controller Pod state first with
kubectl get pods -n ingress-nginx. - Falls through to 404: The
pathTypeor path is off, or the Service/port the backend points to is wrong. Cross-check the target Service and port withkubectl get svc. - TLS isn’t applied: Check the Secret name, type (
kubernetes.io/tls), and namespace, and thattls.hostsmatchesrules.host. - The wrong controller handles it:
ingressClassNameis empty or points to the wrong class.
Exam points #
In CKA’s Services and Networking domain (20%), Ingress is a regular exam topic. Getting the following into your hands saves time.
- The two-layer model where Ingress is the declaration (object) and the controller does the routing. Always recall first that it doesn’t work without a controller.
- In v1 Ingress,
pathTypeis required. Don’t lose marks by omitting it. - Specify the controller with the
spec.ingressClassNamefield, not an annotation. - For TLS, create a
kubernetes.io/tlsSecret and reference it inspec.tls, with the host matchingrulesand the namespace being the same. - The
apiVersionisnetworking.k8s.io/v1. Be careful not to use older notation.
Quickly generating the skeleton with the kubectl create ingress command and then hand-editing is also a valid approach.
# generate the Ingress skeleton with a command, then output YAML
kubectl create ingress shop-ingress \
--class=nginx \
--rule="shop.example.com/api*=api-service:8080" \
--rule="shop.example.com/*=web-service:80" \
--dry-run=client -o yamlTo review the concept of Ingress in a broader context, K8s Intermediate #3 Ingress and Ingress Controller is a good companion read.
Summary #
What this post locked in:
- Ingress is an object that declares host- and path-based L7 routing rules, and the actual traffic handling is done by the Ingress Controller. It doesn’t work without a controller.
- Rules are made of host and path under
spec.rules, and each path has apathType(Prefix/Exact) and a backend (Service + port). UsedefaultBackendto set the default destination for unmatched requests. - IngressClass separates multiple controllers, and an Ingress specifies its class with
spec.ingressClassName. - TLS is set by creating a
kubernetes.io/tlsSecret and referencing it inspec.tls, with host match and the same namespace as conditions. - Validation starts with the ADDRESS from
kubectl get ingressand withdescribe.
Next: Networking 3 #
We’ve sorted out external entry with Ingress. Next is name resolution and traffic control inside the cluster.
In #20 Networking 3: CoreDNS, NetworkPolicy, we’ll cover, from an operations angle, how CoreDNS works and is configured so that Pods and Services find each other by name, and the ingress/egress rules of NetworkPolicy that control which Pod can communicate with which Pod.