Hướng dẫn cài uWSGI kết hợp Nginx phục vụ app Python trên Ubuntu 14.04

Hướng dẫn cài đặt uWSGI và Nginx Để chạy Ứng Dụng Python Trên Ubuntu 14.04

DataOnline sẽ hướng dẫn bạn triển khai một ứng dụng WSGI cơ bản với sự hỗ trợ của uWSGI. Để tối ưu hiệu suất và quản lý kết nối hiệu quả, chúng ta sẽ sử dụng Nginx làm reverse proxy để chuyển tiếp yêu cầu đến máy chủ ứng dụng. Toàn bộ quá trình cài đặt và cấu hình sẽ được thực hiện trên hệ điều hành Ubuntu 14.04, giúp bạn thiết lập một môi trường chạy ứng dụng Python ổn định và linh hoạt.

Định nghĩa và các khái niệm

Giải thích một số thuật ngữ

  • WSGI: Một chuẩn của Python định nghĩa giao diện tiêu chuẩn để giao tiếp giữa một ứng dụng hoặc framework và một máy chủ ứng dụng/web. Chuẩn này được tạo ra nhằm đơn giản hóa và tiêu chuẩn hóa giao tiếp giữa các thành phần, đảm bảo tính nhất quán và khả năng thay thế. Cơ bản, nó định nghĩa một giao diện API có thể sử dụng trên các giao thức khác nhau.

  • uWSGI: Một container máy chủ ứng dụng nhằm cung cấp một bộ công cụ đầy đủ cho việc phát triển và triển khai các ứng dụng và dịch vụ web. Thành phần chính là một máy chủ ứng dụng có khả năng xử lý các ứng dụng viết bằng nhiều ngôn ngữ khác nhau. Nó giao tiếp với ứng dụng theo các phương thức được định nghĩa trong chuẩn WSGI, và với các máy chủ web khác qua nhiều giao thức khác nhau. Đây chính là thành phần chuyển đổi các yêu cầu từ máy chủ web thông thường sang định dạng mà ứng dụng có thể xử lý.

  • uwsgi: Một giao thức nhị phân nhanh được uWSGI triển khai để giao tiếp với một máy chủ web có tính năng đầy đủ hơn. Đây là một giao thức truyền tải dữ liệu (wire protocol), không phải là giao thức vận chuyển. Đây là cách ưu tiên để giao tiếp với các máy chủ web đang chuyển tiếp (proxy) các yêu cầu đến uWSGI.

Yêu cầu của ứng dụng WSGI

Chuẩn WSGI định nghĩa giao diện giữa phần máy chủ web và ứng dụng trong hệ thống. Trong bối cảnh này, “máy chủ web” ám chỉ máy chủ uWSGI, chịu trách nhiệm chuyển đổi các yêu cầu từ client sang ứng dụng theo chuẩn WSGI. Điều này giúp đơn giản hóa giao tiếp và tạo ra các thành phần liên kết lỏng lẻo, cho phép bạn dễ dàng thay thế bất kỳ bên nào mà không gặp quá nhiều khó khăn.

Máy chủ web (uWSGI) phải có khả năng gửi yêu cầu đến ứng dụng bằng cách kích hoạt một callable đã được định nghĩa. Callable đơn giản chỉ là điểm vào của ứng dụng, nơi máy chủ web có thể gọi một hàm với một số tham số. Các tham số được mong đợi là một từ điển chứa các cặp key-value giống như biến môi trường và một callable được cung cấp bởi thành phần máy chủ web (uWSGI).

Đáp lại, ứng dụng trả về một đối tượng có thể lặp (iterable) sẽ được sử dụng để tạo nội dung thân của phản hồi gửi tới client. Nó cũng sẽ gọi callable của máy chủ web mà nó nhận được dưới dạng tham số. Tham số đầu tiên khi gọi callable của máy chủ web sẽ là mã trạng thái HTTP, và tham số thứ hai sẽ là một danh sách các tuple, mỗi tuple định nghĩa một header của phản hồi và giá trị gửi lại cho client.

