DevOps & giám sát

Ngừng thử lại các bài kiểm tra email trong CI Kubernetes và bắt đầu thuê hộp thư

Việc coi hộp thư kiểm thử như các phụ thuộc HTTP có thể thử lại được sẽ gây ra các pipeline CI không ổn định. Việc thuê các hộp thư riêng biệt với thời hạn hết hiệu lực rõ ràng giúp ngăn chặn tin nhắn trùng lặp và điều kiện tranh chấp (race conditions).

Server racks under a glass dome with warning lights and digital lease tags
Ảnh minh họa tạo riêng cho bài viết

Bài được dịch tự động từ bản gốc tiếng Anh.

Các bước xác minh email trong pipeline tích hợp liên tục (CI) thường thất bại không phải vì ứng dụng bị lỗi, mà vì chiến lược kiểm thử đối xử với hộp thư như những endpoint HTTP đơn giản. Khi một pipeline gửi tin nhắn, chờ một chút rồi thử lại khi thất bại, nó có nguy cơ tạo ra các sự kiện trùng lặp hoặc chiếm dụng tài nguyên của các bài kiểm thử song song. Một cách tiếp cận vững chắc hơn là coi hộp thư như một tài nguyên được thuê với quy tắc sở hữu nghiêm ngặt và thời hạn hết hiệu lực.

Chuyện gì đã xảy ra

Trong môi trường CI hiện đại dựa trên Kubernetes, các bài kiểm thử end-to-end thường yêu cầu xác minh rằng ứng dụng đã gửi email. Mô hình phổ biến nhưng sai lầm bao gồm việc kích hoạt một sự kiện, thăm dò tin nhắn và thử lại toàn bộ bài kiểm thử nếu tin nhắn không đến ngay lập tức. Logic thử lại này thường kích hoạt lại chính sự kiện ứng dụng, dẫn đến các tác dụng phụ như đơn hàng trùng lặp hoặc nhiều token đặt lại mật khẩu. Hơn nữa, khi nhiều job kiểm thử chạy song song sử dụng chung các địa chỉ email tạm thời, chúng có thể xung đột, đọc lẫn tin nhắn của nhau hoặc thất bại do điều kiện tranh chấp.

Giải pháp đề xuất thay đổi tư duy từ "thử lại cho đến khi thành công" sang "thuê và xác minh". Trong mô hình này, một lần chạy kiểm thử sẽ dành riêng một hộp thư độc nhất trong một khoảng thời gian giới hạn. Hợp đồng thuê này bao gồm siêu dữ liệu như mã nhận dạng lần chạy (run identifier), nhãn chủ sở hữu và mốc thời gian hết hạn. Bài kiểm thử kích hoạt sự kiện ứng dụng đúng một lần, truyền địa chỉ hộp thư đã thuê dưới dạng cấu hình. Sau đó, nó chỉ thăm dò để xem tin nhắn có được giao hay không, chứ không phải để xem sự kiện có được tạo ra hay không. Nếu bài kiểm thử thất bại hoặc worker gặp sự cố, hợp đồng thuê sẽ tự động hết hạn, cho phép quá trình dọn dẹp thu hồi tài nguyên mà không cần can thiệp thủ công.

Cách tiếp cận này đòi hỏi tách biệt việc cung cấp môi trường kiểm thử khỏi việc thực thi logic kiểm thử. Bằng cách tiêm địa chỉ hộp thư và ID lần chạy vào container kiểm thử thông qua secrets hoặc biến môi trường, các nhóm đảm bảo rằng mỗi job song song hoạt động độc lập. Manifest Job của Kubernetes được cấu hình với backoffLimit: 0 để ngăn orchestrator khởi động lại container và lặp lại các tác dụng phụ. Thay vào đó, mọi lần thử lại cần thiết đều được xử lý trong mã kiểm thử, cụ thể nhắm vào bước thăm dò tin nhắn với các khoảng thời gian giới hạn.

Chi tiết quan trọng

  • Cấu trúc hợp đồng thuê: Mỗi hợp đồng thuê hộp thư chứa run_id, mailbox_id, owner, created_at, expires_at và expected_recipient để thực thi quyền sở hữu nghiêm ngặt.
  • Cấu hình Kubernetes: Các Job kiểm thử nên sử dụng backoffLimit: 0 và restartPolicy: Never để tránh các lần thử lại tự động lặp lại các sự kiện ứng dụng không idempotent.
  • Token tương quan: Các bài kiểm thử phải xác minh một token lần chạy độc nhất được bao gồm trong tiêu đề email hoặc payload, thay vì chỉ dựa vào dòng tiêu đề hoặc địa chỉ người nhận.
  • Ngân sách thử lại: Tách biệt các lần thử lại thành provisioning (tạo hợp đồng thuê idempotent), polling delivery (backoff theo hàm mũ có giới hạn) và assertion (không thử lại cho lỗi logic).
  • An toàn dọn dẹp: Một tiến trình janitor thu hồi các hợp đồng thuê sau khi hết hạn, đảm bảo rằng các pod bị kẹt không giữ hộp thư vô thời hạn.
  • Tạo biên nhận: Các bài kiểm thử thành công nên ghi một biên nhận chứa ID tin nhắn, mốc thời gian giao hàng và kết quả đã được làm sạch (sanitized) để phục vụ mục đích kiểm toán.

