Vì sao kiểm tra sức khỏe trả về 200 OK vẫn có thể che giấu sự cố gián đoạn dịch vụ hoàn toàn
Một yêu cầu GET được cache đã trả về trạng thái thành công trong khi máy chủ nguồn đang gặp sự cố, khiến tất cả các giao dịch đặt hàng của người mua thất bại âm thầm trong nhiều giờ.
Bài được dịch tự động từ bản gốc tiếng Anh.
Một dịch vụ thương mại điện tử tự lưu trữ (self-hosted) trông như hoạt động bình thường đối với hệ thống giám sát trong suốt một buổi tối, liên tục trả về mã trạng thái thành công mỗi năm phút. Trên thực tế, đường hầm kết nối tới máy chủ nguồn (origin server) phía sau đã ngừng hoạt động từ nhiều giờ trước đó, khiến mọi thao tác gửi đơn hàng của người mua đều thất bại, trong khi trang frontend vẫn hiển thị được nhờ cơ chế cache.
Chuyện gì đã xảy ra
Sự cố bắt đầu khi đường hầm origin kết nối giữa điểm biên của mạng phân phối nội dung (CDN) và máy chủ backend ngừng phản hồi. Bất chấp lỗi nghiêm trọng này, quy trình kiểm tra sức khỏe tự động vẫn tiếp tục báo cáo rằng dịch vụ đang hoạt động tốt. Quy trình này thực hiện một yêu cầu GET đơn giản tới trang dành cho người mua, và CDN phục vụ nội dung này từ bộ nhớ đệm (cache) bằng phiên bản HTML hợp lệ cuối cùng. Kết quả là hệ thống nhận được mã trạng thái 200 OK, báo hiệu cho hệ thống giám sát rằng mọi thứ vẫn đang vận hành đúng cách.
Trong khi đó, những người dùng thực tế cố gắng mua hàng lại trải nghiệm sự sụp đổ hoàn toàn. Trình duyệt của họ gửi các yêu cầu POST kèm dữ liệu biểu mẫu để xác nhận đơn hàng. Khác với trang HTML tĩnh, các cuộc gọi API động này không thể được phục vụ từ cache của CDN. Chúng được định tuyến trực tiếp tới đường hầm origin, vốn đã chết. Nút biên (edge node) trả về trang lỗi 1033 cho các yêu cầu này, nhưng vì không ai chủ động theo dõi nhật ký lỗi phía người dùng, sự cố đã không được đội ngũ vận hành phát hiện. Năm lần gửi đơn hàng thực tế từ ba mạng khác nhau đã thất bại trong khoảng thời gian này, nhưng không có cảnh báo nào được kích hoạt.
Tình huống này xuất phát từ một bản sửa lỗi trước đó nhằm giải quyết một vấn đề khác. Một sự cố trước đây liên quan đến một đường dẫn tĩnh mà nút biên tự xử lý độc lập với máy chủ nguồn, dẫn đến các cảnh báo giả (false positives). Đội ngũ đã khắc phục bằng cách chỉ định bài kiểm tra sức khỏe hướng tới trang người mua, nơi thực sự phụ thuộc vào máy chủ nguồn. Tuy nhiên, họ vẫn giữ nguyên phương thức GET cho bài kiểm tra. Mặc dù đường dẫn đã đúng, nhưng phương thức thì không, vì một yêu cầu GET được cache không xác minh được tính sẵn sàng của logic xử lý backend.
Chi tiết chính
- Bài kiểm tra sức khỏe chạy mỗi năm phút và luôn nhận được phản hồi 200 OK từ cache của CDN.
- Đường hầm origin bị sập trong vài giờ, khiến tất cả các yêu cầu POST từ người mua thất bại với lỗi 1033.
- Năm nỗ lực mua hàng thực tế thất bại trên ba mạng riêng biệt mà không kích hoạt bất kỳ cảnh báo vận hành nào.
- Bản sửa lỗi trước đó đã thay đổi mục tiêu kiểm tra sức khỏe sang trang người mua nhưng sai lầm khi vẫn giữ nó dưới dạng yêu cầu GET.
- Một yêu cầu POST tới endpoint JSON-RPC sẽ buộc máy chủ nguồn phải phản hồi, qua đó lộ ra sự cố ngay lập tức.
- Giải pháp cuối cùng chỉ đòi hỏi một thay đổi duy nhất: chuyển phương thức kiểm tra sức khỏe từ GET sang POST.
Bối cảnh
Để hiểu rõ sự cố này, cần phân biệt cách các CDN xử lý các phương thức HTTP khác nhau. Yêu cầu GET dùng để truy xuất dữ liệu và thường có thể được cache. Các CDN lưu trữ bản sao phản hồi tại các vị trí biên gần người dùng để giảm độ trễ và tải cho máy chủ nguồn. Nếu máy chủ nguồn gặp sự cố, CDN vẫn có thể tiếp tục phục vụ các bản sao đã cache, khiến trang web trông như vẫn hoạt động mặc dù backend không thể truy cập. Điều này hữu ích cho việc đọc nội dung nhưng gây hiểu lầm khi dùng để kiểm tra tình trạng hệ thống.
Ngược lại, yêu cầu POST được dùng để gửi dữ liệu tới máy chủ, chẳng hạn như gửi biểu mẫu hoặc tạo bản ghi. Những yêu cầu này thường không được cache vì chúng làm thay đổi trạng thái ứng dụng. Khi một yêu cầu POST đến, CDN phải chuyển tiếp nó tới máy chủ nguồn để xử lý. Nếu máy chủ nguồn gặp sự cố, yêu cầu sẽ thất bại ngay lập tức. Do đó, một bài kiểm tra sức khỏe sử dụng GET có thể chỉ xác minh tính khả dụng của cache, trong khi bài kiểm tra POST xác minh tính khả dụng của logic ứng dụng thực tế và kết nối cơ sở dữ liệu.
Tại sao điều này quan trọng
Đối với các đội ngũ tự vận hành phần mềm, sự cố này nhấn mạnh một bẫy phổ biến trong khả năng quan sát (observability): đo lường sai đối tượng. Mã trạng thái chỉ là bằng chứng về một yêu cầu cụ thể, không phải về toàn bộ dịch vụ. Nếu bài kiểm tra sức khỏe của bạn không mô phỏng đường đi quan trọng nhất (critical path) của người dùng, nó sẽ mang lại cảm giác an toàn giả tạo. Trong trường hợp này, bài kiểm tra đo lường độ bền vững của cache CDN chứ không phải sức khỏe của backend ứng dụng. Sự phân biệt này cực kỳ quan trọng đối với những người tự lưu trữ (self-hosters), những người dựa vào giám sát tự động để phát hiện sự cố trước khi khách hàng kịp nhận ra.
Chi phí cho cấu hình sai này rất lớn xét về mặt mất niềm tin và doanh thu. Vì hệ thống giám sát báo cáo thành công, không kỹ sư nào bị đánh thức, và vấn đề kéo dài trong nhiều giờ. Sự im lặng của nhật ký thật đáng lừa; nhật ký CDN cho thấy các phản hồi 200 thành công, che giấu các lỗi 1033 đang xảy ra trên các đường dẫn không được cache. Điều này chứng minh rằng việc ghi nhật ký và cảnh báo toàn diện phải tính đến sự khác biệt giữa thành công ở cấp độ biên (edge-level) và chức năng ở cấp độ nguồn (origin-level). Nếu không đồng bộ hóa các bài kiểm tra sức khỏe với hành vi của người dùng, các đội ngũ có nguy cơ bỏ sót những thất bại âm thầm, chỉ trở nên rõ ràng khi khách hàng phàn nàn.
Bạn có thể làm gì
- Kiểm toán các bài kiểm tra sức khỏe của bạn để đảm bảo chúng sử dụng cùng phương thức HTTP và đường dẫn như các hành động người dùng quan trọng nhất.
- Thay thế các bài kiểm tra GET đơn giản trên các trang được cache bằng các yêu cầu POST tới các endpoint động đòi hỏi máy chủ nguồn phải xử lý.
- Xác minh rằng bài kiểm tra sức khỏe của bạn mong đợi nội dung cụ thể trong phần thân phản hồi, chứ không chỉ là mã trạng thái 200.
- Thử nghiệm hệ thống giám sát bằng cách cố ý tắt máy chủ nguồn để xem liệu bài kiểm tra sức khỏe có báo cáo đúng sự cố hay không.
- Triển khai các bài kiểm tra riêng biệt cho tính khả dụng của CDN và sức khỏe của origin để phân biệt giữa vấn đề cache biên và lỗi backend.
- Xem xét nhật ký cho cả loại yêu cầu thành công và thất bại để đảm bảo cảnh báo của bạn bao phủ các tương tác động, không được cache.