Với thành phần “máy chủ web” được cung cấp bởi uWSGI trong trường hợp này, chúng ta chỉ cần đảm bảo rằng ứng dụng của mình có những đặc tính đã được mô tả ở trên. Chúng ta cũng sẽ cấu hình Nginx để xử lý các yêu cầu thực tế từ client và chuyển tiếp chúng tới máy chủ uWSGI.

Hướng dẫn cài đặt uWSGI và Nginx Để chạy Ứng Dụng Python Trên Ubuntu 14.04

Cài đặt các thành phần

Để bắt đầu, chúng ta cần cài đặt các thành phần cần thiết trên máy chủ Ubuntu 14.04. Chủ yếu, chúng ta sẽ thực hiện việc này bằng cách sử dụng aptpip.

Đầu tiên, hãy làm mới chỉ mục gói apt và sau đó cài đặt các thư viện và header phát triển của Python, trình quản lý gói pip cho Python, cũng như máy chủ web Nginx và reverse proxy:

sudo apt-get update
sudo apt-get install python-dev python-pip nginx

Khi việc cài đặt các gói hoàn tất, bạn sẽ có quyền truy cập vào trình quản lý gói pip cho Python. Chúng ta có thể sử dụng điều này để cài đặt gói virtualenv, mà chúng ta sẽ dùng để tách biệt môi trường Python của ứng dụng ra khỏi bất kỳ môi trường nào khác có trên hệ thống:

sudo pip install virtualenv

Khi việc này hoàn tất, chúng ta có thể bắt đầu tạo cấu trúc tổng thể cho ứng dụng. Chúng ta sẽ tạo môi trường ảo như đã thảo luận ở trên và cài đặt máy chủ uWSGI trong môi trường đó.

Thiết lập thư mục ứng dụng và môi trường ảo

Chúng ta sẽ bắt đầu bằng cách tạo một thư mục cho ứng dụng. Thư mục này có thể chứa một thư mục con chứa mã nguồn ứng dụng thực tế trong một ứng dụng hoàn chỉnh. Trong trường hợp của chúng ta, thư mục này sẽ chỉ chứa môi trường ảo và điểm vào WSGI của chúng ta:

mkdir ~/myapp/

Tiếp theo, di chuyển vào thư mục đó để thiết lập môi trường cho ứng dụng:

cd ~/myapp/

Tạo một môi trường ảo bằng lệnh virtualenv. Chúng ta sẽ đặt tên môi trường là myappenv để đơn giản hóa:

virtualenv myappenv

Một môi trường Python mới sẽ được thiết lập trong thư mục có tên myappenv. Chúng ta có thể kích hoạt môi trường này bằng cách gõ lệnh:

source myappenv/bin/activate

Giao diện dòng lệnh của bạn sẽ thay đổi để cho thấy rằng bạn đang hoạt động trong môi trường ảo, ví dụ:

(myappenv)username@host:~/my_app$

Nếu bạn muốn thoát khỏi môi trường này bất cứ lúc nào, chỉ cần gõ:

deactivate

Nếu đã thoát, bạn có thể kích hoạt lại môi trường để tiếp tục với hướng dẫn.

Với môi trường này đang hoạt động, bất kỳ gói Python nào được cài đặt sẽ chỉ nằm trong cây thư mục này và không can thiệp vào môi trường Python của hệ thống. Với điều này, bây giờ chúng ta có thể cài đặt máy chủ uWSGI vào môi trường bằng pip. Gói cài đặt này được gọi là uwsgi (vẫn là máy chủ uWSGI, không phải giao thức uwsgi):

pip install uwsgi

Bạn có thể xác nhận rằng uWSGI đã sẵn sàng bằng cách gõ:

uwsgi --version

Nếu lệnh trên trả về số phiên bản, máy chủ uWSGI đã sẵn sàng để sử dụng.

Tạo một ứng dụng WSGI

