Blog

Một lần republish thất bại không bao giờ xóa backend đang chạy

Ngày 25 tháng 9, một lần republish thất bại đã xóa backend của một app. Backend này đã hoạt động ổn định khoảng hai tiếng rưỡi. Đoạn mã hoàn tác một lần publish lỗi đã xóa mọi ứng dụng server mà lần publish đó từng đụng tới, kể cả những ứng dụng đang phục vụ trước khi nó bắt đầu. Giờ đây chỉ những app do chính lần publish tạo ra mới bị xóa. Một app đã tồn tại sẽ được đưa về đúng phiên bản nó đang chạy trước đó. Hai bản sửa tiếp theo trong cùng ngày đảm bảo rằng bước hoàn tác vẫn chạy khi chính công cụ deploy của nền tảng hosting bị lỗi, và rằng một app khỏe mạnh không bị đánh dấu thất bại chỉ vì nó đang ngủ.

Một backend được publish như thế nào

Khi app Mythex của bạn có backend, tức là một API hoặc server chứ không chỉ là file tĩnh, việc publish sẽ build backend từ một Dockerfile (công thức đóng gói app thành một image) rồi đưa image đó lên các máy trên nền tảng hosting của chúng tôi. Sau đó một health check chờ phiên bản mới thực sự khởi động và chạy ổn định, rồi mới đánh dấu publish là đã lên live.

Nếu có gì sai ở bất kỳ bước nào, một rollback sẽ chạy để hoàn tác. Việc làm rollback đúng quan trọng hơn gần như mọi thứ khác trong quá trình publish, vì nó chạy đúng vào lúc mọi thứ đang trục trặc.

Vấn đề 1: rollback xóa cái lẽ ra phải khôi phục

Rollback giữ một danh sách các app server mà lần publish đã deploy lên. Khi thất bại, nó phá hủy tất cả những app trong danh sách đó. Cách này đúng với lần publish đầu tiên: app vừa được tạo ra vài giây trước, chưa có gì phụ thuộc vào nó, và xóa đi thì mọi thứ trở về như cũ.

Nhưng với một lần republish thì cách này hoàn toàn sai. App đã tồn tại và đang phục vụ người dùng thật. Ngày 25 tháng 9, API của một app đã chạy khoảng hai tiếng rưỡi bị xóa do một lần republish thất bại.

Còn một lỗi thứ hai nằm ẩn trong cùng đoạn mã. Khi việc xóa không thành công, image mới bị lỗi vẫn nằm trên các máy, trong khi chúng tôi đã báo cho chủ app rằng trang hiện tại vẫn đang live. Khi health check có thể thất bại, image mới đã được đưa lên mọi máy rồi. Không làm gì không đồng nghĩa với việc hoàn tác.

Cách sửa: chỉ xóa những gì mình đã tạo, khôi phục phần còn lại

Giờ đây, mỗi lần publish ghi lại, với từng app server mà nó đụng tới:

  • lần publish này có tạo ra app đó hay không, và
  • nếu không, nó đang chạy image nào ngay trước đó.

Khi thất bại, mỗi app sẽ được xử lý theo một trong ba cách:

  1. Do lần publish này tạo ra: xóa đi, như trước đây.
  2. Đã tồn tại và biết image cũ: deploy lại image cũ với cùng cấu hình. Không build lại, chỉ đưa image trở về.
  3. Đã tồn tại nhưng không biết image cũ: để nguyên. Một app đã tồn tại thì không bao giờ bị xóa, kể cả khi chúng tôi không thể khôi phục nó.

Các quy tắc này nằm trong một hàm thuần nhỏ, có test riêng, nên quyết định sẽ không thể quay lại kiểu “xóa mọi thứ” mà không có test nào báo lỗi.

Vấn đề 2: công cụ deploy có thể lỗi trước khi chúng tôi ghi lại cách hoàn tác

Ngay sau bản sửa đó, chúng tôi phát hiện một lỗ hổng còn lại. Công cụ deploy của nền tảng hosting tự chạy các kiểm tra smoke: nếu các máy mới bị crash, nó sẽ thoát với lỗi. Nhưng việc này xảy ra sau khi image mới đã nằm trên các máy.

