The CNCF's Certified Cloud Native Platform Engineer exam is hands-on across five domains. Reading docs does not prepare you for that. So I built the platform instead and published it.
github.com/rbstp/cnpe-exam-prep
A full internal developer platform on kind, as layered make targets you install and remove one at a time. Cilium, Gitea, Argo CD, Flux, Argo Rollouts, Tekton, Crossplane, CloudNativePG, Prometheus, Grafana, OpenTelemetry, Jaeger, Loki, OpenCost, Kyverno, Gatekeeper, SPIRE, Istio ambient, and Backstage.
Checking behaviour instead of pod status#
make validate runs 69 checks, and none of them ask whether a pod is Running. It does not ask "is Crossplane installed". It applies an XR and asserts a real namespace appeared with the quota values patched from its spec. It does not ask "is Cilium up". It runs a pod that has to fail to reach the internet, and the check fails if that egress works.
That distinction is the whole point. kindnet does not enforce NetworkPolicy, so on most kind-based labs every network-policy exercise silently passes and you learn nothing at all. Use Cilium, then prove enforcement.
Four things that cost me real time#
The audit policy filename stops the cluster from booting. kube-apiserver is started with --audit-policy-file=/etc/kubernetes/audit/policy.yaml, so the mounted directory has to contain a file called exactly that. Get it wrong and the API server refuses to start. It looks like a cluster that never boots, not a config error.
You cannot break DNS by adding a NetworkPolicy. They are additive allow-lists, so a new restrictive policy cannot revoke an allowance another policy already granted. My fault-injection drill "blocked DNS" by adding one, and it never blocked anything. To actually break resolution you have to remove the rule that permitted it.
An ipBlock on the API server ClusterIP does not work. CloudNativePG hangs forever in a default-deny namespace because its instance manager cannot reach the API server. The obvious fix, allowing egress to 10.96.0.1/32:443, still fails. The ClusterIP is DNAT'd to a node's :6443 before policy evaluation. With Cilium you want toEntities: [kube-apiserver].
Gateway API v1.5 and later blocks older Gateway API CRDs. It ships a policy that rejects any CRD before v1.5.0, and cloud-provider-kind embeds an older bundle it installs at startup. It gets denied, its service controller dies, and no LoadBalancer ever gets an IP. Start it with --gateway-channel=disabled.
Running it#
Tested on a 2019 Intel MacBook Pro, i9-9880H with 32 GB, running Arch. Roughly 70 minutes for everything, about 25 for a useful subset. At rest it is two clusters, around 100 pods and 21 GB of RAM. CPU is the bottleneck, never memory.
git clone https://github.com/rbstp/cnpe-exam-prep.git && cd cnpe-exam-prep
cp lab.env.example lab.env
make host tools core
make validateThe part I will use to study with is make break. It injects one of seven realistic faults into a tenant namespace and starts a seven minute clock, which is about exam pace. make break-fix diagnoses it from cluster state and tells you where the evidence was. Incident response is a third of the observability domain and the hardest thing to practise alone.
MIT licensed. I am still working through the material, so the repo will keep moving.