Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Bài 36 — Hotfix & Emergency Deploy

Đã đọc 0/13 mục · còn khoảng 13 phút
Cách chúng tôi ghi nhận

Một mục được tính là đã đọc khi nó nằm giữa màn hình đủ lâu so với độ dài của nó (bảng và sơ đồ tính một nửa), bạn vẫn đang cuộn hoặc bấm phím, và bạn đã thấy tới cuối mục. Lướt nhanh hoặc để tab mở không được tính. Chúng tôi chỉ lưu thời gian đọc từng mục — không lưu gì khác.

Mở đầu — vì sao bài này quan trọng

Hãy tưởng tượng lúc 21h tối thứ Bảy, đúng vào giờ cao điểm của một chiến dịch flash sale. Hệ thống thanh toán đột nhiên trả về lỗi 500 cho mọi giao dịch thẻ tín dụng. Mỗi phút trôi qua, công ty mất hàng chục triệu đồng doanh thu và hàng nghìn khách hàng bực bội rời đi. Trong khoảnh khắc đó, không ai quan tâm quy trình review thông thường mất bao lâu, không ai bàn về sprint planning. Câu hỏi duy nhất là: "Làm sao đưa bản vá lên production an toàn và nhanh nhất có thể?"

Đó chính là lúc bạn cần một quy trình hotfix và emergency deploy đã được chuẩn bị sẵn. Sự khác biệt giữa một đội ngũ chuyên nghiệp và một đội ngũ nghiệp dư không nằm ở chỗ họ có gặp sự cố hay không — mọi hệ thống production đều sẽ gặp sự cố. Sự khác biệt nằm ở chỗ khi khủng hoảng ập đến, họ có một con đường đã được vạch sẵn để đi qua, hay họ phải vừa chạy vừa suy nghĩ trong hoảng loạn.

Trong toàn bộ khóa học, chúng ta đã nói nhiều về pipeline "bình thường": lint, test, build, deploy theo đúng trình tự chậm rãi và kỹ lưỡng. Bài này nói về mặt còn lại: con đường khẩn cấp — khi bạn cần cắt bớt (nhưng không phải bỏ hết) các bước để cứu production, và làm điều đó mà không tạo ra thảm họa thứ hai. Đây là kỹ năng phân biệt một DevOps engineer thực thụ với người chỉ biết chạy pipeline lúc mọi thứ suôn sẻ.

Khái niệm cốt lõi

Hotfix là gì và khi nào cần hotfix?

Hotfix là một bản vá nhỏ, tập trung, được đưa lên production ngoài chu kỳ release thông thường để giải quyết một vấn đề nghiêm trọng đang xảy ra ngay lúc này. Điểm mấu chốt: hotfix không phải là "deploy nhanh mọi thứ", mà là "deploy đúng một thay đổi tối thiểu để dập tắt đám cháy".

Chúng ta cần phân loại mức độ nghiêm trọng để quyết định có kích hoạt quy trình hotfix hay không. Thông thường trong ngành, người ta dùng thang severity (SEV) hoặc priority (P):

  • P0 / SEV-1: Hệ thống ngừng hoạt động hoàn toàn hoặc chức năng cốt lõi (thanh toán, đăng nhập, đặt hàng) bị hỏng. Ảnh hưởng toàn bộ hoặc phần lớn người dùng. Cần hành động ngay lập tức, bất kể giờ giấc.
  • P1 / SEV-2: Chức năng quan trọng bị suy giảm nghiêm trọng nhưng còn có cách xoay xở (workaround). Cần xử lý trong vài giờ.
  • P2 trở xuống: Có thể đợi đến release thông thường.
Ba tình huống điển hình bắt buộc kích hoạt hotfix:

  • Production critical bug (P0/SEV-1): Ví dụ nút "Đặt hàng" không hoạt động, giỏ hàng tính sai tiền, hoặc trang chủ trắng xóa.
  • Lỗ hổng bảo mật cần vá ngay: Một CVE nghiêm trọng vừa được công bố, hoặc bạn phát hiện endpoint đang lộ dữ liệu người dùng. Kẻ tấn công không đợi bạn đến sprint sau.
  • Nguy cơ hỏng/mất dữ liệu (data corruption): Đây là loại nguy hiểm nhất, vì mỗi giây trôi qua có thể làm hỏng thêm dữ liệu không thể khôi phục. Ví dụ một bug ghi đè sai giá trị vào database.

Hotfix branch pattern

Cách tổ chức Git cho hotfix phụ thuộc vào chiến lược branching của đội bạn, nhưng nguyên tắc chung không đổi: hotfix phải xuất phát từ đúng phiên bản code đang chạy trên production, không phải từ nhánh main/develop đang chứa các thay đổi chưa release.

Với mô hình GitFlow, hotfix có một nhánh riêng biệt:

