Ngày 20 tháng 3 năm 2020 - Máy tính
Phần mềm hiện đại thường được cung cấp dưới dạng dịch vụ web, còn được gọi là phần mềm dưới dạng dịch vụ (SaaS), và nguyên tắc mười hai yếu tố là một phương pháp luận để xây dựng ứng dụng SaaS. Đây không chỉ là nguyên tắc xây dựng ứng dụng SaaS mà còn là hướng dẫn cần tuân theo cho việc phát triển ứng dụng vi dịch vụ và ứng dụng bản địa đám mây.
Nguyên tắc hoặc thực hành tốt nhất của mười hai yếu tố nhằm đạt được:
- Sử dụng tiêu chuẩn thống nhất giúp tiết kiệm thời gian cho các nhà phát triển mới bằng cách làm theo những thực hành tốt nhất.
- Ứng dụng nên tách biệt với hệ điều hành cơ bản để cung cấp khả năng di chuyển cao nhất giữa các môi trường chạy khác nhau.
- Ứng dụng nên phù hợp để triển khai trên các nền tảng đám mây hiện đại, từ bỏ sự phụ thuộc vào máy chủ và quản lý hệ thống.
- Không có nhiều sự khác biệt giữa môi trường phát triển và sản xuất, cho phép triển khai liên tục với mức độ nhanh nhẹn tối đa.
- Có thể mở rộng tự do mà không cần thay đổi đáng kể công cụ, kiến trúc hoặc thực hành phát triển.
Nguyên tắc mười hai yếu tố không giới hạn ngôn ngữ lập trình hoặc loại dịch vụ backend, đây là một phương pháp luận thống nhất.
I. Kho mã nguồn Mỗi ứng dụng sử dụng một kho mã nguồn duy nhất, một kho mã nguồn hỗ trợ nhiều môi trường triển khai. Ứng dụng theo nguyên tắc mười hai yếu tố luôn sử dụng hệ thống kiểm soát phiên bản (như Git, SVN) để theo dõi mã nguồn. Một ứng dụng nên có một kho mã nguồn duy nhất, lưu trữ một bộ mã nguồn duy nhất nhưng có thể có nhiều phiên bản. Các môi trường triển khai khác nhau có thể sử dụng các phiên bản khác nhau của cùng một kho mã nguồn.
Trong thực tế, ví dụ chúng ta sử dụng Git để lưu trữ mã nguồn, mỗi ứng dụng sử dụng một kho riêng, các ứng dụng khác nhau không chia sẻ kho mà tách rời các mối quan hệ phụ thuộc thành các kho khác nhau. Mỗi khi hoàn thành một phiên bản, chúng ta tạo thẻ (tag) hoặc nhánh phát hành (release branch) trong kho, sau đó dần dần cập nhật qua các môi trường phát triển, thử nghiệm, gần sản xuất và sản xuất.
II. Phụ thuộc Khai báo và trích xuất phụ thuộc rõ ràng. Hầu hết các công cụ đều cung cấp chức năng đóng gói, phụ thuộc có thể được cài đặt toàn cầu hoặc trong thư mục chỉ định của ứng dụng, như site-packages của Node.js hay vendoring của Golang.
Nguyên tắc mười hai yếu tố khuyến nghị ứng dụng không nên phụ thuộc ngầm vào các gói toàn cầu của hệ thống. Tất cả phụ thuộc phải được khai báo rõ ràng, đầy đủ và chính xác thông qua tệp cấu hình phụ thuộc. Ngoài ra, cần sử dụng công cụ hỗ trợ phụ thuộc để trích xuất tất cả phụ thuộc, đảm bảo rằng cùng một tập phụ thuộc được áp dụng xuyên suốt quá trình phát triển và sản xuất.
Việc khai báo phụ thuộc rõ ràng cũng đơn giản hóa các bước chuẩn bị cho nhà phát triển mới. Nhà phát triển chỉ cần lấy mã nguồn và cài đặt ngôn ngữ runtime cùng công cụ quản lý phụ thuộc cần thiết. Ví dụ, khi sử dụng Maven để xây dựng dự án Java, tất cả phụ thuộc được khai báo trong tệp pom.xml, nhà phát triển chỉ cần lấy mã nguồn và chạy lệnh mvn package để đóng gói.
Nguyên tắc này cũng khuyến nghị không nên phụ thuộc ngầm vào bất kỳ công cụ hệ thống nào. Ví dụ, nếu ứng dụng cần curl, dù hầu hết các hệ thống đều có sẵn công cụ này, nhưng vẫn có thể xảy ra vấn đề về phiên bản hoặc tương thích. Nếu ứng dụng phụ thuộc mạnh vào công cụ này, hãy xem xét tích hợp nó vào ứng dụng hoặc sử dụng Docker để xây dựng.
III. Cấu hình Lưu trữ cấu hình trong biến môi trường. Cấu hình của một ứng dụng khác nhau giữa các môi trường. Ví dụ:
- Thông tin cấu hình kết nối cơ sở dữ liệu, bộ đệm hoặc các dịch vụ backend khác.
- Khóa API cho các dịch vụ bên ngoài như AWS S3.
- Các thông tin cấu hình đặc thù khi triển khai, chẳng hạn tên miền.
Thỉnh thoảng ứng dụng giữ các thông tin cấu hình này dưới dạng hằng số trong mã nguồn, điều này vi phạm nguyên tắc mười hai yếu tố. Cần tách biệt hoàn toàn cấu hình khỏi mã nguồn, cấu hình thay đổi tùy theo môi trường triển khai trong khi mã nguồn chỉ cần duy trì một phiên bản.
Một cách kiểm tra xem ứng dụng đã trích xuất tất cả thông tin cấu hình khỏi mã nguồn hay chưa là xem kho mã nguồn có thể được công khai mà không lo lộ thông tin mật hay không.
Định nghĩa cấu hình ở đây không bao gồm cấu hình nội bộ của ứng dụng, như cấu hình bean của Spring, vốn không thay đổi giữa các môi trường triển khai và nên được giữ trong mã nguồn.
Một phương pháp khác là sử dụng tệp cấu hình nhưng không đưa chúng vào kiểm soát phiên bản. Điều này đã cải thiện so với việc sử dụng hằng số trong mã nguồn nhưng vẫn có nhiều nhược điểm:
- Dễ vô tình đưa tệp cấu hình vào kho.
- Dễ đặt các tệp cấu hình ở nhiều nơi với các định dạng khác nhau, gây khó khăn trong việc quản lý thống nhất.
- Định dạng tệp cấu hình có thể phụ thuộc vào ngôn ngữ hoặc khung công tác.
Theo nguyên tắc mười hai yếu tố, cấu hình nên được lưu trữ trong biến môi trường. Biến môi trường có thể thay đổi giữa các môi trường triển khai mà không cần thay đổi mã nguồn, không bị đưa nhầm vào kho mã nguồn và không bị ràng buộc bởi ngôn ngữ hoặc hệ điều hành.
IV. Dịch vụ Backend Xem dịch vụ backend như tài nguyên gắn thêm. Dịch vụ backend có thể là bất kỳ dịch vụ nào được ứng dụng tiêu thụ qua mạng như một phần hoạt động bình thường. Ví dụ, cơ sở dữ liệu MySQL, hàng đợi RabbitMQ, bộ đệm Memcached đều là dịch vụ backend.
Dịch vụ backend như cơ sở dữ liệu thường được quản trị viên hệ thống quản lý giống như ứng dụng đang chạy. Ngoài các dịch vụ được quản lý cục bộ, có thể có các dịch vụ được cung cấp và quản lý bởi tổ chức thứ ba, như dịch vụ thu thập chỉ số New Relic, dịch vụ tài sản nhị phân AWS S3, thậm chí là dịch vụ API Twitter.
Nguyên tắc mười hai yếu tố có một quy tắc là ứng dụng không nên phân biệt giữa dịch vụ cục bộ và bên thứ ba, tất cả đều nên được coi là tài nguyên gắn thêm có thể truy cập qua URL hoặc khóa API. Ứng dụng có thể thay thế cơ sở dữ liệu MySQL cục bộ bằng cơ sở dữ liệu bên thứ ba như AWS RDS mà không cần thay đổi mã nguồn, chỉ cần thay đổi cấu hình.
Mỗi dịch vụ backend là một tài nguyên. Hai cơ sở dữ liệu MySQL là hai tài nguyên riêng biệt, không bị ràng buộc bởi môi trường triển khai. Tài nguyên có thể được gắn thêm hoặc gỡ bỏ theo ý muốn triển khai. Ví dụ, nếu một cơ sở dữ liệu đang dùng trong môi trường sản xuất bị hỏng, quản trị viên có thể tạo một phiên bản mới từ lần sao lưu gần nhất mà không cần thay đổi mã nguồn.
V. Xây dựng, Phát hành và Chạy Tách biệt nghiêm ngặt giai đoạn xây dựng và chạy. Kho mã nguồn được triển khai thông qua ba bước:
- Giai đoạn xây dựng là chuyển đổi kho mã nguồn thành gói thực thi.
- Giai đoạn phát hành là kết hợp gói được xây dựng với cấu hình hiện tại.
- Giai đoạn chạy là chạy ứng dụng trong môi trường triển khai.
Nguyên tắc mười hai yếu tố tách biệt nghiêm ngặt ba giai đoạn: xây dựng, phát hành và chạy. Ví dụ, không thể thay đổi mã nguồn trong giai đoạn chạy vì không thể truyền những thay đổi này ngược về giai đoạn xây dựng.
Một số công cụ quản lý phát hành nổi bật có khả năng hỗ trợ quay lại phiên bản trước. Ví dụ, công cụ triển khai Capistrano lưu trữ các phiên bản phát hành trong thư mục con releases, phiên bản hiện tại là một liên kết đến thư mục phát hành hiện tại, lệnh rollback dễ dàng đưa ứng dụng trở lại phiên bản trước đó.
Mỗi phiên bản phát hành nên có một ID duy nhất, chẳng hạn dấu thời gian (ví dụ: 2020-03-20-20:32:17
) hoặc số tăng dần (v100). Phiên bản phát hành chỉ có thể chồng lên và không thể sửa đổi sau khi được tạo, mọi thay đổi phải tạo ra một phiên bản phát hành mới.
VI. Quy trình
Chạy ứng dụng dưới dạng một hoặc nhiều quy trình không trạng thái.
Trong một kịch bản đơn giản: mã nguồn là một tập lệnh độc lập, môi trường chạy là máy của nhà phát triển với runtime ngôn ngữ đã cài đặt, vậy chúng ta có thể khởi chạy tiến trình ứng dụng bằng một lệnh đơn giản (ví dụ: python start.py
). Trong các trường hợp phức tạp hơn, triển khai sản xuất của một ứng dụng phức tạp có thể sử dụng nhiều loại quy trình: có thể khởi tạo thành 0 hoặc nhiều quy trình.
Nguyên tắc mười hai yếu tố khuyến nghị ứng dụng nên là không trạng thái và không chia sẻ bất kỳ tài nguyên nào. Bất kỳ dữ liệu nào cần lưu giữ lâu dài nên được lưu trữ trong dịch vụ backend có trạng thái (thường là cơ sở dữ liệu).
Nguyên tắc này không giả định bất kỳ dữ liệu cache nào trong bộ nhớ hoặc ổ cứng sẽ được sử dụng trong các yêu cầu tiếp theo. Vì ứng dụng chạy dưới dạng nhiều quy trình, yêu cầu tiếp theo có thể không đến đúng quy trình, ngay cả khi chỉ có một quy trình chạy, một lần khởi động lại có thể làm mất tất cả dữ liệu.
Không nên sử dụng hệ thống tệp để cache biên dịch trong môi trường chạy. Nguyên tắc mười hai khuyến nghị đóng gói trong giai đoạn xây dựng, lúc đó các công cụ như maven package
có thể đóng gói ứng dụng, sử dụng trong giai đoạn chạy.
Ngoài ra, một số hệ thống web phụ thuộc vào “session dính”, tức là lưu dữ liệu session của người dùng trong bộ nhớ của quy trình ứng dụng. Điều này không ổn khi có nhiều quy trình ứng dụng vì không thể đảm bảo yêu cầu tiếp theo sẽ đến đúng quy trình. Nên lưu dữ liệu session vào dịch vụ lưu trữ dữ liệu hỗ trợ thời gian hết hạn như Memcached, Redis.
VII. Liên kết cổng Tiết lộ dịch vụ thông qua liên kết cổng. Ứng dụng web đôi khi chạy trong container web, ví dụ ứng dụng Java chạy trong Tomcat.
Nguyên tắc mười hai khuyến nghị ứng dụng nên tự chứa hoàn toàn. Không nên phụ thuộc vào tiêm runtime để tạo giao diện dịch vụ web. Nghĩa là ứng dụng web phơi bày dưới dạng dịch vụ HTTP thông qua liên kết cổng để lắng nghe các yêu cầu đến cổng đó.
Không chỉ dịch vụ HTTP có thể phơi bày qua liên kết cổng, các dịch vụ tầng 4 khác cũng có thể nhận yêu cầu qua cổng (chẳng hạn Redis).
Ngoài ra, một ứng dụng có thể trở thành dịch vụ backend cho ứng dụng khác thông qua liên kết cổng, cung cấp URL để ứng dụng khác sử dụng như tài nguyên.
VIII. Đồng Rik66 Club Game Bài 52Play thời Mở rộng ngang qua mô hình quy trình. Bất kỳ chương trình máy tính nào khi chạy đều biểu diễn dưới dạng một hoặc nhiều quy trình. Ứng dụng web có nhiều cách chạy quy trình. Ví dụ, quy trình PHP chạy dưới dạng con của Apache, khởi tạo theo nhu cầu. Ngược lại, quy trình Java giữ lại một khối lớn tài nguyên hệ thống (CPU và RAM) khi JVM khởi động để tạo ra một quy trình lớn, sử dụng luồng để đồng thời.
Trong ứng dụng mười hai yếu tố, quy trình là công dân hạng nhất. Áp dụng mô hình này, nhà phát triển có thể phân bổ các loại công việc khác nhau cho các loại quy trình khác nhau để xử lý các tải công việc khác nhau. Ví dụ, quy trình web xử lý yêu cầu HTTP, quy trình background xử lý công việc dài.
Điều này không trái với việc sử dụng đa luồng nội bộ (như mô hình sự kiện bất đồng bộ trong Node.js) nhưng máy ảo chỉ có thể mở rộng dọc, do đó ứng dụng cũng cần có khả năng mở rộng ngang, chạy nhiều quy trình trên nhiều máy vật lý.
Mô hình quy trình tỏa sáng khi mở rộng ngang. Đối với ứng dụng mười hai yếu tố không chia sẻ tài nguyên và hỗ trợ phân vùng ngang, hỗ trợ đồng thời là một thao tác đơn giản và đáng tin cậy.
Ứng dụng mười hai yếu tố không nên chạy dưới dạng daemon hay viết tệp PID. Thay vào đó, nên giao cho trình quản lý quy trình của hệ điều hành (như systemd) quản lý luồng đầu ra, xử lý sự cố quy trình và khởi động lại hoặc dừng theo yêu cầu của người dùng.
IX. Độ linh hoạt Sử dụng khởi động nhanh và dừng lịch sự để tối đa hóa độ bền. Quy trình của ứng dụng mười hai yếu tố rất linh hoạt, có thể khởi động và dừng theo yêu cầu. Điều này hỗ trợ mở rộng nhanh chóng và triển khai nhanh chóng khi có thay đổi mã nguồn và cấu hình.
Quy trình nên giảm thiểu thời gian khởi động càng nhiều càng tốt. Lý tưởng là từ khi chạy lệnh đến khi quy trình sẵn sàng nhận yêu cầu hoặc xử lý nhiệm vụ chỉ mất vài giây. Điều này cải thiện sự linh hoạt trong việc triển khai và mở rộng quy trình, và khi cần thiết có thể di chuyển quy trình sang máy vật lý mới nhanh chóng, tăng cường độ bền.
Khi nhận tín hiệu dừng, quy Link đăng nhập Saigon777 Tặng 50k trình nên dừng lịch sự. Đối với ứng dụng web, dừng lịch sự là xử lý xong yêu cầu hiện tại trước khi thoát và ngừng lắng nghe lưu lượng trên cổng dịch vụ. Thông thường các yêu cầu HTTP rất ngắn, thường không vượt quá vài giây, ngoại trừ trường hợp polling dài, khi kết nối bị ngắt, client sẽ cố gắng kết nối lại mà không ảnh hưởng đến người dùng.
Đối với quy trình công việc, dừng lịch sự là trả công việc hiện tại về i9bet41 hàng đợi công việc. Ví dụ, trong Beanstalkd, khi một quy trình công việc bị ngắt, công việc sẽ tự động được trả về hàng đợi.
Quy trình cũng nên đối phó với trường hợp chết đột ngột (như lỗi phần cứng) để tăng cường độ bền. Ví dụ, sử dụng hàng đợi backend Beanstalkd, nó có thể trả công việc về hàng đợi khi client bị ngắt hoặc hết thời gian.
X. Tương đồng giữa môi trường phát triển và sản xuất Môi trường phát triển, thử nghiệm và sản xuất càng giống nhau càng tốt. Do lịch sử, môi trường phát triển và thử nghiệm thường có khoảng cách với sản xuất. Ví dụ:
- Khoảng cách thời gian, mã nguồn phát triển có thể mất nhiều thời gian mới được triển khai lên sản xuất.
- Khoảng cách cá nhân, lập trình viên viết mã nguồn, vận hành triển khai mã nguồn.
- Khoảng cách công cụ, phát triển và sản xuất sử dụng các stack công nghệ khác nhau (phát triển sử dụng Nginx, MySQL, OS X; sản xuất sử dụng Apache, SQLite, Linux).
Nguyên tắc mười hai yếu tố khuyến nghị thiết kế ứng dụng với suy nghĩ về triển khai liên tục, giữ khoảng cách giữa phát triển và sản xuất càng nhỏ càng tốt. Xem lại ba khoảng cách trên:
- Giải quyết khoảng cách thời gian, mã nguồn được phát triển có thể được triển khai lên sản xuất chỉ trong vài phút.
- Giải quyết khoảng cách cá nhân, DevOps kết nối, người viết mã nguồn cần quan tâm đến triển khai.
- Giải quyết khoảng cách công cụ, môi trường phát triển càng gần với sản xuất càng tốt.
Đồng thời, nguyên tắc mười hai tránh sử dụng các dịch vụ backend khác nhau trong môi trường phát triển và sản xuất.
XI. Nhật ký Xem nhật ký như dòng sự kiện. Nhật ký cung cấp khả năng nhìn thấy hành vi của ứng dụng đang chạy. Trong môi trường dựa trên máy chủ, nhật ký thường được ghi vào các tệp log trên đĩa, nhưng đó chỉ là một cách xuất.
Thu thập tất cả các luồng đầu ra từ các quy trình chạy và dịch vụ backend, sau đó kết hợp chúng theo thứ tự thời gian tạo thành dòng nhật ký. Nhật ký ban đầu là định dạng văn bản, mỗi dòng là một sự kiện (khi có ngoại lệ có thể có nhiều dòng), không có điểm bắt đầu hoặc kết thúc cố định, chỉ cần ứng dụng có hoạt động sẽ có nhật ký liên tục.
Nguyên tắc mười hai khuyến nghị không tự định tuyến hoặc lưu trữ luồng đầu ra. Nói cách khác, không nên cố gắng tự ghi nhật ký vào tệp, mà nên để mỗi quy trình chạy ghi nhật ký vào stdout. Trong phát triển cục bộ, nhà phát triển có thể xem nhật ký trên terminal để theo dõi hành vi ứng dụng.
Trong môi trường thử nghiệm và sản xuất, mỗi luồng đầu ra của quy trình sẽ được môi trường chạy bắt giữ và lưu trữ vào một vị trí nào đó để xem xét sau này. Vị trí lưu trữ không nên được cấu hình bởi ứng dụng mà nên giao cho môi trường chạy. Các công cụ định tuyến nhật ký nguồn mở (như Fluent) thực hiện công việc này.
Gửi nhật ký đến các hệ thống tìm kiếm và phân tích (như Splunk) mang lại lợi ích:
- Tìm kiếm các sự kiện cụ thể trước đó.
- Vẽ đồ thị xu hướng lưu lượng.
- Cảnh báo theo quy tắc người dùng định nghĩa (như số lỗi vượt quá ngưỡng mỗi phút).
XII. Quy trình quản lý Chạy các tác vụ quản lý dưới dạng quy trình một lần. Ngoài các quy trình chạy thường xuyên (xử lý yêu cầu web), nhà phát triển thường có mong muốn chạy các tác vụ quản lý và bảo trì ứng dụng, như:
- Chạy tác vụ di chuyển cơ sở dữ liệu.
- Chạy console để thực thi mã hoặc kiểm tra cơ sở dữ liệu trực tuyến.
- Chạy các kịch bản một lần (ví dụ:
php scripts/fix_bad_records.php
).
Các quy trình quản lý một lần nên sử dụng cùng môi trường với các quy trình thường trú. Nghĩa là quy trình quản lý sử dụng cùng mã nguồn và cấu hình với các quy trình khác, và mã quản lý được phát hành cùng ứng dụng để tránh sự không đồng bộ.
Tất cả các loại quy trình nên sử dụng cùng kỹ thuật cô lập phụ thuộc. Ví dụ, chạy quy trình web Ruby bằng bundle exec thin start
và chạy di chuyển dữ liệu bằng bundle exec rake db:migrate
.