Tiếp theo, chúng ta sẽ tạo một ứng dụng WSGI vô cùng đơn giản sử dụng các yêu cầu của chuẩn WSGI đã được thảo luận ở trên. Nhắc lại, ứng dụng của chúng ta cần có các đặc tính sau:

  • Nó phải cung cấp một giao diện thông qua một callable (một hàm hoặc cấu trúc ngôn ngữ nào đó có thể được gọi).
  • Callable phải nhận vào các tham số gồm một từ điển chứa các cặp key-value giống như biến môi trường và một callable được cung cấp bởi máy chủ web (uWSGI).
  • Callable của ứng dụng nên trả về một iterable để tạo ra nội dung thân của phản hồi gửi cho client.
  • Ứng dụng nên gọi callable của máy chủ web với mã trạng thái HTTP và các header của yêu cầu.

Chúng ta sẽ viết ứng dụng của mình trong một tệp có tên wsgi.py trong thư mục ứng dụng:

nano ~/myapp/wsgi.py

Bên trong tệp này, hãy tạo ra ứng dụng tuân thủ WSGI đơn giản nhất có thể. Như với mọi mã Python, hãy chú ý đến việc thụt lề:

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return ["<h1 style='color:blue'>Hello There!</h1>"]

Mã nguồn trên cấu thành một ứng dụng WSGI hoàn chỉnh. Theo mặc định, uWSGI sẽ tìm kiếm một callable có tên application, đó là lý do tại sao chúng ta đặt tên hàm là application. Như bạn thấy, nó nhận hai tham số.

Tham số đầu tiên được gọi là environ vì nó sẽ là một từ điển chứa các cặp key-value giống như biến môi trường. Tham số thứ hai được gọi là start_response và là tên mà ứng dụng sẽ sử dụng nội bộ để tham chiếu đến callable của máy chủ web (uWSGI) được truyền vào. Cả hai tên tham số này được chọn dựa trên các ví dụ trong đặc tả PEP 333 định nghĩa các tương tác WSGI.

Ứng dụng của chúng ta phải nhận thông tin này và thực hiện hai việc. Đầu tiên, nó phải gọi callable nhận được với mã trạng thái HTTP và các header mà nó muốn gửi lại. Trong trường hợp này, chúng ta gửi phản hồi “200 OK” và đặt header Content-Type thành text/html.

Thứ hai, nó cần trả về một đối tượng có thể lặp (iterable) để dùng làm thân của phản hồi. Ở đây, chúng ta chỉ sử dụng một danh sách chứa một chuỗi HTML. Các chuỗi cũng có thể lặp được, nhưng khi nằm trong danh sách, uWSGI sẽ xử lý toàn bộ chuỗi chỉ với một vòng lặp.

Trong một kịch bản thực tế, tệp này có thể được sử dụng như một liên kết đến phần còn lại của mã ứng dụng của bạn. Ví dụ, các dự án Django mặc định bao gồm tệp wsgi.py để chuyển đổi các yêu cầu từ máy chủ web (uWSGI) sang ứng dụng (Django). Giao diện WSGI đơn giản vẫn giữ nguyên bất kể ứng dụng của bạn phức tạp đến đâu. Đây là một trong những điểm mạnh của giao diện này.

Lưu và đóng tệp khi bạn đã hoàn tất.

Để kiểm tra mã, chúng ta có thể khởi động uWSGI. Tạm thời, chúng ta sẽ yêu cầu nó sử dụng HTTP và lắng nghe trên cổng 8080. Chúng ta sẽ truyền cho nó tên của script (không bao gồm hậu tố):

uwsgi --socket 0.0.0.0:8080 --protocol=http -w wsgi

Bây giờ, nếu bạn truy cập địa chỉ IP hoặc tên miền của máy chủ trên trình duyệt, kèm theo “:8080”, bạn sẽ thấy dòng header cấp một mà chúng ta đã truyền làm thân trong tệp wsgi.py.

Ngừng máy chủ bằng cách nhấn CTRL-C khi bạn đã xác nhận rằng nó hoạt động.