git checkout -b hotfix/payment-500-error <production-tag>
# ...sửa lỗi tối thiểu...
git commit -m "fix: handle null gateway response in payment"
# Merge vào cả main VÀ develop để lỗi không tái xuất hiện

Điểm cực kỳ quan trọng và hay bị quên: sau khi hotfix lên production, bạn phải merge ngược thay đổi đó vào nhánh phát triển chính (develop hoặc main). Nếu không, ở lần release tiếp theo, code cũ sẽ đè lên và lỗi quay trở lại — hiện tượng gọi là regression (thoái lui).

Với mô hình trunk-based, bạn thường tạo nhánh ngắn từ commit đang chạy trên production (được đánh dấu bằng tag), vá lỗi, rồi cherry-pick hoặc fast-forward. Nhưng dù mô hình nào, một quy tắc vàng luôn đúng: hotfix branch phải nhỏ. Chỉ chứa đúng thay đổi cần thiết, không kèm theo refactor, không "tiện tay sửa luôn". Càng nhiều thay đổi, càng nhiều rủi ro, và bạn đang deploy trong lúc mà rủi ro là thứ ít được phép nhất.

Emergency deploy pipeline — con đường tắt có kiểm soát

Pipeline bình thường của bạn có thể chạy 20–40 phút với đầy đủ integration test, e2e test, security scan. Trong tình huống P0, 40 phút là quá lâu. Vì vậy nhiều đội xây dựng một emergency pipeline riêng: giữ lại các bước tối thiểu không thể bỏ (build, unit test cốt lõi, smoke test) và tạm bỏ qua các bước tốn thời gian nhất.

Nguyên tắc là cắt tốc độ chứ không cắt an toàn. Không bao giờ được deploy thẳng ra production mà bỏ qua hoàn toàn kiểm thử — vì "sửa nhanh" mà tạo ra bug tệ hơn thì bạn đã biến một đám cháy thành hai đám cháy. Cụm từ cần nhớ: "Speed with guardrails" (nhanh nhưng có rào chắn).

Tình huống thực tế

Tình huống 1 — Tiki và bug tính sai phí vận chuyển giờ cao điểm

Bối cảnh: Một sàn thương mại điện tử lớn tại Việt Nam (gọi là sàn X, quy mô tương tự Tiki) triển khai một thay đổi nhỏ về logic tính phí ship lúc 18h. Đến 19h30, bộ phận CSKH báo hàng loạt khách hàng phản ánh phí ship đội lên gấp 3–4 lần một cách vô lý. Đơn hàng bị hủy tăng vọt 40% chỉ trong 30 phút.

Diễn giải: On-call engineer nhận cảnh báo, xác định đây là P0 (ảnh hưởng doanh thu trực tiếp). Nhóm đứng trước hai lựa chọn: rollback (quay về phiên bản trước) hay hotfix (vá lỗi). Vì bản deploy 18h còn chứa vài tính năng khác đang chạy tốt, họ chọn hotfix: tạo nhánh hotfix/shipping-fee-multiplier từ tag production, sửa đúng một dòng nơi biến hệ số bị nhân nhầm, chạy emergency pipeline (chỉ unit test của module shipping + smoke test), và deploy trong 12 phút.

Bài học: Khi bản deploy hiện tại chứa cả phần tốt lẫn phần xấu, hotfix có mục tiêu (targeted fix) thường tốt hơn rollback toàn bộ. Nhưng điều làm nên khác biệt ở đây là họ đã có sẵn emergency pipeline và on-call runbook — không ai phải tìm kiếm "làm thế nào để deploy khẩn cấp" trong lúc hoảng loạn.

Tình huống 2 — Startup fintech ĐNÁ và lỗ hổng bảo mật lộ token

Bối cảnh: Một startup fintech ở Đông Nam Á (quy mô Series A, khoảng 60 kỹ sư) nhận email từ một researcher bảo mật lúc 23h: một API endpoint đang trả về JWT token của người dùng khác trong một số điều kiện race condition. Đây vừa là lỗ hổng bảo mật, vừa là nguy cơ lộ dữ liệu tài chính — bắt buộc vá ngay.

Diễn giải: Đội security kích hoạt quy trình emergency. Họ không thể đợi đến sáng vì lỗ hổng đã được người ngoài biết. Một senior engineer viết bản vá tối thiểu: thêm kiểm tra ownership trước khi trả token. Vì đây là thay đổi bảo mật nhạy cảm, họ không bỏ qua code review — thay vào đó rút gọn còn một reviewer duy nhất là security lead (thay vì quy trình 2 reviewer thông thường). Bản vá qua emergency pipeline có chạy SAST scan cho riêng file thay đổi, deploy lên production lúc 00h20, kèm feature flag để có thể tắt ngay nếu vá gây tác dụng phụ.

