Daniel Nguyen
← Tất cả dự án

Chuyển API ERP đang chạy từ App Runner sang ECS

Tôi xây deploy pipeline và phần thiết lập AWS để chuyển API Django production cùng các worker sang ECS, với blue/green release tự rollback khi có lỗi.

TTMI
Tự rollbackBlue/green release, đã tắt nền tảng cũ
Mảng
Nền tảng ERP bán lẻ
Thời gian
2025–2026
Vai trò
Xây deploy pipeline và thiết lập AWS
  • AWS ECS
  • AWS App Runner
  • Amazon ECR
  • Docker
  • GitHub Actions
  • PostgreSQL
  • Django
  • CloudWatch
  • Sentry

Các phần ghép với nhau thế nào

Chuyển API ERP đang chạy từ App Runner sang ECSEach release builds images, runs migrations once from the release image, then shifts traffic blue/green with an error-rate alarm that can roll it back.CI deploy pipelinedịch vụContainer registrykho dữ liệuOne-off migration taskworkerRelational databasekho dữ liệuLoad balancerdịch vụAPI: current (blue)dịch vụAPI: new (green)dịch vụError-rate alarmkiểm tra, cảnh báoError tracking + logsbên ngoàipush API + worker imagesrun from release imageadditive schema changestart blue/greenmost trafficsmall test shareerror rateauto rollback to blueerrors + logs
Each release builds images, runs migrations once from the release image, then shifts traffic blue/green with an error-rate alarm that can roll it back.
  • Dịch vụ
  • Kho dữ liệu
  • Worker
  • Kiểm tra, cảnh báo
  • Bên ngoài

Vấn đề

Hệ thống ERP bán lẻ của chúng tôi phục vụ ~50 cửa hàng, ~500 nhân viên và 4 thương hiệu, chạy trên một codebase Python/Django dùng PostgreSQL. API được host trên AWS App Runner, một dịch vụ container được quản lý hoàn toàn. Lúc đầu cách này rất tiện, nhưng gần như không cho chúng tôi kiểm soát cách một bản release được đưa ra, hay cách quay lại khi release bị lỗi. Các background job cũng cần một worker service chạy lâu dài riêng, vốn không hợp với một nền tảng thiết kế cho web request.

Vì vậy chúng tôi muốn chuyển cả API và worker sang ECS. Phần khó là làm việc đó khi hệ thống vẫn đang chạy. Người dùng vẫn làm việc, database migration vẫn tiếp tục được ship, và chúng tôi cần một đường lui an toàn nếu nền tảng mới gặp vấn đề. Trong một khoảng thời gian, cả nền tảng cũ lẫn mới sẽ cùng dùng chung một database.

Tôi xây deploy pipeline và phần thiết lập AWS cho lần chuyển đổi này. Dưới đây là những quyết định định hình nó.

Tôi đã làm gì

Chạy migration một lần, từ chính image đang release

Mỗi bản release build một container image, push lên registry, rồi chạy database migration như một task chạy một lần, khởi động từ đúng image đó. Chỉ khi task thành công thì phiên bản mới mới được deploy. Nhờ vậy, thay đổi schema và đoạn code phụ thuộc vào nó luôn đến từ cùng một bản build, không thể lệch nhau giữa lúc build và lúc deploy. Đổi lại, pipeline chậm hơn và có thêm một bước có thể fail, và một migration lỗi sẽ chặn cả bản release. Đó đúng là điều tôi muốn.

Giữ thay đổi schema tương thích ngược trong giai đoạn chạy song song

Khi cả hai nền tảng cùng nhận traffic, hai phiên bản code chạy trên cùng một database. Chúng tôi thống nhất chỉ cho phép thay đổi kiểu thêm vào trong giai đoạn này: thêm cột, thêm bảng thì được, còn đổi tên, xóa hay đổi kiểu dữ liệu phải chờ đến khi nền tảng cũ được tắt. Việc thay đổi schema chậm lại một thời gian. Bù lại, nền tảng nào cũng có thể nhận traffic bất cứ lúc nào, và việc chuyển đổi thực sự trở thành một lần release bình thường.

Tách image cho API và worker

Tôi chia container build thành nhiều stage, tạo ra một image API gọn nhẹ và một image worker nặng hơn. Worker cần các thư viện cho những việc như tạo báo cáo mà API không bao giờ dùng. Tách riêng giúp image API nhỏ hơn và khởi động nhanh hơn, đổi lại phải duy trì hai định nghĩa image và đảm bảo cả hai đều được build lại ở mỗi bản release.

Blue/green release với alarm theo tỉ lệ lỗi và tự động rollback

Trên App Runner, một bản release thay thế phiên bản đang chạy cùng một lúc. Trên ECS, tôi thiết lập blue/green deployment: phiên bản mới khởi động song song với phiên bản hiện tại, nhận một phần nhỏ traffic trước, và phải ổn định qua một khoảng thời gian theo dõi rồi mới tiếp quản. Một alarm theo dõi tỉ lệ lỗi của phiên bản mới, và khi alarm kích hoạt, traffic tự quay về phiên bản cũ mà không cần ai bấm nút. Đổi lại, release chậm hơn và cần gấp đôi tài nguyên trong lúc rollout. Tôi cũng thêm một bước kiểm tra chỉ đọc, xem cấu hình nền tảng trước khi release mà không build, migrate hay thay đổi gì, để lỗi thiết lập lộ ra sớm.

Làm lỗi hiện rõ trên cả hai nền tảng

Alarm rollback chỉ có ích khi ai đó tìm ra được vì sao nó kích hoạt. Tôi tích hợp Sentry để theo dõi lỗi và gom log của API và worker về CloudWatch Logs, trên cả App Runner lẫn ECS. Một bộ lọc loại bỏ các lỗi phía client có thể đoán trước, như not-found hay validation, trước khi gửi lên Sentry, để luồng lỗi chỉ còn những sự cố thật. Tôi tắt performance tracing để tránh tốn chi phí trên mỗi request, và dựa vào log để xem thời gian xử lý. Các quy tắc lọc cần được cập nhật khi code thay đổi, nhưng mọi lỗi server thật vẫn được ghi lại.

Kết quả

API production và các background worker giờ chạy trên ECS, và môi trường App Runner cũ đã được tắt. Mỗi bản release đều đi qua blue/green với một khoảng thời gian thử ngắn, và phiên bản lỗi sẽ tự rollback dựa trên tỉ lệ lỗi. Khi có sự cố, lỗi xuất hiện trong Sentry và log liên quan nằm ở cùng một chỗ, nên việc điều tra bắt đầu từ bằng chứng.

Nếu làm lại

Tôi sẽ biến quy tắc tương thích ngược thành một bước kiểm tra tự động cho migration mới ngay từ đầu, để nó không phụ thuộc vào việc reviewer có nhớ hay không. Tôi cũng sẽ ghi lại phần rollback phía database rõ ràng như phần traffic, vì alarm có thể đưa traffic về bản cũ nhưng không thể hoàn tác một thay đổi schema.