Chúng ta đã hoàn tất việc thiết kế ứng dụng thực tế tại đây. Bạn có thể hủy kích hoạt môi trường ảo nếu muốn:

deactivate

Cấu Hình Tệp Cấu Hình uWSGI

Trong ví dụ trên, chúng ta đã khởi động máy chủ uWSGI một cách thủ công và truyền cho nó một số tham số qua dòng lệnh. Chúng ta có thể tránh điều này bằng cách tạo một tệp cấu hình. Máy chủ uWSGI có thể đọc cấu hình từ nhiều định dạng khác nhau, nhưng để đơn giản, chúng ta sẽ sử dụng định dạng .ini.

Tiếp tục với cách đặt tên đã sử dụng cho đến nay, chúng ta sẽ gọi tệp này là myapp.ini và đặt nó trong thư mục ứng dụng:

nano ~/myapp/myapp.ini

Bên trong tệp, chúng ta cần thiết lập một phần có tên [uwsgi]. Phần này sẽ chứa tất cả các mục cấu hình. Chúng ta sẽ bắt đầu bằng cách xác định ứng dụng của mình. Máy chủ uWSGI cần biết callable của ứng dụng nằm ở đâu, do đó chúng ta chỉ định tên tệp và hàm bên trong:

[uwsgi]
module = wsgi:application

Chúng ta muốn đánh dấu quá trình uWSGI ban đầu là master và sau đó tạo ra một số tiến trình worker. Chúng ta sẽ bắt đầu với năm worker:

[uwsgi]
module = wsgi:application

master = true
processes = 5

Thực ra, chúng ta sẽ thay đổi giao thức mà uWSGI sử dụng để giao tiếp với bên ngoài. Khi kiểm tra ứng dụng, đã chỉ định –protocol=http để có thể xem qua trình duyệt. Vì chúng ta sẽ cấu hình Nginx làm reverse proxy đứng trước uWSGI, nên có thể thay đổi điều này. Nginx triển khai cơ chế proxy qua giao thức uwsgi, một giao thức nhị phân nhanh mà uWSGI có thể sử dụng để giao tiếp với các máy chủ khác. Giao thức uwsgi thực ra là giao thức mặc định của uWSGI, nên chỉ cần bỏ qua phần chỉ định giao thức, nó sẽ tự động sử dụng uwsgi.

Vì chúng ta đang thiết kế cấu hình này để sử dụng với Nginx, nên chúng ta cũng sẽ chuyển từ việc sử dụng cổng mạng sang sử dụng Unix socket. Điều này an toàn hơn và nhanh hơn. Socket sẽ được tạo trong thư mục hiện tại nếu chúng ta sử dụng đường dẫn tương đối. Chúng ta sẽ đặt tên nó là myapp.sock. Đồng thời, thay đổi quyền truy cập thành “664” để Nginx có thể ghi vào (chúng ta sẽ khởi động uWSGI với nhóm www-data mà Nginx sử dụng). Chúng ta cũng thêm tùy chọn vacuum, giúp xoá socket khi quá trình dừng lại:

[uwsgi]
module = wsgi:application

master = true
processes = 5

socket = myapp.sock
chmod-socket = 664
vacuum = true

Chúng ta cần một tùy chọn cuối cùng vì sẽ tạo tệp Upstart để khởi động ứng dụng khi máy chủ bật. Upstart và uWSGI có quan điểm khác nhau về hành động của tín hiệu SIGTERM đối với ứng dụng. Để xử lý sự không nhất quán này nhằm xử lý các tiến trình như mong đợi với Upstart, chúng ta chỉ cần thêm tùy chọn die-on-term để uWSGI kết thúc tiến trình thay vì tải lại:

[uwsgi]
module = wsgi:application

master = true
processes = 5

socket = myapp.sock
chmod-socket = 664
vacuum = true

die-on-term = true

Lưu và đóng tệp khi bạn đã hoàn tất. Tệp cấu hình này bây giờ đã sẵn sàng để sử dụng cùng với script Upstart.