Bài học: Emergency không có nghĩa là bỏ qua tất cả kiểm soát. Với lỗ hổng bảo mật, bạn giảm số lượng bước nhưng giữ lại đúng những bước bảo vệ quan trọng nhất. Và luôn có kế hoạch B (feature flag để tắt ngay) — vì bản vá viết vội lúc nửa đêm có xác suất sai cao hơn bình thường.

Tình huống 3 — Đội nghiệp dư và cái bẫy "sửa thẳng trên production"

Bối cảnh: Một công ty phần mềm nhỏ gặp bug hiển thị sai số dư ví điện tử. Một developer, vì "cho nhanh", SSH thẳng vào server production và sửa file code trực tiếp lúc 2h sáng, không qua Git, không qua pipeline.

Diễn giải: Bản vá đó tình cờ chạy được, khủng hoảng tạm lắng. Nhưng ba ngày sau, đội deploy một release bình thường — và bản vá thủ công trên server bị đè mất hoàn toàn. Bug số dư quay trở lại, lần này khó truy vết hơn vì không ai nhớ đã sửa gì. Tệ hơn, thay đổi thủ công đó không hề tồn tại trong Git nên hoàn toàn vô hình với cả đội.

Bài học: Đây là anti-pattern kinh điển của hotfix. Dù khẩn cấp đến đâu, hotfix vẫn phải đi qua Git và pipeline. "Nhanh" không có nghĩa là "bỏ qua nguồn sự thật (source of truth)". Một hotfix không được ghi lại trong version control là một quả bom hẹn giờ.

Hướng dẫn từng bước

Dưới đây là quy trình hotfix chuẩn mà bạn có thể áp dụng và điều chỉnh cho đội mình.

Bước 1 — Phát hiện và đánh giá mức độ (Triage). Xác định đây có thực sự là P0/P1 cần hotfix không. Đừng dùng "búa hotfix" cho mọi cái đinh — nếu vấn đề có thể đợi đến release chiều nay thì nó không phải hotfix. Ghi lại severity và người ra quyết định (thường là incident commander).

Bước 2 — Quyết định: Rollback hay Hotfix? Đây là ngã rẽ quan trọng. Nếu sự cố do bản deploy gần nhất gây ra và bản trước đó lành lặn, rollback thường nhanh và an toàn hơn — hãy ưu tiên. Chỉ chọn hotfix khi rollback không khả thi (ví dụ đã có database migration không thể lùi, hoặc bản deploy chứa cả phần tốt cần giữ).

Bước 3 — Tạo hotfix branch từ đúng phiên bản production. Luôn xuất phát từ tag/commit đang chạy trên production, không phải từ nhánh phát triển. Đặt tên rõ ràng: hotfix/<mô-tả-ngắn>.

Bước 4 — Viết bản vá tối thiểu. Chỉ sửa đúng cái gây lỗi. Kháng cự cám dỗ dọn dẹp hay cải tiến kèm theo. Diff càng nhỏ, càng dễ review và càng ít rủi ro.

Bước 5 — Review rút gọn nhưng không bỏ. Có ít nhất một người khác nhìn qua. Bốn con mắt tốt hơn hai, đặc biệt lúc tác giả đang mệt và căng thẳng.

Bước 6 — Chạy emergency pipeline. Build, unit test liên quan, smoke test. Nếu là bảo mật thì thêm SAST cho phần thay đổi. Không deploy thẳng tay.

Bước 7 — Deploy có kiểm soát. Ưu tiên deploy dần (canary/từng phần) nếu hạ tầng cho phép, và luôn có sẵn nút tắt (feature flag hoặc kế hoạch rollback).

Bước 8 — Xác minh trên production (verify). Đừng chỉ deploy rồi tuyên bố xong. Kiểm tra bằng chính hành vi người dùng gặp lỗi ban đầu: đặt thử một đơn hàng, thực hiện thử một giao dịch. Theo dõi metrics và error rate.

Bước 9 — Merge ngược và dọn dẹp. Merge hotfix vào nhánh phát triển chính để lỗi không tái xuất. Đây là bước hay bị bỏ quên nhất khi ai cũng thở phào nhẹ nhõm.

Bước 10 — Postmortem không đổ lỗi (blameless). Sau khi khói tan, ngồi lại phân tích: vì sao lỗi lọt qua? Vì sao mất lâu để phát hiện? Làm sao ngăn nó lần sau? Mục tiêu là cải thiện hệ thống, không phải tìm người để trách.

Lỗi thường gặp & mẹo

Lỗi 1 — Hotfix "sưng phù". Bắt đầu vá một dòng rồi kết thúc bằng một PR 300 dòng vì "tiện tay". Mỗi dòng thêm vào là một rủi ro thêm vào lúc bạn ít được phép sai nhất. Mẹo: nếu diff vượt quá vài chục dòng, dừng lại và tự hỏi có phải bạn đang giải quyết đúng vấn đề tối thiểu không.

