Cách cài đặt và cấu hình Django chạy với Gunicorn, Nginx và PostgreSQL trên Ubuntu

Cách cài đặt và cấu hình Django chạy với Gunicorn, Nginx và PostgreSQL trên Ubuntu

Django là một framework web mạnh mẽ, giúp lập trình viên phát triển nhanh chóng các ứng dụng và website bằng Python. Mặc dù Django cung cấp một máy chủ phát triển tích hợp để thử nghiệm mã nguồn cục bộ, nhưng để triển khai trong môi trường sản xuất, bạn cần một giải pháp máy chủ web an toàn và hiệu suất cao hơn.

Trong hướng dẫn này, bạn sẽ thiết lập và cấu hình một môi trường sản xuất hoàn chỉnh trên Ubuntu 22.04 (hoặc các phiên bản Ubuntu được hỗ trợ khác) để triển khai ứng dụng Django. Trước tiên, bạn sẽ sử dụng PostgreSQL làm hệ quản trị cơ sở dữ liệu thay thế SQLite mặc định, giúp nâng cao khả năng mở rộng và quản lý dữ liệu. Tiếp theo, bạn sẽ cài đặt và cấu hình Gunicorn làm máy chủ ứng dụng để xử lý các yêu cầu HTTP và giao tiếp với ứng dụng Django. Cuối cùng, bạn sẽ triển khai Nginx làm reverse proxy cho Gunicorn, tận dụng khả năng xử lý kết nối mạnh mẽ và các tính năng bảo mật của Nginx nhằm tối ưu hóa hiệu suất và bảo vệ ứng dụng của bạn.

Bên cạnh đó, bạn sẽ cài đặt Django trong một môi trường ảo (virtual environment), đảm bảo tính độc lập cho từng dự án và giúp quản lý dễ dàng các dependencies. Khi cơ sở dữ liệu và ứng dụng đã hoạt động ổn định, bạn sẽ hoàn thiện hệ thống bằng cách tích hợp Gunicorn và Nginx để tối ưu hóa quá trình xử lý yêu cầu.

Với cấu hình này, bạn sẽ có một hệ thống vững chắc để triển khai ứng dụng Django trên môi trường sản xuất, đảm bảo hiệu suất cao, tính bảo mật mạnh mẽ và khả năng mở rộng linh hoạt.

Điều kiện tiên quyết

Nếu bạn đang sử dụng Ubuntu phiên bản 16.04 trở xuống, chúng tôi khuyên bạn nên nâng cấp lên phiên bản mới hơn, vì Ubuntu đã ngừng hỗ trợ các phiên bản này. Bộ sưu tập hướng dẫn này sẽ hỗ trợ bạn trong việc nâng cấp Ubuntu.

Để hoàn thành hướng dẫn này, bạn cần:

  • Một máy chủ chạy Ubuntu,
  • Một người dùng không phải root (non-root) có quyền sudo,
  • Một tường lửa đã được kích hoạt.

Để biết cách thiết lập, hãy chọn phiên bản Ubuntu của bạn trong danh sách này và làm theo hướng dẫn “Initial Server Setup Guide”.

Bước 1 – Cài đặt các gói từ kho Ubuntu

Đầu tiên, bạn sẽ tải và cài đặt tất cả thành phần cần thiết từ kho Ubuntu. Sau đó, bạn sẽ dùng pip (trình quản lý gói Python) để cài thêm các thành phần khác.

Trước hết, hãy cập nhật chỉ mục gói cục bộ apt và cài đặt các gói cần thiết. Những gói này phụ thuộc vào phiên bản Python mà dự án của bạn sử dụng.

Nếu bạn sử dụng Django với Python 3, hãy chạy:

sudo apt update
sudo apt install python3-venv python3-dev libpq-dev postgresql postgresql-contrib nginx curl

Lệnh này sẽ cài công cụ tạo môi trường ảo (virtual environment) cho các dự án Python, các tệp phát triển Python (cần thiết để biên dịch Gunicorn sau này), hệ thống cơ sở dữ liệu Postgres cùng thư viện tương tác với nó, và máy chủ web Nginx.

Bước 2 – Tạo cơ sở dữ liệu và người dùng PostgreSQL

Bây giờ, bạn có thể tạo ngay một cơ sở dữ liệu (database) và một người dùng (user) cho ứng dụng Django của mình.

Mặc định, Postgres dùng phương thức xác thực “peer authentication” cho kết nối cục bộ. Nói đơn giản, nếu tên người dùng hệ điều hành trùng khớp với một người dùng hợp lệ của Postgres, thì người dùng đó có thể đăng nhập mà không cần thêm xác thực.

Trong quá trình cài đặt Postgres, một người dùng hệ điều hành tên postgres đã được tạo, ứng với người dùng quản trị postgres trong PostgreSQL. Bạn cần dùng người dùng này để thực hiện tác vụ quản trị. Bạn có thể dùng sudo và truyền tham số -u để xác định tên người dùng.

Đăng nhập vào phiên làm việc tương tác của Postgres bằng lệnh:

sudo -u postgres psql

Bạn sẽ thấy dấu nhắc lệnh PostgreSQL, nơi bạn có thể thiết lập các yêu cầu của mình.

Trước tiên, tạo một cơ sở dữ liệu cho dự án:

CREATE DATABASE myproject;
Lưu ý: Mọi câu lệnh trong Postgres phải kết thúc bằng dấu chấm phẩy ;. Nếu bạn gặp lỗi, hãy kiểm tra xem câu lệnh đã kết thúc bằng ; chưa.

Tiếp theo, tạo một người dùng cơ sở dữ liệu cho dự án. Hãy chọn mật khẩu an toàn:

CREATE USER myprojectuser WITH PASSWORD 'password';

Sau đó, bạn sẽ điều chỉnh một số tham số kết nối cho người dùng vừa tạo. Điều này giúp tăng tốc độ hoạt động với cơ sở dữ liệu, vì các giá trị đúng không cần phải truy vấn và thiết lập mỗi khi khởi tạo kết nối.

Bạn sẽ đặt mã hóa ký tự mặc định là UTF-8 (Django mong đợi điều này). Bạn cũng thiết lập chế độ cách ly giao dịch mặc định là “read committed” (chặn đọc từ các giao dịch chưa commit). Cuối cùng, bạn đặt múi giờ (timezone). Mặc định, các dự án Django sử dụng UTC. Đây đều là khuyến khích chính thức từ Django:

ALTER ROLE myprojectuser SET client_encoding TO 'utf8';
ALTER ROLE myprojectuser SET default_transaction_isolation TO 'read committed';
ALTER ROLE myprojectuser SET timezone TO 'UTC';

Bây giờ, bạn có thể cấp quyền cho người dùng mới để quản trị cơ sở dữ liệu mới:

GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;

Khi hoàn tất, thoát khỏi phiên làm việc PostgreSQL bằng cách gõ:

\q

Postgres giờ đã sẵn sàng để Django kết nối và quản lý thông tin trong cơ sở dữ liệu.

Bước 3 – Tạo môi trường ảo Python cho dự án của bạn

Giờ bạn đã có một cơ sở dữ liệu, bạn có thể bắt đầu cài đặt các thành phần còn lại cho dự án. Bạn sẽ cài các gói Python trong một virtual environment để dễ quản lý.

Trước tiên, tạo một thư mục và chuyển vào đó để lưu trữ tệp dự án:

mkdir ~/myprojectdir
cd ~/myprojectdir

Trong thư mục dự án, tạo một môi trường ảo Python bằng lệnh:

python3 -m venv myprojectenv

Lệnh này sẽ tạo một thư mục myprojectenv bên trong ~/myprojectdir. Thư mục này chứa phiên bản Python cục bộ và pip cục bộ để quản lý gói. Bạn có thể dùng cấu trúc môi trường ảo này để cài đặt và cấu hình một môi trường Python độc lập cho bất kỳ dự án nào.

Trước khi cài đặt các yêu cầu Python cho dự án, bạn cần kích hoạt môi trường ảo. Thực hiện bằng lệnh:

source myprojectenv/bin/activate

