Module 21 Advanced 5 labs

Release Engineering và Progressive Delivery

Release production an toàn với các kỹ thuật blue/green deployment, canary release, rolling update, feature flags, tự động sinh release notes và quản lý change management — nền tảng để đạt deployment frequency cao mà không tăng Change Failure Rate.

Công cụ thực hành kubectl, Argo Rollouts, Git, GitHub Actions, Helm, curl
Nền tảng Kubernetes (kind / minikube), GitHub, Linux/WSL2
Thời điểm phát hành 23/05/2026
Ngày biên soạn 23/05/2026
Người biên soạn Trần Văn Hòa — Microsoft Certified Trainer (MCT)

Mục tiêu học tập

1. Lý thuyết cốt lõi

1.1. Tại sao cần Progressive Delivery?

The DevOps Handbook (Gene Kim et al.) mô tả Chapter 12 — Automate and Enable Low-Risk Releases: mục tiêu không phải "deploy ít hơn để an toàn hơn" mà là "deploy thường xuyên hơn để rủi ro mỗi lần thấp hơn". Batch nhỏ → diff nhỏ → dễ phát hiện lỗi → rollback nhanh. Nghiên cứu Accelerate (Forsgren, Humble, Kim) chứng minh elite performers deploy nhiều lần/ngày với Change Failure Rate < 15% — thấp hơn hẳn nhóm low performer deploy vài tháng/lần.

Progressive Delivery là tập hợp kỹ thuật kiểm soát ai nhận release mớibao nhiêu lưu lượng trước khi toàn bộ user tiếp cận — giảm blast radius khi có lỗi.

1.2. So sánh các chiến lược deployment

Chiến lược Cơ chế Ưu điểm Nhược điểm
RecreateTắt toàn bộ old, bật newĐơn giảnDowntime
Rolling UpdateThay pod từng bước, luôn có % pod chạyKhông downtime, ít tài nguyênKhó rollback tức thì, chạy 2 version song song
Blue/Green2 env song song, switch traffic qua Load BalancerRollback tức thì (switch lại), test pre-prod dễGấp đôi tài nguyên
CanaryChuyển % nhỏ traffic sang version mới, tăng dầnKiểm soát rủi ro chi tiết, real user testingCần observability tốt, phức tạp hơn
A/B TestingChia user theo nhóm để đo business metricData-driven decisionCần stateful session, lâu hơn

1.3. Feature Flags (Feature Toggles)

Feature flag là kỹ thuật tách deployment ra khỏi release: code được deploy nhưng tính năng bị ẩn cho đến khi flag được bật. Các loại:

Nguyên tắc: flag phải có thời hạn sống. Flag sống quá lâu (flag debt) gây code phân nhánh khó bảo trì. Thiết lập ngày hết hạn và cleanup schedule.

1.4. Auto Rollback và Health Gates

Argo Rollouts hỗ trợ analysis runs: trong quá trình canary, liên tục query metric (error rate, latency p99) từ Prometheus. Nếu metric vượt ngưỡng → tự động rollback mà không cần người ra quyết định. Đây chính là "Way 2 — Feedback" của DevOps Handbook: vòng phản hồi đủ nhanh để ngăn lỗi lan rộng.

1.5. Release Notes và Change Management

The DevOps Handbook Chapter 23 ("Protecting the Deployment Pipeline") phân tích: change management truyền thống (CAB meetings, manual approval) tạo bottleneck mà không giảm Change Failure Rate vì không kiểm tra kỹ thuật. Giải pháp DevOps: standard changes (low-risk, fully automated) bypass CAB; changes phức tạp dùng peer review + automated test thay thế. Release notes nên được sinh tự động từ conventional commits để đảm bảo chính xác và không tốn công.

2. Thực hành (Labs)

LAB-101

Blue/Green Deployment trên Kubernetes

kubectl · kind · curl

🎯 Mục tiêu: Triển khai 2 version app song song (blue = v1, green = v2) và switch traffic không downtime bằng cách thay đổi Service selector.

🧰 Công cụ / nền tảng: kubectl, kind (Kubernetes in Docker), curl. Yêu cầu: Docker Desktop hoặc Docker Engine đã chạy.

📦 Chuẩn bị:

# Cài kind nếu chưa có
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.22.0/kind-linux-amd64
chmod +x ./kind && sudo mv ./kind /usr/local/bin/kind