Lỗi 2 — Quên merge ngược. Hotfix lên production nhưng không merge vào develop/main, khiến lỗi quay lại ở release sau. Mẹo: đưa bước merge-back vào checklist bắt buộc, thậm chí tự động hóa bằng một PR mở sẵn.

Lỗi 3 — Sửa thủ công trên server, bỏ qua Git. Như tình huống 3. Không bao giờ làm điều này. Nếu pipeline quá chậm đến mức bạn bị cám dỗ SSH vào server, thì vấn đề thật sự là pipeline của bạn cần một "làn khẩn cấp" nhanh hơn.

Lỗi 4 — Bỏ qua bước verify. Deploy xong tuyên bố "đã fix" mà chưa thực sự kiểm tra hành vi gốc. Nhiều khi bản vá không đúng chỗ, hoặc gây ra tác dụng phụ mới. Luôn tái hiện lại chính kịch bản lỗi ban đầu.

Lỗi 5 — Không có ai được ủy quyền ra quyết định. Trong khủng hoảng, "ai đó cũng được" nghĩa là "không ai cả". Mẹo: xác định trước vai trò incident commander — người có quyền quyết định deploy khẩn cấp, kể cả khi phải bỏ qua vài quy trình.

Mẹo vàng — Chuẩn bị runbook trước khi cần đến. Cách duy nhất để bình tĩnh lúc 2h sáng là đã diễn tập trước. Có sẵn tài liệu: emergency pipeline ở đâu, ai on-call, cách rollback, cách bật/tắt feature flag. Runbook viết lúc bình yên luôn tốt hơn quyết định ứng biến lúc hoảng loạn.

Bài tập thực hành

  • Thiết kế emergency pipeline. Lấy pipeline CI/CD hiện tại của bạn (hoặc một pipeline giả định 6 bước). Xác định bước nào bắt buộc giữ trong tình huống P0 và bước nào có thể tạm bỏ. Viết lý do cho từng quyết định. Mục tiêu: rút thời gian pipeline xuống dưới 15 phút mà không bỏ bất kỳ "guardrail" quan trọng nào.
  • Viết một hotfix runbook một trang. Bao gồm: bảng phân loại severity, cây quyết định "rollback hay hotfix", các lệnh Git tạo hotfix branch, và danh sách người on-call. Đưa cho một đồng nghiệp đọc và hỏi họ: "Nếu production sập lúc 2h sáng, bạn làm theo được không?"
  • Diễn tập (fire drill). Cùng đội giả lập một sự cố P0: chọn một bug giả định, bấm giờ, và chạy toàn bộ quy trình từ triage đến verify trên môi trường staging. Ghi lại tổng thời gian và những chỗ vấp. Lặp lại cho đến khi cả đội thuần thục.
  • Phân tích ngã rẽ rollback/hotfix. Với ba tình huống sau, quyết định nên rollback hay hotfix và giải thích: (a) bản deploy 10 phút trước gây lỗi 500, chưa có migration; (b) lỗi tồn tại từ 3 ngày trước mới bị phát hiện; (c) bản deploy vừa chạy migration thêm cột và gây lỗi.

Tóm tắt

Hotfix và emergency deploy là mặt "khẩn cấp" của CI/CD — con đường bạn đi khi production đang cháy và không thể đợi chu kỳ release thông thường. Những điểm cốt lõi cần ghi nhớ:

  • Chỉ hotfix cho vấn đề thực sự nghiêm trọng: P0/SEV-1, lỗ hổng bảo mật, hoặc nguy cơ hỏng dữ liệu. Đừng lạm dụng.
  • Cân nhắc rollback trước: thường nhanh và an toàn hơn hotfix, trừ khi rollback không khả thi.
  • Hotfix branch phải xuất phát từ production và phải nhỏ: chỉ đúng thay đổi tối thiểu, không refactor kèm theo.
  • Cắt tốc độ chứ không cắt an toàn: emergency pipeline giữ lại guardrail quan trọng (build, test cốt lõi, smoke test), chỉ bỏ phần chậm.
  • Không bao giờ sửa thủ công trên server: mọi thay đổi phải qua Git — đó là nguồn sự thật duy nhất.
  • Luôn merge ngược và verify: đừng để lỗi tái xuất, và đừng tuyên bố "đã fix" khi chưa thực sự kiểm tra.
  • Chuẩn bị runbook trước: sự bình tĩnh trong khủng hoảng đến từ việc đã diễn tập, không phải từ ứng biến.
Một đội ngũ giỏi không phải là đội không bao giờ gặp sự cố, mà là đội có con đường rõ ràng để đi qua sự cố một cách an toàn. Hãy xây dựng con đường đó ngay hôm nay, khi trời còn quang.

Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi