Hoàn tất chuyển đổi blog thành "Web is on the edge"

Hoàn tất chuyển đổi blog thành "Web is on the edge"

Vấn đề

Xin chào các độc giả, hơn một tuần vừa rồi tôi không có động tĩnh gì. Cũng như bao lần trước, thì tôi vừa hoàn thành xong kế hoạch của mình. Ngày hôm nay, tôi lại ở đây để trình bày cho các bạn xem đó là gì.

Khi mọi người nói "Web is on the edge", có nghĩa là trang web hoặc ứng dụng của bạn sẽ được lưu trữ đồng thời trên nhiều máy chủ ở nhiều nơi trên thế giới. Khi ai đó yêu cầu trang web/ứng dụng của bạn, họ sẽ được chuyển hướng đến máy chủ gần nhất về mặt địa lý. Các máy chủ phân tán này không chỉ phục vụ nội dung tĩnh (static) mà còn có thể thực thi mã tùy chỉnh (serverless) để hình thành nên một ứng dụng web động (dynamic).

Từ lúc xây dựng trang blog này, tôi lưu giữ trong đầu mình một lối mòn truyền thống trong triển khai, đó là cho tất cả lên cùng một máy chủ và trả tiền để duy trì nó. Trải qua nhiều năm, tôi bỏ tiền túi để sử dụng nhiều nhà cung cấp khác nhau, mà sau này tôi thích nhất là Digital Ocean (DO). Đan xen vào đó là những lần di chuyển chúng đến nhà mới, vì tính chất dùng thử: nếu tốt thì ở lại còn nếu không tốt thì đi tìm nhà cung cấp mới. Chính vì thế tôi lựa chọn Docker hỗ trợ triển khai, vì thông qua Docker, tất cả những gì cần làm để chuyển nhà là cài đặt nó, "pull" code về, chạy một dòng lệnh và tất cả service được khởi động, vào quản lý tên miền, cập nhật lại địa chỉ IP mới, thế là xong.

Vài tháng trở lại đây, khi thay đổi môi trường làm việc. Qua nhiều cuộc trò chuyện với anh CTO, tôi được mở mang ra rất nhiều điều mới mẻ. Những điều tưởng chừng mình biết mà hóa ra lại "ngó lơ" vì chưa nhận ra được lợi ích thực sự mà nó mang lại. Một trong số đó là "Edge" và "Serverless".

Bạn đọc còn nhớ tôi có một số vài viết nói về việc chuyển nhà và chuyển tên miền sang Cloudflare (CF) để chống DDOS. Tuần vừa rồi, à mà đúng ra phải là 4 tuần vừa rồi, tôi đã hoàn tất việc chuyển API sang Serverless. Thế là giờ đây, tất cả service của tôi đã "lên cạnh của Internet", tôi không biết chúng được lưu ở đâu trên Internet, nhưng có một chắc chắn là giờ đây, tôi có thể an tâm tắt máy chủ trên DO.

Tôi sẽ kể lại quá trình chuyển đổi API và một số service khác của mình lên Serverless, chủ đề này có thể là nguồn cảm hứng cho tôi trong những bài viết tiếp theo. Hy vọng sẽ được bạn đọc quan tâm và đón nhận.

Chuẩn bị và đánh giá trước khi chuyển đổi

Tất nhiên rồi, trước khi chuyển đổi chúng ta cần đánh giá lại những gì đang có và cần làm tiếp theo.

Sơ qua về mô hình hệ thống trước kia gồm có nhiều service như api, blog, admin, markdown, background, image và docker-sdk… Trong đó: api, blog, admin lần lượt là API server, giao diện trang blog và trang quản trị hệ thống. Markdown là service để parse markdown thành HTML mà tôi đã có một bài viết tại Tôi vừa phải đưa một service markdown ra khỏi API của mình. background chuyên để chạy một vài job thống kê. image là server ảnh lưu tại máy chủ và docker-sdk để phục vụ cho CI/CD…