Lúc này, dấu nhắc lệnh sẽ thay đổi, báo hiệu bạn đang hoạt động trong môi trường ảo Python, ví dụ:

ruby
(myprojectenv)user@host:~/myprojectdir$

Với môi trường ảo đang bật, hãy cài Django, Gunicorn, và bộ kết nối psycopg2 (PostgreSQL) bằng pip cục bộ:

Lưu ý: Khi môi trường ảo đã được kích hoạt (dấu nhắc lệnh có (myprojectenv)), hãy dùng pip thay vì pip3, ngay cả khi bạn dùng Python 3. Bản pip trong môi trường ảo luôn mang tên pip, bất kể phiên bản Python.

pip install django gunicorn psycopg2-binary

Bạn đã có đầy đủ các phần mềm cần để bắt đầu dự án Django.

Bước 4 – Tạo và cấu hình dự án Django mới

Sau khi cài xong các thành phần Python, bạn có thể tạo các tệp dự án Django thực tế.

Vì bạn đã có một thư mục dự án, bạn sẽ bảo Django cài đặt các tệp vào đây. Django sẽ tạo một thư mục con (chứa mã nguồn chính), và đặt một script quản lý ở ngay thư mục này. Mấu chốt là bạn chỉ rõ thư mục thay vì để Django tự động quyết định dựa trên thư mục hiện tại:

Lúc này, thư mục dự án ~/myprojectdir sẽ có những nội dung sau:

  • ~/myprojectdir/manage.py: Tệp script quản lý dự án Django.
  • ~/myprojectdir/myproject/: Gói (package) dự án Django. Thư mục này chứa các tệp __init__.py, settings.py, urls.py, asgi.py, wsgi.py.
  • ~/myprojectdir/myprojectenv/: Thư mục môi trường ảo bạn vừa tạo.

Việc đầu tiên với các tệp dự án mới tạo là điều chỉnh settings. Mở tệp cấu hình:

django-admin startproject myproject ~/myprojectdir

Tìm dòng ALLOWED_HOSTS. Dòng này định nghĩa danh sách địa chỉ IP hoặc tên miền được phép kết nối đến Django. Mọi yêu cầu đến với Host header không nằm trong danh sách này sẽ bị từ chối. Django yêu cầu thiết lập danh sách này để ngăn chặn một số lỗ hổng bảo mật nhất định.

Trong ngoặc vuông, hãy liệt kê các địa chỉ IP hoặc tên miền gắn với máy chủ Django. Mỗi phần tử nên nằm trong dấu nháy, cách nhau bằng dấu phẩy. Nếu bạn muốn cho phép toàn bộ tên miền và các subdomain, hãy thêm dấu chấm . ở đầu tên miền. Dưới đây là ví dụ mô tả:

Lưu ý: Nhớ thêm localhost vì bạn sẽ reverse proxy từ Nginx cục bộ.

~/myprojectdir/myproject/settings.py
. . .
# The simplest case: just add the domain name(s) and IP addresses of your Django server
# ALLOWED_HOSTS = [ 'example.com', '203.0.113.5']
# To respond to 'example.com' and any subdomains, start the domain with a dot
# ALLOWED_HOSTS = ['.example.com', '203.0.113.5']
ALLOWED_HOSTS = ['your_server_domain_or_IP', 'second_domain_or_IP', . . ., 'localhos

Kế tiếp, tìm đến phần cấu hình cơ sở dữ liệu (bắt đầu với DATABASES). Mặc định, cấu hình này dành cho SQLite. Bạn đã tạo một cơ sở dữ liệu PostgreSQL, vì thế bạn cần chỉnh lại cài đặt.

Hãy dùng trình điều khiển psycopg2 mà bạn đã cài qua pip. Bạn cần cung cấp tên cơ sở dữ liệu, tên người dùng, mật khẩu người dùng, rồi chỉ định rằng cơ sở dữ liệu nằm trên máy cục bộ (localhost). Bạn có thể để trống PORT:

~/myprojectdir/myproject/settings.py
. . .

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'myproject',
        'USER': 'myprojectuser',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '',
    }
}

. . .

