Kubernetes operators frequently encounter scenarios where basic pod management and service exposure are insufficient. Production workloads demand fine-grained scheduling control, resilient storage patterns, secure network segmentation, and observable failure domains. This article covers five advanced Kubernetes concepts — pod topology spread constraints, priority classes with preemption, CSI snapshot and restore workflows, network policy egress controls, and custom metrics autoscaling — with version-scoped commands, expected output patterns, and recovery verification steps. Each section follows an observe-first approach: capture current state, apply the smallest justified change, verify the signal, and document the rollback path.
Pod Topology Spread Constraints for Failure Domain Control
Topology spread constraints distribute pods across failure domains such as zones, nodes, or custom labels. Unlike pod anti-affinity, which can leave pods unschedulable when constraints conflict, topology spread uses a scoring mechanism that favors balanced placement while remaining schedulable.
Prerequisites: Kubernetes 1.19+ (beta), 1.24+ (stable). Nodes must carry the topology label topology.kubernetes.io/zone or a custom label such as failure-domain.example.com/rack.
Observe current state:
kubectl get nodes --show-labels | grep -E 'zone|rack'
kubectl get pods -n production -l app=payment-api -o wide
Expected observation: Pods clustered on two of three available zones, leaving one zone empty.
Smallest justified change: Add a topologySpreadConstraint to the Deployment spec with maxSkew: 1, topologyKey: topology.kubernetes.io/zone, and whenUnsatisfiable: ScheduleAnyway.
spec:
template:
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: payment-api
Verify outcome: After rollout, kubectl get pods -n production -l app=payment-api -o jsonpath='{.items[*].spec.nodeName}' | xargs -n1 kubectl get node --show-labels should show pods spread across all three zones with a maximum difference of one pod per zone.
Recovery: Revert the Deployment to the previous revision with kubectl rollout undo deployment/payment-api -n production and confirm pod distribution returns to the prior state.
Priority Classes and Preemption for Critical Workloads
Priority classes assign scheduling precedence to pods. When cluster resources are exhausted, the scheduler preempts lower-priority pods to admit higher-priority ones. This mechanism protects control-plane components and revenue-critical services during contention.
Prerequisites: Kubernetes 1.14+ (beta), 1.19+ (stable). PriorityClass resources must exist before pods reference them.
Observe current state:
kubectl get priorityclasses
kubectl describe priorityclass system-cluster-critical
kubectl get pods -A -o custom-columns=NAME:.metadata.name,NAMESPACE:.metadata.namespace,PRIORITY:.spec.priorityClassName,NODE:.spec.nodeName
Expected observation: No custom PriorityClass for the order-processing workload; its pods run at default priority (0) and are evicted first during pressure.
Smallest justified change: Create a PriorityClass named business-critical with value: 1000000 and preemptionPolicy: PreemptLowerPriority, then patch the Deployment to reference it.
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: business-critical
value: 1000000
preemptionPolicy: PreemptLowerPriority
globalDefault: false
description: "Order processing must not be preempted by batch jobs"
kubectl patch deployment order-processor -n production -p '{"spec":{"template":{"spec":{"priorityClassName":"business-critical"}}}}'
Verify outcome: Simulate resource pressure by deploying a low-priority DaemonSet (value: 100) that requests all remaining CPU. The scheduler should preempt the DaemonSet pods to place new order-processor replicas. Confirm with kubectl get events --field-selector reason=Preempted -n production.
Recovery: Delete the PriorityClass (which fails if referenced) after removing the reference from the Deployment, then roll back the Deployment. Verify no orphaned preemption events remain.
CSI Snapshot and Restore for Stateful Workloads
Container Storage Interface (CSI) snapshots provide point-in-time copies of PersistentVolumes without application downtime. Combined with VolumeSnapshotContent and VolumeSnapshotClass, they enable backup, cloning, and disaster recovery workflows.
Prerequisites: Kubernetes 1.20+ (beta), 1.22+ (stable). CSI driver must support VOLUME_SNAPSHOT capability (e.g., AWS EBS CSI driver v1.14+, GCE PD CSI driver v1.5+, Azure Disk CSI driver v1.5+). VolumeSnapshotClass must exist in the cluster.
Observe current state:
kubectl get volumesnapshotclass
kubectl get pvc -n data-platform -l app=postgres
kubectl describe pvc postgres-data-0 -n data-platform
Expected observation: A single PVC postgres-data-0 bound to a PV provisioned by ebs.csi.aws.com, no existing snapshots.
Smallest justified change: Create a VolumeSnapshot referencing the PVC, then restore to a new PVC for a staging environment.
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: postgres-snap-2024-01-15
namespace: data-platform
spec:
volumeSnapshotClassName: csi-aws-ebs-snapclass
source:
persistentVolumeClaimName: postgres-data-0
kubectl apply -f postgres-snapshot.yaml
kubectl wait --for=condition=Ready volumesnapshot/postgres-snap-2024-01-15 -n data-platform --timeout=10m
Restore:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-staging-restore
namespace: data-platform
spec:
storageClassName: gp3
dataSource:
name: postgres-snap-2024-01-15
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
Verify outcome: kubectl get pvc postgres-staging-restore -n data-platform shows Bound. Deploy a temporary PostgreSQL pod mounting this PVC and run SELECT count(*) FROM orders; to validate data integrity.
Recovery: Delete the staging PVC and VolumeSnapshot. The original PVC and PV remain untouched. Document the snapshot retention policy (e.g., daily snapshots retained for 30 days) in the runbook.
Network Policy Egress Controls for Zero-Trust Segmentation
NetworkPolicy resources restrict pod-to-pod and pod-to-external traffic. Egress rules are often overlooked but are essential for preventing data exfiltration and limiting blast radius when a workload is compromised.
Prerequisites: Kubernetes 1.8+ with a CNI plugin that enforces NetworkPolicy (Calico, Cilium, Weave Net, Antrea). kube-proxy must not be in IPVS strict-ARP mode without CNI support.
Observe current state:
kubectl get networkpolicy -n finance
kubectl exec -n finance deploy/api-gateway -- curl -s -o /dev/null -w "%{http_code}" http://external-payment-provider.example.com
Expected observation: No NetworkPolicy exists in the finance namespace; egress to any destination succeeds.
Smallest justified change: Apply a default-deny egress policy, then allow only required external endpoints by CIDR or DNS (via Cilium FQDN policy or Calico GlobalNetworkPolicy with DNS selector).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-gateway-egress
namespace: finance
spec:
podSelector:
matchLabels:
app: api-gateway
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 203.0.113.0/24 # payment provider CIDR
ports:
- protocol: TCP
port: 443
- to:
- namespaceSelector:
matchLabels:
name: monitoring
ports:
- protocol: TCP
port: 9090
Verify outcome: The curl command from the observe step now returns 000 (connection refused/timeout). Allowed destinations return 200. Check Cilium/Calico logs for DENY verdicts on unexpected egress attempts.
Recovery: Delete the NetworkPolicy with kubectl delete networkpolicy api-gateway-egress -n finance. Confirm egress restores immediately. If using Cilium, verify cilium policy get shows no enforcement gaps.
Custom Metrics Autoscaling with Prometheus Adapter
Horizontal Pod Autoscaler (HPA) supports custom metrics via the Custom Metrics API. The Prometheus Adapter exposes Prometheus queries as scalable metrics, enabling autoscaling on business-level signals (queue depth, request latency, error rate) rather than CPU alone.
Prerequisites: Kubernetes 1.16+ (Custom Metrics API stable). Prometheus Operator v0.50+ with prometheus-adapter deployed. Metrics must be labeled with namespace and pod for pod-level scaling.
Observe current state:
kubectl get apiservice v1beta1.custom.metrics.k8s.io
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/shopping/pods/*/http_requests_per_second" | jq .
Expected observation: API service available. Metric returns {"items":[]} because no pods expose the metric label set.
Smallest justified change: Ensure the application emits a Prometheus counter http_requests_total with labels namespace, pod, service. Configure Prometheus Adapter rule to derive a per-second rate.
# prometheus-adapter config map snippet
rules:
- seriesQuery: 'http_requests_total{namespace!="",pod!=""}'
resources:
overrides:
namespace: {resource: "namespace"}
pod: {resource: "pod"}
name:
matches: "^(.*)_total$"
as: "${1}_per_second"
metricsQuery: 'sum(rate(<<.Series>>{<<.LabelMatchers>>}[2m])) by (<<.GroupBy>>)'
kubectl rollout restart deployment/prometheus-adapter -n monitoring
Create HPA:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: checkout-service-hpa
namespace: shopping
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: checkout-service
minReplicas: 3
maxReplicas: 50
metrics:
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "100"
Verify outcome: Generate load with hey -z 5m -c 50 https://checkout.example.com. Watch kubectl get hpa checkout-service-hpa -n shopping -w — replicas should scale from 3 toward 50 as the metric exceeds 100 req/s per pod. When load stops, scale-down occurs after the stabilization window (default 5 minutes).
Recovery: Delete the HPA. Revert Prometheus Adapter config and restart. Confirm the custom metrics API no longer serves the metric.
Conclusion
Advanced Kubernetes operations require moving beyond default configurations into version-scoped, observable, and reversible patterns. Topology spread constraints eliminate silent single-zone failures. Priority classes with preemption protect revenue-critical paths during contention. CSI snapshots turn storage into a recoverable asset rather than a liability. NetworkPolicy egress rules enforce zero-trust boundaries that survive workload compromise. Custom metrics autoscaling aligns infrastructure cost with business demand signals. Each technique follows the same discipline: observe the current state with concrete commands, apply the smallest justified change, verify the expected signal, and document the rollback path before the incident occurs. Start by implementing one pattern in a staging cluster, measure the verification signals, and promote only when the recovery procedure is tested and documented.