PRD · Hạ tầng · FLYER

Chuyển web FLYER sang Cloudflare

Phần web (Next.js) và file tĩnh chuyển sang Cloudflare; API, Postgres, Redis, Temporal và NATS giữ trên AWS. exam.flyer.us là ứng dụng đầu tiên, chuyển dần theo tỉ lệ khách truy cập.

Xuất xứ / Prompt / Giới hạn
Chi phí AWS hiện tại
~3.000 USD/tháng
Ứng dụng đầu tiên
exam.flyer.us (v1.39.6)
Bản chạy trên Cloudflare
phongthiao.com (kèm Bingo)
Trạng thái
Chờ duyệt bật 5%
Ngày 22/10/2026, các yêu cầu được đặt ra nguyên văn: site exam.flyer.us hiện tại, có chạy 100% trên Cloudflare được không? Chi phí sever có tốt hơn trên AWS không? Anh thấy chi phí sever trên AWS đang 3000$/tháng build cho anh infrastructure để anh move qua Cloudflare đẩy Runbook và Overall plan lên roadmap.flyer.vn/prd nhé

Tài liệu do trợ lý AI soạn trong một phiên làm việc. Nguồn: mã nguồn các repo trên Gitea (flyer/webs, exam, flyer-rs, flyer-go-*, backend-v1, upload); header HTTP của từng tên miền ngày 22/10/2026; bảng giá công khai của Cloudflare (Workers, Containers, R2); kết quả build và kiểm thử trên preview. Mã của giai đoạn 1 nằm ở nhánh infra/cloudflare của flyer/webs, chưa đẩy lên Gitea.

Giới hạn: không có quyền truy cập AWS (Cost Explorer, EKS, RDS), nên phần tiết kiệm trên AWS chưa tính được và bảng phân bổ 3.000 USD chưa có. Thời gian phản hồi đo trên preview gần như không có lưu lượng, nên tỉ lệ khởi động lạnh cao hơn thực tế. Giá Cloudflare Containers lấy từ trang giá khi sản phẩm còn beta. Kết quả kiểm thử đo trên bản preview exam-cf.flyer.us (v1.39.6), đã gỡ ngày 22/10/2026; phongthiao.com chạy cùng bản build Cloudflare, kèm tính năng Bingo, trên bản v1.39.4.

1Phạm vi chuyển

Chuyển 100% không khả thi trong ngắn hạn: Cloudflare không có Postgres, Redis, Temporal hay NATS, và chạy API Go/Rust phải dùng Containers (còn beta).

Thành phầnTrên CloudflareKết luận
Web Next.js (exam, teacher, study, ielts, ranking…)Workers Chuyển
File tĩnh, audio, video (statics, stream)R2 Chuyển
API Go/Rust (exam-v2, flyer-rs, flyer-go)Containers (beta)Giữ AWS
Redis Sentinel, Temporal, NATSKhông có dịch vụ tương đươngGiữ AWS
Postgres (~270 GB)Không có Postgres; D1 là SQLite, tối đa 10 GBGiữ AWS (RDS)

2Hiện trạng phục vụ

Đường điTên miền
Cloudflare proxy → AWSexam.flyer.us, login.flyer.us (cùng bản build với exam), signup.flyer.us, statics.flyer.vn
CloudFront → AWSteacher.flyer.us, teacher.teado.ai, parent, study, ranking, speaking, games, statics.flyer.us, stream.flyer.us
Thẳng vào AWSapi, upload, admin, partner, ielts

Mọi tên miền trên đều nằm trong tài khoản Cloudflare của FLYER, nên chuyển web chỉ cần thêm route cho Worker, không phải đổi nameserver.

3Lộ trình và trạng thái

Giai đoạnNội dungTrạng thái
1. exam webexam.flyer.us, login.flyer.us → Worker; chia dần lưu lượng 5 → 25 → 50 → 100%Đã kiểm thử, chờ duyệt 5%
2. File tĩnhstatics.flyer.vn, statics.flyer.us → R2 flyer-statics, đọc xuyên từ S3 bằng Sippy Đã tạo bucket, chờ khóa AWS
2b. UploadDịch vụ upload ghi thẳng vào R2 qua API S3Chưa làm
3. Các web còn lạiteacher (partner-web v3.39.9), study (webs-study v1.39.7), ranking, ielts, signup, speakingChưa làm
4. Thu nhỏ AWSBỏ pod web trên EKS và CloudFront; Savings Plan/RI cho RDS và node còn lạiSau giai đoạn 1–3

4Chi phí

Dịch vụ CloudflareGiá
Workers Paid5 USD/tháng, gồm 10 triệu request và 30 triệu ms CPU; sau đó 0,30 USD/triệu request, 0,02 USD/triệu ms CPU
R20,015 USD/GB/tháng; băng thông ra miễn phí
Containers (beta)~0,072 USD/vCPU-giờ (chỉ tính khi CPU chạy), ~0,009 USD/GiB-giờ bộ nhớ
Phần tiết kiệm trên AWSChưa tính được. Cần bản xuất Cost Explorer 3 tháng gần nhất, nhóm theo Service, để biết pod web trên EKS, CloudFront và băng thông ra chiếm bao nhiêu trong ~3.000 USD/tháng. Postgres và cụm API vẫn ở AWS, nên phần chi phí đó chỉ giảm nhờ mua trước (Savings Plan/RI) và tách môi trường dev.