# Tạo cluster
kind create cluster --name bluegreen
kubectl cluster-info --context kind-bluegreen

▶️ Các bước:

# Bước 1: Deploy phiên bản BLUE (v1)
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-blue
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
      version: blue
  template:
    metadata:
      labels:
        app: webapp
        version: blue
    spec:
      containers:
      - name: webapp
        image: nginx:1.24
        ports:
        - containerPort: 80
        # Mô phỏng v1: ghi version vào response
        lifecycle:
          postStart:
            exec:
              command: ["/bin/sh","-c","echo 'v1-blue' > /usr/share/nginx/html/index.html"]
EOF

# Bước 2: Tạo Service trỏ vào BLUE
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: webapp-svc
spec:
  selector:
    app: webapp
    version: blue     # <-- đây là switch
  ports:
  - port: 80
    targetPort: 80
  type: ClusterIP
EOF

# Bước 3: Xác nhận service đang trỏ blue
kubectl get endpoints webapp-svc

# Bước 4: Deploy phiên bản GREEN (v2) — KHÔNG ảnh hưởng production
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-green
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
      version: green
  template:
    metadata:
      labels:
        app: webapp
        version: green
    spec:
      containers:
      - name: webapp
        image: nginx:1.25
        ports:
        - containerPort: 80
        lifecycle:
          postStart:
            exec:
              command: ["/bin/sh","-c","echo 'v2-green' > /usr/share/nginx/html/index.html"]
EOF

# Bước 5: Kiểm tra green sẵn sàng
kubectl rollout status deployment/app-green

# Bước 6: Switch traffic sang GREEN (1 lệnh, 0 downtime)
kubectl patch service webapp-svc -p '{"spec":{"selector":{"version":"green"}}}'

# Bước 7: Xác nhận
kubectl get endpoints webapp-svc
# Port-forward để test
kubectl port-forward svc/webapp-svc 8080:80 &
curl http://localhost:8080    # kết quả phải là "v2-green"

# Bước 8: Rollback tức thì (nếu green có vấn đề)
kubectl patch service webapp-svc -p '{"spec":{"selector":{"version":"blue"}}}'
curl http://localhost:8080    # trả về "v1-blue"

✅ Kết quả mong đợi: Service chuyển từ blue sang green trong <1 giây; curl trả đúng version tương ứng; rollback cũng <1 giây. kubectl get deployments hiển thị cả app-blueapp-green Running song song.

🧹 Cleanup:

kill %1   # dừng port-forward
kubectl delete deployment app-blue app-green
kubectl delete service webapp-svc
# Hoặc xóa toàn bộ cluster
kind delete cluster --name bluegreen
LAB-102

Canary Release bằng Argo Rollouts

kubectl · Argo Rollouts · kubectl-argo-rollouts plugin

🎯 Mục tiêu: Chuyển 20% traffic sang version mới, quan sát, sau đó promote 100% — mô phỏng canary release có kiểm soát bằng Argo Rollouts.

🧰 Công cụ / nền tảng: kubectl, Argo Rollouts (CRD), kubectl-argo-rollouts plugin.

📦 Chuẩn bị:

# Cài Argo Rollouts CRDs và controller
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts \
  -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml

# Cài kubectl plugin (Linux)
curl -LO https://github.com/argoproj/argo-rollouts/releases/latest/download/kubectl-argo-rollouts-linux-amd64
chmod +x kubectl-argo-rollouts-linux-amd64
sudo mv kubectl-argo-rollouts-linux-amd64 /usr/local/bin/kubectl-argo-rollouts

# Xác nhận
kubectl argo rollouts version

▶️ Các bước:

# Bước 1: Tạo Rollout với chiến lược canary
cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: canary-demo
spec:
  replicas: 5
  selector:
    matchLabels:
      app: canary-demo
  template:
    metadata:
      labels:
        app: canary-demo
    spec:
      containers:
      - name: canary-demo
        image: argoproj/rollouts-demo:blue   # version hiện tại
        ports:
        - containerPort: 8080
  strategy:
    canary:
      steps:
      - setWeight: 20        # bước 1: 20% traffic → canary
      - pause: {}            # dừng, chờ manual promote
      - setWeight: 50        # bước 2: 50%
      - pause: {duration: 30s}  # tự động sau 30 giây
      - setWeight: 100       # full rollout
EOF

# Tạo Service
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: canary-demo-svc
spec:
  selector:
    app: canary-demo
  ports:
  - port: 8080
    targetPort: 8080
EOF

# Bước 2: Xem trạng thái rollout
kubectl argo rollouts get rollout canary-demo --watch

# Bước 3: Trigger canary — update image sang version mới
kubectl argo rollouts set image canary-demo \
  canary-demo=argoproj/rollouts-demo:yellow

# Bước 4: Quan sát — 20% pod chạy yellow, 80% chạy blue
kubectl argo rollouts get rollout canary-demo
# Output ví dụ:
# NAME          KIND     STATUS     AGE  INFO
# canary-demo   Rollout  Paused     2m   CanaryWeight=20

# Bước 5: Port-forward và test phân phối traffic
kubectl port-forward svc/canary-demo-svc 8080:8080 &
for i in {1..10}; do curl -s http://localhost:8080 | grep -o '"color":"[^"]*"'; done
# Khoảng 2/10 request trả về yellow, 8/10 trả về blue

# Bước 6: Promote canary (chấp nhận tiếp tục)
kubectl argo rollouts promote canary-demo

# Xem rollout tiếp tục qua 50% → 100%
kubectl argo rollouts get rollout canary-demo --watch

# Bước 7: Hoặc ABORT (rollback toàn bộ về blue)
# kubectl argo rollouts abort canary-demo

✅ Kết quả mong đợi: Rollout dừng tại CanaryWeight=20; sau promote rollout chạy tiếp qua 50% → 100%; kubectl argo rollouts get rollout canary-demo hiển thị Healthy. Traffic split xác nhận bằng vòng lặp curl ở bước 5.

🧹 Cleanup:

kill %1
kubectl delete rollout canary-demo
kubectl delete svc canary-demo-svc
LAB-103

Feature Flag Rollout không dùng SaaS

Python/Node · ConfigMap · kubectl

🎯 Mục tiêu: Triển khai feature flag đơn giản dùng Kubernetes ConfigMap làm flag store — bật/tắt tính năng không cần rebuild image.

🧰 Công cụ / nền tảng: kubectl, Python 3, Kubernetes cluster.

📦 Chuẩn bị: Cluster từ LAB-101 hoặc bất kỳ cluster nào.

▶️ Các bước:

# Bước 1: Tạo ConfigMap chứa feature flags
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
  name: feature-flags
data:
  NEW_CHECKOUT_UI: "false"
  DARK_MODE: "true"
  RECOMMENDATION_ENGINE: "false"
EOF

# Bước 2: Deploy ứng dụng Python đọc flag từ env
# (env inject từ ConfigMap)
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: flag-demo-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: flag-demo
  template:
    metadata:
      labels:
        app: flag-demo
    spec:
      containers:
      - name: flag-demo
        image: python:3.11-slim
        command: ["python","-c"]
        args:
        - |
          import os, http.server, socketserver
          class H(http.server.BaseHTTPRequestHandler):
            def do_GET(self):
              flags = {k:os.environ.get(k,'false')
                for k in ['NEW_CHECKOUT_UI','DARK_MODE','RECOMMENDATION_ENGINE']}
              body = str(flags).encode()
              self.send_response(200)
              self.end_headers()
              self.wfile.write(body)
          with socketserver.TCPServer(('',8080),H) as s: s.serve_forever()
        ports:
        - containerPort: 8080
        envFrom:
        - configMapRef:
            name: feature-flags
EOF

kubectl rollout status deployment/flag-demo-app

# Bước 3: Test — xem flags hiện tại
kubectl port-forward deployment/flag-demo-app 8080:8080 &
curl http://localhost:8080
# Output: {'NEW_CHECKOUT_UI': 'false', 'DARK_MODE': 'true', 'RECOMMENDATION_ENGINE': 'false'}

# Bước 4: BẬT NEW_CHECKOUT_UI (không rebuild image!)
kubectl patch configmap feature-flags \
  --type merge -p '{"data":{"NEW_CHECKOUT_UI":"true"}}'

# Bước 5: Restart pod để nhận env mới (hoặc dùng watch + hot-reload)
kubectl rollout restart deployment/flag-demo-app
kubectl rollout status deployment/flag-demo-app

# Bước 6: Kiểm tra flag đã thay đổi
curl http://localhost:8080
# Output: {'NEW_CHECKOUT_UI': 'true', 'DARK_MODE': 'true', ...}

