Overview
- A distillation of Kubernetes Best Practices (Burns, Villalba, Strebel & Evenson, O’Reilly 2019) into checklists, updated for Kubernetes 1.31+. The Cookbook tells you how. This book tells you what good looks like.
- Each section cites its chapter (“BP Ch. n”). Deeper how-tos are linked to the Cookbook notes.
- Back to the hub: Kubernetes Cookbook · Basics: Kubernetes, Deployments, Services, ConfigMap
Changed since the book (2019):
- PodSecurityPolicy (all of Ch. 10’s first half) was removed in 1.25 → Pod Security Admission.
- Helm 2/Tiller advice is obsolete → Helm 3.
- PodPresets never left alpha and were removed.
- KubeFed (federation) was archived → GitOps fleets (Argo CD ApplicationSets, Flux), Cluster API.
- The TTL controller is GA.
- Gatekeeper is still around, now joined by Kyverno and built-in ValidatingAdmissionPolicy.
1. Deploying a Service (BP Ch. 1)
- Deployments for almost everything stateless, Services for a stable address, and an Ingress/Gateway for HTTP with TLS and routing.
- Images:
- Build from trusted bases or from scratch/distroless. Supply-chain attacks are real, so sign images (cosign) and scan them.
- Treat tags as immutable:
v1.0.1-bfeda01f(semver + git SHA). Never:latestin prod. Pinning a digest (@sha256:…) is even stricter.
- Parameterise per environment with Helm or Kustomize (Managing Manifests), not copy-paste.
A “Production-Ready” Deployment, All in One
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app.kubernetes.io/name: web
app.kubernetes.io/version: "1.4.2"
app.kubernetes.io/part-of: shop
app.kubernetes.io/managed-by: argocd
spec:
replicas: 3 # drop this if an HPA owns scaling
revisionHistoryLimit: 5
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app.kubernetes.io/name: web
template:
metadata:
labels:
app.kubernetes.io/name: web
app.kubernetes.io/version: "1.4.2"
spec:
serviceAccountName: web
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
topologySpreadConstraints: # spread across zones
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app.kubernetes.io/name: web
terminationGracePeriodSeconds: 30
containers:
- name: web
image: registry.example.com/shop/web:1.4.2-bfeda01f
ports:
- name: http
containerPort: 8080
envFrom:
- configMapRef:
name: web-config
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
memory: 256Mi # memory limit = request → no OOM surprises from overcommit
readinessProbe:
httpGet:
path: /readyz
port: http
periodSeconds: 5
livenessProbe:
httpGet:
path: /livez
port: http
periodSeconds: 10
failureThreshold: 3
lifecycle:
preStop:
sleep:
seconds: 5 # let endpoints update before shutdown
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]2. Developer Workflows (BP Ch. 2)
- Design for three phases: onboarding → developing → testing.
- One shared dev cluster (namespace per developer) usually beats a cluster each. Give each person their own identity + namespace + ResourceQuota.
- Reap stale namespaces automatically (TTL labels, cleanup jobs), because developers forget.
- Provide shared cluster services (logging, monitoring, maybe databases via Helm) for everyone.
- Inner-loop tools: Skaffold, Tilt, DevSpace,
kind load docker-image(Getting Started & Local Clusters).
3. Monitoring, Logging, Alerting (BP Ch. 3)
- Monitor infrastructure with USE (utilisation, saturation, errors) and services with RED (rate, errors, duration).
- Black-box checks catch symptoms, white-box instrumentation explains them.
- Prometheus-style, label-rich time series. Watch cardinality.
- Logs: stdout + a DaemonSet shipper (not a sidecar per Pod). Keep hot logs ~30–45 days, then archive cheaply.
- Alert on SLO-impacting symptoms, fight alert fatigue, and keep improving alerts.
- Details: Monitoring & Logging
4. Configuration, Secrets, RBAC (BP Ch. 4)
- ConfigMaps/Secrets:
- Mount as volumes for hot reload (not
subPath). - Env vars need a restart to pick up changes.
- Easiest rule: any config change = a new rollout. Use hashed names (Kustomize
configMapGenerator) or a Helmchecksum/configannotation. - Referenced ConfigMaps/Secrets must exist first (or be
optional). A missingconfigMapKeyRefkey blocks Pod start, while invalid keys inenvFromare skipped with anInvalidVariableNamesevent.
- Mount as volumes for hot reload (not
- Secrets:
- Encrypt at rest (KMS).
- Keep the source of truth in an external vault (External Secrets, CSI driver).
- Put
imagePullSecretson the ServiceAccount, not in every Pod spec.
- RBAC:
- Most apps need no RBAC at all. Disable token automount for them.
- Apps that call the API get a dedicated ServiceAccount + least-privilege Role.
- Humans via OIDC/SSO groups. Just-in-time elevation for SREs.
- Separate ServiceAccounts for CI/CD.
- Avoid granting
list/watchon Secrets.
- Details: Volumes & Configuration, Security
5. CI/CD (BP Ch. 5)
- Fast, reliable builds and tests, because quick feedback = productive devs.
- Pipelines as code, versioned with the app.
- Multi-stage builds for small images with a small attack surface.
- Tags traceable to a commit/build ID. No
latest. - Start CD with rolling updates, then graduate to blue/green and canary (Argo Rollouts, Flagger, Gateway API weights).
- Test client-connection draining and DB schema migrations (expand → migrate → contract) as part of rollouts.
- Test in production carefully: chaos experiments with a small blast radius and good monitoring.
- Today: CI builds, signs and pushes the image, then bumps Git. GitOps (Argo CD/Flux) deploys (Managing Manifests).
6. Versioning, Releases, Rollouts (BP Ch. 6)
- Version the app as a whole separately from each container and each Deployment spec, because they change independently.
- Put release name/number labels on objects that match your CD system’s release IDs, for traceability and easy rollback targeting.
- Bundle things that must roll back together into one Helm chart/release. Use hooks for ordered steps (e.g. a pre-upgrade DB backup Job).
- Keep release channels simple: stable / canary / alpha.
7. Global Distribution & Staged Rollouts (BP Ch. 7)
- Replicate images to registries near every region.
- Shift testing left (integration and replay tests) so only confident releases start rolling.
- Roll out canary region first, then low-risk regions, then the rest, with health gates between waves.
- Write down and rehearse rollback. Nobody remembers runbooks mid-incident.
8. Resource Management (BP Ch. 8)
- Requests on every container (scheduling + autoscaling depend on them) and memory limits on every container. CPU limits are debated, since they cause throttling. Many teams set CPU requests only.
- QoS: Guaranteed (req = limit) for critical workloads, Burstable for most, never BestEffort in prod.
- ResourceQuota per namespace/team + LimitRange defaults (API, Namespaces & Labels).
- High availability:
- Pod anti-affinity / topologySpreadConstraints across zones.
- PodDisruptionBudgets for drains and upgrades.
- Special hardware: taint GPU or special nodes and add tolerations only where needed. Use
nodeSelector/affinity to target them. Node pools for mixed workload shapes. - Right-size nodes before production (cost vs. performance).
- Scaling: start manual until you know the workload profile. HPA for spiky workloads. VPA recommendations for sizing. Cluster Autoscaler/Karpenter with an eye on spin-up time. Details: Scaling
9. Networking & Service Mesh (BP Ch. 9)
- CNI:
- Pick for features: NetworkPolicy, eBPF, encryption, multi-cluster. Cilium and Calico are common.
- Make sure your cloud supports it.
- Plan Pod CIDR and IP space generously (default max 110 Pods per node).
- Check that your observability and security tools work with it.
- Services: keep most ClusterIP. Expose only what must be public, and for HTTP use one Ingress/Gateway with TLS.
- Standardise on one ingress controller (annotations aren’t portable), run it HA, and scale on metrics. API-gateway-style controllers suit API-heavy traffic.
- NetworkPolicy: start with ingress rules, add a default-deny per namespace, open explicitly with labels, and keep a permissive test namespace for figuring out flows. Details: Security
- Service mesh: only adopt for concrete needs (mTLS, retries, traffic shifting, observability). Weigh the operational cost. Sometimes your CNI gives you mTLS already. Today’s lighter options: Istio ambient mode (no sidecars) and Linkerd.
10. Pod & Container Security (BP Ch. 10)
- Enforce pod hardening with Pod Security Admission (
baselineeverywhere,restrictedwhere possible) instead of PSP. - RuntimeClass sandboxes (gVisor, Kata) for untrusted workloads. Isolation ≠ secure multi-tenancy, since the API is still shared. Mixed runtimes complicate debugging and portability.
- Runtime threat detection: Falco, Tetragon.
11. Policy & Governance (BP Ch. 11)
- Use a policy engine for org rules: ValidatingAdmissionPolicy (CEL), Kyverno or OPA Gatekeeper. Examples: allowed registries, required labels, no
:latest, required probes and resources. - Enforce at the Pod level (the last hop before runtime). The user then sees the error on the ReplicaSet/events, not on
kubectl apply. Usewarn/audit modes to give early feedback. - Scope constraints tightly (kinds, namespaces, labels). Don’t replicate Secrets into the policy engine’s cache.
- Run policies in audit mode first, then enforce.
12. Multiple Clusters (BP Ch. 12)
- Use multiple clusters for blast radius, compliance (PCI, HIPAA), hard multi-tenancy, regions, and special workloads (HPC/GPU).
- Have a data replication strategy before going multi-region. Stateless is easy, data is hard.
- GitOps for fleet consistency (Argo CD ApplicationSets, Flux). Cluster API for lifecycle. Global load balancing between clusters.
- Plan cross-cluster discovery/networking (mesh, Cilium ClusterMesh, MCS API) and make CD multi-cluster aware.
13. Integrating External Services (BP Ch. 13)
- Calling out:
- Selector-less Service + manual EndpointSlice for fixed IPs.
- ExternalName (DNS CNAME) for a stable hostname, e.g. a managed DB.
- A controller that syncs endpoints if addresses change.
- Exposing in: an internal LoadBalancer (cloud annotation) or NodePort for on-prem consumers.
- Cluster to cluster: export from one cluster, import as a selector-less Service in the other.
14. Machine Learning Workloads (BP Ch. 14)
- Taint GPU nodes (
nvidia.com/gpu:NoSchedule) and autoscale GPU pools to zero when idle. GPUs are the expensive bottleneck, so keep them saturated. - Separate, tainted node pools for ML in mixed clusters. Monitor GPU, CPU, network and storage together.
- Today: NVIDIA GPU Operator, Kueue for batch/job queueing, Kubeflow/Ray (KubeRay) for training, and Dynamic Resource Allocation (DRA) for flexible accelerators.
15. Building Platforms (BP Ch. 15)
- Use admission controllers to validate or mutate (inject sidecars, defaults) and kubectl plugins (krew) to extend the UX.
- Design for users’ growth: an easy platform that traps users is a failure. Keep an escape hatch to plain Kubernetes, and support plain container images and normal Service discovery.
16. State & Stateful Apps (BP Ch. 16)
- Use volumes mainly to share data between containers (
emptyDir).hostPathonly for node agents. Logs go to stdout, not a disk. - Default StorageClass on, with
WaitForFirstConsumerand zone-aware topology (keep the Pod and its disk in the same zone). - Ask first: can a managed DB service do this instead? How hard is it to make the app stateless?
- Back up volumes with CSI VolumeSnapshots or Velero, since Kubernetes doesn’t do it for you. Use
Retainreclaim for sensitive or forensic data. - StatefulSets:
- Create the headless Service yourself.
- Ordinal identity doesn’t always need PVs.
- On a dead node, Pods sit in
Terminating/Unknown. Force-delete only as a last resort, and only when the node can’t come back. - Use
preStophooks for graceful leader hand-off.
- Prefer a mature operator for complex data systems (see Extending Kubernetes).
17. Admission Control & Authorization (BP Ch. 17)
- Mutating runs before validating. Any rejection rejects the whole request.
- Don’t let two mutating webhooks edit the same field. Add a validating check after mutation.
- Choose
failurePolicydeliberately, keep webhooks fast (30 s max), scope with namespaceSelector, skipkube-system, lock webhook configs with RBAC, and don’t send Secrets to webhooks. - Authorization: use RBAC only. ABAC needs file syncing and API server restarts. Authorization webhooks are risky single points of failure.
Summary
- Every workload: immutable image tag, requests + memory limit, readiness + liveness (+ startup), labels (
app.kubernetes.io/*), non-root restricted securityContext, ≥ 2 replicas spread across zones, a PDB. - Every namespace: quota + LimitRange, Pod Security labels, default-deny NetworkPolicy, least-privilege RBAC.
- Every change: through Git and CI/CD (GitOps), traceable tags/labels, rolling or canary rollouts, rehearsed rollback.
- Every cluster: metrics + logs + symptom-based alerts, policy engine in audit then enforce, backups of etcd/objects/volumes.