Tạo tệp Upstart để quản lý ứng dụng

Chúng ta có thể khởi động một phiên bản uWSGI khi máy chủ khởi động để ứng dụng luôn sẵn sàng. Tệp này sẽ được đặt trong thư mục /etc/init mà Upstart kiểm tra. Chúng ta sẽ gọi tệp này là myapp.conf:

sudo nano /etc/init/myapp.conf

Đầu tiên, hãy bắt đầu với một mô tả về dịch vụ và chỉ định các mức runlevel của hệ thống mà dịch vụ sẽ tự động chạy. Các mức runlevel tiêu chuẩn cho người dùng là 2 đến 5. Chúng ta sẽ yêu cầu Upstart dừng dịch vụ khi hệ thống chuyển sang bất kỳ runlevel nào ngoài nhóm này (ví dụ: khi máy chủ khởi động lại hoặc ở chế độ người dùng đơn):

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

Tiếp theo, báo cho Upstart biết ứng dụng sẽ chạy dưới tài khoản và nhóm nào. Chúng ta muốn chạy ứng dụng dưới tài khoản của chính mình (trong hướng dẫn này, sử dụng demo, nhưng bạn nên thay thế bằng tài khoản của riêng bạn). Tuy nhiên, nhóm cần được đặt là www-data – nhóm mà Nginx sử dụng. Điều này cần thiết vì máy chủ web cần có khả năng đọc và ghi vào socket mà tệp .ini của chúng ta tạo ra:

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

setuid demo
setgid www-data

Tiếp theo, chúng ta sẽ chạy các lệnh thực tế để khởi động uWSGI. Vì đã cài đặt uWSGI trong môi trường ảo, có một số công đoạn thêm cần thực hiện. Thay vì chỉ cung cấp đường dẫn đầy đủ đến file thực thi uWSGI, chúng ta sẽ kích hoạt môi trường ảo. Điều này giúp dễ dàng nếu ứng dụng phụ thuộc vào phần mềm bổ sung cài đặt trong môi trường đó.

Để thực hiện, sử dụng khối script. Bên trong, chuyển đến thư mục ứng dụng, kích hoạt môi trường ảo (trong script, dùng dấu . thay vì source), và khởi động phiên bản uWSGI trỏ đến tệp .ini của chúng ta:

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

setuid demo
setgid www-data

script
    cd /home/demo/myapp
    . myappenv/bin/activate
    uwsgi --ini myapp.ini
end script

Với đó, script Upstart đã hoàn thành. Lưu và đóng tệp khi đã xong.

Bây giờ, khởi động dịch vụ bằng lệnh:

sudo start myapp

Xác minh dịch vụ đã khởi động bằng lệnh:

ps aux | grep myapp

Ví dụ, kết quả có thể hiển thị như sau:

demo   14618  0.0  0.5  35868  5996 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14619  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14620  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14621  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14622  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14623  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   15520  0.0  0.0  11740   936 pts/0    S+   15:53   0:00 grep --color=auto myapp

Dịch vụ này sẽ tự động khởi động cùng với máy chủ. Bạn có thể dừng dịch vụ bất cứ lúc nào bằng lệnh:

sudo stop myapp

Cấu hình Nginx để Proxy tới uWSGI

Tại thời điểm này, chúng ta đã có ứng dụng WSGI và đã xác nhận rằng uWSGI có thể đọc và phục vụ nó. Chúng ta đã tạo tệp cấu hình và script Upstart. Quá trình uWSGI của chúng ta sẽ lắng nghe trên một socket và giao tiếp sử dụng giao thức uwsgi.

Bây giờ, hãy cấu hình Nginx làm reverse proxy.
Nginx có khả năng chuyển tiếp yêu cầu sử dụng giao thức uwsgi để giao tiếp với uWSGI. Đây là giao thức nhanh hơn HTTP và hoạt động hiệu quả hơn.

