Mở đầu — vì sao bài này quan trọng
Suốt 58 bài trước, chúng ta đã học rời rạc từng mảnh ghép: workflow của GitHub Actions, blue-green, canary, feature flags, secret management, self-hosted runners, monorepo, observability... Nhưng trong thực tế, một hệ thống CI/CD trưởng thành không rơi từ trên trời xuống. Nó là kết quả của một hành trình dài, đầy những quyết định đánh đổi, những lần "cháy nhà" lúc nửa đêm, và những bài học đắt giá khi đội ngũ phình to.
Bài này là bài tổng hợp mang tính case study. Thay vì dạy thêm một công cụ mới, tôi muốn dẫn bạn đi qua câu chuyện tiến hóa CI/CD của một công ty e-commerce Việt Nam — từ lúc chỉ có 5 kỹ sư deploy bằng FTP tay, cho đến khi thành 50+ kỹ sư vận hành hàng chục microservice với hàng trăm lần deploy mỗi ngày. Đây là kịch bản mà rất nhiều startup Việt (Tiki, Sendo, các sàn nội địa, các công ty như Shopee VN mở rộng đội local) đã và đang trải qua.
Giá trị của case study nằm ở chỗ: bạn sẽ thấy thứ tự các quyết định. Cái gì nên làm trước, cái gì để sau, và quan trọng nhất — cái gì sẽ "gãy" nếu bạn nhảy cóc. Khi bạn hiểu được logic tiến hóa này, bạn sẽ biết công ty của mình đang ở đâu trên bản đồ, và bước tiếp theo hợp lý là gì. Đây chính là tư duy của một DevOps engineer giỏi: không chạy theo trend, mà giải đúng bài toán của giai đoạn hiện tại.
Khái niệm cốt lõi
CI/CD trưởng thành theo giai đoạn, không phải một-lần-xong
Sai lầm phổ biến nhất của kỹ sư mới là muốn xây ngay một pipeline "chuẩn Google" cho một team 5 người. Điều đó vừa lãng phí vừa phản tác dụng. CI/CD là một hệ thống sống, tiến hóa theo ba trục cùng lúc:
- Quy mô đội ngũ (team scale): 5 → 20 → 50+ kỹ sư. Càng đông, càng cần tự động hóa vì không thể phối hợp bằng miệng.
- Kiến trúc phần mềm (architecture): monolith → modular monolith → microservices. Kiến trúc thay đổi thì pipeline cũng phải đổi theo.
- Tần suất & rủi ro deploy (deployment frequency & risk): 1 lần/tuần lúc nửa đêm → hàng chục lần/ngày trong giờ hành chính mà không ai lo.
Bốn cột mốc điển hình
Tôi gọi đây là bốn "nấc thang" mà hầu hết công ty e-commerce Việt Nam đều leo qua:
Nấc 1 — Deploy thủ công (Manual): Kéo file qua FTP, SSH vào server chạy git pull, restart bằng tay. Nhanh gọn khi nhỏ, nhưng không lặp lại được, không có lịch sử, không rollback.
Nấc 2 — CI cơ bản (Automated build & test): Có một server CI (Jenkins hoặc GitHub Actions) chạy test mỗi khi push. Deploy vẫn bán thủ công qua một script. Đây là lúc "xanh/đỏ" xuất hiện trên pull request.
Nấc 3 — CD tự động cho nhiều môi trường (Multi-env delivery): Có pipeline promote qua dev → staging → production, dùng container, có approval gate, có rollback bằng nút bấm. Bắt đầu tách môi trường rõ ràng.
Nấc 4 — Progressive delivery & platform hóa (Self-service): Canary, feature flags, observability gắn vào pipeline, và một "internal platform" để các team tự deploy mà không cần đội DevOps can thiệp từng lần.
Điểm mấu chốt: mỗi nấc giải quyết nỗi đau của nấc trước, chứ không phải công nghệ mới cho vui. Bạn chỉ leo lên nấc trên khi nỗi đau hiện tại đủ lớn.
Tình huống thực tế
Ví dụ 1 — "ShopViet": từ 5 kỹ sư, FTP deploy đến CI cơ bản (Năm 1)
Bối cảnh: ShopViet là một sàn thương mại điện tử giả định (mô phỏng giai đoạn đầu của nhiều sàn Việt như Sendo thời sơ khai). Năm đầu tiên: 5 kỹ sư, một monolith PHP/Laravel, database MySQL, chạy trên một VPS duy nhất ở Hostinger. Quy trình deploy: anh team lead build local, dùng FileZilla kéo file lên public_html, rồi mở trình duyệt bấm F5 cầu nguyện.
Diễn giải: Ban đầu mọi thứ ổn vì cả team ngồi cạnh nhau. Nhưng đến tháng thứ 8, một sự cố xảy ra: một bạn dev kéo nhầm thư mục vendor cũ đè lên production lúc 11h đêm giờ cao điểm sale, làm sập giỏ hàng trong 40 phút. Không có cách nào rollback nhanh vì không ai biết chính xác trạng thái cũ là gì. Doanh thu mất ước tính khoảng 60 triệu đồng trong 40 phút đó.
Sau sự cố, team làm bước tối thiểu nhưng hiệu quả nhất: dựng GitHub Actions chạy composer install, phpunit, và lint mỗi khi mở PR; deploy production đổi từ FTP tay sang một workflow chạy rsync qua SSH, chỉ trigger khi merge vào nhánh main và có 1 người approve. Chi phí: khoảng 2 ngày công. Kết quả: sự cố "kéo nhầm file" biến mất hoàn toàn, và mỗi lần deploy đều có log để truy vết.
Bài học rút ra: Ở giai đoạn nhỏ, đừng cầu toàn. Chỉ cần loại bỏ thao tác tay dễ sai nhất (kéo file thủ công) và thêm một lớp test tự động là đã tạo ra bước nhảy vọt về độ ổn định. ROI của việc chuyển từ Nấc 1 lên Nấc 2 là cao nhất trong cả hành trình.
Ví dụ 2 — "ShopViet" mở rộng: 50 kỹ sư, 20 microservice, nghẽn pipeline (Năm 3)
Bối cảnh: Sau 3 năm, ShopViet gọi vốn thành công, đội ngũ lên 50 kỹ sư chia thành 6 squad (Cart, Payment, Search, Seller, Logistics, Platform). Monolith cũ bị tách thành khoảng 20 microservice chạy trên Kubernetes (EKS ở AWS Singapore). Mỗi ngày có 40–80 lần deploy. Nhưng team bắt đầu than phiền dữ dội.
Vấn đề: Vì tất cả service ban đầu nằm chung một monorepo với một workflow khổng lồ, mỗi lần sửa một dòng ở service Payment thì CI vẫn build lại toàn bộ 20 service, mất 47 phút. Kỹ sư phải xếp hàng chờ runner. Tệ hơn, khi 5 squad cùng deploy vào giờ chiều, họ giẫm chân nhau ở môi trường staging — một team đang test thì team khác deploy đè lên. Lead time (từ commit đến production) phình lên trung bình 3 tiếng.
Diễn giải & giải pháp: Đội Platform (mới thành lập, 4 người tách ra chuyên lo CI/CD) xử lý theo thứ tự:
- Áp dụng build có chọn lọc bằng path filter và caching: chỉ build service nào thực sự thay đổi. Thời gian CI trung bình rớt từ 47 phút xuống 6 phút.
- Chuyển sang self-hosted runners trên chính cụm K8s để giảm chi phí và tăng tính song song, vì hóa đơn GitHub-hosted runner đã lên tới hơn 2.000 USD/tháng.
- Cấp cho mỗi PR một preview environment ephemeral (namespace K8s riêng, tự xóa sau khi merge), chấm dứt cảnh giẫm chân nhau ở staging.
- Với service Payment nhạy cảm, bật canary deployment kết hợp feature flag: chỉ 5% traffic đi vào version mới, theo dõi error rate qua metric trong 15 phút rồi mới rollout đủ.
Ví dụ 3 — Tiki và bài toán ngày sale lớn (bối cảnh thực tế hóa)
Bối cảnh: Các sàn lớn ở Việt Nam như Tiki, Shopee VN đối mặt với những ngày sale khủng (11.11, 12.12) khi traffic tăng gấp 20–50 lần. Trong bối cảnh đó, một quyết định vận hành CI/CD rất quan trọng: code freeze.
Diễn giải: Nhiều đội áp dụng chính sách "đóng băng deploy" trong 48–72 giờ quanh ngày sale lớn — không cho deploy tính năng mới, chỉ cho phép hotfix khẩn cấp qua một đường pipeline riêng có approval nghiêm ngặt từ trưởng nhóm và on-call lead. Lý do: mỗi thay đổi trong giai đoạn traffic cực đại là một rủi ro không đáng có. Đồng thời, họ tận dụng feature flag để "giấu" các tính năng đã deploy sẵn từ trước và chỉ bật lên đúng 0h ngày sale mà không cần deploy lại — tránh rủi ro deploy vào đúng giờ vàng.
Một chi tiết đáng học: các đội này thường chạy disaster recovery drill (diễn tập sự cố) trước mùa sale vài tuần, mô phỏng việc rollback nhanh một service quan trọng để chắc chắn nút "quay lui" thực sự hoạt động khi cần.
Bài học rút ra: CI/CD không chỉ là tự động hóa kỹ thuật, mà còn là chính sách vận hành gắn với bối cảnh kinh doanh. Càng lên cao, pipeline càng phải "biết" khi nào nên đi nhanh và khi nào nên phanh gấp. Tốc độ deploy cao không có nghĩa là lúc nào cũng deploy — mà là có khả năng deploy nhanh khi cần, và có kỷ luật để dừng khi rủi ro lớn.
Hướng dẫn từng bước
Nếu bạn đang là kỹ sư trong một công ty e-commerce Việt và muốn vẽ lộ trình tiến hóa CI/CD, đây là các bước tôi khuyên:
Bước 1 — Định vị công ty đang ở nấc nào. Trả lời trung thực: Deploy hiện tại thủ công bao nhiêu phần trăm? Có test tự động chặn merge không? Có rollback trong 5 phút được không? Có tách môi trường không? Câu trả lời cho bạn biết bạn đang ở Nấc 1, 2, 3 hay 4.
Bước 2 — Xác định nỗi đau lớn nhất hiện tại, không phải nỗi đau tưởng tượng. Đừng xây canary khi bạn còn deploy bằng tay. Hãy hỏi: sự cố gần nhất do đâu? Kỹ sư mất thời gian chờ ở đâu? Nếu answer là "deploy tay hay sai" → ưu tiên tự động hóa deploy. Nếu là "CI chậm" → ưu tiên caching và selective build.
Bước 3 — Chọn một cải tiến có ROI cao nhất cho nấc kế tiếp. Từ Nấc 1 lên 2: dựng CI chạy test + tự động hóa deploy. Từ 2 lên 3: container hóa + tách môi trường + approval gate + rollback. Từ 3 lên 4: preview env, canary/feature flag, observability trong pipeline, và self-service.
Bước 4 — Đo lường bằng DORA metrics. Bốn chỉ số vàng: lead time (commit đến prod), deployment frequency (tần suất deploy), change failure rate (tỷ lệ deploy gây lỗi), và MTTR (thời gian phục hồi trung bình). Ghi lại con số trước khi cải tiến để chứng minh giá trị.
Bước 5 — Chỉ lập đội Platform riêng khi chi phí phối hợp vượt chi phí đội. Thường là quanh mốc 20–30 kỹ sư. Trước đó, để CI/CD là trách nhiệm chung của cả team hiệu quả hơn.
Bước 6 — Viết lại (retrospective) sau mỗi sự cố lớn. Mỗi lần "cháy nhà" là một cơ hội để nâng nấc. Sự cố kéo nhầm file của ShopViet chính là thứ đẩy họ lên Nấc 2.
Lỗi thường gặp & mẹo
Lỗi 1 — Over-engineering từ sớm. Team 5 người dựng service mesh, canary, ArgoCD đủ bộ. Kết quả: 80% thời gian dành để bảo trì hạ tầng thay vì làm sản phẩm. Mẹo: chọn công cụ vừa đủ cho nấc hiện tại, để lại chỗ trống có thể mở rộng.
Lỗi 2 — Bỏ qua rollback cho đến khi cần nó. Rất nhiều team chỉ nghĩ đến rollback lúc đang cháy. Mẹo: dựng và thử nghiệm nút rollback ngay từ Nấc 2, kể cả khi nó chỉ là deploy lại tag cũ.
Lỗi 3 — Tách microservice quá sớm. ShopViet đã đúng khi giữ monolith đến năm 3. Tách service khi đội còn nhỏ chỉ tạo ra "distributed monolith" — đau hơn cả monolith. Mẹo: tách theo ranh giới đội ngũ, không theo cảm hứng kiến trúc.
Lỗi 4 — Không cache và không selective build. Đây là nguyên nhân số 1 khiến CI chậm khi codebase lớn. Mẹo: đo thời gian CI mỗi tuần; khi nó vượt 10 phút, đó là tín hiệu phải tối ưu.
Lỗi 5 — Deploy vào giờ cao điểm mà không có cơ chế phanh. Mẹo: dùng feature flag để tách "deploy code" khỏi "bật tính năng", và cân nhắc code freeze quanh sự kiện lớn.
Mẹo vàng: Mỗi khi định thêm một công cụ CI/CD mới, tự hỏi "công cụ này giải nỗi đau nào có thật của tôi tuần này?" Nếu không trả lời được cụ thể, chưa cần nó.
Bài tập thực hành
- Tự định vị: Chọn một công ty/dự án bạn đang làm (hoặc từng làm). Xác định nó đang ở Nấc 1, 2, 3 hay 4 theo bốn tiêu chí ở Bước 1. Viết 3–5 câu lý giải.
- Vẽ lộ trình: Với công ty đó, đề xuất một cải tiến duy nhất cho nấc kế tiếp và giải thích tại sao nó có ROI cao nhất. Ước lượng chi phí (ngày công) và lợi ích (giảm bao nhiêu phút lead time, hay giảm rủi ro gì).
- Phân tích ShopViet: Giả sử ShopViet năm 4 muốn thêm một sàn quốc tế mới, cần deploy ra cả AWS Singapore lẫn một region mới. Hãy liệt kê 3 thay đổi bạn sẽ làm với pipeline hiện tại (gợi ý: multi-region, promotion strategy, config theo môi trường).
- Thiết kế chính sách sale: Viết một chính sách code freeze + hotfix cho ShopViet trong 72 giờ quanh ngày 11.11. Ai được approve hotfix? Đường pipeline khẩn cấp khác đường thường ở điểm nào?
- Đo lường: Chọn một trong bốn DORA metrics và mô tả cách bạn sẽ thu thập số liệu đó tự động từ pipeline (gợi ý: gắn timestamp ở lúc commit và lúc deploy thành công).
Tóm tắt
CI/CD của một công ty e-commerce Việt Nam không phải là một bản thiết kế cố định, mà là một hành trình tiến hóa qua bốn nấc: từ deploy thủ công, đến CI cơ bản, đến CD đa môi trường, và cuối cùng là progressive delivery cộng platform hóa. Câu chuyện của ShopViet cho thấy mỗi nấc được sinh ra để giải quyết nỗi đau cụ thể của nấc trước: sự cố kéo nhầm file đẩy họ lên CI tự động; nghẽn pipeline và giẫm chân nhau khi lên 50 kỹ sư đẩy họ tới selective build, preview environment và canary. Bài học từ các sàn lớn như Tiki, Shopee VN nhắc ta rằng CI/CD trưởng thành còn là chuyện chính sách vận hành — biết khi nào đi nhanh và khi nào phanh gấp quanh mùa sale.
Điều quan trọng nhất bạn nên mang theo: hãy luôn giải đúng nỗi đau của giai đoạn hiện tại, đo lường bằng DORA metrics, và chỉ leo lên nấc trên khi nỗi đau hiện tại đủ lớn. Đó chính là tư duy phân biệt một DevOps engineer chạy theo trend với một người thực sự tạo ra giá trị cho tổ chức.