Tiếp theo, cuộn xuống cuối tệp và thêm cấu hình nơi đặt static files. Điều này cần thiết để Nginx có thể xử lý yêu cầu các tệp tĩnh. Dòng sau báo Django đặt chúng vào thư mục static trong thư mục gốc dự án:

~/myprojectdir/myproject/settings.py
. . .
STATIC_URL = 'static/'

# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

import os
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

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

Bước 5 – Hoàn tất thiết lập ban đầu cho dự án

Bây giờ, bạn có thể chạy lệnh migrate để áp dụng schema cơ sở dữ liệu ban đầu lên PostgreSQL:

~/myprojectdir/manage.py makemigrations
~/myprojectdir/manage.py migrate

Tạo người dùng quản trị (admin) cho dự án:

~/myprojectdir/manage.py createsuperuser

Bạn sẽ phải nhập tên người dùng, email, mật khẩu và xác nhận mật khẩu.

Bạn cũng có thể gom tất cả nội dung tĩnh (static) vào thư mục static đã cấu hình bằng lệnh:

~/myprojectdir/manage.py collectstatic

Bạn cần xác nhận thao tác. Các tệp tĩnh sẽ được đưa vào thư mục static trong thư mục dự án.

Nếu bạn làm theo hướng dẫn “initial server setup,” bạn có thể đã bật UFW firewall để bảo vệ máy chủ. Để thử nghiệm máy chủ phát triển (development server), bạn cần cho phép truy cập cổng 8000:

sudo ufw allow 8000

Cuối cùng, hãy kiểm tra dự án bằng cách khởi chạy máy chủ phát triển Django:

~/myprojectdir/manage.py runserver 0.0.0.0:8000

Mở trình duyệt và truy cập địa chỉ IP hoặc tên miền máy chủ kèm :8000:

http://server_domain_or_IP:8000

Bạn sẽ thấy trang mặc định của Django:

Thiet ke chua co ten 34

Nếu bạn thêm /admin vào cuối URL, bạn sẽ được yêu cầu đăng nhập bằng tài khoản admin đã tạo với createsuperuser:

Thiet ke chua co ten 35

Sau khi xác thực, bạn có thể truy cập giao diện quản trị mặc định của Django:

Thiet ke chua co ten 36

Khi thử nghiệm xong, nhấn CTRL-C trong cửa sổ terminal để dừng máy chủ phát triển.

Bước 6 – Kiểm tra khả năng phục vụ dự án của Gunicorn

Trước khi thoát khỏi môi trường ảo, bạn cần kiểm tra Gunicorn có thể phục vụ ứng dụng hay không. Chuyển vào thư mục dự án và dùng Gunicorn để tải module WSGI của dự án:

cd ~/myprojectdir
gunicorn --bind 0.0.0.0:8000 myproject.wsgi

Lệnh này khởi chạy Gunicorn trên cùng cổng mà máy chủ phát triển Django đang dùng. Bạn có thể kiểm tra lại ứng dụng trong trình duyệt.

Lưu ý: Giao diện quản trị sẽ không có CSS vì Gunicorn không biết cách tìm các tệp CSS tĩnh.

Bạn đã truyền cho Gunicorn một module bằng cách chỉ định đường dẫn đến tệp wsgi.py của Django (điểm vào ứng dụng) theo cú pháp module của Python. Bên trong tệp này, hàm application được định nghĩa để giao tiếp với ứng dụng.

Khi kiểm tra xong, nhấn CTRL-C để dừng Gunicorn.

Lúc này, bạn đã cấu hình xong ứng dụng Django. Bạn có thể thoát khỏi môi trường ảo:

deactivate

Dấu nhắc lệnh sẽ trở lại bình thường (không còn (myprojectenv)).

Bước 7 – Tạo các tệp Socket và Service của Gunicorn trên systemd

Bạn đã thử nghiệm Gunicorn tương tác với ứng dụng Django, nhưng cần một cách khởi động/dừng dịch vụ mạnh mẽ hơn. Bạn sẽ tạo tệp servicesocket cho systemd.

