Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn là kỹ sư DevOps tại một startup fintech ở TP.HCM. Mỗi lần release, cả team phải thức đến 2 giờ sáng cuối tuần, một người "canh" server, một người "canh" log, một người sẵn sàng gõ lệnh rollback. Release xong ai nấy đều thở phào như vừa qua một cửa ải. Vấn đề không nằm ở con người — mà nằm ở việc mỗi lần deploy là một sự kiện lớn, rủi ro cao, và tần suất thấp.
Continuous Deployment (CD) sinh ra để đảo ngược logic đó: thay vì deploy hiếm và to, ta deploy thường xuyên và nhỏ. Mỗi commit vượt qua CI sẽ tự động đi thẳng ra production, không cần bàn tay con người bấm nút. Nghe thì đáng sợ, nhưng chính vì mỗi lần thay đổi rất nhỏ nên rủi ro của từng lần lại thấp hơn nhiều so với release "cục to" hàng tháng.
Bài học này tập trung vào phần cốt lõi và cũng khó nhất của CD: các chiến lược triển khai (deployment strategies). Đây chính là kỹ thuật giúp bạn đưa code mới ra production mà không làm gián đoạn người dùng, và nếu có sự cố thì thu hồi được ngay. Nắm vững phần này, bạn sẽ tự tin nói với sếp: "Chúng ta có thể deploy giữa giờ trưa mà khách hàng không hề hay biết."
Khái niệm cốt lõi
Continuous Deployment khác Continuous Delivery
Hai khái niệm này rất hay bị nhầm, và sự khác biệt nằm đúng ở cái cổng cuối cùng:
- Continuous Delivery: mỗi commit pass CI sẽ được đóng gói thành artifact sẵn sàng để deploy, nhưng việc bấm nút đưa ra production vẫn do con người quyết định (manual gate / manual approval).
- Continuous Deployment: bỏ luôn cổng thủ công đó. Commit pass toàn bộ pipeline (build, test, quét bảo mật...) là tự động ra production, không ai bấm nút.
Điều quan trọng cần hiểu: bạn không nhất thiết phải nhảy thẳng lên Continuous Deployment. Rất nhiều đội ở Việt Nam bắt đầu với Continuous Delivery (giữ manual gate cho môi trường production), và chỉ chuyển sang Continuous Deployment cho những service đã đủ trưởng thành về test và monitoring.
Vì sao cần "chiến lược" deploy?
Nếu bạn chỉ có một cách deploy duy nhất — tắt server cũ, bật server mới — thì trong khoảng thời gian chuyển giao, người dùng sẽ bị lỗi. Đó gọi là downtime. Với hệ thống có hàng nghìn người dùng đồng thời, dù chỉ 30 giây downtime cũng là mất tiền và mất niềm tin.
Deployment strategy là cách bạn điều phối quá trình thay thế phiên bản cũ bằng phiên bản mới sao cho: (1) không gián đoạn dịch vụ, (2) kiểm soát được rủi ro, (3) rollback nhanh khi có sự cố. Dưới đây là các chiến lược phổ biến nhất, xếp theo mức độ tinh vi tăng dần.
Recreate (Big Bang)
Đây là kiểu đơn giản nhất: tắt hết instance cũ, rồi bật instance mới. Có downtime trong lúc chuyển giao. Phù hợp cho ứng dụng nội bộ, môi trường dev/staging, hoặc khi bạn bắt buộc phải chạy đúng một phiên bản tại một thời điểm (ví dụ do ràng buộc database schema). Ưu điểm: đơn giản, không tốn tài nguyên gấp đôi. Nhược điểm: người dùng thấy trang lỗi trong vài giây đến vài phút.
Rolling Update (Cập nhật cuốn chiếu)
Thay vì thay tất cả cùng lúc, ta thay từng phần một. Giả sử bạn có 10 pod đang chạy phiên bản v1. Rolling update sẽ tạo pod v2, chờ nó healthy, xóa bớt một pod v1, rồi lặp lại cho đến khi toàn bộ là v2. Trong suốt quá trình luôn có đủ pod phục vụ, nên không downtime.
Đây là chiến lược mặc định của Kubernetes Deployment. Hai tham số bạn cần nhớ: maxSurge (số pod được phép tạo thêm vượt mức mong muốn) và maxUnavailable (số pod được phép ngừng phục vụ tại một thời điểm). Nhược điểm: trong lúc cuốn chiếu, cả v1 và v2 cùng chạy — nếu hai phiên bản không tương thích (ví dụ format API khác nhau) sẽ sinh lỗi. Rollback cũng chậm vì phải cuốn chiếu ngược lại.
Blue-Green Deployment
Bạn duy trì hai môi trường song song y hệt nhau: Blue (đang chạy production) và Green (phiên bản mới). Bạn deploy code mới lên Green, test kỹ trên đó, rồi chuyển toàn bộ traffic từ Blue sang Green chỉ bằng một thao tác đổi router/load balancer. Nếu Green có vấn đề, chỉ cần chuyển traffic ngược lại Blue — rollback tức thì.
Ưu điểm lớn nhất là rollback cực nhanh và bạn có thể test Green với traffic thật trước khi cắt. Nhược điểm: tốn gấp đôi tài nguyên (phải chạy cả hai môi trường), và bài toán database chung giữa hai môi trường cần xử lý cẩn thận.
Canary Deployment
Lấy cảm hứng từ "chim hoàng yến trong mỏ than" — thợ mỏ ngày xưa mang chim vào hầm; chim ngất trước thì họ biết có khí độc và rút lui. Canary deployment cũng vậy: bạn đưa phiên bản mới ra cho một phần nhỏ người dùng (ví dụ 5%), theo dõi metrics (tỷ lệ lỗi, độ trễ), nếu ổn thì tăng dần lên 25%, 50%, 100%. Nếu có vấn đề, chỉ 5% người dùng bị ảnh hưởng và bạn rút lui ngay.
Đây là chiến lược an toàn nhất cho hệ thống lớn, nhưng cũng phức tạp nhất vì cần: cơ chế chia traffic theo tỷ lệ, monitoring tốt để quan sát canary, và lý tưởng là automated analysis để tự động quyết định tăng traffic hay rollback. Các công cụ như Argo Rollouts hay Flagger sinh ra để tự động hóa quy trình canary này.
A/B Testing (phân biệt với Canary)
Nhiều bạn nhầm A/B testing với canary vì cả hai đều "chia traffic". Khác biệt cốt lõi: canary là để giảm rủi ro kỹ thuật (phiên bản mới có bug không?), còn A/B testing là để đo hiệu quả business (nút màu xanh hay đỏ giúp tăng tỷ lệ mua hàng?). Canary chia traffic ngẫu nhiên và quan tâm đến metrics hệ thống; A/B testing chia traffic theo thuộc tính người dùng và quan tâm đến metrics sản phẩm.
Tình huống thực tế
Ví dụ 1: Tiki và bài toán deploy giờ cao điểm
Giả định một sàn thương mại điện tử lớn như Tiki, với các đợt flash sale lúc 12h trưa và 20h tối. Nếu team dùng chiến lược Recreate, mỗi lần deploy phải chọn khung giờ 3-4h sáng để tránh khách — vừa hại sức khỏe kỹ sư, vừa khiến release dồn ứ (cả tuần mới deploy một lần vì "chờ đến đêm").
Sau khi chuyển sang Rolling Update trên Kubernetes với maxUnavailable: 0 và maxSurge: 25%, họ deploy được bất kỳ lúc nào trong ngày mà không downtime. Với những thay đổi nhạy cảm ở phần thanh toán, họ dùng thêm canary 5%: chỉ 5% đơn hàng đi qua code mới, một dashboard riêng theo dõi tỷ lệ thanh toán thành công của nhóm canary so với nhóm chính. Có lần code mới làm tỷ lệ thất bại thanh toán tăng từ 0.2% lên 1.8% ở nhóm canary — hệ thống tự rollback trong 4 phút, và chỉ 5% khách bị ảnh hưởng thay vì toàn bộ.
Bài học: chiến lược deploy đúng biến "release là sự kiện đáng sợ" thành "release là chuyện thường ngày", đồng thời giới hạn thiệt hại khi có bug.
Ví dụ 2: Startup SaaS 12 người và Blue-Green
Một startup SaaS B2B ở Hà Nội, khoảng 12 người, cung cấp phần mềm quản lý kho cho khách hàng doanh nghiệp. Khách của họ dùng phần mềm trong giờ hành chính, và rất khó chịu với bất kỳ lỗi nào vì nó chặn nghiệp vụ nhập/xuất kho. Họ chọn Blue-Green vì hai lý do: rollback phải tức thì (khách không chấp nhận "chờ 10 phút cuốn chiếu ngược"), và họ muốn chạy toàn bộ bộ test smoke trên môi trường Green với dữ liệu giống production trước khi cắt traffic.
Quy trình của họ: CI pass → deploy lên Green → chạy 200 test smoke tự động trên Green → nếu xanh, đổi load balancer sang Green → giữ Blue nguyên trạng thêm 1 giờ để phòng hờ. Có một lần sau khi cắt sang Green, một khách hàng lớn báo lỗi export Excel; team chỉ mất 30 giây để trỏ load balancer về Blue, khách gần như không nhận ra. Cái giá phải trả: chi phí hạ tầng cao hơn ~40% do phải duy trì hai môi trường, nhưng với họ đó là khoản đầu tư xứng đáng để giữ khách.
Bài học: không có chiến lược "tốt nhất" tuyệt đối. Blue-Green đắt hơn nhưng đổi lại rollback tức thì và khả năng kiểm thử trước — phù hợp khi độ tin cậy quan trọng hơn chi phí.
Ví dụ 3: Cái bẫy schema database khi làm Canary
Một team giả định làm ví điện tử triển khai canary cho service tài khoản. Code mới đọc thêm một cột kyc_level trong bảng users. Họ chạy migration thêm cột, rồi bắt đầu canary 10%. Vấn đề: 90% pod cũ (v1) không biết cột đó, còn một đoạn code v1 dùng câu SELECT * rồi map cứng số lượng cột — kết quả là pod v1 bắt đầu lỗi khi cột mới xuất hiện. Sự cố lan ra cả traffic v1 chứ không chỉ canary.
Nguyên nhân gốc: trong mọi chiến lược có "hai phiên bản cùng chạy" (rolling, canary, blue-green dùng chung DB), thay đổi database phải tương thích ngược (backward compatible). Cách đúng là tách làm nhiều bước: thêm cột nullable trước (không code nào dùng) → deploy code biết đọc cột → deploy code ghi cột → cuối cùng mới bắt buộc. Đây gọi là expand-contract pattern.
Bài học: chiến lược deploy không chỉ là chuyện traffic của tầng ứng dụng. Khi có state (database), bạn phải thiết kế thay đổi để hai phiên bản code cùng sống hòa bình với một schema.
Hướng dẫn từng bước
Dưới đây là lộ trình thiết lập một pipeline Continuous Deployment với chiến lược canary trên Kubernetes cho một service điển hình:
- Đảm bảo nền tảng CI vững: pipeline phải chạy đủ unit test, integration test và quét bảo mật. Vì không còn manual gate, độ tin cậy của test chính là lưới an toàn duy nhất. Đặt mục tiêu test đáng tin cậy, tránh flaky test.
- Đóng gói artifact bất biến: build image Docker gắn tag bằng commit SHA (ví dụ
myapp:a1b2c3d), không dùng taglatest. Mỗi phiên bản phải xác định được rõ ràng để rollback chính xác.
- Chọn chiến lược theo bối cảnh: dịch vụ nội bộ → recreate/rolling; dịch vụ khách hàng chịu tải cao → canary; dịch vụ cần rollback tức thì → blue-green. Ghi lại lý do lựa chọn.
- Định nghĩa health check thật sự: cấu hình readiness probe và liveness probe để hệ thống biết pod mới đã sẵn sàng nhận traffic hay chưa. Nếu health check giả (luôn trả 200), rolling update sẽ đưa pod hỏng vào phục vụ.
- Thiết lập chia traffic: dùng Argo Rollouts hoặc Flagger để khai báo các bước canary, ví dụ 5% → chờ 5 phút → 25% → 50% → 100%.
- Gắn automated analysis: định nghĩa các chỉ số quyết định (success rate ≥ 99%, p95 latency ≤ 300ms). Nếu canary vi phạm ngưỡng, tự động rollback mà không cần người can thiệp.
- Chuẩn bị đường rollback: đảm bảo lệnh/pipeline quay về phiên bản trước chạy được trong vài phút. Diễn tập rollback ít nhất một lần trên staging.
- Quan sát sau deploy: theo dõi dashboard 15-30 phút đầu. Deploy tự động không có nghĩa là "deploy xong quên luôn".
Lỗi thường gặp & mẹo
- Coi CD là bật công tắc là xong: sai lầm lớn nhất. CD chỉ an toàn khi test đủ mạnh và monitoring đủ tốt. Không có hai thứ đó, tự động deploy chỉ là tự động phát tán bug ra production nhanh hơn.
- Quên tính tương thích ngược của database: như ví dụ 3. Luôn dùng expand-contract; đừng bao giờ deploy code và migration "phá vỡ" trong cùng một bước khi còn phiên bản cũ đang chạy.
- Health check hời hợt: probe chỉ kiểm tra "process còn sống" mà không kiểm tra "kết nối được database, cache" khiến pod hỏng vẫn nhận traffic. Health check phải phản ánh khả năng phục vụ thật.
- Không có rollback được diễn tập: nhiều team có nút rollback nhưng chưa từng bấm thử, đến lúc sự cố mới phát hiện nó không hoạt động. Hãy diễn tập rollback định kỳ.
- Canary quá ngắn: chuyển từ 5% lên 100% chỉ trong 30 giây thì canary vô nghĩa — chưa đủ thời gian để lỗi bộc lộ. Cho canary "ngâm" đủ lâu để quan sát metrics thật.
- Dùng tag
latest: khiến bạn không biết chính xác đang chạy phiên bản nào, rollback thành trò may rủi. Luôn tag theo commit SHA.
- Mẹo tách deploy khỏi release: kết hợp deployment strategy với feature flag. Bạn deploy code ra production (code đã chạy) nhưng tính năng vẫn tắt sau flag, rồi bật dần cho người dùng độc lập với việc deploy. Đây là chiếc "van an toàn" bổ sung cho mọi chiến lược.
Bài tập thực hành
- Phân loại chiến lược: với mỗi tình huống sau, chọn chiến lược deploy phù hợp và giải thích: (a) một cron job xử lý batch ban đêm; (b) API thanh toán của sàn TMĐT giờ cao điểm; (c) phần mềm SaaS cần rollback trong vài giây; (d) trang landing nội bộ cho nhân viên.
- Thiết kế canary: viết ra các bước canary (phần trăm traffic, thời gian ngâm mỗi bước, chỉ số quyết định) cho một service checkout. Xác định ngưỡng success rate và latency mà nếu vi phạm sẽ rollback tự động.
- Tình huống expand-contract: bạn cần đổi tên cột
phonethànhphone_number. Hãy liệt kê trình tự các bước deploy an toàn để trong suốt quá trình luôn có hai phiên bản code cùng chạy mà không lỗi.
- Phân tích chi phí: ước tính chi phí hạ tầng tăng thêm khi chuyển từ rolling update sang blue-green cho một cụm 20 instance. Với con số đó, bạn sẽ tư vấn team dùng blue-green cho service nào và không dùng cho service nào?
- Thực hành thật: nếu có cụm Kubernetes (dùng minikube/kind cũng được), triển khai một Deployment đơn giản với rolling update, chỉnh
maxSurge/maxUnavailable, rồi quan sát quá trình cuốn chiếu quakubectl rollout statusvà thửkubectl rollout undo.
Tóm tắt
Continuous Deployment nghĩa là mỗi commit pass CI sẽ tự động ra production — khác với Continuous Delivery vốn còn giữ một cổng thủ công. Vì bỏ đi bàn tay con người ở bước cuối, CD chỉ an toàn khi test và monitoring đủ mạnh.
Trái tim của CD là các deployment strategy: Recreate (đơn giản, có downtime), Rolling Update (cuốn chiếu, không downtime, mặc định của Kubernetes), Blue-Green (hai môi trường, rollback tức thì nhưng tốn tài nguyên) và Canary (thả nhỏ, an toàn nhất cho hệ thống lớn, cần monitoring và automated analysis). A/B testing tuy giống canary về mặt chia traffic nhưng phục vụ mục tiêu business chứ không phải giảm rủi ro kỹ thuật.
Không có chiến lược "tốt nhất" cho mọi trường hợp — hãy chọn theo yêu cầu về downtime, tốc độ rollback, chi phí và độ nhạy cảm của dịch vụ. Và đừng quên: khi có database, mọi thay đổi phải tương thích ngược theo expand-contract, còn feature flag là chiếc van an toàn giúp bạn tách bạch "deploy code" với "release tính năng". Nắm vững những nguyên tắc này, bạn sẽ biến việc đưa code ra production từ một cửa ải căng thẳng thành một hoạt động bình thường, an toàn và diễn ra nhiều lần mỗi ngày.