🎯 Practice objective__HTMLTAG_68___
- Deploy production-ready application to local cluster (simulate cloud)
- Configure PodDisruptionBudget and test node drain__HTMLTAG_73___
- HPA configuration with custom metrics__HTMLTAG_75___
- Apply security best practices (PSS, Network Policies)
- Test graceful shutdown
- Configuring Karpenter consolidation (simulation)
Lab 1: Production-Ready Deployment__HTMLTAG_84___
kubectl create namespace lab10
Deploy nginx với full production config
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
namespace: lab10
labels:
app: webapp
version: "1.0"
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
terminationGracePeriodSeconds: 60
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: webapp
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "300m"
memory: "256Mi"
startupProbe:
httpGet:
path: /
port: 80
failureThreshold: 10
periodSeconds: 3
readinessProbe:
httpGet:
path: /
port: 80
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /
port: 80
periodSeconds: 10
failureThreshold: 3
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 5 && nginx -s quit"]
apiVersion: v1
kind: Service
metadata:
name: webapp
namespace: lab10
spec:
selector:
app: webapp
ports:
- port: 80
targetPort: 80
EOF
kubectl get pods -n lab10 -w
kubectl create namespace lab10
Deploy nginx với full production config
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
namespace: lab10
labels:
app: webapp
version: "1.0"
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
terminationGracePeriodSeconds: 60
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: webapp
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "300m"
memory: "256Mi"
startupProbe:
httpGet:
path: /
port: 80
failureThreshold: 10
periodSeconds: 3
readinessProbe:
httpGet:
path: /
port: 80
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /
port: 80
periodSeconds: 10
failureThreshold: 3
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 5 && nginx -s quit"]
apiVersion: v1
kind: Service
metadata:
name: webapp
namespace: lab10
spec:
selector:
app: webapp
ports:
- port: 80
targetPort: 80
EOF
kubectl get pods -n lab10 -w
Lab 2: PodDisruptionBudget
# Tạo PDB cat <<EOF | kubectl apply -f - apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: webapp-pdb namespace: lab10 spec: minAvailable: 2 # luôn có ít nhất 2 pods selector: matchLabels: app: webapp EOFKiểm tra PDB
kubectl get pdb -n lab10
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS
webapp-pdb 2 N/A 1
Test: chọn 1 node có pods
NODE=$(kubectl get pods -n lab10 -o wide | awk 'NR==2{print $7}')
Drain node — PDB sẽ cho phép evict tối đa 1 pod tại 1 thời điểm
kubectl drain $NODE --ignore-daemonsets --delete-emptydir-data
Observe: drain chờ đến khi pods rescheduled trước khi evict pod tiếp theo
kubectl get pods -n lab10 -o wide -w
kubectl uncordon $NODE
Lab 3: Horizontal Pod Autoscaler__HTMLTAG_88___
# Đảm bảo metrics-server đang chạy
kubectl get deployment metrics-server -n kube-system
Tạo HPA
cat <<EOF | kubectl apply -f -
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: webapp-hpa
namespace: lab10
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: webapp
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
behavior:
scaleUp:
stabilizationWindowSeconds: 30
policies:
- type: Pods
value: 2
periodSeconds: 30
scaleDown:
stabilizationWindowSeconds: 120
EOF
Xem HPA status
kubectl get hpa webapp-hpa -n lab10
Load test để trigger scale up
kubectl run load-generator --image=busybox:1.36 -n lab10 --restart=Never --
/bin/sh -c "while true; do wget -q -O- http://webapp.lab10.svc.cluster.local/; done"
Theo dõi HPA scale up
kubectl get hpa webapp-hpa -n lab10 -w
Stop load test
kubectl delete pod load-generator -n lab10
Theo dõi scale down (sau 2 phút stabilization)
kubectl get hpa webapp-hpa -n lab10 -w
# Đảm bảo metrics-server đang chạy
kubectl get deployment metrics-server -n kube-system
Tạo HPA
cat <<EOF | kubectl apply -f -
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: webapp-hpa
namespace: lab10
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: webapp
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
behavior:
scaleUp:
stabilizationWindowSeconds: 30
policies:
- type: Pods
value: 2
periodSeconds: 30
scaleDown:
stabilizationWindowSeconds: 120
EOF
Xem HPA status
kubectl get hpa webapp-hpa -n lab10
Load test để trigger scale up
kubectl run load-generator --image=busybox:1.36 -n lab10 --restart=Never --
/bin/sh -c "while true; do wget -q -O- http://webapp.lab10.svc.cluster.local/; done"
Theo dõi HPA scale up
kubectl get hpa webapp-hpa -n lab10 -w
Stop load test
kubectl delete pod load-generator -n lab10
Theo dõi scale down (sau 2 phút stabilization)
kubectl get hpa webapp-hpa -n lab10 -w
Lab 4: Network Policies
# Default deny all trong namespace cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny namespace: lab10 spec: podSelector: {} policyTypes: [Ingress, Egress] EOFTest: từ pod khác không thể access webapp
kubectl run test-pod --image=busybox:1.36 -n lab10 --restart=Never -- sleep 300 kubectl exec test-pod -n lab10 -- wget -T5 -q http://webapp || echo "BLOCKED (expected)"
Allow ingress từ các pods trong namespace
cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-internal namespace: lab10 spec: podSelector: matchLabels: app: webapp policyTypes: [Ingress] ingress:
- from:
- podSelector: {} # tất cả pods trong namespace ports:
- port: 80 EOF
Allow egress ra internet (DNS + HTTP)
cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-egress-dns namespace: lab10 spec: podSelector: {} policyTypes: [Egress] egress:
- ports:
- port: 53 protocol: UDP
- port: 53 protocol: TCP EOF
Test lại: bây giờ test-pod có thể access webapp
kubectl exec test-pod -n lab10 -- wget -T5 -q -O- http://webapp | head -5
kubectl delete pod test-pod -n lab10
Lab 5: Security Context__HTMLTAG_92___
# Deploy pod với security best practices
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
namespace: lab10
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: nginx:1.27
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"]
resources:
requests:
cpu: "100m"
memory: "64Mi"
limits:
cpu: "200m"
memory: "128Mi"
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache/nginx
- name: run
mountPath: /var/run
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
- name: run
emptyDir: {}
EOF
kubectl get pod secure-pod -n lab10
kubectl exec secure-pod -n lab10 -- id
uid=1000 gid=1000
Test readOnlyRootFilesystem
kubectl exec secure-pod -n lab10 -- touch /test-file || echo "Read-only filesystem (expected)"
kubectl exec secure-pod -n lab10 -- touch /tmp/test-file && echo "Can write to /tmp (allowed)"
# Deploy pod với security best practices
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
namespace: lab10
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: nginx:1.27
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"]
resources:
requests:
cpu: "100m"
memory: "64Mi"
limits:
cpu: "200m"
memory: "128Mi"
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache/nginx
- name: run
mountPath: /var/run
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
- name: run
emptyDir: {}
EOF
kubectl get pod secure-pod -n lab10
kubectl exec secure-pod -n lab10 -- id
uid=1000 gid=1000
Test readOnlyRootFilesystem
kubectl exec secure-pod -n lab10 -- touch /test-file || echo "Read-only filesystem (expected)"
kubectl exec secure-pod -n lab10 -- touch /tmp/test-file && echo "Can write to /tmp (allowed)"
Lab 6: Graceful Shutdown Test
# Deploy app với graceful shutdown cat <<EOF | kubectl apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: graceful-app namespace: lab10 spec: replicas: 2 selector: matchLabels: app: graceful-app template: metadata: labels: app: graceful-app spec: terminationGracePeriodSeconds: 30 containers: - name: app image: busybox:1.36 command: ["/bin/sh"] args: - -c - | trap 'echo "SIGTERM received, graceful shutdown..."; sleep 5; echo "Shutdown complete"; exit 0' TERM echo "App started, waiting..." while true; do sleep 1; done lifecycle: preStop: exec: command: ["/bin/sh", "-c", "echo 'preStop hook running'; sleep 3"] EOFXem shutdown behavior
POD=$(kubectl get pods -n lab10 -l app=graceful-app -o name | head -1) kubectl logs -f $POD &
Delete pod — observe graceful shutdown
kubectl delete $POD -n lab10
Log output:
preStop hook running
SIGTERM received, graceful shutdown...
Shutdown complete
Cleanup
kubectl delete namespace lab10