Chuyển đến nội dung chính

LESSON 47: PRACTICE — CLOUD PLATFORMS AND PRODUCTION READINESS

Module 10 practice: Deploy to managed K8s (EKS/GKE/AKS), configure PDB, HPA, cost optimization with Spot nodes, production readiness checklist, Karpenter node consolidation.

🔒 DevSecOps — Lesson 47 LESSON 47: PRACTICE — CLOUD PLATFORMS AND PRODUCTION READINESS

KUBERNETES: FROM BASIC TO ADVANCED

Module 10: Cloud & Production

xdev.asia

🎯 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

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
EOF

Kiể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

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]
EOF

Test: 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)"

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"]
EOF

Xem 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

Summary__HTMLTAG_98___
  • ✅ Production deployment: rolling update + probes + graceful shutdown
  • ✅ PDB: prevent drain if minimum availability is violated__HTMLTAG_103___
  • ✅ HPA: auto-scale based on CPU/RPS
  • ✅ Network Policies: default deny + allow specific traffic__HTMLTAG_107___
  • ✅ Security context: non-root, readOnly filesystem, no capabilities
  • ✅ Graceful shutdown: preStop hook + SIGTERM handler