
Operating Kubernetes
- 46 installs
- 426 repo stars
- Updated December 11, 2025
- ancoleman/ai-design-components
operating-kubernetes is a skill for running production Kubernetes clusters, covering resource management, scheduling, networking, security, and autoscaling.
About
A skill for operating production Kubernetes clusters. A developer uses it to size resources and QoS, configure scheduling and networking, harden security with RBAC and NetworkPolicies, set up autoscaling, and troubleshoot failures like CrashLoopBackOff. It matters because production Kubernetes reliability depends on correct resource, scheduling, and security configuration.
- Right-sizes workloads with QoS classes, resource requests/limits, ResourceQuotas, and LimitRanges
- Applies advanced scheduling (affinity, taints, topology spread) and zero-trust NetworkPolicies
- Covers autoscaling (HPA, VPA, KEDA), RBAC/Pod Security hardening, and CrashLoopBackOff troubleshooting
Operating Kubernetes by the numbers
- 46 all-time installs (skills.sh)
- Ranked #734 of 1,039 Cloud & Infrastructure skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
operating-kubernetes capabilities & compatibility
- Capabilities
- kubernetes operations · resource sizing · cluster scheduling · kubernetes security · autoscaling
- Works with
- kubernetes
- Use cases
- devops · security audit
- Runs
- Runs locally
- Pricing
- Free
What operating-kubernetes says it does
Operating production Kubernetes clusters effectively with resource management, advanced scheduling, networking, storage, security hardening, and autoscaling.
Guaranteed (Highest Priority):
Implement default-deny security with NetworkPolicies:
npx skills add https://github.com/ancoleman/ai-design-components --skill operating-kubernetesAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 46 |
|---|---|
| repo stars | ★ 426 |
| Last updated | December 11, 2025 |
| Repository | ancoleman/ai-design-components ↗ |
What it does
Operate production Kubernetes: right-size resources, schedule pods, enforce NetworkPolicies and RBAC, and set up autoscaling.
Who is it for?
Platform and DevOps engineers running production Kubernetes workloads
Skip if: First-time users learning basic kubectl on a local minikube
When should I use this skill?
Deploying to Kubernetes, configuring cluster resources, setting up autoscaling, or troubleshooting pod failures
What you get
Right-sized, securely scheduled, autoscaling workloads with troubleshooting playbooks
- resource/QoS config
- scheduling constraints
- NetworkPolicies
By the numbers
- 3 QoS classes (Guaranteed, Burstable, BestEffort)
- 4 autoscaling mechanisms covered (HPA, VPA, KEDA, cluster autoscaler)
Files
Kubernetes Operations
Purpose
Operating Kubernetes clusters in production requires mastery of resource management, scheduling patterns, networking architecture, storage strategies, security hardening, and autoscaling. This skill provides operations-first frameworks for right-sizing workloads, implementing high-availability patterns, securing clusters with RBAC and Pod Security Standards, and systematically troubleshooting common failures.
Use this skill when deploying applications to Kubernetes, configuring cluster resources, implementing NetworkPolicies for zero-trust security, setting up autoscaling (HPA, VPA, KEDA), managing persistent storage, or diagnosing operational issues like CrashLoopBackOff or resource exhaustion.
When to Use This Skill
Common Triggers:
- "Deploy my application to Kubernetes"
- "Configure resource requests and limits"
- "Set up autoscaling for my pods"
- "Implement NetworkPolicies for security"
- "My pod is stuck in Pending/CrashLoopBackOff"
- "Configure RBAC with least privilege"
- "Set up persistent storage for my database"
- "Spread pods across availability zones"
Operations Covered:
- Resource management (CPU/memory, QoS classes, quotas)
- Advanced scheduling (affinity, taints, topology spread)
- Networking (NetworkPolicies, Ingress, Gateway API)
- Storage operations (StorageClasses, PVCs, CSI)
- Security hardening (RBAC, Pod Security Standards, policies)
- Autoscaling (HPA, VPA, KEDA, cluster autoscaler)
- Troubleshooting (systematic debugging playbooks)
Resource Management
Quality of Service (QoS) Classes
Kubernetes assigns QoS classes based on resource requests and limits:
Guaranteed (Highest Priority):
- Requests equal limits for CPU and memory
- Never evicted unless exceeding limits
- Use for critical production services
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "512Mi" # Same as request
cpu: "500m"Burstable (Medium Priority):
- Requests less than limits (or only requests set)
- Can burst above requests
- Evicted under node pressure
- Use for web servers, most applications
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi" # 2x request
cpu: "500m"BestEffort (Lowest Priority):
- No requests or limits set
- First to be evicted under pressure
- Use only for development/testing
Decision Framework: Which QoS Class?
| Workload Type | QoS Class | Configuration |
|---|---|---|
| Critical API/Database | Guaranteed | requests == limits |
| Web servers, services | Burstable | limits 1.5-2x requests |
| Batch jobs | Burstable | Low requests, high limits |
| Dev/test environments | BestEffort | No limits |
Resource Quotas and LimitRanges
Enforce multi-tenancy with ResourceQuotas (namespace limits) and LimitRanges (per-container defaults):
# ResourceQuota: Namespace-level limits
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-quota
namespace: team-alpha
spec:
hard:
requests.cpu: "10"
requests.memory: "20Gi"
limits.cpu: "20"
limits.memory: "40Gi"
pods: "50"For detailed resource management patterns including Vertical Pod Autoscaler (VPA), see references/resource-management.md.
Advanced Scheduling
Node Affinity
Control which nodes pods schedule on with required (hard) or preferred (soft) constraints:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node.kubernetes.io/instance-type
operator: In
values:
- g4dn.xlarge # GPU instanceTaints and Tolerations
Reserve nodes for specific workloads (inverse of affinity):
# Taint GPU nodes to prevent non-GPU workloads
kubectl taint nodes gpu-node-1 workload=gpu:NoSchedule# Pod tolerates GPU taint
tolerations:
- key: "workload"
operator: "Equal"
value: "gpu"
effect: "NoSchedule"Topology Spread Constraints
Distribute pods evenly across failure domains (zones, nodes):
topologySpreadConstraints:
- maxSkew: 1 # Max difference in pod count
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: critical-appFor advanced scheduling patterns including pod priority and preemption, see references/scheduling-patterns.md.
Networking
NetworkPolicies (Zero-Trust Security)
Implement default-deny security with NetworkPolicies:
# Default deny all traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress# Allow specific ingress (frontend → backend)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-allow-frontend
spec:
podSelector:
matchLabels:
app: backend
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080Ingress vs. Gateway API
Ingress (Legacy):
- Widely supported, mature ecosystem
- Limited expressiveness
- Use for existing applications
Gateway API (Modern):
- Role-oriented design (cluster ops vs. app devs)
- More expressive (HTTPRoute, TCPRoute, TLSRoute)
- Recommended for new applications (GA in Kubernetes 1.29+)
# Gateway API example
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-routes
spec:
parentRefs:
- name: production-gateway
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: backend
port: 8080For detailed networking patterns including service mesh integration, see references/networking.md.
Storage
StorageClasses (Define Performance Tiers)
StorageClasses define storage tiers for different workload needs:
# AWS EBS SSD (high performance)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: ebs.csi.aws.com
parameters:
type: gp3
iopsPerGB: "50"
encrypted: "true"
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
reclaimPolicy: DeleteStorage Decision Matrix
| Workload | Performance | Access Mode | Storage Class |
|---|---|---|---|
| Database | High | ReadWriteOnce | SSD (gp3/io2) |
| Shared files | Medium | ReadWriteMany | NFS/EFS |
| Logs (temp) | Low | ReadWriteOnce | Standard HDD |
| ML models | High | ReadOnlyMany | Object storage (S3) |
Access Modes:
- ReadWriteOnce (RWO): Single node read-write (most common)
- ReadOnlyMany (ROX): Multiple nodes read-only
- ReadWriteMany (RWX): Multiple nodes read-write (requires network storage)
For detailed storage operations including volume snapshots and CSI drivers, see references/storage.md.
Security
RBAC (Role-Based Access Control)
Implement least-privilege access with RBAC:
# Role (namespace-scoped)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: production
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
---
# RoleBinding (assign role to user)
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: production
subjects:
- kind: User
name: jane@example.com
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.ioPod Security Standards
Enforce secure pod configurations at the namespace level:
# Namespace with Restricted PSS (most secure)
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restrictedPod Security Levels:
- Restricted: Most secure, removes all privilege escalations (use for applications)
- Baseline: Minimally restrictive, prevents known escalations
- Privileged: Unrestricted (only for system workloads)
For detailed security patterns including policy enforcement (Kyverno/OPA) and secrets management, see references/security.md.
Autoscaling
Horizontal Pod Autoscaler (HPA)
Scale pod replicas based on CPU, memory, or custom metrics:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # Wait 5min before scaling downKEDA (Event-Driven Autoscaling)
Scale based on events beyond CPU/memory (queues, cron schedules, Prometheus metrics):
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: rabbitmq-scaler
spec:
scaleTargetRef:
name: message-processor
minReplicaCount: 0 # Scale to zero when queue empty
maxReplicaCount: 30
triggers:
- type: rabbitmq
metadata:
queueName: tasks
queueLength: "10" # Scale up when >10 messagesAutoscaling Decision Matrix
| Scenario | Use HPA | Use VPA | Use KEDA | Use Cluster Autoscaler |
|---|---|---|---|---|
| Stateless web app with traffic spikes | ✅ | ❌ | ❌ | Maybe |
| Single-instance database | ❌ | ✅ | ❌ | Maybe |
| Queue processor (event-driven) | ❌ | ❌ | ✅ | Maybe |
| Pods pending (insufficient nodes) | ❌ | ❌ | ❌ | ✅ |
For detailed autoscaling patterns including VPA and cluster autoscaler configuration, see references/autoscaling.md.
Troubleshooting
Common Pod Issues
Pod Stuck in Pending:
kubectl describe pod <pod-name>
# Common causes:
# - Insufficient CPU/memory: Reduce requests or add nodes
# - Node selector mismatch: Fix nodeSelector or add labels
# - PVC not bound: Create PVC or fix name
# - Taint intolerance: Add toleration or remove taintCrashLoopBackOff:
kubectl logs <pod-name>
kubectl logs <pod-name> --previous # Check previous crash
# Common causes:
# - Application crash: Fix code or configuration
# - Missing environment variables: Add to deployment
# - Liveness probe failing: Increase initialDelaySeconds
# - OOMKilled: Increase memory limit or fix leakImagePullBackOff:
kubectl describe pod <pod-name>
# Common causes:
# - Image doesn't exist: Fix image name/tag
# - Authentication required: Create imagePullSecrets
# - Network issues: Check NetworkPolicies, firewall rulesService Not Accessible:
kubectl get endpoints <service-name> # Should list pod IPs
# If endpoints empty:
# - Service selector doesn't match pod labels
# - Pods aren't ready (readiness probe failing)
# - Check NetworkPolicies blocking trafficFor systematic troubleshooting playbooks including networking and storage issues, see references/troubleshooting.md.
Reference Documentation
Deep Dives
- references/resource-management.md - Resource requests/limits, QoS classes, ResourceQuotas, VPA
- references/scheduling-patterns.md - Node affinity, taints/tolerations, topology spread, priority
- references/networking.md - NetworkPolicies, Ingress, Gateway API, service mesh integration
- references/storage.md - StorageClasses, PVCs, CSI drivers, volume snapshots
- references/security.md - RBAC, Pod Security Standards, policy enforcement, secrets
- references/autoscaling.md - HPA, VPA, KEDA, cluster autoscaler configuration
- references/troubleshooting.md - Systematic debugging playbooks for common failures
Examples
- examples/manifests/ - Copy-paste ready YAML manifests
- examples/python/ - Automation scripts (audit, cost analysis, validation)
- examples/go/ - Operator development examples
Tools
- scripts/validate-resources.sh - Audit pods without resource limits
- scripts/audit-networkpolicies.sh - Find namespaces without NetworkPolicies
- scripts/cost-analysis.sh - Resource cost breakdown by namespace
Related Skills
- building-ci-pipelines - Deploy to Kubernetes from CI/CD (kubectl apply, Helm, GitOps)
- observability - Monitor clusters and workloads (Prometheus, Grafana, tracing)
- secret-management - Secure secrets in Kubernetes (External Secrets, Sealed Secrets)
- testing-strategies - Test manifests and deployments (Kubeval, Conftest, Kind)
- infrastructure-as-code - Provision Kubernetes clusters (Terraform, Cluster API)
- gitops-workflows - Declarative cluster management (Flux, ArgoCD)
Best Practices Summary
Resource Management:
- Always set CPU/memory requests and limits
- Use VPA for automated rightsizing
- Implement resource quotas per namespace
- Monitor actual usage vs. requests
Scheduling:
- Use topology spread constraints for high availability
- Apply taints for workload isolation (GPU, spot instances)
- Set pod priority for critical workloads
Networking:
- Implement NetworkPolicies with default-deny
- Use Gateway API for new applications
- Apply rate limiting at ingress layer
Storage:
- Use CSI drivers (not legacy provisioners)
- Define StorageClasses per performance tier
- Enable volume snapshots for stateful apps
Security:
- Enforce Pod Security Standards (Restricted for apps)
- Implement RBAC with least privilege
- Use policy engines for guardrails (Kyverno/OPA)
- Scan images for vulnerabilities
Autoscaling:
- Use HPA for stateless workloads
- Use KEDA for event-driven workloads
- Enable cluster autoscaler with limits
- Set PodDisruptionBudgets to prevent over-disruption
# Horizontal Pod Autoscaler Example
# Scale based on CPU and memory utilization
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-app-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: AverageValue
averageValue: 500Mi
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # Wait 5min before scaling down
policies:
- type: Percent
value: 50 # Scale down max 50% of current pods
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100 # Can double pods in 1 minute
periodSeconds: 60
- type: Pods
value: 4 # Or add 4 pods, whichever is higher
periodSeconds: 60
selectPolicy: Max
# KEDA RabbitMQ Scaler Example
# Scale to zero when queue is empty
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: rabbitmq-scaler
namespace: production
spec:
scaleTargetRef:
name: message-processor
minReplicaCount: 0 # Scale to zero when queue empty
maxReplicaCount: 30
triggers:
- type: rabbitmq
metadata:
host: amqp://user:password@rabbitmq.default.svc.cluster.local:5672
queueName: tasks
queueLength: "10" # Scale up when >10 messages
# Allow Frontend to Backend Traffic
# Permit specific ingress from frontend pods to backend pods
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-allow-frontend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
# Default Deny All Traffic
# Implement zero-trust networking by denying all ingress and egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {} # Applies to all pods in namespace
policyTypes:
- Ingress
- Egress
# Pod Security Standards - Restricted
# Most secure configuration for applications
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
---
apiVersion: v1
kind: Pod
metadata:
name: secure-app
namespace: production
spec:
# Pod-level security context
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: nginx:1.21
# Container-level security context
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
# Writable directories (since root filesystem is read-only)
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache/nginx
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
# Burstable QoS Example
# Requests less than limits for cost-effective scaling
apiVersion: v1
kind: Pod
metadata:
name: burstable-pod
labels:
qos: burstable
app: web-server
spec:
containers:
- name: app
image: nginx:1.21
resources:
requests:
memory: "256Mi"
cpu: "250m" # 0.25 CPU cores
limits:
memory: "512Mi" # 2x request = can burst
cpu: "500m" # 2x request = can burst
ports:
- containerPort: 80
# Guaranteed QoS Example
# Requests equal limits for predictable performance
apiVersion: v1
kind: Pod
metadata:
name: guaranteed-pod
labels:
qos: guaranteed
app: critical-service
spec:
containers:
- name: app
image: nginx:1.21
resources:
requests:
memory: "512Mi"
cpu: "500m" # 0.5 CPU cores
limits:
memory: "512Mi" # Same as request = Guaranteed QoS
cpu: "500m" # Same as request = Guaranteed QoS
ports:
- containerPort: 80
# RBAC Least Privilege Example
# ServiceAccount with minimal permissions
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: production
automountServiceAccountToken: false # Don't auto-mount unless needed
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: configmap-reader
namespace: production
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
resourceNames: ["app-config"] # Further restrict to specific resource
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: app-configmap-access
namespace: production
subjects:
- kind: ServiceAccount
name: my-app-sa
namespace: production
roleRef:
kind: Role
name: configmap-reader
apiGroup: rbac.authorization.k8s.io
# AWS EBS SSD StorageClass
# High-performance storage for databases
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: ebs.csi.aws.com
parameters:
type: gp3 # General Purpose SSD v3
iopsPerGB: "50"
throughput: "1000" # MB/s
encrypted: "true"
kmsKeyId: "arn:aws:kms:us-east-1:123456789:key/your-key-id"
volumeBindingMode: WaitForFirstConsumer # Topology-aware
allowVolumeExpansion: true
reclaimPolicy: Delete
#!/usr/bin/env python3
"""
Generate Kubernetes Cost Report
Demonstrates: Calculate namespace-level resource costs based on requests
Dependencies:
pip install kubernetes
Usage:
python generate_cost_report.py
"""
from kubernetes import client, config
# Cost per resource (example pricing)
COST_PER_CPU_HOUR = 0.03 # $0.03 per CPU core per hour
COST_PER_GB_HOUR = 0.004 # $0.004 per GB memory per hour
def parse_cpu(cpu_str):
"""Convert CPU string to cores (e.g., '500m' -> 0.5)"""
if not cpu_str:
return 0
if cpu_str.endswith('m'):
return float(cpu_str[:-1]) / 1000
return float(cpu_str)
def parse_memory(mem_str):
"""Convert memory string to GB (e.g., '512Mi' -> 0.5)"""
if not mem_str:
return 0
units = {
'Ki': 1024,
'Mi': 1024**2,
'Gi': 1024**3,
'Ti': 1024**4,
}
for unit, multiplier in units.items():
if mem_str.endswith(unit):
return float(mem_str[:-len(unit)]) * multiplier / (1024**3)
# Assume bytes if no unit
return float(mem_str) / (1024**3)
def main():
# Load kubeconfig
config.load_kube_config()
v1 = client.CoreV1Api()
# Get all pods
pods = v1.list_pod_for_all_namespaces()
# Calculate costs by namespace
namespace_costs = {}
for pod in pods.items:
ns = pod.metadata.namespace
if ns not in namespace_costs:
namespace_costs[ns] = {
'cpu_cores': 0,
'memory_gb': 0,
'pod_count': 0
}
namespace_costs[ns]['pod_count'] += 1
for container in pod.spec.containers:
if container.resources.requests:
cpu = parse_cpu(container.resources.requests.get('cpu'))
memory = parse_memory(container.resources.requests.get('memory'))
namespace_costs[ns]['cpu_cores'] += cpu
namespace_costs[ns]['memory_gb'] += memory
# Calculate costs
print("=== Kubernetes Resource Cost Report ===\n")
print(f"{'Namespace':<30} {'Pods':<8} {'CPU Cores':<12} {'Memory (GB)':<14} {'Cost/Hour':<12} {'Cost/Month'}")
print("-" * 100)
total_cpu = 0
total_memory = 0
total_cost_hour = 0
for ns, resources in sorted(namespace_costs.items(), key=lambda x: x[1]['cpu_cores'], reverse=True):
cpu = resources['cpu_cores']
memory = resources['memory_gb']
pods = resources['pod_count']
cost_hour = (cpu * COST_PER_CPU_HOUR) + (memory * COST_PER_GB_HOUR)
cost_month = cost_hour * 24 * 30
total_cpu += cpu
total_memory += memory
total_cost_hour += cost_hour
print(f"{ns:<30} {pods:<8} {cpu:<12.2f} {memory:<14.2f} ${cost_hour:<11.2f} ${cost_month:.2f}")
# Totals
print("-" * 100)
total_cost_month = total_cost_hour * 24 * 30
print(f"{'TOTAL':<30} {sum(c['pod_count'] for c in namespace_costs.values()):<8} {total_cpu:<12.2f} {total_memory:<14.2f} ${total_cost_hour:<11.2f} ${total_cost_month:.2f}")
print(f"\n* Costs based on: CPU ${COST_PER_CPU_HOUR}/core/hr, Memory ${COST_PER_GB_HOUR}/GB/hr")
print("* Actual cloud costs may vary based on instance types and commitments")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
List Pods Without Resource Limits
Demonstrates: Finding pods missing resource limits for cost optimization
Dependencies:
pip install kubernetes
Usage:
python list_pods_without_limits.py
"""
from kubernetes import client, config
def main():
# Load kubeconfig
config.load_kube_config()
v1 = client.CoreV1Api()
# Get all pods across all namespaces
pods = v1.list_pod_for_all_namespaces()
missing_limits = []
for pod in pods.items:
for container in pod.spec.containers:
if not container.resources.limits:
missing_limits.append({
'namespace': pod.metadata.namespace,
'pod': pod.metadata.name,
'container': container.name
})
# Print results
print(f"Found {len(missing_limits)} containers without resource limits:\n")
for item in missing_limits:
print(f" {item['namespace']}/{item['pod']}/{item['container']}")
# Summary by namespace
print("\n--- Summary by Namespace ---")
namespaces = {}
for item in missing_limits:
ns = item['namespace']
namespaces[ns] = namespaces.get(ns, 0) + 1
for ns, count in sorted(namespaces.items(), key=lambda x: x[1], reverse=True):
print(f" {ns}: {count} containers")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Validate RBAC Configuration
Demonstrates: Audit RBAC permissions for security compliance
Dependencies:
pip install kubernetes
Usage:
python validate_rbac.py
"""
from kubernetes import client, config
def check_overpermissive_roles(rbac_api):
"""Find roles with wildcard permissions"""
print("=== Checking for Overpermissive Roles ===\n")
overpermissive = []
# Check Roles
for namespace in ['default', 'production', 'staging']:
try:
roles = rbac_api.list_namespaced_role(namespace)
for role in roles.items:
for rule in role.rules:
if '*' in rule.verbs or '*' in rule.resources or '*' in (rule.api_groups or []):
overpermissive.append({
'type': 'Role',
'name': role.metadata.name,
'namespace': namespace,
'rule': rule
})
except client.exceptions.ApiException:
pass
# Check ClusterRoles
cluster_roles = rbac_api.list_cluster_role()
for role in cluster_roles.items:
for rule in role.rules:
if '*' in rule.verbs or '*' in rule.resources or '*' in (rule.api_groups or []):
overpermissive.append({
'type': 'ClusterRole',
'name': role.metadata.name,
'namespace': 'N/A',
'rule': rule
})
if overpermissive:
print(f"Found {len(overpermissive)} overpermissive roles:\n")
for item in overpermissive:
print(f" {item['type']}: {item['name']} (namespace: {item['namespace']})")
print(f" API Groups: {item['rule'].api_groups}")
print(f" Resources: {item['rule'].resources}")
print(f" Verbs: {item['rule'].verbs}\n")
else:
print("No overpermissive roles found.\n")
def check_default_sa_usage(v1):
"""Find pods using default ServiceAccount"""
print("=== Checking for Default ServiceAccount Usage ===\n")
pods = v1.list_pod_for_all_namespaces()
default_sa_pods = []
for pod in pods.items:
if pod.spec.service_account_name in [None, 'default']:
default_sa_pods.append({
'namespace': pod.metadata.namespace,
'pod': pod.metadata.name
})
if default_sa_pods:
print(f"Found {len(default_sa_pods)} pods using default ServiceAccount:\n")
for item in default_sa_pods[:10]: # Show first 10
print(f" {item['namespace']}/{item['pod']}")
if len(default_sa_pods) > 10:
print(f" ... and {len(default_sa_pods) - 10} more\n")
else:
print("All pods use dedicated ServiceAccounts. Good!\n")
def check_cluster_admin_bindings(rbac_api):
"""Find cluster-admin bindings"""
print("=== Checking for cluster-admin Bindings ===\n")
cluster_role_bindings = rbac_api.list_cluster_role_binding()
admin_bindings = []
for binding in cluster_role_bindings.items:
if binding.role_ref.name == 'cluster-admin':
for subject in binding.subjects or []:
admin_bindings.append({
'binding': binding.metadata.name,
'subject_kind': subject.kind,
'subject_name': subject.name,
'namespace': getattr(subject, 'namespace', 'N/A')
})
if admin_bindings:
print(f"Found {len(admin_bindings)} cluster-admin bindings:\n")
for item in admin_bindings:
print(f" Binding: {item['binding']}")
print(f" Subject: {item['subject_kind']}/{item['subject_name']} (ns: {item['namespace']})\n")
else:
print("No cluster-admin bindings found.\n")
def main():
# Load kubeconfig
config.load_kube_config()
v1 = client.CoreV1Api()
rbac_api = client.RbacAuthorizationV1Api()
print("=" * 60)
print("Kubernetes RBAC Security Audit")
print("=" * 60 + "\n")
check_overpermissive_roles(rbac_api)
check_default_sa_usage(v1)
check_cluster_admin_bindings(rbac_api)
print("=" * 60)
print("Audit Complete")
print("=" * 60)
if __name__ == "__main__":
main()
skill: "operating-kubernetes"
version: "1.0"
domain: "infrastructure"
# Base outputs required for all Kubernetes operations projects
base_outputs:
- path: "kubernetes/"
must_contain: ["deployment.yaml", "service.yaml"]
reason: "Core Kubernetes deployment and service manifests"
- path: "kubernetes/deployment.yaml"
must_contain: ["kind: Deployment", "resources:", "limits:", "requests:"]
reason: "Deployment with resource requests and limits configured"
- path: "kubernetes/service.yaml"
must_contain: ["kind: Service", "selector:", "ports:"]
reason: "Service for exposing application"
- path: "scripts/"
must_contain: []
reason: "Operational scripts for common K8s tasks"
# Conditional outputs based on configuration
conditional_outputs:
maturity:
starter:
- path: "kubernetes/deployment.yaml"
must_contain: ["replicas:", "livenessProbe:", "readinessProbe:"]
reason: "Basic deployment with health checks"
- path: "kubernetes/configmap.yaml"
must_contain: ["kind: ConfigMap", "data:"]
reason: "ConfigMap for application configuration"
- path: "kubernetes/namespace.yaml"
must_contain: ["kind: Namespace"]
reason: "Namespace for workload isolation"
- path: "scripts/deploy.sh"
must_contain: ["kubectl apply", "-f"]
reason: "Simple deployment script"
intermediate:
- path: "kubernetes/hpa.yaml"
must_contain: ["kind: HorizontalPodAutoscaler", "minReplicas:", "maxReplicas:", "metrics:"]
reason: "Horizontal Pod Autoscaler for scaling"
- path: "kubernetes/pdb.yaml"
must_contain: ["kind: PodDisruptionBudget", "minAvailable"]
reason: "Pod Disruption Budget for high availability"
- path: "kubernetes/networkpolicy.yaml"
must_contain: ["kind: NetworkPolicy", "podSelector:", "policyTypes:"]
reason: "NetworkPolicy for zero-trust security"
- path: "kubernetes/resourcequota.yaml"
must_contain: ["kind: ResourceQuota", "hard:"]
reason: "ResourceQuota for namespace limits"
- path: "kubernetes/rbac.yaml"
must_contain: ["kind: Role", "kind: RoleBinding", "rules:"]
reason: "RBAC for least-privilege access"
- path: "scripts/validate-resources.sh"
must_contain: ["kubectl get pods", "resources"]
reason: "Script to audit resource configurations"
advanced:
- path: "kubernetes/kustomization.yaml"
must_contain: ["resources:", "apiVersion: kustomize"]
reason: "Kustomize for environment-specific configs"
- path: "kubernetes/overlays/production/"
must_contain: ["kustomization.yaml"]
reason: "Production overlay with specific configurations"
- path: "kubernetes/overlays/staging/"
must_contain: ["kustomization.yaml"]
reason: "Staging overlay for testing"
- path: "kubernetes/vpa.yaml"
must_contain: ["kind: VerticalPodAutoscaler", "updateMode:"]
reason: "VPA for automated resource rightsizing"
- path: "kubernetes/pod-security.yaml"
must_contain: ["pod-security.kubernetes.io/enforce"]
reason: "Pod Security Standards enforcement"
- path: "kubernetes/servicemonitor.yaml"
must_contain: ["kind: ServiceMonitor", "endpoints:"]
reason: "Prometheus ServiceMonitor for metrics"
- path: "scripts/audit-networkpolicies.sh"
must_contain: ["kubectl get networkpolicy", "namespace"]
reason: "Audit script for NetworkPolicy coverage"
- path: "scripts/cost-analysis.sh"
must_contain: ["kubectl top", "resources"]
reason: "Resource cost analysis script"
service_mesh:
istio:
- path: "kubernetes/istio/virtualservice.yaml"
must_contain: ["kind: VirtualService", "http:"]
reason: "Istio VirtualService for traffic routing"
- path: "kubernetes/istio/destinationrule.yaml"
must_contain: ["kind: DestinationRule", "trafficPolicy:"]
reason: "Istio DestinationRule for load balancing"
- path: "kubernetes/istio/gateway.yaml"
must_contain: ["kind: Gateway", "servers:"]
reason: "Istio Gateway for ingress"
- path: "kubernetes/istio/peerauthentication.yaml"
must_contain: ["kind: PeerAuthentication", "mtls:"]
reason: "mTLS configuration for service mesh"
linkerd:
- path: "kubernetes/linkerd/server.yaml"
must_contain: ["kind: Server", "proxyProtocol:"]
reason: "Linkerd Server resource"
- path: "kubernetes/linkerd/serverauthorization.yaml"
must_contain: ["kind: ServerAuthorization", "server:"]
reason: "Linkerd authorization policy"
- path: "kubernetes/deployment.yaml"
must_contain: ["linkerd.io/inject", "enabled"]
reason: "Linkerd injection annotation"
none:
- path: "kubernetes/ingress.yaml"
must_contain: ["kind: Ingress", "rules:", "host:"]
reason: "Standard Ingress for external access"
observability:
prometheus_grafana:
- path: "kubernetes/servicemonitor.yaml"
must_contain: ["kind: ServiceMonitor", "interval:"]
reason: "Prometheus scraping configuration"
- path: "kubernetes/prometheusrule.yaml"
must_contain: ["kind: PrometheusRule", "alert:"]
reason: "Prometheus alerting rules"
- path: "monitoring/grafana-dashboard.json"
must_contain: ['"type":', '"title":']
reason: "Grafana dashboard for Kubernetes metrics"
datadog:
- path: "kubernetes/deployment.yaml"
must_contain: ["ad.datadoghq.com"]
reason: "Datadog autodiscovery annotations"
- path: "kubernetes/datadog-agent.yaml"
must_contain: ["datadog/agent"]
reason: "Datadog agent DaemonSet"
newrelic:
- path: "kubernetes/newrelic-infrastructure.yaml"
must_contain: ["newrelic/infrastructure-k8s"]
reason: "New Relic infrastructure monitoring"
cloud_provider:
aws:
- path: "kubernetes/storageclass.yaml"
must_contain: ["provisioner: ebs.csi.aws.com", "type: gp3"]
reason: "AWS EBS storage class for persistent volumes"
- path: "kubernetes/service.yaml"
must_contain: ["service.beta.kubernetes.io/aws-load-balancer"]
reason: "AWS load balancer annotations"
gcp:
- path: "kubernetes/storageclass.yaml"
must_contain: ["provisioner: pd.csi.storage.gke.io", "type: pd-ssd"]
reason: "GCP persistent disk storage class"
- path: "kubernetes/service.yaml"
must_contain: ["cloud.google.com/load-balancer-type"]
reason: "GCP load balancer annotations"
azure:
- path: "kubernetes/storageclass.yaml"
must_contain: ["provisioner: disk.csi.azure.com", "skuName:"]
reason: "Azure disk storage class"
- path: "kubernetes/service.yaml"
must_contain: ["service.beta.kubernetes.io/azure-load-balancer"]
reason: "Azure load balancer annotations"
iac_tool:
terraform:
- path: "terraform/kubernetes.tf"
must_contain: ["kubernetes_deployment", "kubernetes_service"]
reason: "Terraform Kubernetes provider resources"
- path: "terraform/variables.tf"
must_contain: ["variable", "replicas"]
reason: "Terraform variables for configuration"
pulumi:
- path: "pulumi/index.ts"
must_contain: ["k8s.apps.v1.Deployment", "k8s.core.v1.Service"]
reason: "Pulumi Kubernetes resources"
- path: "pulumi/Pulumi.yaml"
must_contain: ["name:", "runtime:"]
reason: "Pulumi project configuration"
helm:
- path: "helm/Chart.yaml"
must_contain: ["name:", "version:", "apiVersion: v2"]
reason: "Helm chart metadata"
- path: "helm/values.yaml"
must_contain: ["replicas:", "image:"]
reason: "Helm values for customization"
- path: "helm/templates/deployment.yaml"
must_contain: ["{{", "Values.", "Release."]
reason: "Helm templated deployment"
# Scaffolding files that should be created as starting points
scaffolding:
- path: "kubernetes/"
type: "directory"
description: "Root directory for Kubernetes manifests"
- path: "kubernetes/base/"
type: "directory"
description: "Base Kustomize manifests"
- path: "kubernetes/overlays/"
type: "directory"
description: "Environment-specific overlays"
- path: "kubernetes/overlays/development/"
type: "directory"
description: "Development environment configuration"
- path: "kubernetes/overlays/staging/"
type: "directory"
description: "Staging environment configuration"
- path: "kubernetes/overlays/production/"
type: "directory"
description: "Production environment configuration"
- path: "scripts/"
type: "directory"
description: "Operational and maintenance scripts"
- path: "monitoring/"
type: "directory"
description: "Monitoring dashboards and configurations"
- path: "docs/RUNBOOK.md"
type: "file"
template: |
# Kubernetes Operations Runbook
## Common Operations
### Deploy Application
```bash
kubectl apply -f kubernetes/
kubectl rollout status deployment/<app-name>
```
### Scale Application
```bash
kubectl scale deployment/<app-name> --replicas=5
```
### Check Pod Status
```bash
kubectl get pods
kubectl describe pod <pod-name>
kubectl logs <pod-name>
```
### Troubleshooting
**Pod Pending:**
- Check resource requests: `kubectl describe pod <pod-name>`
- Verify node capacity: `kubectl top nodes`
- Check events: `kubectl get events`
**CrashLoopBackOff:**
- View logs: `kubectl logs <pod-name> --previous`
- Check liveness probe configuration
- Increase resource limits if OOMKilled
**Service Not Accessible:**
- Check endpoints: `kubectl get endpoints <service-name>`
- Verify NetworkPolicy: `kubectl get networkpolicy`
- Check Ingress: `kubectl describe ingress <ingress-name>`
- path: "kubernetes/README.md"
type: "file"
template: |
# Kubernetes Manifests
This directory contains Kubernetes manifests for deploying and operating the application.
## Structure
```
kubernetes/
├── base/ # Base manifests (Kustomize)
├── overlays/ # Environment-specific configs
│ ├── development/
│ ├── staging/
│ └── production/
├── deployment.yaml # Application deployment
├── service.yaml # Service definition
├── configmap.yaml # Configuration
├── hpa.yaml # Horizontal Pod Autoscaler
├── networkpolicy.yaml # Network policies
└── rbac.yaml # RBAC configuration
```
## Deployment
### Using kubectl
```bash
kubectl apply -f kubernetes/
```
### Using Kustomize
```bash
kubectl apply -k kubernetes/overlays/production/
```
## Resource Management
All pods have resource requests and limits configured:
- **Guaranteed QoS:** requests == limits (critical services)
- **Burstable QoS:** limits > requests (most applications)
## Security
- NetworkPolicies implement default-deny
- RBAC enforces least-privilege
- Pod Security Standards: Restricted
## Monitoring
Prometheus metrics exposed at `/metrics` endpoint.
Grafana dashboards in `monitoring/` directory.
# Metadata
metadata:
primary_blueprints: ["k8s", "ml-pipeline", "api-first"]
contributes_to:
- "Kubernetes deployment and operations"
- "Resource management and autoscaling"
- "Security hardening and NetworkPolicies"
- "High availability and fault tolerance"
- "Observability and monitoring"
common_patterns:
- name: "Resource Management"
description: "CPU/memory requests and limits with QoS classes"
files: ["kubernetes/deployment.yaml"]
- name: "Autoscaling"
description: "HPA for horizontal scaling, VPA for vertical"
files: ["kubernetes/hpa.yaml", "kubernetes/vpa.yaml"]
- name: "Zero-Trust Networking"
description: "NetworkPolicies with default-deny"
files: ["kubernetes/networkpolicy.yaml"]
- name: "High Availability"
description: "Multi-replica with topology spread and PDB"
files: ["kubernetes/deployment.yaml", "kubernetes/pdb.yaml"]
- name: "Security Hardening"
description: "RBAC, Pod Security Standards, non-root containers"
files: ["kubernetes/rbac.yaml", "kubernetes/pod-security.yaml"]
integration_points:
ci_cd: "Deploy to Kubernetes from CI/CD pipelines"
observability: "Prometheus metrics and Grafana dashboards"
secret_management: "External Secrets Operator or Sealed Secrets"
service_mesh: "Istio or Linkerd for advanced traffic management"
storage: "Persistent volumes with CSI drivers"
typical_directory_structure: |
project/
├── kubernetes/
│ ├── base/
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ ├── configmap.yaml
│ │ ├── hpa.yaml
│ │ ├── pdb.yaml
│ │ ├── networkpolicy.yaml
│ │ ├── rbac.yaml
│ │ └── kustomization.yaml
│ ├── overlays/
│ │ ├── development/
│ │ ├── staging/
│ │ └── production/
│ └── monitoring/
│ ├── servicemonitor.yaml
│ └── prometheusrule.yaml
├── scripts/
│ ├── validate-resources.sh
│ ├── audit-networkpolicies.sh
│ └── cost-analysis.sh
├── monitoring/
│ └── grafana-dashboard.json
└── docs/
└── RUNBOOK.md
validation_checks:
- "All deployments have resource requests and limits"
- "Health checks (liveness and readiness) configured"
- "NetworkPolicies present in all namespaces"
- "RBAC configured with least privilege"
- "Pod Security Standards enforced"
- "HPA configured for stateless workloads"
- "PDB configured to prevent over-disruption"
- "Non-root containers enforced"
- "ServiceMonitor exists for monitoring"
tools:
manifest_validation:
- name: "kubeval"
use_when: "Validate YAML syntax"
- name: "kube-score"
use_when: "Best practices validation"
- name: "conftest"
use_when: "Policy enforcement with OPA"
deployment:
- name: "kubectl"
use_when: "Direct manifest application"
- name: "kustomize"
use_when: "Environment-specific overlays"
- name: "helm"
use_when: "Parameterized deployments"
policy:
- name: "Kyverno"
use_when: "Kubernetes-native policy engine"
- name: "OPA Gatekeeper"
use_when: "Complex policy enforcement"
anti_patterns:
- name: "No resource limits"
avoid: "Pods without CPU/memory limits"
use: "Always set requests and limits"
- name: "BestEffort QoS in production"
avoid: "Pods with no resource configuration"
use: "Guaranteed or Burstable QoS"
- name: "No NetworkPolicies"
avoid: "Open network without restrictions"
use: "Default-deny with explicit allow rules"
- name: "Running as root"
avoid: "Privileged containers"
use: "Non-root user with read-only filesystem"
- name: "No health checks"
avoid: "Pods without liveness/readiness probes"
use: "Always configure health checks"
Autoscaling
Table of Contents
1. Horizontal Pod Autoscaler (HPA) 2. Vertical Pod Autoscaler (VPA) 3. KEDA (Event-Driven Autoscaling) 4. Cluster Autoscaler 5. Autoscaling Decision Framework
Horizontal Pod Autoscaler (HPA)
HPA v2 (Recommended)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-app-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: AverageValue
averageValue: 500Mi
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # Wait 5min before scaling down
policies:
- type: Percent
value: 50 # Scale down max 50% of current pods
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100 # Can double pods in 1 minute
periodSeconds: 60
- type: Pods
value: 4 # Or add 4 pods, whichever is higher
periodSeconds: 60
selectPolicy: MaxTarget Types
Utilization (Percentage):
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # 70% of requested CPUAverageValue (Absolute):
metrics:
- type: Resource
resource:
name: memory
target:
type: AverageValue
averageValue: 500Mi # 500MB per podValue (Total):
metrics:
- type: Resource
resource:
name: cpu
target:
type: Value
value: "2" # 2 CPU cores total across all podsCustom Metrics (Prometheus)
Prerequisites:
# Install Prometheus Adapter
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus-adapter prometheus-community/prometheus-adapter \
--namespace monitoringHPA with Custom Metric:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 2
maxReplicas: 20
metrics:
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "100" # Scale when >100 req/s per podPrometheus Adapter ConfigMap:
rules:
- seriesQuery: 'http_requests_total{namespace="production"}'
resources:
overrides:
namespace: {resource: "namespace"}
pod: {resource: "pod"}
name:
matches: "^(.*)_total$"
as: "${1}_per_second"
metricsQuery: 'rate(<<.Series>>{<<.LabelMatchers>>}[2m])'External Metrics (Datadog, New Relic)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 2
maxReplicas: 20
metrics:
- type: External
external:
metric:
name: datadog.metric.name
selector:
matchLabels:
app: api
target:
type: Value
value: "100"Scaling Behavior
Aggressive Scale-Up, Conservative Scale-Down:
behavior:
scaleUp:
stabilizationWindowSeconds: 0 # Scale up immediately
policies:
- type: Percent
value: 100 # Double pods
periodSeconds: 15
- type: Pods
value: 4 # Or add 4 pods
periodSeconds: 15
selectPolicy: Max
scaleDown:
stabilizationWindowSeconds: 300 # Wait 5 minutes
policies:
- type: Percent
value: 25 # Remove max 25% of pods
periodSeconds: 60Prevent Flapping:
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # Look at 5min window
selectPolicy: Max # Take max recommendation
policies:
- type: Pods
value: 1
periodSeconds: 60HPA Troubleshooting
# Check HPA status
kubectl get hpa -n production
# Describe HPA (see events)
kubectl describe hpa web-app-hpa -n production
# Common issues:
# 1. "unable to get metrics": Metrics server not installed
# 2. "failed to get cpu utilization": No resource requests set
# 3. "invalid metrics": Wrong metric name or queryFix:
# Install metrics server
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
# Ensure pods have resource requests
kubectl get pods -n production -o json | \
jq '.items[].spec.containers[].resources.requests'Vertical Pod Autoscaler (VPA)
Installation
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler
./hack/vpa-up.shVPA Configuration
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: my-app-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Auto" # Off, Initial, Recreate, Auto
resourcePolicy:
containerPolicies:
- containerName: app
minAllowed:
cpu: "100m"
memory: "128Mi"
maxAllowed:
cpu: "2"
memory: "2Gi"
controlledResources: ["cpu", "memory"]
mode: "Auto"
- containerName: sidecar
mode: "Off" # Don't VPA this containerUpdate Modes
Off (Recommendations Only):
updatePolicy:
updateMode: "Off"- VPA calculates recommendations
- No automatic updates
- View:
kubectl describe vpa my-app-vpa
Initial:
updatePolicy:
updateMode: "Initial"- Apply recommendations on pod creation
- Existing pods unchanged
- Good for StatefulSets
Recreate:
updatePolicy:
updateMode: "Recreate"- Update running pods (causes restart)
- Evicts and recreates with new resources
- Not for single-replica
Auto (Future):
updatePolicy:
updateMode: "Auto"- In-place updates without restart
- Not yet production-ready
View VPA Recommendations
kubectl describe vpa my-app-vpa
# Output:
# Recommendation:
# Container Recommendations:
# Container Name: app
# Lower Bound:
# Cpu: 250m
# Memory: 256Mi
# Target:
# Cpu: 500m
# Memory: 512Mi
# Upper Bound:
# Cpu: 1
# Memory: 1Gi
# Uncapped Target:
# Cpu: 750m
# Memory: 768MiInterpretation:
- Lower Bound: Minimum for cost efficiency
- Target: Recommended values
- Upper Bound: Maximum safe values
- Uncapped Target: What VPA would recommend without limits
VPA + HPA Compatibility
Compatible:
# VPA on CPU/memory
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: my-app-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Auto"
resourcePolicy:
containerPolicies:
- containerName: app
controlledResources: ["memory"] # Only memory
---
# HPA on CPU
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70Not Compatible:
- VPA Auto/Recreate + HPA on same metric (CPU or memory)
- Results in thrashing and instability
KEDA (Event-Driven Autoscaling)
Installation
helm repo add kedacore https://kedacore.github.io/charts
helm install keda kedacore/keda --namespace keda --create-namespaceRabbitMQ Scaler
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: rabbitmq-scaler
namespace: production
spec:
scaleTargetRef:
name: message-processor
minReplicaCount: 0 # Scale to zero when queue empty
maxReplicaCount: 30
triggers:
- type: rabbitmq
metadata:
host: amqp://user:password@rabbitmq.default.svc.cluster.local:5672
queueName: tasks
queueLength: "10" # Scale up when >10 messagesKafka Scaler
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: kafka-scaler
spec:
scaleTargetRef:
name: kafka-consumer
minReplicaCount: 1
maxReplicaCount: 50
triggers:
- type: kafka
metadata:
bootstrapServers: kafka-broker.kafka:9092
consumerGroup: my-consumer-group
topic: events
lagThreshold: "100" # Scale when lag >100AWS SQS Scaler
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: sqs-scaler
spec:
scaleTargetRef:
name: sqs-processor
minReplicaCount: 0
maxReplicaCount: 20
triggers:
- type: aws-sqs-queue
metadata:
queueURL: https://sqs.us-east-1.amazonaws.com/123456789/my-queue
queueLength: "5"
awsRegion: us-east-1
identityOwner: operator # Use IRSAPrometheus Scaler
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: prometheus-scaler
spec:
scaleTargetRef:
name: api-server
minReplicaCount: 2
maxReplicaCount: 20
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus.monitoring.svc:9090
metricName: http_requests_per_second
query: sum(rate(http_requests_total{service="api"}[2m]))
threshold: "100" # Scale when >100 req/sec totalCron Scaler (Schedule-Based)
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: cron-scaler
spec:
scaleTargetRef:
name: batch-processor
minReplicaCount: 0
maxReplicaCount: 10
triggers:
- type: cron
metadata:
timezone: America/New_York
start: 0 8 * * 1-5 # Scale up at 8am Mon-Fri
end: 0 18 * * 1-5 # Scale down at 6pm Mon-Fri
desiredReplicas: "5"CPU/Memory Scaler (KEDA Alternative to HPA)
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: cpu-scaler
spec:
scaleTargetRef:
name: web-app
minReplicaCount: 2
maxReplicaCount: 10
triggers:
- type: cpu
metricType: Utilization
metadata:
value: "70"
- type: memory
metricType: Utilization
metadata:
value: "80"KEDA Fallback
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: fallback-scaler
spec:
scaleTargetRef:
name: processor
fallback:
failureThreshold: 3
replicas: 5 # Scale to 5 if scaler fails
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus.monitoring.svc:9090
query: queue_depth
threshold: "10"Cluster Autoscaler
AWS Installation
helm repo add autoscaler https://kubernetes.github.io/autoscaler
helm install cluster-autoscaler autoscaler/cluster-autoscaler \
--namespace kube-system \
--set autoDiscovery.clusterName=my-cluster \
--set awsRegion=us-east-1 \
--set rbac.create=trueConfiguration
apiVersion: v1
kind: ConfigMap
metadata:
name: cluster-autoscaler-status
namespace: kube-system
data:
scale-down-enabled: "true"
scale-down-delay-after-add: "10m"
scale-down-unneeded-time: "10m"
scale-down-utilization-threshold: "0.5"
max-node-provision-time: "15m"
skip-nodes-with-local-storage: "false"
skip-nodes-with-system-pods: "true"Node Groups (AWS)
# Auto Scaling Group tags
k8s.io/cluster-autoscaler/<cluster-name>=owned
k8s.io/cluster-autoscaler/enabled=true
k8s.io/cluster-autoscaler/node-template/label/workload=generalCluster Autoscaler Behavior
Scale Up:
- Pods are Pending due to insufficient resources
- Provision new nodes from auto-scaling group
- Takes 2-5 minutes (cloud provider dependent)
Scale Down:
- Node utilization < threshold (default 50%)
- All pods can be rescheduled elsewhere
- Node not running system pods (unless configured)
- Wait 10 minutes (configurable) before scale down
Prevent Scale Down:
# Annotate pod to prevent node scale-down
apiVersion: v1
kind: Pod
metadata:
annotations:
cluster-autoscaler.kubernetes.io/safe-to-evict: "false"PodDisruptionBudget (PDB)
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-pdb
namespace: production
spec:
minAvailable: 2 # Keep at least 2 replicas
selector:
matchLabels:
app: apiAlternative (percentage):
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-pdb
spec:
maxUnavailable: 1 # Max 1 pod disrupted
selector:
matchLabels:
app: apiAutoscaling Decision Framework
Decision Matrix
| Workload | Scaling Dimension | Use HPA | Use VPA | Use KEDA | Use Cluster Autoscaler |
|---|---|---|---|---|---|
| Stateless web app (traffic-driven) | Horizontal | ✅ | ❌ | ❌ | If needed |
| Single DB instance | Vertical | ❌ | ✅ | ❌ | Maybe |
| Queue processor | Horizontal | ❌ | ❌ | ✅ | If needed |
| Scheduled batch job | Horizontal | ❌ | ❌ | ✅ (cron) | If needed |
| Pods pending (no nodes) | Nodes | ❌ | ❌ | ❌ | ✅ |
| Unknown resource needs | Vertical | ❌ | ✅ (Off mode) | ❌ | ❌ |
Pattern Combinations
Web Application (Full Stack):
# 1. HPA for horizontal scaling
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-hpa
spec:
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
---
# 2. VPA for right-sizing (recommendations only)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: web-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: web
updatePolicy:
updateMode: "Off" # Recommendations only
---
# 3. PDB to protect during scale-down
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: web
---
# 4. Cluster Autoscaler (configured at cluster level)Queue Processor:
# KEDA for event-driven scaling
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: processor-scaler
spec:
scaleTargetRef:
name: processor
minReplicaCount: 0
maxReplicaCount: 50
triggers:
- type: rabbitmq
metadata:
queueName: tasks
queueLength: "10"
---
# VPA for right-sizing (Off mode)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: processor-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: processor
updatePolicy:
updateMode: "Off"Summary
Autoscaling Pattern Selection:
| Scenario | Pattern | Configuration |
|---|---|---|
| Traffic-driven web app | HPA | CPU/memory metrics, 2-10 replicas |
| Queue-based processing | KEDA | RabbitMQ/Kafka/SQS scaler, 0-50 replicas |
| Schedule-based workload | KEDA | Cron scaler, business hours |
| Unknown resource needs | VPA | Off mode, review recommendations |
| Single-instance DB | VPA | Initial/Recreate mode |
| Insufficient nodes | Cluster Autoscaler | Configured at cluster level |
Best Practices: 1. Start with HPA for stateless workloads 2. Use KEDA for event-driven scaling (better than HPA for queues) 3. Use VPA in Off mode first (review recommendations) 4. Don't combine VPA Auto + HPA on same metric 5. Set PodDisruptionBudgets to protect critical workloads 6. Enable Cluster Autoscaler with appropriate limits 7. Monitor autoscaling events and adjust thresholds 8. Test autoscaling behavior under load before production
Networking
Table of Contents
1. NetworkPolicies 2. Services 3. Ingress 4. Gateway API 5. Service Mesh Integration 6. DNS and Service Discovery
NetworkPolicies
Default Deny Pattern
Implement zero-trust networking with default-deny policies:
# Step 1: Deny all ingress and egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {} # Applies to all pods
policyTypes:
- Ingress
- Egress# Step 2: Allow specific traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-allow-frontend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080Ingress Policies
Allow from specific namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-frontend-ns
namespace: backend
spec:
podSelector:
matchLabels:
app: api
ingress:
- from:
- namespaceSelector:
matchLabels:
name: frontend
ports:
- protocol: TCP
port: 8080Allow from external IP range:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-external-lb
namespace: production
spec:
podSelector:
matchLabels:
app: web
ingress:
- from:
- ipBlock:
cidr: 10.0.0.0/16 # Load balancer CIDR
except:
- 10.0.5.0/24 # Exclude monitoring subnet
ports:
- protocol: TCP
port: 80Egress Policies
Allow DNS only:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
name: kube-system
- podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53Allow external API:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-external-api
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Egress
egress:
# Allow DNS
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: UDP
port: 53
# Allow external API
- to:
- ipBlock:
cidr: 203.0.113.0/24 # External API CIDR
ports:
- protocol: TCP
port: 443Allow all egress (common pattern):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-egress
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Egress
egress:
- {} # Allow all egressDatabase Access Pattern
# Backend → Database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: postgres-allow-backend
namespace: database
spec:
podSelector:
matchLabels:
app: postgres
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: backend
podSelector:
matchLabels:
tier: api
ports:
- protocol: TCP
port: 5432Multi-Tier Application Pattern
# Tier 1: Frontend → only from ingress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-policy
namespace: production
spec:
podSelector:
matchLabels:
tier: frontend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 80
egress:
- to:
- podSelector:
matchLabels:
tier: backend
ports:
- protocol: TCP
port: 8080
- to: # DNS
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: UDP
port: 53
---
# Tier 2: Backend → only from frontend
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-policy
namespace: production
spec:
podSelector:
matchLabels:
tier: backend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
tier: frontend
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
tier: database
ports:
- protocol: TCP
port: 5432
- to: # DNS
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: UDP
port: 53
---
# Tier 3: Database → only from backend
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database-policy
namespace: production
spec:
podSelector:
matchLabels:
tier: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
tier: backend
ports:
- protocol: TCP
port: 5432Testing NetworkPolicies
# Test connectivity from pod
kubectl run test --rm -it --image=busybox -- wget -O- http://backend:8080
# If connection refused:
# 1. Check NetworkPolicy exists
kubectl get networkpolicies -n production
# 2. Describe policy
kubectl describe networkpolicy backend-allow-frontend -n production
# 3. Check pod labels match selectors
kubectl get pods -n production --show-labels
# 4. Test DNS resolution
kubectl run test --rm -it --image=busybox -- nslookup backend
# 5. Check CNI plugin supports NetworkPolicies
kubectl get nodes -o wide
# (Calico, Cilium, Weave support NetworkPolicies; Flannel does not)Services
Service Types
ClusterIP (Default):
- Internal cluster access only
- Use for internal microservices
apiVersion: v1
kind: Service
metadata:
name: backend
spec:
type: ClusterIP
selector:
app: backend
ports:
- protocol: TCP
port: 80 # Service port
targetPort: 8080 # Container portNodePort:
- Expose on each node's IP at static port
- Use for testing or edge cases
apiVersion: v1
kind: Service
metadata:
name: nodeport-svc
spec:
type: NodePort
selector:
app: web
ports:
- protocol: TCP
port: 80
targetPort: 8080
nodePort: 30080 # Optional (auto-assigned if omitted)LoadBalancer:
- Provision cloud load balancer
- Use for external HTTP/gRPC services
apiVersion: v1
kind: Service
metadata:
name: web-lb
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: nlb # AWS NLB
spec:
type: LoadBalancer
selector:
app: web
ports:
- protocol: TCP
port: 80
targetPort: 8080ExternalName:
- DNS CNAME to external service
- Use for migrating to Kubernetes
apiVersion: v1
kind: Service
metadata:
name: legacy-db
spec:
type: ExternalName
externalName: db.example.comHeadless Services
For StatefulSets requiring stable network IDs:
apiVersion: v1
kind: Service
metadata:
name: postgres
spec:
clusterIP: None # Headless
selector:
app: postgres
ports:
- protocol: TCP
port: 5432DNS entries:
postgres-0.postgres.default.svc.cluster.localpostgres-1.postgres.default.svc.cluster.local
Session Affinity
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800 # 3 hours
ports:
- protocol: TCP
port: 80Service Topology
Route traffic to same zone/node for cost savings:
apiVersion: v1
kind: Service
metadata:
name: backend
spec:
selector:
app: backend
topologyKeys:
- "kubernetes.io/hostname" # Prefer same node
- "topology.kubernetes.io/zone" # Then same zone
- "*" # Then any node
ports:
- protocol: TCP
port: 8080Ingress
Basic Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: myapp.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /
pathType: Prefix
backend:
service:
name: frontend
port:
number: 80TLS/HTTPS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-ingress
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.example.com
secretName: myapp-tls # Created by cert-manager
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend
port:
number: 80Path Types
Prefix:
path: /api
pathType: Prefix
# Matches: /api, /api/, /api/v1, /api/v1/usersExact:
path: /api
pathType: Exact
# Matches: /api only (not /api/ or /api/v1)ImplementationSpecific:
path: /api
pathType: ImplementationSpecific
# Depends on ingress controllerCommon Annotations (Nginx)
metadata:
annotations:
# Rate limiting
nginx.ingress.kubernetes.io/limit-rps: "10"
# Timeouts
nginx.ingress.kubernetes.io/proxy-connect-timeout: "30"
nginx.ingress.kubernetes.io/proxy-send-timeout: "30"
nginx.ingress.kubernetes.io/proxy-read-timeout: "30"
# CORS
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://example.com"
# Authentication
nginx.ingress.kubernetes.io/auth-url: "https://auth.example.com"
nginx.ingress.kubernetes.io/auth-response-headers: "X-User-ID"
# Rewrite
nginx.ingress.kubernetes.io/rewrite-target: /$2
# Matches: /something(/|$)(.*)Gateway API
Gateway Resource
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production-gateway
spec:
gatewayClassName: nginx
listeners:
- name: http
protocol: HTTP
port: 80
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: myapp-tls
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
gateway: productionHTTPRoute
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-routes
namespace: production
spec:
parentRefs:
- name: production-gateway
namespace: default
hostnames:
- myapp.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: backend
port: 8080
weight: 100
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: frontend
port: 80Advanced Routing
Header-based routing:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: header-route
spec:
parentRefs:
- name: production-gateway
rules:
- matches:
- headers:
- name: X-Version
value: v2
backendRefs:
- name: backend-v2
port: 8080
- matches:
- headers:
- name: X-Version
value: v1
backendRefs:
- name: backend-v1
port: 8080Traffic splitting (canary):
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: canary-route
spec:
parentRefs:
- name: production-gateway
rules:
- backendRefs:
- name: backend-v2
port: 8080
weight: 10 # 10% traffic
- name: backend-v1
port: 8080
weight: 90 # 90% trafficQuery parameter routing:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: query-route
spec:
parentRefs:
- name: production-gateway
rules:
- matches:
- queryParams:
- name: debug
value: "true"
backendRefs:
- name: backend-debug
port: 8080Gateway vs. Ingress Comparison
| Feature | Ingress | Gateway API |
|---|---|---|
| Role separation | No | Yes (Gateway for ops, HTTPRoute for devs) |
| Multi-namespace | No | Yes |
| Header routing | Annotations | Native |
| Weight-based routing | Limited | Native |
| TCP/UDP routing | No | Yes (TCPRoute, UDPRoute) |
| Status | Mature | GA (v1 in 1.29+) |
Service Mesh Integration
Istio Integration
Enable sidecar injection:
kubectl label namespace production istio-injection=enabledVirtualService (L7 routing):
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: backend
spec:
hosts:
- backend
http:
- match:
- headers:
x-version:
exact: v2
route:
- destination:
host: backend
subset: v2
- route:
- destination:
host: backend
subset: v1DestinationRule (load balancing, circuit breaking):
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: backend
spec:
host: backend
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
connectionPool:
tcp:
maxConnections: 100
http:
http1MaxPendingRequests: 50
http2MaxRequests: 100
outlierDetection:
consecutiveErrors: 5
interval: 30s
baseEjectionTime: 30sLinkerd Integration
Inject proxy:
kubectl annotate namespace production linkerd.io/inject=enabledServiceProfile (retries, timeouts):
apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: backend.production.svc.cluster.local
namespace: production
spec:
routes:
- name: GET /api/users
condition:
method: GET
pathRegex: /api/users
responseClasses:
- condition:
status:
min: 500
max: 599
isFailure: true
retries:
maxAttempts: 3
timeout: 10sDNS and Service Discovery
DNS Names
Within same namespace:
backend→backend.production.svc.cluster.local
Cross-namespace:
backend.production→backend.production.svc.cluster.local
Full FQDN:
backend.production.svc.cluster.local
Custom DNS
apiVersion: v1
kind: Pod
metadata:
name: custom-dns
spec:
dnsPolicy: "None"
dnsConfig:
nameservers:
- 8.8.8.8
- 8.8.4.4
searches:
- production.svc.cluster.local
- svc.cluster.local
- cluster.local
options:
- name: ndots
value: "2"
containers:
- name: app
image: myapp:latestDNS Caching
# NodeLocal DNSCache (DaemonSet)
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-local-dns
namespace: kube-system
spec:
selector:
matchLabels:
k8s-app: node-local-dns
template:
spec:
containers:
- name: node-cache
image: registry.k8s.io/dns/k8s-dns-node-cache:1.22.20Summary
Networking Pattern Selection:
| Use Case | Pattern |
|---|---|
| Internal microservices | ClusterIP + NetworkPolicies |
| External HTTP/HTTPS | Ingress or Gateway API |
| TCP/UDP external | LoadBalancer Service |
| Service mesh (advanced routing) | Istio or Linkerd |
| Multi-tenant isolation | NetworkPolicies (default-deny) |
| Zero-trust security | NetworkPolicies + mTLS |
Best Practices: 1. Always implement NetworkPolicies (start with default-deny) 2. Use Gateway API for new applications 3. Enable TLS for external traffic (cert-manager) 4. Consider service mesh for advanced routing and mTLS 5. Test NetworkPolicies thoroughly before production 6. Monitor DNS resolution latency 7. Use headless services for StatefulSets
Resource Management
Table of Contents
1. Resource Requests and Limits 2. Quality of Service Classes 3. Resource Quotas 4. LimitRanges 5. Vertical Pod Autoscaler 6. Cost Optimization Patterns
Resource Requests and Limits
CPU Resources
CPU Units:
- 1 CPU = 1 vCPU/core on cloud provider
- 1000m (millicores) = 1 CPU
- 100m = 0.1 CPU (10% of one core)
CPU Requests:
- Minimum guaranteed CPU for scheduling
- Scheduler finds nodes with available CPU
- Pod can use more than request (up to limit)
CPU Limits:
- Maximum CPU pod can use
- Enforced by throttling (CFS quota)
- Pod slowed down, not killed
Example:
resources:
requests:
cpu: "500m" # Guaranteed 0.5 cores
limits:
cpu: "1" # Max 1 core (can burst 2x)Memory Resources
Memory Units:
- Ki/Mi/Gi (binary, 1024-based): 1Ki = 1024 bytes, 1Mi = 1048576 bytes
- k/M/G (decimal, 1000-based): 1k = 1000 bytes
- Use binary units (Ki/Mi/Gi) for consistency
Memory Requests:
- Minimum guaranteed memory for scheduling
- Pod evicted if exceeds request under node pressure
Memory Limits:
- Maximum memory pod can allocate
- Pod killed (OOMKilled) if exceeds limit
- No throttling like CPU
Example:
resources:
requests:
memory: "512Mi" # Guaranteed 512MB
limits:
memory: "1Gi" # Max 1GB (OOMKilled if exceeded)Compressible vs. Incompressible Resources
Compressible (CPU):
- Can be throttled without killing pod
- Degraded performance, but pod survives
- Safer to overcommit
Incompressible (Memory):
- Cannot be reclaimed once allocated
- Pod killed if exceeds limit or under pressure
- Must be carefully sized
Quality of Service Classes
Kubernetes assigns QoS classes automatically based on requests and limits:
Guaranteed QoS
Criteria:
- Every container has CPU and memory requests
- Every container has CPU and memory limits
- Requests equal limits for both CPU and memory
Behavior:
- Highest priority
- Never evicted unless exceeding limits
- Predictable performance
- Most expensive (can't overcommit)
Example:
apiVersion: v1
kind: Pod
metadata:
name: guaranteed-pod
spec:
containers:
- name: app
image: myapp:latest
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "512Mi" # Same as request
cpu: "500m" # Same as requestUse Cases:
- Critical production services (payments, auth)
- Databases requiring stable performance
- Real-time processing systems
- Compliance-required workloads
Burstable QoS
Criteria:
- At least one container has CPU or memory request
- Requests less than limits (or only requests set)
Behavior:
- Medium priority
- Can burst above requests (use spare capacity)
- Evicted under node pressure (after BestEffort)
- Cost-effective balance
Example:
apiVersion: v1
kind: Pod
metadata:
name: burstable-pod
spec:
containers:
- name: app
image: myapp:latest
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi" # 2x request (can burst)
cpu: "500m" # 2x requestUse Cases:
- Web servers (handle traffic spikes)
- API services (most common pattern)
- Batch jobs (use spare capacity)
- Non-critical background workers
BestEffort QoS
Criteria:
- No requests or limits set for any container
Behavior:
- Lowest priority
- First to be evicted under node pressure
- No resource guarantees
- Most cost-effective
Example:
apiVersion: v1
kind: Pod
metadata:
name: besteffort-pod
spec:
containers:
- name: app
image: myapp:latest
# No resources specifiedUse Cases:
- Development environments
- Testing workloads
- Experimental services
- Low-priority batch jobs
QoS Selection Decision Tree
START: Which QoS class for my workload?
Q1: Is this a critical production service?
├─ YES → Can you tolerate ANY performance variability?
│ ├─ YES → Burstable (cost-effective)
│ └─ NO → Guaranteed (predictable)
└─ NO → Is this production at all?
├─ YES → Burstable (default for most apps)
└─ NO → BestEffort (dev/test only)Resource Quotas
Namespace-Level Limits
ResourceQuotas enforce limits at the namespace level for multi-tenancy:
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-quota
namespace: team-alpha
spec:
hard:
# Compute resources
requests.cpu: "10" # Max 10 CPU cores requested
requests.memory: "20Gi" # Max 20GB memory requested
limits.cpu: "20" # Max 20 CPU cores limit
limits.memory: "40Gi" # Max 40GB memory limit
# Storage
persistentvolumeclaims: "10"
requests.storage: "100Gi"
# Object counts
pods: "50"
services: "10"
secrets: "20"
configmaps: "20"Quota Scopes
Apply quotas to specific pod classes:
apiVersion: v1
kind: ResourceQuota
metadata:
name: best-effort-quota
namespace: team-alpha
spec:
hard:
pods: "5" # Max 5 BestEffort pods
scopes:
- BestEffort
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: high-priority-quota
namespace: team-alpha
spec:
hard:
requests.cpu: "20"
requests.memory: "40Gi"
scopeSelector:
matchExpressions:
- operator: In
scopeName: PriorityClass
values: ["high"]Checking Quota Usage
# View quota details
kubectl describe resourcequota -n team-alpha
# Example output:
# Name: team-quota
# Namespace: team-alpha
# Resource Used Hard
# -------- ---- ----
# requests.cpu 8 10
# requests.memory 15Gi 20Gi
# pods 35 50LimitRanges
Default Constraints per Container
LimitRanges set defaults and boundaries for individual containers:
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: team-alpha
spec:
limits:
- max:
memory: "2Gi" # No container can exceed 2GB
cpu: "2" # No container can exceed 2 cores
min:
memory: "128Mi" # All containers must request at least 128MB
cpu: "100m" # All containers must request at least 0.1 cores
default:
memory: "512Mi" # Applied if no limit specified
cpu: "500m"
defaultRequest:
memory: "256Mi" # Applied if no request specified
cpu: "250m"
maxLimitRequestRatio:
memory: "2" # Limit can't exceed 2x request
cpu: "2"
type: ContainerLimitRange for PVCs
apiVersion: v1
kind: LimitRange
metadata:
name: storage-limits
namespace: team-alpha
spec:
limits:
- max:
storage: "50Gi"
min:
storage: "1Gi"
type: PersistentVolumeClaimLimitRange Behavior
When pod is created: 1. If no request set → apply defaultRequest 2. If no limit set → apply default 3. Validate against min and max 4. Validate maxLimitRequestRatio 5. Reject pod if violations found
Vertical Pod Autoscaler
VPA Modes
Off (Recommendations Only):
- Safest starting point
- VPA calculates recommendations
- No automatic changes
- View recommendations:
kubectl describe vpa
Initial:
- Apply recommendations only on pod creation
- Existing pods unchanged
- Useful for StatefulSets
Recreate:
- Update running pods (causes restart)
- Evicts and recreates pods with new resources
- Not suitable for single-replica workloads
Auto (Future):
- Update requests in-place without restart
- Not yet available in production
VPA Configuration
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: my-app-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Off" # Start with recommendations only
resourcePolicy:
containerPolicies:
- containerName: app
minAllowed:
cpu: "100m"
memory: "128Mi"
maxAllowed:
cpu: "2"
memory: "2Gi"
controlledResources: ["cpu", "memory"]
mode: "Auto" # Per-container overrideVPA Recommendations
View VPA recommendations:
kubectl describe vpa my-app-vpa
# Output includes:
# Recommendation:
# Container Recommendations:
# Container Name: app
# Lower Bound: (minimum for cost efficiency)
# Cpu: 250m
# Memory: 256Mi
# Target: (recommended values)
# Cpu: 500m
# Memory: 512Mi
# Upper Bound: (maximum safe values)
# Cpu: 1
# Memory: 1GiVPA Best Practices
When to Use VPA:
- ✅ Stateless applications with predictable patterns
- ✅ Long-running services (collect 7+ days of metrics)
- ✅ Applications where restarts are acceptable
- ✅ Unknown resource requirements (let VPA learn)
When to Avoid VPA:
- ❌ Running with HPA on same metric (conflicts)
- ❌ Stateful apps requiring stable resources
- ❌ Single-replica critical workloads
- ❌ Short-lived jobs (insufficient data)
VPA + HPA Compatibility:
- ✅ VPA on CPU/memory + HPA on custom metric
- ✅ VPA off mode (recommendations) + HPA on any metric
- ❌ VPA auto mode + HPA on same metric (CPU/memory)
VPA Installation
# Install VPA (Helm)
helm repo add fairwinds-stable https://charts.fairwinds.com/stable
helm install vpa fairwinds-stable/vpa --namespace vpa --create-namespace
# Or via manifest
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler
./hack/vpa-up.shCost Optimization Patterns
Right-Sizing Workflow
Phase 1: Baseline (Week 1) 1. Deploy with conservative requests (overestimate) 2. Set higher limits (allow bursting) 3. Monitor actual usage
# Initial deployment (conservative)
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "2"
memory: "2Gi"Phase 2: Observation (Week 2-4) 1. Deploy VPA in "Off" mode 2. Collect 2-4 weeks of usage data 3. Review VPA recommendations 4. Check Prometheus/metrics for actual usage
# View actual usage
kubectl top pods -n production
# View VPA recommendations
kubectl describe vpa my-app-vpaPhase 3: Optimization (Week 5+) 1. Apply VPA recommendations 2. Monitor for OOMKilled or CPU throttling 3. Adjust based on real behavior 4. Iterate quarterly
# Optimized resources (based on VPA)
resources:
requests:
cpu: "250m" # Reduced from 500m
memory: "256Mi" # Reduced from 512Mi
limits:
cpu: "500m" # Tighter than initial 2
memory: "512Mi" # Tighter than initial 2GiCost Monitoring
# Audit pods without resource limits
kubectl get pods --all-namespaces -o json | \
jq -r '.items[] | select(.spec.containers[].resources.limits == null) | .metadata.name'
# Calculate namespace cost (requests)
kubectl get pods -n production -o json | \
jq -r '.items[].spec.containers[] |
(.resources.requests.cpu // "0") + " " +
(.resources.requests.memory // "0")'Cost Allocation Tags
Label resources for cost allocation:
apiVersion: v1
kind: Pod
metadata:
name: my-app
labels:
app: my-app
team: backend # Cost center
env: production # Environment
cost-center: "12345" # Finance tracking
spec:
containers:
- name: app
image: myapp:latestSavings Estimates
Typical Cost Reductions:
- Overprovisioned CPU: 40-60% reduction
- Overprovisioned Memory: 30-50% reduction
- BestEffort → Burstable: No cost change (same requests)
- Burstable → Guaranteed: Cost increase (tighter limits)
Example Calculation:
- Before: 100 pods × 2 CPU × 4 GB = 200 cores, 400GB
- After VPA: 100 pods × 0.5 CPU × 2 GB = 50 cores, 200GB
- Savings: 75% CPU, 50% memory = ~60% total cost
Resource Waste Detection
# Python script: detect overprovisioned pods
from kubernetes import client, config
config.load_kube_config()
metrics = client.CustomObjectsApi()
v1 = client.CoreV1Api()
# Get pod metrics
metrics_data = metrics.list_cluster_custom_object(
"metrics.k8s.io", "v1beta1", "pods"
)
for pod in metrics_data["items"]:
for container in pod["containers"]:
usage_cpu = container["usage"]["cpu"]
usage_mem = container["usage"]["memory"]
# Compare to requests
pod_obj = v1.read_namespaced_pod(
pod["metadata"]["name"],
pod["metadata"]["namespace"]
)
for c in pod_obj.spec.containers:
if c.resources.requests:
req_cpu = c.resources.requests.get("cpu")
req_mem = c.resources.requests.get("memory")
# Flag if usage < 20% of request
# (potential waste)Monitoring and Alerts
Key Metrics to Track
Per-Pod:
container_cpu_usage_seconds_total- Actual CPU usagecontainer_memory_working_set_bytes- Actual memory usagekube_pod_container_resource_requests- Configured requestskube_pod_container_resource_limits- Configured limits
Per-Node:
kube_node_status_allocatable- Available resourceskube_node_status_capacity- Total capacity- Node CPU/memory utilization
Prometheus Queries
# CPU throttling (pods hitting limits)
rate(container_cpu_cfs_throttled_seconds_total[5m]) > 0.1
# Memory usage vs. limit
container_memory_working_set_bytes / container_spec_memory_limit_bytes > 0.9
# Pods without requests
kube_pod_container_resource_requests{resource="memory"} == 0
# Node CPU pressure
(1 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m]))) > 0.8Recommended Alerts
# Alert: Pod OOMKilled
- alert: PodOOMKilled
expr: increase(kube_pod_container_status_terminated_reason{reason="OOMKilled"}[5m]) > 0
annotations:
summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} was OOMKilled"
# Alert: High CPU throttling
- alert: HighCPUThrottling
expr: rate(container_cpu_cfs_throttled_seconds_total[5m]) > 0.2
annotations:
summary: "Pod {{ $labels.pod }} is CPU throttled >20%"
# Alert: No resource limits
- alert: MissingResourceLimits
expr: kube_pod_container_resource_limits{resource="memory"} == 0
annotations:
summary: "Pod {{ $labels.pod }} has no memory limit"Summary
Key Takeaways: 1. Always set requests and limits to enable QoS 2. Use Guaranteed QoS for critical services 3. Use Burstable QoS for most applications 4. Implement ResourceQuotas for multi-tenancy 5. Use VPA for automated rightsizing 6. Monitor actual usage vs. requests 7. Iterate on resource sizing quarterly 8. Label resources for cost allocation
Scheduling Patterns
Table of Contents
1. Node Affinity 2. Taints and Tolerations 3. Topology Spread Constraints 4. Pod Priority and Preemption 5. Pod Affinity and Anti-Affinity 6. Scheduling Decision Framework
Node Affinity
Required Node Affinity (Hard Constraint)
Pod will not schedule unless node matches criteria:
apiVersion: v1
kind: Pod
metadata:
name: gpu-workload
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node.kubernetes.io/instance-type
operator: In
values:
- g4dn.xlarge # AWS GPU instance
- g4dn.2xlarge
- key: topology.kubernetes.io/zone
operator: In
values:
- us-west-2a
- us-west-2b
containers:
- name: ml-training
image: pytorch:latest
resources:
limits:
nvidia.com/gpu: 1Preferred Node Affinity (Soft Constraint)
Scheduler tries to match but will schedule anyway if no match:
apiVersion: v1
kind: Pod
metadata:
name: cache-service
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100 # Highest preference
preference:
matchExpressions:
- key: node.kubernetes.io/instance-type
operator: In
values:
- r5.xlarge # Prefer memory-optimized
- weight: 50 # Lower preference
preference:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- us-west-2a # Prefer this zone (cost savings)
containers:
- name: redis
image: redis:7Node Affinity Operators
| Operator | Behavior |
|---|---|
In | Label value in list |
NotIn | Label value not in list |
Exists | Label key exists (any value) |
DoesNotExist | Label key doesn't exist |
Gt | Label value greater than (numeric) |
Lt | Label value less than (numeric) |
Common Node Labels
# Built-in labels
kubernetes.io/hostname=node-1
kubernetes.io/os=linux
kubernetes.io/arch=amd64
node.kubernetes.io/instance-type=m5.xlarge
topology.kubernetes.io/zone=us-west-2a
topology.kubernetes.io/region=us-west-2
# Custom labels
kubectl label nodes node-1 workload=gpu
kubectl label nodes node-2 storage=ssd
kubectl label nodes node-3 team=backendUse Cases
GPU Scheduling:
# Require GPU nodes
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: accelerator
operator: In
values:
- nvidia-tesla-v100SSD Storage:
# Prefer SSD nodes for databases
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: storage
operator: In
values:
- ssdCost Optimization:
# Prefer spot instances (non-critical workloads)
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: node.kubernetes.io/instance-lifecycle
operator: In
values:
- spotTaints and Tolerations
Taint Effects
NoSchedule:
- New pods won't schedule
- Existing pods remain
PreferNoSchedule:
- Scheduler tries to avoid
- Will schedule if no other options
NoExecute:
- Evict existing pods immediately
- New pods won't schedule
Applying Taints
# Taint GPU nodes
kubectl taint nodes gpu-node-1 workload=gpu:NoSchedule
# Taint spot instances (may be terminated)
kubectl taint nodes spot-node-1 instance-type=spot:NoSchedule
# Taint nodes for maintenance
kubectl taint nodes node-1 maintenance=true:NoExecute
# Remove taint
kubectl taint nodes node-1 maintenance-Pod Tolerations
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
tolerations:
- key: "workload"
operator: "Equal"
value: "gpu"
effect: "NoSchedule"
- key: "instance-type"
operator: "Equal"
value: "spot"
effect: "NoSchedule"
containers:
- name: ml-training
image: pytorch:latestToleration Operators
Equal:
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"Exists:
# Tolerate any value for key1
tolerations:
- key: "key1"
operator: "Exists"
effect: "NoSchedule"Wildcard:
# Tolerate all taints
tolerations:
- operator: "Exists"Toleration with Timeout
apiVersion: v1
kind: Pod
metadata:
name: fault-tolerant-app
spec:
tolerations:
- key: "node.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 300 # Wait 5min before eviction
- key: "node.kubernetes.io/not-ready"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 300
containers:
- name: app
image: myapp:latestCommon Taint Use Cases
Dedicated Nodes for Team:
# Taint nodes for team-alpha only
kubectl taint nodes node-1 node-2 node-3 team=alpha:NoSchedule
# Team-alpha pods tolerate
tolerations:
- key: "team"
operator: "Equal"
value: "alpha"
effect: "NoSchedule"Isolate System Workloads:
# Taint nodes for system components only
kubectl taint nodes master-1 node-role.kubernetes.io/master:NoScheduleHandle Hardware Failures:
# Taint nodes with disk issues
kubectl taint nodes node-1 disk=failing:NoExecuteTopology Spread Constraints
Even Distribution Across Zones
apiVersion: apps/v1
kind: Deployment
metadata:
name: critical-app
spec:
replicas: 9
selector:
matchLabels:
app: critical-app
template:
metadata:
labels:
app: critical-app
spec:
topologySpreadConstraints:
- maxSkew: 1 # Max difference in pod count
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule # Hard constraint
labelSelector:
matchLabels:
app: critical-app
containers:
- name: app
image: myapp:latestResult: 9 replicas spread evenly across 3 zones (3 per zone)
Multi-Level Spreading
topologySpreadConstraints:
# Level 1: Spread across zones (hard)
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: critical-app
# Level 2: Spread across nodes within zones (soft)
- maxSkew: 2
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: critical-appmaxSkew Behavior
maxSkew: 1 (Strict)
- Zone A: 3 pods
- Zone B: 3 pods
- Zone C: 3 pods
- Cannot add pod to Zone A until B and C catch up
maxSkew: 2 (Relaxed)
- Zone A: 4 pods
- Zone B: 3 pods
- Zone C: 2 pods
- Max difference is 2 (4-2=2)
whenUnsatisfiable
DoNotSchedule (Hard):
- Pod remains Pending if constraint violated
- Strict high-availability requirement
ScheduleAnyway (Soft):
- Scheduler tries to satisfy but schedules anyway
- Preference, not requirement
minDomains
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: critical-app
minDomains: 3 # Require at least 3 zonesTopology Spread vs. Pod Anti-Affinity
Topology Spread (Modern):
- More intuitive (maxSkew semantics)
- Better control over distribution
- Recommended for new applications
Pod Anti-Affinity (Legacy):
- More complex configuration
- Less flexible
- Still widely used
Migration Example:
# Old: Pod anti-affinity
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: myapp
topologyKey: kubernetes.io/hostname
# New: Topology spread
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: myappPod Priority and Preemption
Priority Classes
# High priority for critical services
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "Critical production services"
---
# Medium priority (default)
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: medium-priority
value: 500000
globalDefault: true
description: "Standard production workloads"
---
# Low priority for batch jobs
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: low-priority
value: 100000
globalDefault: false
description: "Batch jobs and background tasks"Using Priority Classes
apiVersion: v1
kind: Pod
metadata:
name: critical-api
spec:
priorityClassName: high-priority
containers:
- name: api
image: api:latestPreemption Behavior
When node resources exhausted: 1. Scheduler tries to find node for high-priority pod 2. If no node available, scheduler looks for preemption candidates 3. Lower-priority pods evicted to make room 4. High-priority pod scheduled
Preemption Example:
- Node capacity: 4 CPU cores
- Running: 2 medium-priority pods (2 cores each)
- Pending: 1 high-priority pod (2 cores)
- Result: 1 medium-priority pod evicted, high-priority scheduled
PodDisruptionBudget (Protect from Preemption)
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-pdb
spec:
minAvailable: 2 # Keep at least 2 replicas running
selector:
matchLabels:
app: apiAlternative (percentage):
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-pdb
spec:
maxUnavailable: 1 # Max 1 pod disrupted at a time
selector:
matchLabels:
app: apiPriority Use Cases
Critical Services:
# Payment API - highest priority
priorityClassName: high-priority
value: 1000000Standard Applications:
# Web frontend - medium priority
priorityClassName: medium-priority
value: 500000Batch Jobs:
# ETL jobs - low priority (preemptible)
priorityClassName: low-priority
value: 100000Development:
# Dev environments - lowest priority
priorityClassName: dev-priority
value: 0Pod Affinity and Anti-Affinity
Pod Affinity (Co-locate Pods)
Schedule pods on same node or zone as other pods:
apiVersion: v1
kind: Pod
metadata:
name: web-frontend
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: cache
topologyKey: kubernetes.io/hostname # Same node
containers:
- name: frontend
image: frontend:latestUse Case: Co-locate frontend with cache for low latency
Pod Anti-Affinity (Spread Pods)
Schedule pods away from other pods:
apiVersion: v1
kind: Pod
metadata:
name: api-pod
labels:
app: api
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: api
topologyKey: kubernetes.io/hostname # Different nodes
containers:
- name: api
image: api:latestUse Case: Spread API replicas across nodes for high availability
Preferred Anti-Affinity
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 5
template:
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: web-app
topologyKey: topology.kubernetes.io/zone # Prefer different zones
- weight: 50
podAffinityTerm:
labelSelector:
matchLabels:
app: web-app
topologyKey: kubernetes.io/hostname # Then different nodesScheduling Decision Framework
Decision Tree
START: How should I schedule this workload?
Q1: Does it need special hardware?
├─ YES → Use Node Affinity (GPU, SSD, etc.)
└─ NO → Q2
Q2: Should it be isolated from other workloads?
├─ YES → Use Taints + Tolerations
└─ NO → Q3
Q3: Does it need high availability?
├─ YES → Use Topology Spread Constraints
└─ NO → Q4
Q4: Is it more critical than other workloads?
├─ YES → Use Priority Class
└─ NO → Default schedulingPattern Combinations
High-Availability API:
spec:
# 1. Topology spread (even distribution)
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: api
# 2. High priority
priorityClassName: high-priority
# 3. PodDisruptionBudget
# (separate resource)ML Training Job:
spec:
# 1. Node affinity (GPU nodes)
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: accelerator
operator: In
values:
- nvidia-tesla-v100
# 2. Toleration (GPU taints)
tolerations:
- key: workload
operator: Equal
value: gpu
effect: NoSchedule
# 3. Low priority (preemptible)
priorityClassName: low-priorityCost-Optimized Batch Job:
spec:
# 1. Prefer spot instances
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: node.kubernetes.io/instance-lifecycle
operator: In
values:
- spot
# 2. Tolerate spot interruptions
tolerations:
- key: instance-type
operator: Equal
value: spot
effect: NoSchedule
# 3. Lowest priority
priorityClassName: batch-prioritySummary
Scheduling Pattern Selection:
| Use Case | Pattern | Example |
|---|---|---|
| GPU workloads | Node Affinity (required) | ML training, video encoding |
| Isolated workloads | Taints + Tolerations | GPU nodes, spot instances |
| High availability | Topology Spread | Critical APIs, databases |
| Cost optimization | Node Affinity (preferred) | Spot instances, cheaper zones |
| Critical services | Priority Class | Payment API, auth services |
| Low latency | Pod Affinity | Frontend + cache co-location |
| Fault tolerance | Pod Anti-Affinity | Spread replicas |
Best Practices: 1. Use topology spread over pod anti-affinity 2. Combine node affinity with taints for isolation 3. Set PodDisruptionBudgets with priority classes 4. Test scheduling constraints in dev/staging 5. Monitor pod Pending events 6. Use preferred constraints when possible (more flexible)
Security
Table of Contents
1. RBAC (Role-Based Access Control) 2. Pod Security Standards 3. Policy Enforcement 4. Secrets Management 5. Image Security 6. Network Security
RBAC (Role-Based Access Control)
Roles and ClusterRoles
Role (Namespace-scoped):
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: production
rules:
- apiGroups: [""] # Core API group
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list"]ClusterRole (Cluster-wide):
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: node-admin
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]RoleBindings and ClusterRoleBindings
RoleBinding (Namespace-scoped):
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: production
subjects:
- kind: User
name: jane@example.com
apiGroup: rbac.authorization.k8s.io
- kind: Group
name: developers
apiGroup: rbac.authorization.k8s.io
- kind: ServiceAccount
name: app-sa
namespace: production
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.ioClusterRoleBinding (Cluster-wide):
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: node-admins
subjects:
- kind: Group
name: sre-team
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: node-admin
apiGroup: rbac.authorization.k8s.ioServiceAccounts
Create ServiceAccount:
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: production
automountServiceAccountToken: false # Don't auto-mount unless neededUse in Pod:
apiVersion: v1
kind: Pod
metadata:
name: my-app
namespace: production
spec:
serviceAccountName: my-app-sa
containers:
- name: app
image: myapp:latestGrant Permissions:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: configmap-reader
namespace: production
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: app-configmap-access
namespace: production
subjects:
- kind: ServiceAccount
name: my-app-sa
namespace: production
roleRef:
kind: Role
name: configmap-reader
apiGroup: rbac.authorization.k8s.ioCommon RBAC Patterns
Read-Only Access:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: readonly
namespace: production
rules:
- apiGroups: ["", "apps", "batch"]
resources: ["*"]
verbs: ["get", "list", "watch"]Deployment Manager:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployment-manager
namespace: production
rules:
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]Debug Access (Exec into Pods):
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: debug-access
namespace: production
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create"]RBAC Best Practices
Least Privilege:
# Bad: Too permissive
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
# Good: Specific permissions
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
resourceNames: ["app-config"] # Further restrict to specific resourceAvoid Default ServiceAccount:
# Bad: Using default ServiceAccount
spec:
serviceAccountName: default
# Good: Dedicated ServiceAccount
spec:
serviceAccountName: my-app-saAudit RBAC:
# Check user permissions
kubectl auth can-i create deployments --namespace production --as jane@example.com
# List all RoleBindings
kubectl get rolebindings -A
# Audit who can delete pods
kubectl get rolebindings,clusterrolebindings -A -o json | \
jq -r '.items[] | select(.roleRef.name=="cluster-admin")'Pod Security Standards
Namespace Enforcement
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restrictedEnforcement Modes:
- enforce: Block pod creation if violates standard
- audit: Log violations but allow
- warn: Show warning but allow
Security Levels:
- privileged: Unrestricted (system workloads only)
- baseline: Minimally restrictive (prevents known escalations)
- restricted: Most secure (removes all privilege escalations)
Restricted Pod Configuration
apiVersion: v1
kind: Pod
metadata:
name: secure-app
namespace: production
spec:
# Pod-level security context
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:latest
# Container-level security context
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
# Writable directories
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /app/cache
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}Security Context Options
runAsNonRoot:
securityContext:
runAsNonRoot: true # Prevent root userreadOnlyRootFilesystem:
securityContext:
readOnlyRootFilesystem: true # Immutable filesystemCapabilities:
securityContext:
capabilities:
drop:
- ALL # Drop all capabilities
add:
- NET_BIND_SERVICE # Add only specific capabilityseccomp Profile:
securityContext:
seccompProfile:
type: RuntimeDefault # Apply default seccomp profileAppArmor:
metadata:
annotations:
container.apparmor.security.beta.kubernetes.io/app: runtime/defaultPod Security Standard Migration
# Audit current violations
kubectl label namespace production \
pod-security.kubernetes.io/audit=restricted \
pod-security.kubernetes.io/warn=restricted
# Review warnings
kubectl get pods -n production
# Fix violations, then enforce
kubectl label namespace production \
pod-security.kubernetes.io/enforce=restrictedPolicy Enforcement
Kyverno Policies
Installation:
helm repo add kyverno https://kyverno.github.io/kyverno/
helm install kyverno kyverno/kyverno --namespace kyverno --create-namespaceRequire Resource Limits:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-resources
spec:
validationFailureAction: enforce
background: true
rules:
- name: check-resources
match:
any:
- resources:
kinds:
- Pod
validate:
message: "CPU and memory resource requests and limits are required"
pattern:
spec:
containers:
- resources:
requests:
memory: "?*"
cpu: "?*"
limits:
memory: "?*"
cpu: "?*"Block Latest Tag:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-latest-tag
spec:
validationFailureAction: enforce
rules:
- name: require-image-tag
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Using 'latest' image tag is not allowed"
pattern:
spec:
containers:
- image: "!*:latest"Require Labels:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-labels
spec:
validationFailureAction: enforce
rules:
- name: check-labels
match:
any:
- resources:
kinds:
- Deployment
validate:
message: "Deployments must have 'app' and 'team' labels"
pattern:
metadata:
labels:
app: "?*"
team: "?*"Mutate (Add Default Resources):
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-default-resources
spec:
background: true
rules:
- name: add-resources
match:
any:
- resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
spec:
containers:
- (name): "*"
resources:
requests:
+(memory): "256Mi"
+(cpu): "250m"
limits:
+(memory): "512Mi"
+(cpu): "500m"OPA Gatekeeper
Installation:
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/deploy/gatekeeper.yamlConstraint Template:
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
spec:
crd:
spec:
names:
kind: K8sRequiredLabels
validation:
openAPIV3Schema:
type: object
properties:
labels:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredlabels
violation[{"msg": msg}] {
provided := {label | input.review.object.metadata.labels[label]}
required := {label | label := input.parameters.labels[_]}
missing := required - provided
count(missing) > 0
msg := sprintf("Missing required labels: %v", [missing])
}Constraint:
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: require-app-team-labels
spec:
match:
kinds:
- apiGroups: ["apps"]
kinds: ["Deployment"]
parameters:
labels: ["app", "team"]Secrets Management
Kubernetes Secrets
Create Secret:
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=securepassword \
--namespace production# YAML definition
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: production
type: Opaque
stringData:
username: admin
password: securepasswordUse in Pod:
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: myapp:latest
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: passwordMount as Volume:
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: secrets
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secrets
secret:
secretName: db-credentialsEncryption at Rest
Enable encryption (kube-apiserver flag):
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {}External Secrets Operator
Installation:
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets \
--namespace external-secrets-system --create-namespaceSecretStore (AWS Secrets Manager):
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-secrets
namespace: production
spec:
provider:
aws:
service: SecretsManager
region: us-east-1
auth:
jwt:
serviceAccountRef:
name: external-secretsExternalSecret:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets
kind: SecretStore
target:
name: db-secret
creationPolicy: Owner
data:
- secretKey: password
remoteRef:
key: production/postgres/passwordImage Security
Image Scanning
Trivy (Open Source):
# Scan image
trivy image myapp:latest
# Scan for HIGH/CRITICAL only
trivy image --severity HIGH,CRITICAL myapp:latest
# Output JSON
trivy image --format json -o results.json myapp:latestAdmission Controller (Scan on Deploy):
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: scan-images
spec:
validationFailureAction: enforce
webhookTimeoutSeconds: 30
rules:
- name: scan-image
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Image has HIGH/CRITICAL vulnerabilities"
foreach:
- list: "request.object.spec.containers"
deny:
conditions:
any:
- key: "{{ scan('{{ element.image }}').critical }}"
operator: GreaterThan
value: 0Image Pull Secrets
# Create registry secret
kubectl create secret docker-registry regcred \
--docker-server=myregistry.azurecr.io \
--docker-username=myuser \
--docker-password=mypassword \
--docker-email=myemail@example.com \
--namespace production# Use in Pod
spec:
imagePullSecrets:
- name: regcred
containers:
- name: app
image: myregistry.azurecr.io/myapp:v1.0Image Signing (Sigstore/Cosign)
# Sign image
cosign sign --key cosign.key myregistry.io/myapp:v1.0
# Verify signature
cosign verify --key cosign.pub myregistry.io/myapp:v1.0Kyverno Verification:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signature
spec:
validationFailureAction: enforce
rules:
- name: verify-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "myregistry.io/*"
attestors:
- entries:
- keys:
publicKeys: |-
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----Network Security
TLS for In-Cluster Communication
cert-manager Installation:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yamlClusterIssuer:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginxCertificate:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: myapp-tls
namespace: production
spec:
secretName: myapp-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- myapp.example.comSummary
Security Checklist:
- [ ] Enable RBAC and follow least-privilege principle
- [ ] Create dedicated ServiceAccounts (don't use default)
- [ ] Enforce Pod Security Standards (Restricted for apps)
- [ ] Implement policy enforcement (Kyverno or OPA)
- [ ] Use External Secrets Operator for sensitive data
- [ ] Enable encryption at rest for Secrets
- [ ] Scan images for vulnerabilities
- [ ] Use image pull secrets for private registries
- [ ] Implement NetworkPolicies (default-deny)
- [ ] Enable TLS for ingress traffic (cert-manager)
- [ ] Regularly audit RBAC permissions
- [ ] Monitor security events and policy violations
Recommended Tools:
- RBAC: Built-in Kubernetes
- Pod Security: Pod Security Standards (built-in)
- Policy: Kyverno (easier) or OPA Gatekeeper (more powerful)
- Secrets: External Secrets Operator + cloud provider
- Image Scanning: Trivy
- TLS: cert-manager
- Audit: Falco, kube-bench
#!/bin/bash
# Audit NetworkPolicies Script
# Find namespaces without NetworkPolicies (security risk)
set -euo pipefail
echo "=== NetworkPolicy Audit ==="
echo
# Get all namespaces (exclude kube-system and kube-public)
NAMESPACES=$(kubectl get namespaces -o json | jq -r '
.items[] |
select(.metadata.name | test("^kube-") | not) |
.metadata.name
')
MISSING_POLICIES=()
HAS_POLICIES=()
echo "Checking namespaces for NetworkPolicies..."
echo
for ns in $NAMESPACES; do
POLICY_COUNT=$(kubectl get networkpolicies -n "$ns" --no-headers 2>/dev/null | wc -l | tr -d ' ')
if [ "$POLICY_COUNT" -eq 0 ]; then
MISSING_POLICIES+=("$ns")
echo " $ns: NO POLICIES (RISK)"
else
HAS_POLICIES+=("$ns")
echo " $ns: $POLICY_COUNT policies"
fi
done
echo
echo "=== Summary ==="
echo "Total namespaces checked: ${#MISSING_POLICIES[@]} + ${#HAS_POLICIES[@]}"
echo "Namespaces WITH policies: ${#HAS_POLICIES[@]}"
echo "Namespaces WITHOUT policies: ${#MISSING_POLICIES[@]}"
if [ ${#MISSING_POLICIES[@]} -gt 0 ]; then
echo
echo "WARNING: These namespaces have NO NetworkPolicies:"
for ns in "${MISSING_POLICIES[@]}"; do
echo " - $ns"
done
echo
echo "Recommendation: Implement default-deny NetworkPolicies"
exit 1
else
echo
echo "PASS: All namespaces have NetworkPolicies"
exit 0
fi
#!/bin/bash
# Cost Analysis Script
# Calculate resource costs by namespace
set -euo pipefail
# Cost per resource (example pricing)
COST_PER_CPU_HOUR=0.03 # $0.03 per CPU core per hour
COST_PER_GB_HOUR=0.004 # $0.004 per GB memory per hour
echo "=== Kubernetes Resource Cost Analysis ==="
echo
# Get all pods
PODS=$(kubectl get pods -A -o json)
# Calculate costs by namespace
NAMESPACES=$(echo "$PODS" | jq -r '.items[].metadata.namespace' | sort -u)
printf "%-30s %10s %12s %14s %12s\n" "Namespace" "CPU Cores" "Memory (GB)" "Cost/Hour" "Cost/Month"
echo "--------------------------------------------------------------------------------"
TOTAL_CPU=0
TOTAL_MEMORY=0
for ns in $NAMESPACES; do
# Calculate CPU requests (convert 'm' to cores)
CPU=$(echo "$PODS" | jq -r --arg ns "$ns" '
.items[] |
select(.metadata.namespace == $ns) |
.spec.containers[].resources.requests.cpu // "0" |
if endswith("m") then
(.[:-1] | tonumber) / 1000
else
tonumber
end
' | awk '{sum += $1} END {print sum}')
# Calculate memory requests (convert to GB)
MEMORY=$(echo "$PODS" | jq -r --arg ns "$ns" '
.items[] |
select(.metadata.namespace == $ns) |
.spec.containers[].resources.requests.memory // "0" |
if endswith("Mi") then
(.[:-2] | tonumber) / 1024
elif endswith("Gi") then
.[:-2] | tonumber
else
0
end
' | awk '{sum += $1} END {print sum}')
# Calculate costs
COST_HOUR=$(echo "$CPU $MEMORY" | awk -v cpu_cost=$COST_PER_CPU_HOUR -v mem_cost=$COST_PER_GB_HOUR '{
print ($1 * cpu_cost) + ($2 * mem_cost)
}')
COST_MONTH=$(echo "$COST_HOUR" | awk '{print $1 * 24 * 30}')
# Print row
printf "%-30s %10.2f %12.2f \$%11.2f \$%10.2f\n" \
"$ns" "$CPU" "$MEMORY" "$COST_HOUR" "$COST_MONTH"
TOTAL_CPU=$(echo "$TOTAL_CPU $CPU" | awk '{print $1 + $2}')
TOTAL_MEMORY=$(echo "$TOTAL_MEMORY $MEMORY" | awk '{print $1 + $2}')
done
# Totals
TOTAL_COST_HOUR=$(echo "$TOTAL_CPU $TOTAL_MEMORY" | awk -v cpu_cost=$COST_PER_CPU_HOUR -v mem_cost=$COST_PER_GB_HOUR '{
print ($1 * cpu_cost) + ($2 * mem_cost)
}')
TOTAL_COST_MONTH=$(echo "$TOTAL_COST_HOUR" | awk '{print $1 * 24 * 30}')
echo "--------------------------------------------------------------------------------"
printf "%-30s %10.2f %12.2f \$%11.2f \$%10.2f\n" \
"TOTAL" "$TOTAL_CPU" "$TOTAL_MEMORY" "$TOTAL_COST_HOUR" "$TOTAL_COST_MONTH"
echo
echo "* Costs based on: CPU \$$COST_PER_CPU_HOUR/core/hr, Memory \$$COST_PER_GB_HOUR/GB/hr"
echo "* Actual cloud costs may vary based on instance types and commitments"
#!/bin/bash
# Validate Resources Script
# Check that all pods have resource requests and limits set
set -euo pipefail
NAMESPACE="${1:-}"
if [ -z "$NAMESPACE" ]; then
echo "Usage: $0 <namespace>"
echo "Example: $0 production"
exit 1
fi
echo "=== Validating Resources in Namespace: $NAMESPACE ==="
echo
# Get all pods in namespace
PODS=$(kubectl get pods -n "$NAMESPACE" -o json)
# Check for missing requests
echo "Checking for missing resource requests..."
MISSING_REQUESTS=$(echo "$PODS" | jq -r '
.items[] |
select(.spec.containers[]? | .resources.requests == null) |
.metadata.name
')
if [ -n "$MISSING_REQUESTS" ]; then
echo "ERROR: Pods without resource requests:"
echo "$MISSING_REQUESTS" | while read -r pod; do
echo " - $pod"
done
echo
else
echo "PASS: All pods have resource requests"
echo
fi
# Check for missing limits
echo "Checking for missing resource limits..."
MISSING_LIMITS=$(echo "$PODS" | jq -r '
.items[] |
select(.spec.containers[]? | .resources.limits == null) |
.metadata.name
')
if [ -n "$MISSING_LIMITS" ]; then
echo "WARNING: Pods without resource limits:"
echo "$MISSING_LIMITS" | while read -r pod; do
echo " - $pod"
done
echo
else
echo "PASS: All pods have resource limits"
echo
fi
# Summary
TOTAL_PODS=$(echo "$PODS" | jq '.items | length')
echo "=== Summary ==="
echo "Total pods checked: $TOTAL_PODS"
if [ -z "$MISSING_REQUESTS" ] && [ -z "$MISSING_LIMITS" ]; then
echo "Status: ALL CHECKS PASSED"
exit 0
elif [ -n "$MISSING_REQUESTS" ]; then
echo "Status: FAILED (missing requests)"
exit 1
else
echo "Status: WARNINGS (missing limits)"
exit 0
fi
Related skills
FAQ
Which QoS class for a critical database?
Guaranteed, where requests equal limits for CPU and memory, so the pod is never evicted unless it exceeds its limits.
How do you implement zero-trust networking?
Apply a default-deny-all NetworkPolicy, then add explicit allow policies for required pod-to-pod traffic.