Socket của Gunicorn sẽ được tạo khi khởi động máy (boot) và lắng nghe kết nối. Khi có kết nối, systemd sẽ tự động khởi chạy Gunicorn để xử lý.

Bắt đầu bằng cách tạo tệp socket của Gunicorn với quyền sudo:

sudo nano /etc/systemd/system/gunicorn.socket

Bên trong, tạo phần [Unit] mô tả socket, phần [Socket] xác định vị trí socket, và phần [Install] đảm bảo socket được tạo đúng lúc:

etc/systemd/system/gunicorn.socket
[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Lưu và đóng tệp.

Tiếp theo, tạo và mở tệp service cho Gunicorn (với quyền sudo). Tên tệp dịch vụ nên trùng với tên socket, chỉ khác phần mở rộng:

sudo nano /etc/systemd/system/gunicorn.service

Bắt đầu với phần [Unit], chứa metadata và các phụ thuộc. Đưa mô tả cho dịch vụ, yêu cầu chỉ khởi động sau khi mạng (network.target) đã sẵn sàng. Vì dịch vụ phụ thuộc vào socket, bạn cần Requires=gunicorn.socket:

/etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

Kế tiếp, mở phần [Service]. Chỉ định user và group mà tiến trình chạy dưới quyền đó. Ở đây, bạn để user là người dùng thường (sở hữu các tệp dự án), group là www-data để Nginx giao tiếp dễ dàng với Gunicorn.

Sau đó, bạn chỉ định thư mục làm việc (working directory) và câu lệnh khởi chạy dịch vụ (đường dẫn đầy đủ tới Gunicorn trong môi trường ảo). Bạn bind tiến trình đến Unix socket /run/gunicorn.sock để Nginx có thể kết nối. Ghi log ra stdout để journald thu thập. Bạn cũng có thể thêm tham số Gunicorn tùy chọn. Ví dụ, bạn chỉ định 3 worker:

/etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myprojectdir
ExecStart=/home/sammy/myprojectdir/myprojectenv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          myproject.wsgi:application

Cuối cùng, thêm phần [Install] để cho systemd biết liên kết dịch vụ này với đơn vị nào khi kích hoạt khởi động cùng hệ thống. Bạn muốn dịch vụ này khởi chạy khi hệ thống multi-user bình thường:

/etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myprojectdir
ExecStart=/home/sammy/myprojectdir/myprojectenv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          myproject.wsgi:application

[Install]
WantedBy=multi-user.target

Hoàn tất tệp dịch vụ. Lưu và đóng.

Bây giờ, bạn có thể khởi động và kích hoạt socket Gunicorn. Lệnh này tạo file socket tại /run/gunicorn.sock ngay và khi khởi động. Khi có kết nối đến socket, systemd sẽ tự động chạy gunicorn.service để xử lý:

sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket

Bạn có thể xác nhận hoạt động bằng cách kiểm tra file socket.

Bước 8 – Kiểm tra tệp Socket của Gunicorn

Kiểm tra trạng thái quá trình để xem nó có khởi động được không:

sudo systemctl status gunicorn.socket

Bạn sẽ thấy kết quả tương tự:

Output
● gunicorn.socket - gunicorn socket
     Loaded: loaded (/etc/systemd/system/gunicorn.socket; enabled; vendor preset: enabled)
     Active: active (listening) since Mon 2022-04-18 17:53:25 UTC; 5s ago
   Triggers: ● gunicorn.service
     Listen: /run/gunicorn.sock (Stream)
     CGroup: /system.slice/gunicorn.socket

Apr 18 17:53:25 django systemd[1]: Listening on gunicorn socket.

Tiếp theo, kiểm tra sự tồn tại của gunicorn.sock trong thư mục /run:

file /run/gunicorn.sock
Output
/run/gunicorn.sock: socket

Nếu systemctl status cho thấy lỗi hoặc bạn không tìm thấy gunicorn.sock trong /run, nghĩa là socket Gunicorn chưa được tạo thành công. Kiểm tra log của socket:

sudo journalctl -u gunicorn.socket

Xem lại tệp /etc/systemd/system/gunicorn.socket để khắc phục nếu cần.

Bước 9 – Kiểm tra kích hoạt socket

Hiện tại, nếu bạn chỉ khởi động gunicorn.socket mà chưa có kết nối nào đến, gunicorn.service sẽ chưa chạy. Kiểm tra bằng lệnh:

sudo systemctl status gunicorn
Output
○ gunicorn.service - gunicorn daemon
     Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
     Active: inactive (dead)
TriggeredBy: ● gunicorn.socket

Để thử cơ chế socket activation, bạn có thể gửi kết nối đến socket qua curl:

bash
curl --unix-socket /run/gunicorn.sock localhost

Bạn sẽ thấy mã HTML trả về từ ứng dụng. Điều này cho thấy Gunicorn đã khởi chạy và phục vụ ứng dụng Django. Kiểm tra trạng thái Gunicorn:

sudo systemctl status gunicorn
Output
● gunicorn.service - gunicorn daemon
     Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
     Active: active (running) since Mon 2022-04-18 17:54:49 UTC; 5s ago
TriggeredBy: ● gunicorn.socket
   Main PID: 102674 (gunicorn)
      Tasks: 4 (limit: 4665)
     Memory: 94.2M
        CPU: 885ms
     CGroup: /system.slice/gunicorn.service
             ├─102674 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock myproject.wsgi:application
             ├─102675 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock myproject.wsgi:application
             ├─102676 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock myproject.wsgi:application
             └─102677 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock myproject.wsgi:application

Apr 18 17:54:49 django systemd[1]: Started gunicorn daemon.
Apr 18 17:54:49 django gunicorn[102674]: [2022-04-18 17:54:49 +0000] [102674] [INFO] Starting gunicorn 20.1.0
Apr 18 17:54:49 django gunicorn[102674]: [2022-04-18 17:54:49 +0000] [102674] [INFO] Listening at: unix:/run/gunicorn.sock (102674)
Apr 18 17:54:49 django gunicorn[102674]: [2022-04-18 17:54:49 +0000] [102674] [INFO] Using worker: sync
Apr 18 17:54:49 django gunicorn[102675]: [2022-04-18 17:54:49 +0000] [102675] [INFO] Booting worker with pid: 102675
Apr 18 17:54:49 django gunicorn[102676]: [2022-04-18 17:54:49 +0000] [102676] [INFO] Booting worker with pid: 102676
Apr 18 17:54:50 django gunicorn[102677]: [2022-04-18 17:54:50 +0000] [102677] [INFO] Booting worker with pid: 102677
Apr 18 17:54:50 django gunicorn[102675]:  - - [18/Apr/2022:17:54:50 +0000] "GET / HTTP/1.1" 200 10697 "-" "curl/7.81.0"

Nếu curl hoặc systemctl status cho thấy lỗi, hãy xem log:

sudo journalctl -u gunicorn

Kiểm tra lại tệp /etc/systemd/system/gunicorn.service. Nếu bạn chỉnh sửa tệp này, hãy reload daemon và khởi động lại Gunicorn:

sudo systemctl daemon-reload
sudo systemctl restart gunicorn

Đảm bảo bạn xử lý xong lỗi trước khi tiếp tục.

Bước 10 – Cấu hình Nginx để chuyển tiếp yêu cầu đến Gunicorn

Khi đã thiết lập xong Gunicorn, bạn cần cấu hình Nginx để chuyển tiếp (proxy) lưu lượng đến Gunicorn.

Tạo và mở một server block mới trong thư mục sites-available của Nginx:

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

Bên trong, mở một khối server {}. Đầu tiên, bạn chỉ định server này lắng nghe cổng 80 thông thường, và phản hồi với tên miền hoặc IP của máy chủ:

/etc/nginx/sites-available/myproject
server {
    listen 80;
    server_name server_domain_or_IP;
}

Kế tiếp, bạn bảo Nginx bỏ qua lỗi liên quan đến favicon, đồng thời chỉ cho nó biết nơi chứa các tệp tĩnh trong ~/myprojectdir/static. Tất cả các tệp này đều có tiền tố URI “/static”, nên bạn có thể tạo một khối location để khớp các yêu cầu này:

/etc/nginx/sites-available/myproject
server {
    listen 80;
    server_name server_domain_or_IP;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/sammy/myprojectdir;
    }
}