Bối cảnh

Trong các hệ thống phân tán, tính idempotency đảm bảo rằng việc thực hiện một thao tác nhiều lần có cùng tác dụng như thực hiện nó một lần. Việc gửi email thường không idempotent; gửi email "chào mừng" hai lần gây nhầm lẫn cho người dùng và làm rối log kiểm thử. Các lần thử lại CI truyền thống thường bỏ qua điều này, giả định rằng độ trễ mạng là nguyên nhân duy nhất gây thất bại. Tuy nhiên, trong môi trường Kubernetes được song song hóa, sự cạnh tranh tài nguyên là một yếu tố đáng kể. Các tài nguyên chia sẻ như địa chỉ email tạm thời chung trở thành nút thắt cổ chai. Bằng cách giới thiệu khái niệm hợp đồng thuê, tương tự như cách hoạt động của khóa cơ sở dữ liệu hoặc đặt trước tài nguyên đám mây, các nhóm có thể thực thi quyền truy cập độc quyền vào các fixture kiểm thử trong suốt thời gian diễn ra một lần chạy kiểm thử cụ thể.

Tại sao điều này quan trọng

Đối với các nhóm kỹ thuật vận hành phần mềm tự lưu trữ (self-hosted) hoặc quản lý pipeline CI nội bộ, các bài kiểm thử không ổn định làm xói mòn niềm tin vào quy trình triển khai. Khi các bài kiểm thử email thất bại ngắt quãng do vấn đề thời gian hoặc xung đột tài nguyên, các nhà phát triển mất hàng giờ để điều tra các báo động giả (false positives). Điều này làm giảm tốc độ phát triển và tăng tải nhận thức cho các kỹ sư DevOps, những người phải duy trì các script thử lại mong manh. Triển khai mô hình dựa trên hợp đồng thuê chuyển đổi việc kiểm thử email từ một vấn đề thăm dò mơ hồ thành một vấn đề vòng đời tài nguyên có thể quản lý được. Nó cung cấp các ranh giới rõ ràng về thời điểm bắt đầu và kết thúc của một bài kiểm thử, giúp việc gỡ lỗi trở nên dễ dàng hơn đáng kể.

Hơn nữa, cách tiếp cận này cải thiện hiệu quả chi phí và sự ổn định của hệ thống. Các lần thử lại không giới hạn tiêu tốn tài nguyên tính toán và có thể làm quá tải các dịch vụ email bên ngoài hoặc SMTP relay nội bộ. Bằng cách giới hạn các khoảng thời gian thăm dò và đặt thời gian hết hạn nghiêm ngặt, các nhóm ngăn chặn các tiến trình chạy mất kiểm soát tiêu thụ tài nguyên quá mức. Các biên nhận tường minh được tạo ra bởi phương pháp này cũng cung cấp khả năng quan sát tốt hơn, cho phép các trưởng nhóm truy vết chính xác lần chạy kiểm thử nào đã nhận được tin nhắn nào, hỗ trợ phân tích nguyên nhân gốc rễ nhanh hơn khi xảy ra lỗi ứng dụng thực sự.

Bạn có thể làm gì

  • Triển khai bản ghi hợp đồng thuê: Tạo một kho lưu trữ trạng thái nhỏ hoặc API cấp phát ID hộp thư độc nhất kèm mốc thời gian hết hạn cho mỗi lần chạy kiểm thử.
  • Cập nhật manifest Kubernetes: Đặt backoffLimit: 0 trong spec Job kiểm thử của bạn để ngăn orchestrator tự động thử lại các container thất bại.
  • Tiêm mã nhận dạng độc nhất: Truyền một run_id độc nhất và địa chỉ hộp thư đã thuê vào container kiểm thử của bạn thông qua biến môi trường hoặc secrets.
  • Tái cấu trúc logic assertion: Sửa đổi các bài kiểm thử để thăm dò một token tương quan cụ thể trong payload email thay vì chỉ kiểm tra xem tin nhắn có đến hay không.
  • Thêm tiến trình janitor: Bổ sung một tiến trình dọn dẹp để thu hồi các hợp đồng thuê hết hạn.

Tin khác

Tất cả tin