# Cleanup flag debt: sau khi tính năng stable, xóa flag khỏi code và ConfigMap
kubectl patch configmap feature-flags \
  --type json -p '[{"op":"remove","path":"/data/NEW_CHECKOUT_UI"}]'

✅ Kết quả mong đợi: Flag thay đổi không cần rebuild image; curl lần 2 trả về NEW_CHECKOUT_UI: true. Đây là minh họa release toggle — deploy trước, bật sau khi sẵn sàng.

🧹 Cleanup:

kill %1
kubectl delete deployment flag-demo-app
kubectl delete configmap feature-flags
LAB-104

Auto Rollback dựa trên Health Check

Argo Rollouts · AnalysisTemplate · Prometheus

🎯 Mục tiêu: Cấu hình Argo Rollouts tự động rollback khi error rate của canary vượt ngưỡng — không cần người bấm nút.

🧰 Công cụ / nền tảng: Argo Rollouts (từ LAB-102), Prometheus (hoặc dùng job analysis giả lập).

📦 Chuẩn bị: Argo Rollouts đã cài; nếu chưa có Prometheus, dùng AnalysisTemplate dạng job.

▶️ Các bước:

# Bước 1: Tạo AnalysisTemplate giả lập kiểm tra health
# (dùng job thay Prometheus để không cần cài thêm)
cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: health-check
spec:
  metrics:
  - name: success-rate
    interval: 10s
    count: 3            # chạy 3 lần
    successCondition: result == "true"
    failureLimit: 1     # cho phép 1 lần thất bại
    provider:
      job:
        spec:
          template:
            spec:
              containers:
              - name: check
                image: curlimages/curl:latest
                # Kiểm tra endpoint /healthz trả HTTP 200
                command:
                - sh
                - -c
                - |
                  status=$(curl -o /dev/null -s -w "%{http_code}" \
                    http://canary-demo-svc:8080 2>/dev/null || echo "000")
                  [ "$status" = "200" ] && echo "true" || echo "false"
              restartPolicy: Never
EOF

# Bước 2: Cập nhật Rollout có gắn analysis
cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: canary-health
spec:
  replicas: 4
  selector:
    matchLabels:
      app: canary-health
  template:
    metadata:
      labels:
        app: canary-health
    spec:
      containers:
      - name: app
        image: argoproj/rollouts-demo:blue
        ports:
        - containerPort: 8080
  strategy:
    canary:
      analysis:
        templates:
        - templateName: health-check
        startingStep: 1   # bắt đầu analysis từ bước 1
      steps:
      - setWeight: 25
      - pause: {duration: 30s}
      - setWeight: 100
EOF

# Service cho rollout này
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: canary-demo-svc
spec:
  selector:
    app: canary-health
  ports:
  - port: 8080
    targetPort: 8080
EOF

# Bước 3: Trigger rollout với image "bad" để xem auto-rollback
kubectl argo rollouts set image canary-health \
  app=argoproj/rollouts-demo:bad   # image demo trả lỗi

# Bước 4: Theo dõi analysis chạy và rollback tự động
kubectl argo rollouts get rollout canary-health --watch
# Xem trạng thái chuyển:
# Progressing → Paused(canary) → Degraded → Rollback

# Bước 5: Xem lý do rollback
kubectl argo rollouts get rollout canary-health
# Section "Message" hiển thị: "RollbackCompleted: ...metric 'success-rate' assessed Failed"

# Xác nhận rollout quay về image cũ (blue)
kubectl get rollout canary-health -o jsonpath='{.status.currentPodHash}'

✅ Kết quả mong đợi: Sau khoảng 30–60 giây, rollout tự chuyển sang trạng thái Degraded rồi Healthy (image cũ); không cần thao tác thủ công. kubectl argo rollouts get rollout canary-health ghi rõ lý do abort.

🧹 Cleanup:

kubectl delete rollout canary-health
kubectl delete analysistemplate health-check
kubectl delete svc canary-demo-svc
LAB-105

Tự động sinh Release Notes bằng GitHub Actions

Git · GitHub Actions · Conventional Commits

🎯 Mục tiêu: Tự động sinh release notes từ conventional commits và tạo GitHub Release khi tag mới được push — không viết tay.

🧰 Công cụ / nền tảng: Git, GitHub repo, GitHub Actions, conventional commits.

📦 Chuẩn bị: Repo GitHub public/private; commit theo format feat:, fix:, chore:.

▶️ Các bước:

# Bước 1: Tạo cấu trúc repo và file workflow
mkdir release-demo && cd release-demo
git init
git remote add origin https://github.com/<user>/release-demo.git

# Bước 2: Tạo workflow file
mkdir -p .github/workflows
cat > .github/workflows/release.yml <<'EOF'
name: Auto Release Notes

on:
  push:
    tags:
      - 'v*.*.*'    # trigger khi push tag semver

jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0   # cần full history để lấy commits

      - name: Generate Release Notes
        id: notes
        run: |
          # Lấy tag trước đó
          PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
          CURR_TAG=${GITHUB_REF_NAME}

          if [ -z "$PREV_TAG" ]; then
            RANGE="HEAD"
          else
            RANGE="${PREV_TAG}..HEAD"
          fi

          echo "## What's Changed" > notes.md
          echo "" >> notes.md

          # Features
          FEATS=$(git log $RANGE --oneline --grep="^feat" --format="- %s (%h)")
          if [ -n "$FEATS" ]; then
            echo "### Features" >> notes.md
            echo "$FEATS" >> notes.md
            echo "" >> notes.md
          fi

          # Bug fixes
          FIXES=$(git log $RANGE --oneline --grep="^fix" --format="- %s (%h)")
          if [ -n "$FIXES" ]; then
            echo "### Bug Fixes" >> notes.md
            echo "$FIXES" >> notes.md
            echo "" >> notes.md
          fi

          echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREV_TAG}...${CURR_TAG}" >> notes.md
          cat notes.md

          # Export cho bước tiếp theo
          echo "notes_file=notes.md" >> $GITHUB_OUTPUT

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          body_path: notes.md
          tag_name: ${{ github.ref_name }}
          name: "Release ${{ github.ref_name }}"
          draft: false
          prerelease: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') }}
EOF

# Bước 3: Tạo vài commit conventional
echo "# App" > README.md
git add . && git commit -m "chore: initial project setup"
echo "login feature" > login.txt
git add . && git commit -m "feat: add login with JWT authentication"
echo "bugfix" > fix.txt
git add . && git commit -m "fix: resolve token expiry edge case"
git add . && git commit -m "feat: add user profile endpoint"

# Bước 4: Push và tag
git push origin main

# Tag version 1.0.0 → trigger workflow
git tag v1.0.0
git push origin v1.0.0

# Bước 5: Theo dõi Actions
# Vào github.com/<user>/release-demo/actions
# Hoặc dùng gh CLI:
gh run list --repo <user>/release-demo
gh run watch --repo <user>/release-demo

✅ Kết quả mong đợi: Trong tab Releases của GitHub repo xuất hiện v1.0.0 với release notes tự động phân loại "Features" và "Bug Fixes" từ conventional commits. gh release view v1.0.0 hiển thị nội dung đúng.

🧹 Cleanup:

# Xóa repo test sau khi học
gh repo delete <user>/release-demo --yes

3. Tình huống doanh nghiệp thực tế

Bối cảnh

Một công ty fintech có 2 triệu người dùng muốn ra mắt tính năng "Instant Transfer 2.0" — cơ chế xử lý hoàn toàn mới. Đội Risk e ngại: nếu có bug, 2 triệu người bị ảnh hưởng ngay. Release theo kiểu truyền thống (maintenance window cuối tuần) không đáp ứng được tốc độ thị trường.

Giải pháp Progressive Delivery

  • Feature flag (permission toggle): Deploy code mới lên production nhưng chỉ bật cho 500 internal testers trước. Phát hiện edge case trong luồng thanh toán → fix mà không ảnh hưởng user thật.
  • Canary 1%: Bật cho 1% user ngẫu nhiên. Theo dõi error rate và p99 latency trong 24h. Prometheus alert nếu error rate > 0.1%.
  • Canary 10% → 25% → 100%: Mỗi giai đoạn kéo dài 48h, Argo Rollouts tự động promote nếu metric ổn, tự rollback nếu không.
  • Change management: Không cần CAB meeting cho từng canary step. Chỉ cần approval một lần cho kế hoạch rollout tổng thể. Audit trail tự động từ Argo Rollouts + GitHub Actions.
  • Kết quả: Tính năng ra mắt trong 5 ngày thay vì 3 tuần. 0 major incident. Change Failure Rate giảm từ 12% xuống 2%.

📚 Nguồn tham khảo

Module 20: GitOps với Argo CD & Flux Module 22: Monitoring, Logging, Metrics & Tracing
Zalo