5Công thức cho mỗi web Next.js

Rút ra khi chuyển exam; áp dụng lại cho từng repo ở giai đoạn 3.

  1. Thêm @opennextjs/cloudflare và wrangler; nâng Next.js lên bản OpenNext yêu cầu.
  2. .env.cloudflare: thay địa chỉ nội bộ cluster bằng địa chỉ công khai.
  3. Đóng gói mọi thư viện vào bundle (bundlePagesRouterDependencies), tránh hai bản React/Router.
  4. Thay gói không chạy được hoặc quá nặng trên Worker: Sentry phía server, next/error, googleapis.
  5. Bỏ JSON ảnh động khỏi bundle phía server nếu Worker vượt 10 MB.
  6. API route Edge runtime chuyển sang Node runtime.
  7. Worker đặt header X-Forwarded-Host để api.flyer.us đặt cookie phiên đúng tên miền.
  8. Preview trên tên miền mới, rồi route tên miền thật với CF_SHARE.

6Việc cần quyết định

ViệcNgười làmMở khóa
Duyệt bật 5% cho exam.flyer.usChủ dự ánGiai đoạn 1
Đẩy nhánh infra/cloudflare, thêm secret CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID trên GiteaĐội hạ tầngCI tự deploy Worker theo tag
Khóa IAM chỉ đọc bucket statics.flyer.vn; R2 API tokenĐội hạ tầngGiai đoạn 2
Bản xuất Cost Explorer 3 tháng theo ServiceKế toán / hạ tầngTính phần tiết kiệm

1Kiến trúc

Trình duyệtexam.flyer.us, login.flyer.us
Worker flyer-exam-web-productionCF_SHARE% khách: Next.js render trong Worker. Phần còn lại: chuyển nguyên request về AWS.
AWS (không đổi)api.flyer.us, Postgres; pod exam-web cho phần khách chưa chuyển
  • Next.js đóng gói bằng OpenNext ; Worker nặng 8,3 MB nén (giới hạn 10 MB).
  • Thiết lập chỉ dành cho Worker nằm trong IS_CLOUDFLARE của next.config.mjs và src/lib/cloudflare/; bản build cho pod không đổi.
  • Cookie cf_side giữ mỗi khách ở một phía, để trang và file JS luôn cùng một bản build.
  • Smart Placement bật, vì mỗi trang gọi api.flyer.us nhiều lần.

2Build và chạy thử

cd apps/exam
pnpm install
pnpm cf:build     # .env = .env.prod + .env.cloudflare → codegen → next build → OpenNext
pnpm cf:preview   # chạy Worker trên máy (workerd), dữ liệu production

.env.cloudflare chỉ thay địa chỉ nội bộ cluster (exam-v2:4000, flyer-backend-rust:3000, student-api:8081) bằng https://api.flyer.us; các giá trị khác lấy từ .env.prod.

3Chuyển lưu lượng

Tỉ lệ khách do Worker phục vụ là CF_SHARE trong wrangler.jsonc (môi trường production). Đổi tỉ lệ bằng cách sửa giá trị này và commit; lần deploy sau ghi đè giá trị đặt tay.

BướcViệcGhi chú
0. CIGitea › flyer/webs › Settings: secret CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID; biến CF_EXAM_PRODUCTION=trueMỗi tag v*.*.* deploy cả pod lẫn Worker
1. Bật 5%pnpm cf:deploy --env productionTạo route exam.flyer.us/*, login.flyer.us/*; không đổi DNS
2. Theo dõi 1–2 ngàyCloudflare › Workers › flyer-exam-web-production › Observability; SentrySo lỗi 5xx và thời gian phản hồi với AWS
3. Tăng dầnCF_SHARE 25 → 50 → 100Mỗi mức ít nhất 1 ngày
4. Hoàn tất100% trong 1 tuầnSau đó mới thu nhỏ exam-web trên EKS

4Quay lại AWS

scripts/cloudflare-rollback.sh            # CF_SHARE=0: mọi request về AWS sau vài giây
scripts/cloudflare-rollback.sh --remove   # xoá Worker production và route

Sau khi chạy lệnh đầu, sửa CF_SHARE trong wrangler.jsonc cho khớp để lần deploy sau không bật lại.

5Kết quả kiểm thử

Đăng nhập, cookie flyerId; Domain=flyer.us, /home, /check-point, /edit-profile, /history, /ranking và /api/tts hoạt động như bản trên AWS.

Thời gian phản hồi (byte đầu tiên)CloudflareAWS
Worker đang chạy0,2–0,6 giây0,2–0,25 giây
Khởi động lạnh 1,5–3,5 giây—
Vì sao bắt đầu từ 5%Khởi động lạnh chậm hơn AWS rõ rệt. Với lưu lượng thật, Worker được giữ sẵn nên tỉ lệ này giảm; cần đo lại ở mức 5% trước khi tăng.

6Giới hạn đã biết

  • Sentry phía server tắt trên Worker; Sentry trên trình duyệt vẫn chạy.
  • /api/google/* dùng bản OAuth2 tối giản thay cho googleapis (20 MB).
  • Ảnh động Lottie/Spine không render phía server; trình duyệt vẫn chạy đầy đủ.
  • Next.js nâng 16.3.1 → 16.3.8 (bản vá bảo mật, OpenNext yêu cầu).