12 yếu tố tạo nên 1 web app xịn xò – Lược dịch và giải thích Twelve-Factor – Phần 2

Như đã giới thiệu ở bài trước, mình sẽ giới thiệu về twlve-factor app. Đây là 12 yếu tố cần thiết để xây dựng 1 ứng dụng “xịn xò”, ổn định, dễ mở rộng, dễ deploy.

Trong bài này, mình sẽ giải thích các yếu tố từ 3 tới 7 nhé:

  • 3. Config: Lưu trữ thiết lập vào biến môi trường (environment variable)
  • 4. Backing services: Xem các service đi kèm (database, API, …) như là resource của app
  • 5. Build, release, run: Tách riêng quá trình release, build và run
  • 6. Processes: App nên chạy dưới dạng 1 (hoặc nhiều) stateless processes
  • 7. Port binding: Mỗi service có thể được truy cập thông qua 1 port cố định

 

Đây là phần 2 trong series 3 phần về Twelve-Factor App:

  1. Lược dịch và giải thích Twelve-Factor. Giải thích Codebase và Dependencies
  2. Giải thích Config, Backing Service, Build -> Release -> Run, Processes, Port Binding
  3. Giải thích Concurrency, Disposability, Dev/Prod Parity, Logs, Admin Processes

3. Config: Lưu trữ thiết lập vào biến môi trường

Như đã nói ở phần 1, một source code có thể được deploy ở nhiều môi trường khác nhau. Mỗi môi trường sẽ có các thiết lập khác nhau như:

  • Connection String tới Database (App staging sẽ kết nối tới DB Staging, App Production sẽ kết nối tới DB Production)
  • API Key, Token để gọi tới các service khác
  • Hostname, URL của ứng dụng, …..

Cách … ngu nhất là hard code toàn bộ các thiết lập này, sau đó sửa code lại mỗi lần deploy. Một số cách khác ổn hơn thì lưu các thiết lập này vào 1 file riêng, mỗi môi trường sẽ có 1 file:

  • Bên C#, ta sẽ có Web.config, Web.staging.config chứa các thiết lập Staging, Web.production.config chứa các thiết lập Production
  • Bên NodeJS, ta có thể lưu vào file .env, tách thành .env.staging, .env.production
Đừng hardcode Connection String, mà hãy bỏ nó vào file config

Cá nhân mình thấy cách lưu file này cũng đã tạm ổn. Tuy nhiên, ta sẽ phải đảm bảo chỉ một số ít người mới có quyền xem các thiết lập Production (Lộ DB là chết á), hoặc dễ quên lưu nhầm file vào source control.

Theo Twelve Factor app, toàn bộ các thiết lập này nên được bỏ vào biến môi trường. Các biến này có thể dễ dàng được thay đổi lúc Deploy.

4. Backing services: Tách rời các service đi kèm (database, API, …), xem như là resource của app

Như mình đã nói ở phần 3, để 1 ứng dụng chạy được, ngoài trừ server chạy app, ta phải có các server đi kèm như Database Server (SQL, MongoDB) , Cache Server (Redis Memcache), Message Queue.

Các service này nên được tách rời, deploy tách riêng với app. Khi ta muốn nâng cấp cache server, hoặc muốn SQL Server trên Cloud thay cho server nội bộ để dễ mở rộng hơn; ta đều có thể dễ dàng nâng cấp mà không cần đụng tới app hiện tại.

Ngoài ra, như đã nói ở điều 3. ta cũng nên lưu kết nối tới các server này vào file config/biến môi trường, khi thay đổi db server, message queue chỉ cần thay đổi các biến này và restart app là xong, không cần phải sửa source code.

5. Build, Release, Run -> Tách rời các môi trường build, release và chạy app

Để biến source code thành 1 ứng dụng chạy được, ta thường phải đi qua 3 bước sau:

  • Build: Lấy code từ source control, tải các thư viện (dependencies) cần thiết. Thực hiện build để biến code thành binary (.jar, .dll) có thể chạy được. (Với 1 số ngôn ngữ như PHP, NodeJS, Python thì không cần build, nhưng vẫn cần tải thư viện)
  • Release: Kết hợp source code đã được build, cùng với các thiết lập môi trường (config trong phần 3) để gom thành 1 cục có thể chạy được. Chuyển cục này vào server production để có thể chạy.
  • Run:  Chạy cục vừa chuyển (bằng lệnh node server.js, python index.py) trên server production