Trước đó, tôi đã Chuyển nhà - Từ DigitalOcean sang Cloudflare Pages. Việc di chuyển api sang Cloudflare Worker (Worker) là một dạng của Serverless, đã khiến tôi mất nhiều thời gian hơn dự kiến:

  • Thứ nhất, api hiện tại đang viết bằng Go. Worker không hỗ trợ Go, tuy nhiên nó hỗ trợ WebAssembly, tôi có thể qua một bước dịch mà Go thành Wasm nhưng vấn đề này khó và mất nhiều thời gian để tìm hiểu. Vì thế tôi lựa chọn cách nhanh chóng hơn là viết lại toàn bộ api bằng Javascript.
  • Thứ hai, cơ sở dữ liệu trước kia là Redisearch. Worker hiện tại chưa hỗ trợ kết nối trực tiếp đến server Redis thông qua TCP. Tôi có thể thử chuyển qua Upstash - một nền tảng cho phép sử dụng miễn phí máy chủ Redis mà Worker hỗ trợ, tuy nhiên nó lại thiếu mất tính năng modules. tức là tôi không thể bật được modules redisearch và redisjson, cho nên mọi nỗ lực sử dụng là vô dụng. Vì thế tôi quyết định chuyển luôn cơ sở dữ liệu sang PostgreSQL.

Có thể bạn đọc sẽ nhận ra vì sao tôi lại dùng Redisearch. Nếu chưa, bạn đọc có thể tìm lại bài viết Redisearch là gì? 2coffee.dev đang sử dụng redisearch làm cơ sở dữ liệu!. Việc lựa chọn redisearch sẽ giúp cho việc tìm kiếm fulltext trở nên mạnh mẽ. Tuy nhiên, qua tìm hiểu, PostgreSQL cũng hỗ trợ tìm kiếm fulltext. Tôi đã kiểm tra và thấy rằng nó hoàn toàn đáp ứng được nhu cầu tìm kiếm cho nên không có lý do gì để gạt đi suy nghĩ này được nữa.

Superbase là một nền tảng cho phép chúng ta sử dụng miễn phí máy chủ PostgreSQL với một số giới hạn nhất định. Qua tìm hiểu, tôi thấy nó bao gồm tất cả chức năng của một máy của Postgres thực thụ và không có giới hạn nào đáng để "lo lắng". Đến đây, tôi đã giải quyết được vấn đề lưu trữ và sử dụng modules pg để kết nối đến cơ sở dữ liệu này.

Cloudflare cung cấp dịch vụ R2 - Object Storage tương tự như S3 của Amazon. Đây là một kho lưu trữ tất cả dữ liệu như ảnh, video, các tệp tin… Đặc biệt, R2 miễn phí 1 triệu requests cho nên có thể tận dụng nó để làm kho lưu trữ hình ảnh cho image service - thay vì phải lưu trữ cục bộ trên máy chủ như trước kia.

Worker của Cloudflare làm được rất nhiều thứ, trong số đó là Cron - cho phép bạn chạy một job khi đến thời gian quy định trước đó, nó tương tự với các chương trình lập lịch bằng cron có trong nhiều ngôn ngữ lập trình mà bạn đang dùng. Sử dụng Cron tôi có thể thay thế cho background service - vốn dĩ cũng là thiết lập cron để chạy một số job thống kê.

Còn lại, markdown và docker-sdk là 2 service có thể bỏ đi hoàn toàn. Vì api đã chuyển sang javascript cho nên tôi có thể sử dụng luôn package showdown để chuyển đổi Markdown sang HTML. Các thiết lập CI/CD trước đó cũng trở nên không phù hợp với Worker, cho nên docker-sdk cũng bị loại bỏ.

Thế là bây giờ, stack của tôi chỉ còn có api, page, admin, image và background.

Các bước thực hiện

Đầu tiên tôi cần viết lại api service bằng JavaScript. Worker của Cloudflare dưới nền là sử dụng V8 của Chrome cho nên chạy mã JS rất tốt. Tại sao lại là JS mà không phải là Node.js? Node cũng sử dụng V8 nhưng ngoài ra nó còn triển khai nhiều N-API làm của riêng, cho nên Worker không thể chạy được một số API của Node. Nói tóm lại Worker có thể không thể chạy được nhiều npm package của Node, nhưng nó có thể chạy được npm package thuần JS hay là các package hỗ trợ chạy trong trình duyệt web.

Nếu đọc tài liệu về Worker, bạn có thể thấy đoạn mã để bắt đầu trông giống như:

export default {
  async fetch(request) {
    const data = {
      hello: "world",
    };

    const json = JSON.stringify(data, null, 2);

    return new Response(json, {
      headers: {
        "content-type": "application/json;charset=UTF-8",
      },
    });
  },
};

Mã trên trả về một JSON kiểu { "hello" : "world" }, và nó khác xa kiểu triển khai máy chủ bằng express.js truyền thống. Chính vì thế, chúng ta cần tìm một thư viện giống express cho Worker để giúp cho việc viết CURD trở nên đơn giản hơn.