Cấu hình Nginx mà chúng ta sẽ thiết lập cực kỳ đơn giản. Tạo một tệp mới trong thư mục sites-available của cấu trúc cấu hình Nginx. Chúng ta sẽ đặt tên tệp này là myapp để phù hợp với tên ứng dụng đã sử dụng:

sudo nano /etc/nginx/sites-available/myapp

Bên trong tệp, chỉ định số cổng và tên miền mà khối server sẽ phản hồi. Trong trường hợp này, sử dụng cổng mặc định 80:

server {
    listen 80;
    server_name server_domain_or_IP;
}

Vì chúng ta muốn chuyển tất cả các yêu cầu trên tên miền hoặc địa chỉ IP này tới ứng dụng WSGI, nên tạo một khối location cho các yêu cầu bắt đầu bằng / (khớp với mọi thứ). Bên trong, sử dụng chỉ thị include để thêm vào các tham số mặc định từ tệp uwsgi_params nằm trong thư mục cấu hình của Nginx. Sau đó, chuyển tiếp lưu lượng truy cập tới phiên bản uWSGI qua giao thức uwsgi, sử dụng Unix socket đã cấu hình trước đó:

server {
    listen 80;
    server_name server_domain_or_IP;

    location / {
        include         uwsgi_params;
        uwsgi_pass      unix:/home/demo/myapp/myapp.sock;
    }
}

Đó thực sự là tất cả những gì cần thiết cho một ứng dụng đơn giản. Có thể mở rộng thêm cho một ứng dụng hoàn chỉnh hơn, chẳng hạn như định nghĩa nhiều máy chủ uWSGI upstream bên ngoài khối này, thêm các tham số uWSGI khác, hoặc xử lý các tệp tĩnh trực tiếp qua Nginx và chỉ chuyển các yêu cầu động tới uWSGI. Tuy nhiên, với ứng dụng ba dòng của chúng ta, không cần thêm tính năng nào như vậy, vì vậy hãy lưu và đóng tệp.

Kích hoạt cấu hình máy chủ vừa tạo bằng cách liên kết nó tới thư mục sites-enabled:

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled

Kiểm tra tệp cấu hình xem có lỗi cú pháp không:

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled

Nếu không có lỗi, khởi động lại Nginx để áp dụng các thay đổi:

sudo service nginx restart

Khi Nginx khởi động lại, bạn sẽ có thể truy cập tên miền hoặc địa chỉ IP của máy chủ (không cần số cổng) và thấy ứng dụng đã cấu hình.

Kết luận

Sau khi hoàn thành hướng dẫn này, bạn đã triển khai thành công một ứng dụng WSGI đơn giản, đồng thời nắm bắt cách tổ chức và mở rộng các ứng dụng phức tạp hơn. Chúng ta đã thiết lập máy chủ uWSGI trong môi trường ảo riêng biệt, tạo tệp cấu hình phù hợp, đồng thời sử dụng script Upstart để tự động hóa quy trình khởi động. Ngoài ra, bạn cũng đã triển khai Nginx làm reverse proxy để tối ưu hóa việc xử lý kết nối, giao tiếp hiệu quả với uWSGI qua giao thức nhị phân uwsgi.

Từ đây, bạn có thể mở rộng hệ thống để đáp ứng nhu cầu thực tế trong môi trường sản xuất. Chẳng hạn, sử dụng chế độ “emperor mode” của uWSGI để quản lý nhiều ứng dụng, mở rộng cấu hình Nginx để cân bằng tải giữa các phiên bản uWSGI hoặc xử lý tệp tĩnh cho ứng dụng. Bạn cũng có thể cân nhắc cài đặt uWSGI toàn cục thay vì trong môi trường ảo nếu cần phục vụ nhiều ứng dụng trên cùng một máy chủ. Với sự linh hoạt của các thành phần này, bạn có thể điều chỉnh và tối ưu hóa hệ thống để phù hợp với các kịch bản triển khai khác nhau, đảm bảo hiệu suất và tính ổn định cao nhất.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *