Triển khai Django lên production với Gunicorn và Nginx
Đưa ứng dụng Django lên production với Gunicorn qua systemd socket, Nginx reverse proxy, SSL bằng Certbot, xoay log và script deploy đơn giản.
Long Nguyen
Lập trình viên Fullstack · Kỹ sư AI · Nhà nghiên cứu
Từ chạy được đến triển khai thực tế
Trong chín phần vừa qua, chúng ta đã xây dựng một ứng dụng phỏng vấn AI hoàn chỉnh — từ phân tích dữ liệu, pipeline agent, xử lý nền, giọng nói cho đến chấm điểm. Ở phần 9, ứng dụng đã chạy xuyên suốt trên một chiếc laptop. Phần cuối này tập trung vào khoảng cách mà hầu hết tutorial thường bỏ qua: đưa ứng dụng lên Internet một cách ổn định để người dùng thật có thể truy cập. Điều đó đồng nghĩa với việc triển khai Django lên production bằng Gunicorn và Nginx, kèm SSL, logging và một bước deploy có thể lặp lại.
Hướng dẫn này giả định một thành phần đã được chuẩn bị sẵn: cơ sở dữ liệu production. SQLite không phù hợp cho một hệ thống triển khai thực tế, vì vậy trước khi deploy, bạn nên thiết lập một database server đúng chuẩn — có thể là PostgreSQL hoặc MySQL, mỗi lựa chọn đều có hướng dẫn riêng. Khi phần này đã sẵn sàng, các bước còn lại sẽ đưa chính ứng dụng lên online.
Kiến trúc được sử dụng rất phổ biến và đã được kiểm chứng: Nginx đứng ở phía trước, xử lý HTTPS và static files, sau đó chuyển các request động qua một socket cục bộ đến Gunicorn, thành phần thực sự chạy Django. Không có gì quá phức tạp — mỗi công cụ chỉ đảm nhiệm đúng phần việc mà nó làm tốt nhất.
Chạy Gunicorn dưới dạng systemd Service
Gunicorn là application server dùng để chạy Django trong môi trường production. Thay vì khởi chạy thủ công, chúng ta để systemd quản lý nó — nhờ vậy Gunicorn tự khởi động cùng hệ thống, tự khởi động lại khi gặp sự cố và có thể điều khiển như mọi service khác. Cấu hình này sử dụng hai unit: một socket và một service.
Socket unit tạo Unix socket để Nginx kết nối:
# /etc/systemd/system/aiinterviewer.socket
[Unit]
Description=aiinterviewer gunicorn socket
[Socket]
ListenStream=/run/aiinterviewer.sock
[Install]
WantedBy=sockets.target
Service unit trực tiếp chạy Gunicorn và bind vào socket đó:
# /etc/systemd/system/aiinterviewer.service
[Unit]
Description=aiinterviewer gunicorn daemon
Requires=aiinterviewer.socket
After=network.target
[Service]
Group=www-data
WorkingDirectory=/var/www/ai_interviewer
ExecStart=/var/www/ai_interviewer/venv/bin/gunicorn \
--workers 3 \
--timeout 180 \
--log-level info \
--capture-output \
--bind unix:/run/aiinterviewer.sock \
--access-logfile /var/www/ai_interviewer/media/log/gunicorn/access.log \
--error-logfile /var/www/ai_interviewer/media/log/gunicorn/error.log \
ai_interviewer.wsgi:application
[Install]
WantedBy=multi-user.target
Một vài flag của Gunicorn đáng để tìm hiểu. --workers 3 chạy ba worker process để xử lý request song song; điểm bắt đầu phổ biến là khoảng số core CPU nhân hai rồi cộng thêm một. --timeout 180 được đặt khá cao một cách có chủ ý — ứng dụng này thực hiện các cuộc gọi AI chậm, và timeout mặc định nghiêm ngặt hơn có thể kết thúc những request chạy lâu. Xem tài liệu cài đặt Gunicorn để biết đầy đủ danh sách tùy chọn.
Sau khi đã tạo các unit, hãy reload systemd rồi khởi động socket — Gunicorn sẽ tự động chạy khi request đầu tiên được gửi đến:
sudo systemctl daemon-reload
sudo systemctl start aiinterviewer.socket
sudo systemctl enable aiinterviewer.socket
# verify the socket exists and responds
sudo systemctl status aiinterviewer.socket
curl --unix-socket /run/aiinterviewer.sock localhost
Nginx làm Reverse Proxy
Nginx là cổng vào công khai của hệ thống. Nó phục vụ static files trực tiếp (nhanh và không cần Python) rồi chuyển mọi request còn lại đến Gunicorn qua socket:
# /etc/nginx/sites-available/aiinterviewer
server {
listen 80;
server_name ai-interviewer.tech www.ai-interviewer.tech;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /var/www/ai_interviewer;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/aiinterviewer.sock;
}
}
Hãy bật site, kiểm tra cấu hình trước khi reload (nếu không, một lỗi cú pháp có thể khiến Nginx ngừng hoạt động), rồi khởi động lại:
sudo ln -s /etc/nginx/sites-available/aiinterviewer /etc/nginx/sites-enabled
sudo nginx -t # always test before reloading
sudo systemctl restart nginx
Phục vụ static files từ Nginx thay vì Django là một bước rất quan trọng: Nginx được thiết kế để làm việc này, đồng thời giải phóng các Python worker để xử lý những request thực sự cần đến chúng.
HTTPS với Certbot
Một ứng dụng production không nên chạy bằng HTTP thuần — đặc biệt là ứng dụng xử lý CV. Certbot sẽ lấy certificate miễn phí từ Let's Encrypt và tự cấu hình lại Nginx để dùng HTTPS chỉ với một lệnh:
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d ai-interviewer.tech -d www.ai-interviewer.tech
Certbot chỉnh sửa cấu hình Nginx để phục vụ HTTPS, thiết lập chuyển hướng từ HTTP sang HTTPS và cài timer tự động gia hạn certificate trước khi hết hạn. Đây là một trong số ít trải nghiệm “cứ chạy là được” khi quản trị server.
Firewall, xoay log và deploy có thể lặp lại
Ba bước hoàn thiện sau đây sẽ biến một server đang chạy được thành hệ thống mà bạn có thể thực sự vận hành.
Firewall, để chỉ những port cần thiết mới có thể truy cập — SSH và lưu lượng web, không thêm gì khác:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
Xoay log, để access log và error log của Gunicorn không âm thầm chiếm đầy ổ đĩa. Cấu hình dưới đây giữ lại 90 ngày và nén các file log:
# /etc/logrotate.d/aiinterviewer
/var/www/ai_interviewer/media/log/gunicorn/*.log {
daily
rotate 90
compress
delaycompress
missingok
notifempty
copytruncate
}
Tùy chọn copytruncate đặc biệt hữu ích với Gunicorn: nó cho phép xoay log mà không cần gửi signal hoặc khởi động lại process. Nếu không quản lý, log là một trong những nguyên nhân phổ biến nhất khiến một server nhỏ hết dung lượng sau vài tháng.
Deploy script, để việc đưa bản cập nhật lên server chỉ còn là một lệnh thay vì một chuỗi thao tác phải nhớ:
# build.sh
#!/bin/bash
git pull origin master
source venv/bin/activate
python manage.py migrate --noinput
python manage.py collectstatic --noinput
sudo systemctl restart aiinterviewer
echo "Build completed!"
Mỗi lần deploy giờ đây đều chạy cùng một nhóm bước theo đúng thứ tự — pull, migrate, collect static rồi restart. Việc viết script không chỉ để tiện hơn; nó loại bỏ nguy cơ quên một bước vào lúc 2 giờ sáng, đúng thời điểm các lần deploy thường diễn ra.
Ứng dụng đã online — và series đã hoàn tất
Đó là toàn bộ hành trình: từ một Django project trống đến một ứng dụng phỏng vấn AI hoạt động thực tế, được bảo mật bằng HTTPS, có thể phân tích CV, thực hiện phỏng vấn bằng giọng nói với agent, chấm điểm câu trả lời và gửi báo cáo — bất kỳ ai cũng có thể truy cập qua một domain thật. Mỗi phần đều là thành phần thực tế được xây dựng và triển khai, không phải một sản phẩm minh họa đơn giản.
Bạn có thể dùng thử sản phẩm hoàn chỉnh, miễn phí và không cần đăng ký, tại ai-interviewer.tech. Nếu muốn bắt đầu từ source hoàn chỉnh, sẵn sàng cho production, thay vì tự xây dựng cả mười phần — bao gồm prompt đã được tinh chỉnh, các thành phần được gia cố và toàn bộ cấu hình triển khai — sản phẩm này được đóng gói dưới dạng AI Mock Interview SaaS Starter Kit. Dù tự xây dựng hay bắt đầu từ source có sẵn, giờ đây bạn đã hiểu rõ một sản phẩm AI thực tế cần những gì — từ đầu đến cuối.
CÂU HỎI THƯỜNG GẶP
Câu hỏi thường gặp
Tại sao nên dùng Unix socket giữa Nginx và Gunicorn thay vì một port?
Unix socket giữ lưu lượng giữa Nginx và Gunicorn ở phạm vi nội bộ của máy, nhờ đó nhanh hơn một chút và không cần expose Gunicorn qua một network port. Nginx xử lý kết nối công khai; Gunicorn chỉ giao tiếp với Nginx thông qua socket.
Tại sao nên chạy Gunicorn qua systemd thay vì khởi động thủ công?
systemd khởi động Gunicorn cùng hệ thống, tự khởi động lại nếu process gặp sự cố và cho phép bạn điều khiển nó như mọi service khác. Nếu chạy thủ công, Gunicorn sẽ dừng khi đóng terminal và không tự chạy lại sau khi server reboot.
Nên dùng bao nhiêu Gunicorn worker?
Điểm bắt đầu phổ biến là khoảng số core CPU nhân hai rồi cộng thêm một. Ứng dụng này sử dụng ba worker. Sau đó, hãy điều chỉnh dựa trên lưu lượng thực tế và lượng bộ nhớ mỗi worker sử dụng.
Certbot có tự động gia hạn SSL certificate không?
Có. Certbot cài một timer để gia hạn certificate Let's Encrypt trước khi hết hạn, vì vậy sau khi thiết lập xong, nhìn chung bạn không cần phải bận tâm thêm.
Tại sao Gunicorn cần timeout cao như vậy?
Ứng dụng này thực hiện các cuộc gọi AI chậm, còn timeout mặc định có thể kết thúc những request chạy lâu đó. Tăng timeout lên 180 giây giúp pipeline AI có đủ thời gian hoàn tất. Với các ứng dụng không thực hiện cuộc gọi bên ngoài chậm, bạn thường có thể giữ timeout thấp hơn nhiều.