hono.dev là một trong những thư viện nhỏ, đơn giản và nhanh chóng cho các ứng dụng web "Edges". Hono có thiết kế trông giống koa.js hơn là express.js. Nó hỗ trợ Routing, Middleware và Adapter.

Trong quá trình viết lại api, tôi cần giữ cho response giống như cũ nhiều nhất có thể để hạn chế sửa đổi ở page và admin. Tuy nhiên, trong quá trình làm vẫn cần một vài thay đổi nhỏ kéo theo là cần sửa cả page và admin, khiến cho thời gian bị dài ra thêm.

R2 cũng là một thách thức trong quá trình này, mặc dù nó giống như S3 nhưng tôi chỉ nghe đến chứ chưa thực sự làm việc với nó bao giờ. Bỏ ra một vài ngày nghiên cứu, tôi cũng hiểu được "concept" của nó và cần tạo thêm 1 con Worker để lưu/lấy ảnh ra từ R2. Để hiểu một cách đơn giản, R2 là một kho lưu trữ thuần túy, nó cung cấp API để thêm/sửa/xóa các tệp tin. Việc chúng ta cần làm là gọi các API thích hợp để lưu trữ và lấy ra được tệp tin mong muốn.

Để chạy các job thống kê như trước, tôi tạo thêm một Worker nữa và sử dụng Cron.

Cuối cùng là migrate dữ liệu từ Redisearch sang PostgreSQL. Vì dữ liệu không quá phức tạp và không nhiều lắm nên chỉ cần viết một đoạn mã lấy dữ liệu từ Redisearch rồi ghi vào Postgres. Quá trình này tốn khá nhiều thời gian vì cần thử lại nhiều lần đến khi dữ liệu được chuyển sang chính xác và tương thích hoàn toàn với api mới.

Sau tất cả, triển khai mọi thứ lên Worker tương đối nhanh chóng và dễ dàng, tôi chỉ mất khoảng 10 phút để hoàn tất việc này. Những gì cần làm là gõ một lệnh như npm run deploy -e prod cho từng project là xong.

Tổng kết

Công nghệ luôn cập nhật và đưa ra giải pháp mới cho nhiều vấn đề trong hiện tại. Nếu như giữ cho mình lối mòn thì có lẽ giờ này tôi vẫn phải trả 6$ cho mỗi tháng lưu trữ mọi thứ trên DO. Thì giờ đây, tôi đã giảm thiểu mọi thứ thành "Zero Cost".

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.
Author

Xin chào, tôi tên là Hoài - một anh Dev kể chuyện bằng cách viết ✍️ và làm sản phẩm 🚀. Với nhiều năm kinh nghiệm lập trình, tôi đã đóng góp một phần công sức cho nhiều sản phẩm mang lại giá trị cho người dùng tại nơi đang làm việc, cũng như cho chính bản thân. Sở thích của tôi là đọc, viết, nghiên cứu... Tôi tạo ra trang Blog này với sứ mệnh mang đến những bài viết chất lượng cho độc giả của 2coffee.dev.Hãy theo dõi tôi qua các kênh LinkedIn, Facebook, Instagram, Telegram.

Bạn thấy bài viết này có ích?
Không

Bình luận (1)

Avatar
Anh (Daniel) Le11 tháng trước
đọc xong bài này tôi thấy bác tay to thật 🤣. tầm này năm ngoái tôi cũng thử R2, D1, KV các thứ cho con pet project nhưng phải workaround nhiều quá nên xin quit game. sản phẩm của CF này làm project cá nhân thì ok còn làm business thì khoai vì họ toàn release alpha, beta xong vứt đó 🙏.
Trả lời
Avatar
Xuân Hoài Tống11 tháng trước
Bác nói cũng hợp lý, trước kia mình chưa từng nghĩ đến việc sử dụng các service bên ngoài, thay vào đó cảm giác tự mình control được mọi thứ sẽ tốt hơn. Thật vậy mình sẽ phải làm nhiều hơn và đôi khi còn phải dành nhiều thời gian để tìm cách khắc phục sự cố không đáng có. Mình mới dùng CF được một thời gian và cảm thấy nó tương đối ổn định, có thật là phải tìm hiểu nhiều thứ hơn. Nói thật thì các dự án mình làm gần đây cũng đang ứng dụng Worker, KV, R2... rất là nhiều 😄