Mã của chúng tôi chỉ đăng ký app vào danh sách rollback khi công cụ deploy trả về thành công. Khi chính công cụ đó làm deploy thất bại, app chưa có trong danh sách rollback, nên không có gì được khôi phục. Trên môi trường staging của chúng tôi, một lần republish hỏng đã để lại image lỗi tiếp tục phục vụ.

Cách sửa: đăng ký cách hoàn tác trước khi bắt đầu

Giờ đây app được đăng ký để rollback, kèm thông tin là nó có được tạo mới hay không và đang chạy image nào, ngay khi nó được chuẩn bị xong và trước khi có bất kỳ thứ gì được đưa lên nó. Dù lỗi xảy ra ở bước nào sau đó, rollback đều biết cần đưa lại thứ gì. Chúng tôi đã kiểm tra trên máy local: một lần republish hỏng bị dừng ở các kiểm tra smoke của công cụ deploy, và phiên bản trước vẫn tiếp tục trả lời.

Vấn đề 3: một app đang ngủ bị đánh dấu là “chưa bao giờ khởi động”

Các app đã publish trên Mythex sẽ ngủ khi không hoạt động để tiết kiệm credit. Một lần republish để máy đang ngủ ở trạng thái dừng, nên health check phải đánh thức nó. Trước đây, health check chỉ yêu cầu nền tảng khởi động máy một lần và bỏ qua câu trả lời.

Ngày 25 tháng 9, yêu cầu khởi động duy nhất đó đã không đi đến đâu. Một API khỏe mạnh bị dừng suốt năm phút của health check và bị đánh dấu thất bại với thông báo “1 máy chưa bao giờ khởi động (trạng thái cuối: stopped)”.

Nền tảng đôi khi từ chối yêu cầu khởi động. Khi chúng tôi đang tái hiện lỗi này, nó đã trả về xung đột “lease currently held” mà không cần chúng tôi làm gì để gây ra, và nó từ chối khởi động khi một máy đang được cập nhật.

Cách sửa: cứ hỏi lại, và gõ cửa

Giờ đây health check:

  • hỏi lại yêu cầu khởi động một máy đang dừng mỗi 20 giây trong lúc chờ,
  • ghi lại câu trả lời của nền tảng mỗi khi nó từ chối, và
  • gửi một request thông thường đến app, giống như một người truy cập sẽ làm. Proxy của nền tảng sẽ đánh thức máy đang ngủ khi có request đến.

Chúng tôi đã tái hiện trên nền tảng thật bằng một app thử nghiệm mà lần khởi động đầu tiên bị từ chối. Trước khi sửa, publish thất bại sau 91 giây. Sau khi sửa, nó publish thành công trong 20 giây. Một app thực sự bị crash khi khởi động vẫn sẽ thất bại, đúng như cần thiết.

Điều chúng tôi rút ra

  • Rollback phải phân biệt được cái mới và cái đã tồn tại. “Hoàn tác” với thứ bạn vừa tạo là xóa. “Hoàn tác” với thứ vốn đã có là khôi phục. Xử lý hai trường hợp này như nhau chính là cách một cơ chế an toàn lại xóa mất một app đang chạy.
  • Ghi lại cách hoàn tác trước khi hành động. Nếu cách hoàn tác chỉ được ghi lại sau khi một bước thành công, thì một bước thất bại giữa chừng sẽ không còn gì để hoàn tác.
  • Đừng bao giờ bỏ qua câu trả lời của một yêu cầu mà bạn phụ thuộc vào. Một lần “không” bị bỏ qua đã biến một app khỏe mạnh thành một lần publish thất bại.

Một thay đổi liên quan trong cùng ngày: app bị crash ngay sau khi publish. Nếu publish thất bại ở phía bạn, hãy xem xử lý sự cố khi publish trong tài liệu.

Mythex · Pricing · Xây dựng mọi thứ với AI · Mẫu website và ứng dụng · So sánh Mythex · Thuật ngữ xây dựng ứng dụng bằng AI · Công cụ miễn phí để xây dựng ứng dụng bằng AI · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti فارسی Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو 简体中文 繁體中文(香港) 繁體中文(台灣)