A candidate on a coaching call last month asked me a question I have now heard four times: if a Service of type LoadBalancer already gives an application a public address, why would an exam objective bother mentioning ingress controllers at all. He had built every demo he owned on LoadBalancer services and had never run out of anything. That is the honest reason the question keeps coming up. In a lab with four applications, nothing breaks. In an estate with forty, the platform runs out of addresses, and the objective is written by people who have watched that happen.
Four deployment models on one VKS estate
Objective 4.12 names four things in one sentence, and candidates read that as four unrelated topics. They are not. Pods, virtual machines, ingress controllers and private registries are the four decisions you make about a single application: where the process runs, how traffic reaches it, and where its image comes from. Broadcom documents deploying workloads to a VKS cluster using pods, services, persistent volumes and higher level resources such as deployments and replica sets, which is ordinary Kubernetes. What is not ordinary is the second half of every one of those sentences, because on VKS the platform answers are supplied by the Supervisor.
Trace it once and it stays traced. A Service of type LoadBalancer inside a workload cluster does not create anything by itself. It asks the cloud provider running in that cluster, which asks the Supervisor, which asks whichever load balancer you selected when the Supervisor was enabled. If that was Avi, an address comes out of a VIP range configured at Supervisor enablement, and no amount of editing the service manifest changes how many addresses are in that range. Same shape for images: a pod in a workload cluster pulls through containerd on a worker node, and whether that pull succeeds depends on trust material baked into the cluster spec, not on anything you can patch into the deployment afterwards. Both of those chains are covered in Part 13 and Part 23, so I will not restate the mechanics, only the consequence.
Virtual machines belong in this objective for a reason that catches container people rather than vSphere people. A VM Service machine lives on the Supervisor, in a vSphere Namespace, not inside the workload cluster. So an application composed of a containerised web tier in a VKS cluster and a licensed database on a VM Service machine has two different exposure stories, two different address sources, and no shared Kubernetes Service object to join them. Candidates lose points by drawing one ingress in front of both.
flowchart TD
A[Workload needs to be reachable] --> B{Protocol is HTTP or HTTPS}
B -->|no| C[Service type LoadBalancer, one Avi VIP per service]
B -->|yes| D{More than two routes expected in this cluster}
D -->|no| C
D -->|yes| E[Ingress controller, one shared Avi VIP for the cluster]
E --> F{Callers need a stable name}
F -->|yes| G[Publish records with external dns on the Supervisor]
F -->|no| H[Hand out the Envoy VIP directly]
C --> I{Workload is a VM Service machine}
I -->|yes| J[Expose on the Supervisor, not in the cluster]
Preflight before a single manifest lands
Last Part left the estate with a PostgreSQL StatefulSet holding expanded volumes and a stateless web deployment sitting on cluster internal addresses only. This Part puts that web deployment in front of a browser and makes it pull from Harbor rather than a public registry. Four checks before anything is applied, and each one prevents a failure I have actually watched.
Check two is the one candidates skip and the one that decides everything below. If your Supervisor was enabled with a VIP range of 30 addresses and eleven are already spoken for, you have nineteen LoadBalancer services left in the entire estate, across every workload cluster, forever, until somebody extends the range at the Supervisor. Check three returning an empty result is a guaranteed image pull failure later, and it is far cheaper to see it now than at step five.
Numbered run, deployment to service to ingress to private image
Step 1. Deploy the workload and expose it the naive way, so you have a measured baseline to argue against. Three replicas, one Service of type LoadBalancer, and a stopwatch on how long the address takes to appear.
Step 2. Watch the pods fail to start, because the image came from a private registry and nothing told the cluster how to authenticate. This is the failure the objective expects you to recognise from its error text rather than by guessing.
Read that error carefully, because two different registry failures produce visually similar output and the exam distinguishes them. A 401 Unauthorized means the cluster reached Harbor, completed a TLS handshake, and was refused credentials, so the answer is a pull secret. An x509 message naming an unknown authority means the handshake never completed, so the answer is trust material in the cluster spec, and a pull secret will not help. HARBOR_USER and HARBOR_PASS are environment variables in my shell; a credential typed on a command line lands in shell history and, on a shared jump host, in somebody else audit log.
Step 3. Install an ingress controller, and here is where the published tutorial actively hurts you. Broadcom documentation for VCF 9.0 still walks through Contour release 1.9, a ClusterRoleBinding against psp:vmware-system-privileged, and an Ingress manifest on networking.k8s.io/v1beta1. PodSecurityPolicy was removed from Kubernetes in 1.25 and that beta Ingress API stopped being served in 1.22. On a VKS 3.3.1 cluster running Kubernetes 1.32 all three fail.
The version string above is what my repository carried on the day I ran this; yours comes out of that same command and will differ. Pinning the package version by reading it from the repository rather than typing it is the habit that survives an upgrade, and it is exactly the pattern the package objective in Part 23 sets up.
Step 4. Convert the application from its own virtual IP to a route behind the shared one. Note the two fields that did not exist in the published tutorial and are mandatory now: ingressClassName replaces the old annotation, and pathType is required on every path.
Nine seconds, and no new address consumed. Every application added after this one costs zero VIPs. That single arithmetic difference is the reason objective 4.12 lists ingress controllers alongside pods.
Verification, rollback and a failure lookup
Green looks like three things agreeing: an Envoy service holding an external address, an Ingress reporting that same address, and an HTTP 200 arriving with the Host header set. Checking only the first two is how people convince themselves a broken route works.
Rollback order matters more than it looks. Delete the projectcontour namespace while ingress objects still reference it and those objects survive with no controller behind them, reporting a stale address that answers nothing. I have handed a stale address to an application team and spent twenty minutes of their time proving my own mess.
Below is the artifact worth keeping from this Part: a failure to cause lookup covering the four exposure and registry errors that account for nearly every broken deployment I have debugged on VKS, each with the layer that actually owns the fix.
| What you see | Real cause | Where the fix lives |
|---|---|---|
| Service EXTERNAL-IP stuck at pending past two minutes | Avi VIP range exhausted, or the Supervisor was enabled with no load balancer at all | Supervisor, not the workload cluster. Extend the range or switch the application to ingress. |
| ImagePullBackOff with 401 Unauthorized | No imagePullSecrets on the pod spec, or a secret in the wrong namespace | Workload namespace. Secrets are namespaced and do not travel with a deployment. |
| ImagePullBackOff with x509 certificate signed by unknown authority | Registry CA absent from the cluster trust bundle | Cluster spec on the Supervisor. Needs a rolling update, not a pod restart. |
| Ingress created but ADDRESS column stays empty | No ingress controller installed, or ingressClassName names a class that does not exist | Workload cluster. Compare against kubectl get ingressclass. |
| 404 from Envoy while the address answers | Host header does not match the rule, or pathType was omitted so nothing matched | Ingress object. Test with curl and an explicit resolve before blaming DNS. |
Read that chart the way an architect would rather than the way a stopwatch would. Installing the controller is the slowest bar and it happens once per cluster. Every bar after it is faster than the LoadBalancer path and free in address terms. Optimising the one time cost is the wrong instinct.
Exam focus for objective 4.12
Objective checkpoint
Answer: b. An Ingress is only a set of rules; with no controller watching for them, nothing is programmed and no address is claimed. Option c would work but spends a VIP to solve a problem that costs none.
Answer: c. An x509 failure happens before authentication, so credentials are irrelevant. Trust material is part of the cluster specification and reaching the nodes requires a rolling update.
Answer: c. A VM Service machine is a Supervisor object living in a vSphere Namespace. No selector in the workload cluster can reach it, because it is not a member of that cluster.
Exposure defaults I would standardise on
My verdict is narrow and I will defend it. Install one ingress controller per VKS cluster on day one, before any application arrives, and treat a Service of type LoadBalancer as an exception that needs a sentence of justification. Reserve it for protocols ingress cannot carry, which in practice means databases, message brokers and anything doing mutual TLS that must terminate at the application. Avoid the pattern of one LoadBalancer per microservice entirely, even though it is what every quick start demonstrates and what most people build their first estate on.
I earned that opinion badly. On a customer demo estate sized with a 30 address VIP pool, a platform team shipped fourteen services of type LoadBalancer across two clusters over about six weeks, one per microservice, exactly as their tutorials showed. Service fifteen sat at pending, and I spent forty minutes convinced Avi had a health problem because the Avi console looked entirely healthy: nothing was down, the pool was simply full. Reversing it meant collapsing eleven of those services behind a single Contour instance in an evening change window, and the whole estate then ran on three virtual IPs instead of fourteen. Nobody noticed a latency change. What we bought back was eleven addresses and a routing layer we could put certificates and host rules on, which the previous design had no place for.
Second recommendation, smaller and less obvious. Take your ingress controller from the standard package repository rather than from an upstream manifest on the internet, and pin the version by reading it out of the repository. Broadcom own published Contour tutorial is the argument for this: it has drifted far enough that following it produces three separate errors on a supported cluster, and an upstream manifest you pasted a year ago will drift the same way. Package repositories in Part 23 and Harbor as the image source in Part 19 exist precisely so this Part has somewhere to pull from. Broader product context for all of it sits in the VKS Series, and the exam map is on the guide page.
Tonight, in your own lab, do one thing: count your remaining virtual IPs, then take an application that currently owns one and move it behind an ingress route. Watch the address return to the pool. Twenty minutes, and the trade off in objective 4.12 becomes something you have felt rather than something you memorised. Next Part moves to backup and restore with Velero.
References
Deploying Workloads on VKS Clusters, VMware Cloud Foundation 9.0 documentation
Ingress Using Contour, Broadcom TechDocs, the tutorial discussed in step 3
Using Private Registries with VKS Clusters, Broadcom TechDocs
Deprecated API Migration Guide, Kubernetes documentation, Ingress v1beta1 removal in 1.22 and PodSecurityPolicy removal in 1.25
VMware Certified Advanced Professional vSphere Kubernetes Service exam guide, 3V0-24.25


DrJha