Platform Architecture Basics
This section introduces the building blocks of an internal developer platform on Kubernetes, contrasting a paved, self-service path with raw cluster access.
The examples move from a single Deployment a developer might write by hand toward the abstractions a platform provides.
Prerequisites
- Kubernetes 1.36.2 cluster access with
kubectlconfigured (containerd is the node runtime). - Docker Engine 29.6.1 with BuildKit for building images locally.
- Helm 3 and optionally Kustomize for packaging manifests.
- Argo CD or Flux if you want to follow the GitOps examples.
# verify your tooling versions
kubectl version --short
docker version --format '{{.Server.Version}}'
helm version --shortBasic Examples
1. The raw Deployment a developer writes without a platform
Without abstraction, every team hand-writes the same boilerplate.
apiVersion: apps/v1
kind: Deployment
metadata:
name: checkout-api
spec:
replicas: 2
selector:
matchLabels:
app: checkout-api
template:
metadata:
labels:
app: checkout-api
spec:
containers:
- name: app
image: registry.example.com/checkout-api:1.4.0
ports:
- containerPort: 8080- This works, but it omits resource requests, probes, and security context.
- Each team copies and subtly mis-edits this file, causing drift.
- There is no policy stopping an unsafe image or a root container.
- The platform's job is to make the safe version the default.
2. Adding resource requests and limits
Requests and limits are the baseline for scheduling and stability.
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
memory: "512Mi"- requests tell the scheduler how much capacity the pod needs.
- A memory limit prevents one pod from starving its neighbors.
- Leaving CPU without a hard limit avoids throttling latency-sensitive apps.
- A platform sets sane defaults so developers rarely touch these.
3. Liveness and readiness probes
Probes let Kubernetes route traffic and restart correctly.
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
livenessProbe:
httpGet:
path: /healthz
port: 8080- readinessProbe gates traffic until the app can serve.
- livenessProbe restarts a wedged container.
- Missing probes cause traffic to hit not-ready pods during rollouts.
- The platform injects these from a template so every service has them.
4. A non-root, hardened security context
Security defaults belong in the golden path, not in a developer's memory.
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
seccompProfile:
type: RuntimeDefault
capabilities:
drop: ["ALL"]runAsNonRootaligns with the restricted Pod Security Standard.- Dropping all capabilities shrinks the attack surface.
RuntimeDefaultseccomp blocks dangerous syscalls.- Admission control rejects pods that skip these fields.
5. Exposing the service internally
A Service gives pods a stable virtual IP and DNS name.
apiVersion: v1
kind: Service
metadata:
name: checkout-api
spec:
selector:
app: checkout-api
ports:
- port: 80
targetPort: 8080- The selector binds the Service to matching pods.
portis what clients call;targetPortis the container port.- Other services reach it at
checkout-api.<namespace>.svc. - The platform generates this alongside the Deployment.
6. Building the image with BuildKit
Docker builds the artifact; containerd runs it in-cluster.
# multi-stage build keeps the runtime image small and rootless
FROM golang:1.23 AS build
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o /app ./cmd/checkout
FROM gcr.io/distroless/static:nonroot
COPY --from=build /app /app
USER nonroot
ENTRYPOINT ["/app"]- A multi-stage build ships only the binary, not the toolchain.
- A distroless
nonrootbase pairs withrunAsNonRoot. docker builduses BuildKit by default in Engine 29.- The pushed image runs under containerd on the node.
7. A default-deny NetworkPolicy
Start closed and open only what is needed.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes: ["Ingress"]- An empty
podSelectormatches every pod in the namespace. - With no
ingressrules, all inbound traffic is denied. - Teams then add explicit allow rules per dependency.
- The platform ships this in every namespace it provisions.
Intermediate Examples
8. Autoscaling with an HPA
The platform wires horizontal scaling from a single input.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: checkout-api
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: checkout-api
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70- The HPA scales between
minReplicasandmaxReplicason CPU. - The
autoscaling/v2API supports memory and custom metrics too. - Requests must be set or CPU utilization cannot be computed.
- A developer only picks min and max; the platform fills the rest.
9. GitOps delivery with Argo CD
Instead of kubectl apply, the platform reconciles from Git.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: checkout-api
namespace: argocd
spec:
project: default
source:
repoURL: https://git.example.com/apps/checkout-api
path: deploy
targetRevision: main
destination:
server: https://kubernetes.default.svc
namespace: checkout
syncPolicy:
automated:
prune: true
selfHeal: true- Argo CD watches Git and converges the cluster to it.
selfHealreverts manual drift back to the declared state.pruneremoves resources deleted from Git.- Git becomes the single source of truth and the audit log.
10. Packaging the golden path as a Helm chart
A chart turns the developer's small input into full manifests.
# render what the platform would apply, from a few values
helm template checkout-api ./golden-path-chart \
--set image=registry.example.com/checkout-api:1.4.0 \
--set port=8080- The chart encodes probes, security context, and policy defaults.
- Developers override only a handful of values.
helm templatelets teams preview the generated output.- Versioning the chart lets the platform roll out changes safely.
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).