CNPA — Cloud Native Platform Engineering Associate
Building One Golden Path End to End
Goal
You bundle the things that must always be attached when a new service is born — standard labels, resource requests/limits, autoscaling, a disruption budget, and a network policy — into one set, and deploy it for real into a namespace that has guardrails. Next you package the same service as a Helm chart, branch it with per-environment values, and deploy the render result.
Why it matters
The value of a golden path lies less in 'what was put in' than in 'whether it is there even though the developer did not ask for it.' If the scaffold attaches resource limits and a network policy by default, the developer's effort to follow those rules drops to 0. Conversely, if you write 'Be sure to set limits' on a wiki, half of people forget. The location of guardrails is in the same vein — ResourceQuota and LimitRange judge immediately at creation time, so they create a much better developer experience than an after-the-fact audit report. The combination of these two in particular often appears on the exam. If a quota limits CPU, a Pod that does not specify requests is rejected, and the LimitRange default fills that gap. The Helm part later is the golden path's second face, 'branching the same scaffold by values' — it is a way to satisfy both dev and prod with a single template.
Steps
- Write the Deployment
paymentsin/root/cnpa-path/manifests/deployment.yaml—spec.replicas: 2,app.kubernetes.io/name: paymentsin the selector and Pod labels,app.kubernetes.io/name: paymentsandapp.kubernetes.io/part-of: cnpa-platformin the metadata labels, container nameapp, imageghcr.io/labhub/payments:1.0.0, containerPort8080,resources.requestsof cpu100m/ memory128Mi, andresources.limitsof cpu300m/ memory256Mi. Write the Servicepaymentsin/root/cnpa-path/manifests/service.yaml— typeClusterIP, port80, targetPort8080, selectorapp.kubernetes.io/name: payments. - Write the HorizontalPodAutoscaler
paymentsin/root/cnpa-path/manifests/hpa.yaml(apiVersionautoscaling/v2, scaleTargetRef pointing to the apps/v1 Deploymentpayments,minReplicas: 2,maxReplicas: 6, cpu Utilization target70). Write the PodDisruptionBudgetpayments(minAvailable: 1, selectorapp.kubernetes.io/name: payments) in/root/cnpa-path/manifests/pdb.yaml, and the NetworkPolicypaymentsin/root/cnpa-path/manifests/netpol.yaml(podSelectorapp.kubernetes.io/name: payments, first policyTypes entryIngress, and an ingress that allows only TCP8080from Pods that have the labelapp.kubernetes.io/part-of: cnpa-platform). - Create the namespace
cnpa-app, and in it create the ResourceQuotacnpa-app-quota(requests.cpu: "1",requests.memory: 1Gi,limits.cpu: "2",limits.memory: 2Gi,pods: "8") and the LimitRangecnpa-app-limits(typeContainer,defaultcpu200m/ memory256Mi,defaultRequestcpu100m/ memory128Mi). - Apply all five manifests in
/root/cnpa-path/manifests/to thecnpa-appnamespace. - Create a chart with
helm create /root/cnpa-path/payments, and editvalues.yaml—replicaCount: 2,image.repository: ghcr.io/labhub/payments,image.tag: "1.0.0". - Create
/root/cnpa-path/payments/values-dev.yaml(replicaCount: 1,image.tag: "1.0.0") and/root/cnpa-path/payments/values-prod.yaml(replicaCount: 3,image.tag: "1.2.0",fullnameOverride: payments-v2). Then save the result ofhelm template payments-v2 /root/cnpa-path/payments -f /root/cnpa-path/payments/values-prod.yaml -n cnpa-appto/root/cnpa-path/render-prod.yaml. - Apply
/root/cnpa-path/render-prod.yamlto thecnpa-appnamespace. After applying, thepayments-v2Deployment must be running with replicas3and imageghcr.io/labhub/payments:1.2.0, and the quota from step 3 must remain intact.
Notes
- To apply a whole directory, use
kubectl apply -n cnpa-app -f /root/cnpa-path/manifests/. helm templatedoes not create anything in the cluster. Only the render result goes to standard output.- If you set
fullnameOverride, the names of the resources that helm creates are pinned to that value. It is a device to keep them from colliding with thepaymentsyou deployed in step 4. - Common mistake 1: omitting the HPA's
scaleTargetRef.apiVersion. A Deployment isapps/v1. - Common mistake 2: omitting the NetworkPolicy's
policyTypes. If omitted, only the directions that have rules apply, which may differ from your intent.
Service scaffold — Deployment and Service
Write the Deployment payments in /root/cnpa-path/manifests/deployment.yaml — spec.replicas: 2, app.kubernetes.io/name: payments in the selector and Pod labels, app.kubernetes.io/name: payments and app.kubernetes.io/part-of: cnpa-platform in the metadata labels, container name app, image ghcr.io/labhub/payments:1.0.0, containerPort 8080, resources.requests of cpu 100m / memory 128Mi, and resources.limits of cpu 300m / memory 256Mi. Write the Service payments in /root/cnpa-path/manifests/service.yaml — type ClusterIP, port 80, targetPort 8080, selector app.kubernetes.io/name: payments.
A golden path's defaults are 'things nobody asked for but that are always there.' Standard labels and resource requests/limits are examples.
HPA · PDB · NetworkPolicy
Write the HorizontalPodAutoscaler payments in /root/cnpa-path/manifests/hpa.yaml (apiVersion autoscaling/v2, scaleTargetRef pointing to the apps/v1 Deployment payments, minReplicas: 2, maxReplicas: 6, cpu Utilization target 70). Write the PodDisruptionBudget payments (minAvailable: 1, selector app.kubernetes.io/name: payments) in /root/cnpa-path/manifests/pdb.yaml, and the NetworkPolicy payments in /root/cnpa-path/manifests/netpol.yaml (podSelector app.kubernetes.io/name: payments, first policyTypes entry Ingress, and an ingress that allows only TCP 8080 from Pods that have the label app.kubernetes.io/part-of: cnpa-platform).
All three resources find their target workload with a selector. Only the HPA uses a target reference instead of a selector.
A namespace with guardrails
Create the namespace cnpa-app, and in it create the ResourceQuota cnpa-app-quota (requests.cpu: "1", requests.memory: 1Gi, limits.cpu: "2", limits.memory: 2Gi, pods: "8") and the LimitRange cnpa-app-limits (type Container, default cpu 200m / memory 256Mi, defaultRequest cpu 100m / memory 128Mi).
If a quota limits a resource, every container in that namespace must declare requests/limits. There is a separate object that rescues containers that did not declare them.
Apply the scaffold to the cluster
Apply all five manifests in /root/cnpa-path/manifests/ to the cnpa-app namespace.
Deploy all five resources into the same namespace. There is an option that applies a whole directory at once.
A local chart with helm create
Create a chart with helm create /root/cnpa-path/payments, and edit values.yaml — replicaCount: 2, image.repository: ghcr.io/labhub/payments, image.tag: "1.0.0".
helm create makes a chart skeleton locally without the internet. Edit the generated values.yaml to fit this service.
Per-environment values and checking the render
Create /root/cnpa-path/payments/values-dev.yaml (replicaCount: 1, image.tag: "1.0.0") and /root/cnpa-path/payments/values-prod.yaml (replicaCount: 3, image.tag: "1.2.0", fullnameOverride: payments-v2). Then save the result of helm template payments-v2 /root/cnpa-path/payments -f /root/cnpa-path/payments/values-prod.yaml -n cnpa-app to /root/cnpa-path/render-prod.yaml.
Values files are layered. The file given later wins. Do the render with a command that does not touch the cluster.
Deploy the render result inside the guardrails
Apply /root/cnpa-path/render-prod.yaml to the cnpa-app namespace. After applying, the payments-v2 Deployment must be running with replicas 3 and image ghcr.io/labhub/payments:1.2.0, and the quota from step 3 must remain intact.
Make sure the name is pinned in the values so that no name collision occurs. You can simply apply the rendered file as it is.