Kubernetes Patterns
Best practices for deploying, scaling, securing, and managing workloads on Kubernetes. This skill covers resource management, Helm chart structure, service mesh configuration, autoscaling strategies, and security hardening.
Resource Management
Every container must declare resource requests and limits. Without them, the scheduler cannot make informed placement decisions and nodes can become overcommitted.
| Resource Type | Request (Guaranteed) | Limit (Maximum) | What Happens at Limit | |---------------|---------------------|-----------------|----------------------| | CPU | Reserved on node | Throttled (not killed) | Container slows down | | Memory | Reserved on node | OOM-killed | Container restarts | | Ephemeral Storage | Reserved on node | Evicted | Pod removed from node | | GPU | Reserved on node | Hard limit | Cannot exceed |
QoS Classes
Kubernetes assigns QoS classes based on resource declarations. This determines eviction priority.
| QoS Class | Condition | Eviction Priority | |-----------|-----------|-------------------| | Guaranteed | requests == limits for all containers | Last (highest priority) | | Burstable | requests < limits for at least one container | Middle | | BestEffort | No requests or limits set | First (lowest priority) |
Resource Declaration Best Practices
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
version: v2.1.0
spec:
# Topology spread for high availability
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: api-server
containers:
- name: api
image: ghcr.io/our-org/api@sha256:a1b2c3d4e5f6
ports:
- containerPort: 8080
protocol: TCP
resources:
requests:
cpu: 250m # 0.25 cores -- baseline
memory: 256Mi # baseline memory
limits:
cpu: "1" # burst to 1 core
memory: 512Mi # hard cap prevents OOM cascade
# Probes are essential for rolling updates
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
failureThreshold: 3
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30
periodSeconds: 2
Namespace Isolation Strategies
Namespaces provide logical boundaries. Combine with NetworkPolicies and RBAC for true isolation.
| Strategy | Isolation Level | Use Case | |----------|----------------|----------| | Per-team | Medium | Small org, shared cluster | | Per-environment | Medium | Dev/staging/prod in one cluster | | Per-application | High | Microservices with strict boundaries | | Per-tenant | Highest | Multi-tenant SaaS |
Resource Quotas and Limit Ranges
# ResourceQuota: caps total resource consumption per namespace
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-alpha-quota
namespace: team-alpha
spec:
hard:
requests.cpu: "10"
requests.memory: 20Gi
limits.cpu: "20"
limits.memory: 40Gi
pods: "50"
services: "20"
persistentvolumeclaims: "10"
secrets: "30"
configmaps: "30"
---
# LimitRange: sets defaults and bounds per container
apiVersion: v1
kind: LimitRange
metadata:
name: team-alpha-limits
namespace: team-alpha
spec:
limits:
- type: Container
default:
cpu: 500m
memory: 256Mi
defaultRequest:
cpu: 100m
memory: 128Mi
min:
cpu: 50m
memory: 64Mi
max:
cpu: "4"
memory: 4Gi
- type: PersistentVolumeClaim
min:
storage: 1Gi
max:
storage: 50Gi
Network Policy for Namespace Isolation
# Default deny all ingress and egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: team-alpha
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# Allow only within namespace + DNS
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-intra-namespace
namespace: team-alpha
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector: {}
egress:
- to:
- podSelector: {}
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
Helm Chart Structure
Helm charts package Kubernetes manifests with templating and dependency management.
Standard Chart Layout
my-app/
Chart.yaml # Chart metadata, version, dependencies
Chart.lock # Locked dependency versions
values.yaml # Default configuration values
values-staging.yaml # Environment-specific overrides
values-production.yaml # Environment-specific overrides
templates/
_helpers.tpl # Template helper functions
deployment.yaml # Deployment manifest
service.yaml # Service manifest
ingress.yaml # Ingress manifest
hpa.yaml # HorizontalPodAutoscaler
configmap.yaml # ConfigMap
secret.yaml # Secret (sealed or external)
serviceaccount.yaml # ServiceAccount
networkpolicy.yaml # NetworkPolicy
pdb.yaml # PodDisruptionBudget
tests/
test-connection.yaml # Helm test hooks
charts/ # Dependency charts (vendored)
Chart.yaml Best Practices
apiVersion: v2
name: my-app
description: A Helm chart for the My App API service
type: application
version: 1.4.0 # Chart version (bump on chart changes)
appVersion: "2.1.0" # Application version (bump on app changes)
dependencies:
- name: postgresql
version: "13.x"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
- name: redis
version: "18.x"
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
maintainers:
- name: Platform Team
email: platform@company.com
Helm Template with Guards
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "my-app.fullname" . }}
labels:
{{- include "my-app.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "my-app.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
# Force rollout on config changes
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
labels:
{{- include "my-app.selectorLabels" . | nindent 8 }}
spec:
serviceAccountName: {{ include "my-app.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http