Kubernetes Deployment
Kubernetes (K8s) is the industry standard for orchestrating containerized microservices at scale.Learning Objectives:
- Understand Kubernetes architecture
- Create Deployments and Services
- Manage configuration with ConfigMaps and Secrets
- Set up Ingress for external access
- Use Helm for package management
Kubernetes Architecture
Copy
┌─────────────────────────────────────────────────────────────────────────────┐
│ KUBERNETES ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ CONTROL PLANE │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ API │ │ Sched- │ │Controller│ │ etcd │ │ │
│ │ │ Server │ │ uler │ │ Manager │ │ (store) │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ kubectl, API calls │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ WORKER NODES │ │
│ │ │ │
│ │ ┌─────────────────────────────┐ ┌─────────────────────────────┐ │ │
│ │ │ NODE 1 │ │ NODE 2 │ │ │
│ │ │ │ │ │ │ │
│ │ │ ┌───────┐ ┌───────┐ │ │ ┌───────┐ ┌───────┐ │ │ │
│ │ │ │ Pod │ │ Pod │ │ │ │ Pod │ │ Pod │ │ │ │
│ │ │ │Order-1│ │Order-2│ │ │ │Pay-1 │ │Inv-1 │ │ │ │
│ │ │ └───────┘ └───────┘ │ │ └───────┘ └───────┘ │ │ │
│ │ │ │ │ │ │ │
│ │ │ ┌────────┐ ┌──────────┐ │ │ ┌────────┐ ┌──────────┐ │ │ │
│ │ │ │ kubelet│ │kube-proxy│ │ │ │ kubelet│ │kube-proxy│ │ │ │
│ │ │ └────────┘ └──────────┘ │ │ └────────┘ └──────────┘ │ │ │
│ │ │ │ │ │ │ │
│ │ └─────────────────────────────┘ └─────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Core Resources
Namespace
Copy
# k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: microservices
labels:
name: microservices
environment: production
Deployment
Copy
# k8s/order-service/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: microservices
labels:
app: order-service
version: v1
spec:
replicas: 3
selector:
matchLabels:
app: order-service
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: order-service
version: v1
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "3000"
prometheus.io/path: "/metrics"
spec:
serviceAccountName: order-service
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: order-service
image: myregistry/order-service:1.0.0
imagePullPolicy: Always
ports:
- containerPort: 3000
name: http
env:
- name: NODE_ENV
value: production
- name: PORT
value: "3000"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: order-service-secrets
key: database-url
- name: KAFKA_BROKERS
valueFrom:
configMapKeyRef:
name: order-service-config
key: kafka-brokers
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /health/live
port: 3000
initialDelaySeconds: 15
periodSeconds: 20
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health/ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: order-service
topologyKey: kubernetes.io/hostname
Service
Copy
# k8s/order-service/service.yaml
apiVersion: v1
kind: Service
metadata:
name: order-service
namespace: microservices
labels:
app: order-service
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 3000
protocol: TCP
name: http
selector:
app: order-service
ConfigMap
Copy
# k8s/order-service/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: order-service-config
namespace: microservices
data:
kafka-brokers: "kafka.microservices.svc.cluster.local:9092"
redis-host: "redis.microservices.svc.cluster.local"
redis-port: "6379"
log-level: "info"
# Configuration file
app-config.json: |
{
"features": {
"newCheckout": true,
"splitPayments": false
},
"limits": {
"maxOrderItems": 50,
"maxOrderValue": 10000
}
}
Secret
Copy
# k8s/order-service/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: order-service-secrets
namespace: microservices
type: Opaque
stringData:
database-url: "postgresql://user:password@order-db:5432/orders"
jwt-secret: "your-jwt-secret-here"
# For sensitive data, use external secrets managers:
# - HashiCorp Vault with External Secrets Operator
# - AWS Secrets Manager
# - Azure Key Vault
ServiceAccount with RBAC
Copy
# k8s/order-service/rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: order-service
namespace: microservices
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: order-service-role
namespace: microservices
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["order-service-secrets"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: order-service-binding
namespace: microservices
subjects:
- kind: ServiceAccount
name: order-service
namespace: microservices
roleRef:
kind: Role
name: order-service-role
apiGroup: rbac.authorization.k8s.io
Ingress Configuration
NGINX Ingress
Copy
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: microservices-ingress
namespace: microservices
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- api.example.com
secretName: api-tls-secret
rules:
- host: api.example.com
http:
paths:
- path: /orders
pathType: Prefix
backend:
service:
name: order-service
port:
number: 80
- path: /payments
pathType: Prefix
backend:
service:
name: payment-service
port:
number: 80
- path: /inventory
pathType: Prefix
backend:
service:
name: inventory-service
port:
number: 80
Rate Limiting with Ingress
Copy
# k8s/ingress-rate-limit.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
annotations:
nginx.ingress.kubernetes.io/limit-rps: "10"
nginx.ingress.kubernetes.io/limit-connections: "5"
nginx.ingress.kubernetes.io/limit-rate: "500"
nginx.ingress.kubernetes.io/limit-rate-after: "1000"
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-gateway
port:
number: 80
Horizontal Pod Autoscaler (HPA)
Copy
# k8s/order-service/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service-hpa
namespace: microservices
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "1000"
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
- type: Pods
value: 4
periodSeconds: 15
selectPolicy: Max
Pod Disruption Budget
Copy
# k8s/order-service/pdb.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: order-service-pdb
namespace: microservices
spec:
minAvailable: 2
# Or use: maxUnavailable: 1
selector:
matchLabels:
app: order-service
Network Policies
Copy
# k8s/network-policies.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: order-service-network-policy
namespace: microservices
spec:
podSelector:
matchLabels:
app: order-service
policyTypes:
- Ingress
- Egress
ingress:
# Allow traffic from ingress controller
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 3000
# Allow traffic from api-gateway
- from:
- podSelector:
matchLabels:
app: api-gateway
ports:
- protocol: TCP
port: 3000
egress:
# Allow DNS
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
# Allow to databases
- to:
- podSelector:
matchLabels:
app: order-db
ports:
- protocol: TCP
port: 5432
# Allow to Kafka
- to:
- podSelector:
matchLabels:
app: kafka
ports:
- protocol: TCP
port: 9092
# Allow to other services
- to:
- podSelector:
matchLabels:
app: payment-service
- podSelector:
matchLabels:
app: inventory-service
ports:
- protocol: TCP
port: 3000
Helm Charts
Chart Structure
Copy
order-service/
├── Chart.yaml
├── values.yaml
├── values-staging.yaml
├── values-production.yaml
├── templates/
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── configmap.yaml
│ ├── secret.yaml
│ ├── hpa.yaml
│ ├── pdb.yaml
│ ├── serviceaccount.yaml
│ └── ingress.yaml
└── charts/
Chart.yaml
Copy
# Chart.yaml
apiVersion: v2
name: order-service
description: Order Service Helm Chart
type: application
version: 1.0.0
appVersion: "1.0.0"
dependencies:
- name: postgresql
version: "12.x.x"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
values.yaml
Copy
# values.yaml
replicaCount: 3
image:
repository: myregistry/order-service
tag: "1.0.0"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
targetPort: 3000
ingress:
enabled: true
className: nginx
hosts:
- host: api.example.com
paths:
- path: /orders
pathType: Prefix
tls:
- secretName: api-tls
hosts:
- api.example.com
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 20
targetCPUUtilizationPercentage: 70
podDisruptionBudget:
enabled: true
minAvailable: 2
config:
logLevel: info
kafkaBrokers: kafka:9092
redisHost: redis
secrets:
databaseUrl: ""
jwtSecret: ""
postgresql:
enabled: true
auth:
database: orders
Deployment Template
Copy
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "order-service.fullname" . }}
labels:
{{- include "order-service.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "order-service.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
labels:
{{- include "order-service.selectorLabels" . | nindent 8 }}
spec:
serviceAccountName: {{ include "order-service.serviceAccountName" . }}
securityContext:
runAsNonRoot: true
runAsUser: 1000
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
env:
- name: NODE_ENV
value: production
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: {{ include "order-service.fullname" . }}-config
key: log-level
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: {{ include "order-service.fullname" . }}-secrets
key: database-url
livenessProbe:
httpGet:
path: /health/live
port: http
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /health/ready
port: http
initialDelaySeconds: 5
periodSeconds: 10
resources:
{{- toYaml .Values.resources | nindent 12 }}
Helpers Template
Copy
# templates/_helpers.tpl
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "order-service.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "order-service.labels" -}}
helm.sh/chart: {{ include "order-service.chart" . }}
{{ include "order-service.selectorLabels" . }}
app.kubernetes.io/version: {{ .Values.image.tag | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "order-service.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "order-service.serviceAccountName" -}}
{{- default (include "order-service.fullname" .) .Values.serviceAccount.name }}
{{- end }}
Helm Commands
Copy
# Install
helm install order-service ./order-service -n microservices
# Install with custom values
helm install order-service ./order-service \
-n microservices \
-f values-production.yaml \
--set image.tag=1.2.0
# Upgrade
helm upgrade order-service ./order-service \
-n microservices \
--set image.tag=1.3.0
# Rollback
helm rollback order-service 1 -n microservices
# Uninstall
helm uninstall order-service -n microservices
# Template (dry-run)
helm template order-service ./order-service -n microservices
kubectl Commands Reference
Copy
# Context & Namespace
kubectl config get-contexts
kubectl config use-context production
kubectl config set-context --current --namespace=microservices
# Get resources
kubectl get pods -n microservices
kubectl get pods -o wide
kubectl get pods -l app=order-service
kubectl get all -n microservices
# Describe resources
kubectl describe pod order-service-xxx
kubectl describe deployment order-service
# Logs
kubectl logs order-service-xxx
kubectl logs -f order-service-xxx
kubectl logs order-service-xxx --previous
kubectl logs -l app=order-service --all-containers
# Execute commands
kubectl exec -it order-service-xxx -- /bin/sh
kubectl exec order-service-xxx -- env
# Port forwarding
kubectl port-forward svc/order-service 3000:80
kubectl port-forward pod/order-service-xxx 3000:3000
# Apply/Delete
kubectl apply -f k8s/
kubectl apply -f k8s/ -n microservices
kubectl delete -f k8s/
# Scale
kubectl scale deployment order-service --replicas=5
# Rollout
kubectl rollout status deployment/order-service
kubectl rollout history deployment/order-service
kubectl rollout undo deployment/order-service
kubectl rollout restart deployment/order-service
Interview Questions
Q1: Explain the difference between Deployment and StatefulSet
Q1: Explain the difference between Deployment and StatefulSet
Answer:Deployment:
- Stateless workloads
- Pods are interchangeable
- Random pod names (order-xxx-yyy)
- Parallel scaling
- Use for: API servers, web apps
- Stateful workloads
- Stable network identity (order-0, order-1)
- Ordered deployment/scaling
- Persistent storage per pod
- Use for: Databases, message queues
- StatefulSet maintains pod identity across restarts
- Each StatefulSet pod gets its own PVC
- Ordered, graceful deployment and scaling
Q2: What are liveness and readiness probes?
Q2: What are liveness and readiness probes?
Answer:Liveness Probe:
- “Is the container alive?”
- Failure → container restart
- Detect deadlocks, infinite loops
- Usually checks internal health
- “Can the container accept traffic?”
- Failure → removed from service endpoints
- Container keeps running
- Checks dependencies (DB, cache)
- Liveness: Simple, fast check
- Readiness: Check dependencies
- Different endpoints for each
- Appropriate timeouts and thresholds
Q3: How does HPA work?
Q3: How does HPA work?
Answer:Horizontal Pod Autoscaler:
-
Metrics collection (every 15s default)
- CPU, memory via Metrics Server
- Custom metrics via Prometheus Adapter
-
Calculation:
Copy
desiredReplicas = ceil(currentReplicas * (currentMetric/targetMetric)) -
Scaling decision:
- Considers stabilization window
- Applies scaling policies
- Respects min/max replicas
stabilizationWindowSeconds: Prevent flappingscaleDown.policies: Gradual scale downscaleUp.policies: Aggressive scale up
Summary
Key Takeaways
- Use Deployments for stateless services
- ConfigMaps and Secrets for configuration
- HPA for automatic scaling
- Network Policies for security
- Helm for package management
Next Steps
In the next chapter, we’ll cover Testing Strategies for microservices.