Tại sao phải tách rời 3 bước này làm chi cho mệt vậy?? Việc này có 1 số lợi thế sau:

  • Ngăn cản việc sửa code trực tiếp khi ứng dụng đang chạy
  • Bước build thường khá phức tạp, dễ gặp lỗi, tốn nhiều tài nguyên. Tách rời bước build giúp developer có thể dễ dàng test/build thử trên server trước khi deploy.
  • Dễ dàng lưu trữ, xem lại log của những lần release. Ta cũng có thể lưu trữ các release trong quá khứ, dễ dàng rollback lại khi hệ thống có vấn đề
  • Tách rời bước Run ra giúp ta dễ dàng restart lại hệ thống khi hệ thống bị crash hoặc server gặp vấn đề.

6. Process – Chạy app dưới dạng 1 (hoặc nhiều) stateless process

Để dễ quản lý và mở rộng, 1 app nên chạy dưới dạng 1 hoặc nhiều stateless process.

Nói đơn giản, stateless process tức là… không lưu trữ state, không lưu trữ bất kì thông tin gì trong quá khứ. Giao thức HTTP cũng là stateless, nó không nhớ được bạn là ai (nên phải dùng kèm với cookie để server nhận biết được người dùng).

Với stateless process, khi cần lưu trữ (session, cache, …), ta sẽ lưu trữ vào cache server riêng bên ngoài. Chạy app dưới dạng stateless process mang lại rất nhiều lợi ích:

  • Mỗi lần deploy lại code, đổi config, restart server, ta có thể restart lại app mà không sợ mất dữ liệu (Vì dữ liệu đã lưu ở server ngoài, đâu có gì mà mất)
  • Khi lưu lượng truy cập nhiều, ta có thể dễ dàng scale bằng cách tăng số lượng process, chạy nhiều process trên nhiều máy. Các process này stateless nên có thể chạy hoàn toàn độc lập với nhau.

7. Port binding – Mỗi service có thể được truy cập thông qua 1 port cố định

Nếu từng làm Java hoặc PHP, các bạn không thể chạy trực tiếp ứng dụng, mà phải dựa vào 1 web server nào đó (PHP thì phải có Apache, Java thì phải dùng Tomcat hay JBoss).

Tuy nhiên, theo khuyến cáo của Twelve Factor, ứng dụng nên chạy độc lập, không phụ thuộc vào web server. Nếu các bạn dùng Python hoặc NodeJS sẽ thấy, bạn có thể dễ dàng chạy node server.js hoặc python server.py mà không cần cài web server gì.

Những logic xử lý HTTP/port binding đã nằm trong code của ứng dụng. Cách này sẽ giúp ứng dụng dễ chạy, dễ deploy hơn. Developer cũng không cần phải cài web server vào máy.

Khuyến cáo: Hiện tại, phần lớn các app có thể chạy đơn lẻ, không phụ thuộc vào web server. Tuy nhiên, trong thực tế, thường người ta vẫn cho các app nằm sau 1 reverse proxy như nginx/haproxy để có các chức năng như SSL, Logging, Rate Limit, block IP, gzip …. mà không cần phải implement lại vào trong app.

Tạm kết

Bài này đã dài rồi nên mình tạm kết thúc ở đây. Mình sẽ tiếp tục giải thích các yếu tố còn lại ở những phần sau nhé.

Các nội dung mình chia sẻ trong bài hơi nặng tính chuyên ngành, đôi khi khó hiểu nếu bạn chưa áp dụng vào dự án thực tế. Do vậy, nếu có đoạn nào không hiểu, các bạn cứ comment để mình giải thích kĩ hơn nha!

7 thoughts on “12 yếu tố tạo nên 1 web app xịn xò – Lược dịch và giải thích Twelve-Factor – Phần 2”

  1. Anh nghĩ sao về việc dùng các ORM như sequelize hoặc mongoose trên nodejs khiến cấu trúc của database bị lệ thuộc vào code node ạ, liệu nó có phải là 1 yếu tố khiến app kém xịn xò ??

    Like

    1. anh có thể nói thêm về config được không ? , trong quá trình học 1 farmework khi mà em vô doc 1 hồi là em lạc lối luôn , em không nói trừu tượng đâu , anh có thể chỉ cho em là ở phần config mình nên chú ý tới những mục nào vì sao được không anh ?

      Like

  2. em hỏi ko liên quan nhưng mà hồi còn sv anh có bị rớt môn nào ko ạ, em mới vừa bị rớt môn nên down mood quá anh ạ. Bạn bè ai cũng điểm cao còn em thì điểm lè tè toàn C với D :((

    Like

  3. ở bước 3, tách config ra một nơi riêng, bạn có best practice nào cho việc này không?
    những config này nên được lưu ở đâu? nên lưu vào trong 1 cái git repo khác (như bạn bảo là bảo mật thông tin db) hay là ông DevOps/Release Manager tự quản lý riêng để khi release thì ổng tự thêm vào rồi run app?

    Like

Leave a comment