Cuối cùng, tạo một khối location / {} để khớp mọi yêu cầu khác. Bên trong, bạn thêm proxy_params mặc định và chuyển tiếp lưu lượng đến socket Gunicorn:

/etc/nginx/sites-available/myproject
server {
    listen 80;
    server_name server_domain_or_IP;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/sammy/myprojectdir;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

Lưu và đóng tệp khi xong. Kích hoạt cấu hình bằng cách liên kết (symlink) đến sites-enabled:

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

Kiểm tra cấu hình Nginx:

sudo nginx -t

Nếu không có lỗi, hãy khởi động lại Nginx:

sudo systemctl restart nginx

Cuối cùng, bạn cần mở tường lửa để cho phép truy cập cổng 80. Vì bạn không còn cần cổng 8000 (máy chủ phát triển) nữa, hãy xóa quy tắc cũ:

sudo ufw delete allow 8000
sudo ufw allow 'Nginx Full'

Giờ bạn có thể truy cập domain hoặc IP của máy chủ để xem ứng dụng.

sudo ufw delete allow 8000
sudo ufw allow 'Nginx Full'

Nếu bạn có tên miền, cách dễ nhất để lấy chứng chỉ SSL bảo mật lưu lượng là dùng Let’s Encrypt. Hãy theo dõi hướng dẫn dành cho Ubuntu 22.04 / Ubuntu 20.04 / Ubuntu 18.04 để thiết lập Let’s Encrypt với Nginx trên Ubuntu 22.04. Thực hiện theo thủ tục, sử dụng server block Nginx bạn vừa tạo trong hướng dẫn này.

Bước 11 – Khắc phục sự cố Nginx và Gunicorn

Nếu bước cuối không hiển thị được ứng dụng, bạn cần xử lý sự cố cài đặt.

Nginx hiển thị trang mặc định thay vì ứng dụng Django.

Nếu Nginx hiển thị trang mặc định thay vì proxy đến ứng dụng, thường là do bạn cần chỉnh server_name trong /etc/nginx/sites-available/myproject trỏ đến IP hoặc domain của máy chủ.

Nginx dùng server_name để quyết định block nào sẽ phản hồi yêu cầu. Nếu bạn thấy trang mặc định, nghĩa là Nginx không khớp được request với block cụ thể, nên nó quay về block mặc định trong /etc/nginx/sites-available/default.

server_name trong block dự án phải cụ thể hơn block mặc định để được chọn.

Nginx đang hiển thị lỗi 502 Bad Gateway thay vì ứng dụng Django.

Mã 502 cho biết Nginx không thể proxy yêu cầu thành công. Nhiều lỗi cấu hình khác nhau có thể gây ra 502, nên bạn cần thông tin chi tiết hơn.

Nơi tốt nhất để kiểm tra là error logs của Nginx:

sudo tail -F /var/log/nginx/error.log

Tiếp theo, làm mới trang để tạo lỗi mới. Bạn sẽ thấy log lỗi mới. Đọc thông báo sẽ giúp thu hẹp nguyên nhân.

Bạn có thể gặp thông báo:

connect() to unix:/run/gunicorn.sock failed (2: No such file or directory)

Điều này có nghĩa Nginx không tìm thấy gunicorn.sock tại vị trí được chỉ định. So sánh đường dẫn proxy_pass trong /etc/nginx/sites-available/myproject với vị trí thực của gunicorn.sock được tạo bởi gunicorn.socket.

Nếu bạn không tìm thấy gunicorn.sock trong /run, có thể systemd socket không tạo được file này. Hãy quay lại phần kiểm tra file socket Gunicorn để xử lý.

Hoặc bạn gặp thông báo:

connect() to unix:/run/gunicorn.sock failed (13: Permission denied)

Nghĩa là Nginx không kết nối được đến socket Gunicorn do vấn đề quyền truy cập. Thường xảy ra nếu bạn thao tác bằng tài khoản root thay vì sudo user. Mặc dù systemd tạo được socket, Nginx không thể truy cập nó.

Điều này có thể do quyền hạn bị giới hạn ở bất kỳ thư mục nào từ / đến gunicorn.sock. Bạn có thể kiểm tra quyền và chủ sở hữu của socket và các thư mục cha bằng lệnh:

namei -l /run/gunicorn.sock
namei -l /run/gunicorn.sock

Dựa trên cột quyền (permissions), chủ sở hữu (owner), nhóm (group), bạn sẽ biết quyền truy cập thế nào.

Trong ví dụ trên, socket và các thư mục dẫn đến nó đều có quyền “read và execute” cho mọi người (thư mục có r-x). Nhờ vậy, Nginx có thể truy cập socket.

Nếu bất kỳ thư mục nào thiếu quyền r-x cho mọi người, Nginx không thể truy cập socket trừ khi bạn cấp quyền này cho mọi người hoặc đảm bảo nhóm sở hữu là một nhóm mà Nginx tham gia.

Django hiển thị: “không thể kết nối đến máy chủ: Kết nối bị từ chối”

Bạn có thể thấy thông báo này khi truy cập một phần nào đó của ứng dụng:

OperationalError at /admin/login/
could not connect to server: Connection refused
    Is the server running on host "localhost" (127.0.0.1) and accepting
    TCP/IP connections on port 5432?

Thông báo này cho thấy Django không thể kết nối đến Postgres. Hãy đảm bảo Postgres đang chạy:

sudo systemctl status postgresql

Nếu Postgres không chạy, bạn có thể khởi động và bật nó tự động chạy khi khởi động:

sudo systemctl start postgresql
sudo systemctl enable postgresql

Nếu vẫn lỗi, hãy kiểm tra lại cài đặt database trong ~/myprojectdir/myproject/settings.py.

Khắc phục sự cố thêm

Để xử lý thêm, bạn có thể xem log để tìm manh mối. Kiểm tra lần lượt:

  •  Log tiến trình Nginx: sudo journalctl -u nginx
  • Log truy cập Nginx:sudo less /var/log/nginx/access.log
  • Log lỗi Nginx:sudo less /var/log/nginx/error.log
  • Log ứng dụng Gunicorn:sudo journalctl -u gunicorn
  • Log socket Gunicorn:sudo journalctl -u gunicorn.socket

Khi bạn cập nhật cấu hình hoặc ứng dụng, có thể cần khởi động lại tiến trình.

Nếu bạn cập nhật ứng dụng Django, khởi động lại Gunicorn để áp dụng thay đổi:

sudo systemctl restart gunicorn

Nếu bạn thay đổi tệp socket/service Gunicorn, reload daemon và khởi động lại:

sudo systemctl daemon-reload
sudo systemctl restart gunicorn.socket gunicorn.service

Nếu bạn chỉnh sửa cấu hình server block của Nginx, kiểm tra cú pháp rồi khởi động lại:

sudo nginx -t && sudo systemctl restart nginx

Các lệnh trên hữu ích khi bạn tinh chỉnh cấu hình.

Kết luận

Với hướng dẫn này, bạn đã hoàn tất việc thiết lập một dự án Django trong môi trường ảo, đảm bảo tính độc lập và quản lý dễ dàng các dependencies. Bạn cũng đã cấu hình Gunicorn để xử lý các yêu cầu HTTP và chuyển tiếp chúng đến ứng dụng Django, đồng thời triển khai Nginx làm reverse proxy để tối ưu hóa hiệu suất và bảo mật khi phục vụ nội dung đến người dùng.

Django mang lại sự linh hoạt và mạnh mẽ trong việc phát triển ứng dụng web, giúp bạn tận dụng các thành phần có sẵn để tập trung vào logic nghiệp vụ cốt lõi. Bằng cách áp dụng các công nghệ và kỹ thuật triển khai được đề cập trong bài viết này, bạn có thể dễ dàng quản lý, mở rộng và triển khai các ứng dụng Django một cách hiệu quả trên máy chủ sản xuấ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 *