Introduction
Kubernetes has become the de facto standard for container orchestration, powering everything from small startups to Fortune 500 companies. However, running Kubernetes in production requires more than just deploying applications. This comprehensive guide covers essential best practices that ensure your Kubernetes clusters are secure, reliable, scalable, and maintainable.
Resource Management
Setting Resource Requests and Limits
One of the most critical yet often overlooked aspects of Kubernetes deployments is proper resource management.
Always Set Resource Requests:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 3
template:
spec:
containers:
- name: api
image: myapp:v1.0.0
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
Best Practices:
- Requests: What the container is guaranteed to get
- Limits: Maximum resources the container can use
- Start with generous limits and optimize based on metrics
- Use Vertical Pod Autoscaler (VPA) to find optimal values
Quality of Service (QoS) Classes
Kubernetes assigns QoS classes based on resource specifications:
- Guaranteed: Requests equal limits for all resources
- Burstable: At least one resource has a request
- BestEffort: No resource requests or limits
Production Recommendation: Aim for Guaranteed or Burstable QoS for critical workloads.
Namespace Resource Quotas
Implement namespace-level resource controls:
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: production
spec:
hard:
requests.cpu: "100"
requests.memory: 200Gi
limits.cpu: "200"
limits.memory: 400Gi
persistentvolumeclaims: "10"
services.loadbalancers: "2"
Security Best Practices
Pod Security Standards
Implement Pod Security Standards (replacing Pod Security Policies in 1.25+):
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
Security Contexts
Always define security contexts for your pods:
apiVersion: v1
kind: Pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
Network Policies
Implement zero-trust networking with NetworkPolicies:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-server-netpol
namespace: production
spec:
podSelector:
matchLabels:
app: api-server
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
RBAC Configuration
Follow the principle of least privilege:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: deployment-manager
rules:
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: deployment-manager-binding
namespace: production
subjects:
- kind: ServiceAccount
name: deployment-manager
namespace: production
roleRef:
kind: Role
name: deployment-manager
apiGroup: rbac.authorization.k8s.io
Secrets Management
Never hardcode secrets. Use proper secret management:
- External Secret Operators:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: vault-secret
spec:
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: app-secret
data:
- secretKey: password
remoteRef:
key: secret/data/database
property: password
- Sealed Secrets for GitOps:
# Encrypt secret
echo -n mypassword | kubectl create secret generic mysecret \
--dry-run=client --from-file=password=/dev/stdin -o yaml | \
kubeseal -o yaml > mysealedsecret.yaml
High Availability and Reliability
Multi-Zone Deployments
Spread pods across availability zones:
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: api-server
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: api-server
topologyKey: kubernetes.io/hostname
Health Checks
Implement comprehensive health checks:
apiVersion: v1
kind: Pod
spec:
containers:
- name: app
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
startupProbe:
httpGet:
path: /startup
port: 8080
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 30
Graceful Shutdown
Handle SIGTERM signals properly:
// Node.js example
process.on('SIGTERM', async () => {
console.log('SIGTERM received, starting graceful shutdown');
// Stop accepting new requests
server.close(() => {
console.log('HTTP server closed');
});
// Close database connections
await database.close();
// Wait for ongoing requests to complete (max 30s)
await waitForRequestsToComplete(30000);
process.exit(0);
});
Pod configuration:
spec:
terminationGracePeriodSeconds: 60
containers:
- name: app
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15"]
Observability
Structured Logging
Implement structured JSON logging:
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
data:
fluent-bit.conf: |
[SERVICE]
Parsers_File parsers.conf
[INPUT]
Name tail
Path /var/log/containers/*.log
Parser docker
Tag kube.*
[FILTER]
Name kubernetes
Match kube.*
Kube_URL https://kubernetes.default.svc:443
Merge_Log On
Keep_Log Off
[OUTPUT]
Name elasticsearch
Match *
Host elasticsearch.logging.svc.cluster.local
Port 9200
Logstash_Format On
Replace_Dots On
Retry_Limit False
Metrics and Monitoring
Deploy Prometheus with proper scrape configs:
apiVersion: v1
kind: Service
metadata:
labels:
app: myapp
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
prometheus.io/path: "/metrics"
spec:
ports:
- name: metrics
port: 9090
targetPort: 9090
Custom application metrics:
// Go example with Prometheus
var (
httpRequestsTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests",
},
[]string{"method", "endpoint", "status"},
)
httpRequestDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "HTTP request latency",
},
[]string{"method", "endpoint"},
)
)
Distributed Tracing
Implement OpenTelemetry for tracing:
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: app
env:
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "http://otel-collector:4317"
- name: OTEL_RESOURCE_ATTRIBUTES
value: "service.name=api-server,service.version=1.0.0"
- name: OTEL_TRACES_SAMPLER
value: "parentbased_traceidratio"
- name: OTEL_TRACES_SAMPLER_ARG
value: "0.1" # Sample 10% of traces
Deployment Strategies
Blue-Green Deployments
Using Flagger for automated blue-green deployments:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: api-server
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
service:
port: 80
analysis:
interval: 1m
threshold: 10
maxWeight: 50
stepWeight: 10
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 1m
- name: request-duration
thresholdRange:
max: 500
interval: 1m
Progressive Delivery
Implement feature flags with ConfigMaps:
apiVersion: v1
kind: ConfigMap
metadata:
name: feature-flags
data:
features.json: |
{
"new-checkout-flow": {
"enabled": false,
"rollout_percentage": 10
},
"enhanced-search": {
"enabled": true,
"rollout_percentage": 100
}
}
Cluster Management
GitOps with ArgoCD
Implement GitOps for declarative cluster management:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: production-apps
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/company/k8s-manifests
targetRevision: main
path: production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
Backup and Disaster Recovery
Implement Velero for cluster backups:
# Install Velero
velero install \
--provider aws \
--plugins velero/velero-plugin-for-aws:v1.5.0 \
--bucket velero-backups \
--secret-file ./credentials-velero \
--backup-location-config region=us-west-2 \
--snapshot-location-config region=us-west-2
# Create backup schedule
velero schedule create daily-backup \
--schedule="0 2 * * *" \
--ttl 720h0m0s
Cost Optimization
Cluster Autoscaling
Configure cluster autoscaler properly:
apiVersion: apps/v1
kind: Deployment
metadata:
name: cluster-autoscaler
namespace: kube-system
spec:
template:
spec:
containers:
- image: k8s.gcr.io/autoscaling/cluster-autoscaler:v1.27.0
name: cluster-autoscaler
command:
- ./cluster-autoscaler
- --v=4
- --stderrthreshold=info
- --cloud-provider=aws
- --skip-nodes-with-local-storage=false
- --expander=least-waste
- --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/prod-cluster
- --balance-similar-node-groups
- --skip-nodes-with-system-pods=false
Spot Instances
Leverage spot instances for non-critical workloads:
apiVersion: v1
kind: Node
metadata:
labels:
node.kubernetes.io/lifecycle: spot
karpenter.sh/capacity-type: spot
---
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
tolerations:
- key: spot
operator: Equal
value: "true"
effect: NoSchedule
nodeSelector:
karpenter.sh/capacity-type: spot
Maintenance and Updates
Rolling Updates Strategy
Configure proper rolling update parameters:
apiVersion: apps/v1
kind: Deployment
spec:
replicas: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2
maxUnavailable: 1
minReadySeconds: 30
revisionHistoryLimit: 10
progressDeadlineSeconds: 600
Automated Image Updates
Use Flux for automated image updates:
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
name: flux-system
namespace: flux-system
spec:
interval: 1m0s
sourceRef:
kind: GitRepository
name: flux-system
git:
checkout:
ref:
branch: main
commit:
author:
email: fluxcdbot@users.noreply.github.com
name: fluxcdbot
messageTemplate: 'Update image to {{range .Updated.Images}}{{println .}}{{end}}'
push:
branch: main
update:
path: ./clusters/production
strategy: Setters
Conclusion
Running Kubernetes in production requires attention to many details beyond basic deployment. By following these best practices—proper resource management, comprehensive security measures, high availability design, robust observability, and efficient cluster management—you can build a production-grade Kubernetes environment that is secure, reliable, and scalable.
Remember that Kubernetes is a powerful but complex system. Start with the fundamentals, gradually adopt more advanced practices, and always test changes in non-production environments first. Continuous monitoring, regular updates, and ongoing optimization are key to maintaining a healthy Kubernetes cluster.
The journey to Kubernetes excellence is ongoing, but with these best practices as your foundation, you’re well-equipped to handle the challenges of production deployments.