Triển khai ứng dụng Python Flask bằng Gunicorn, Nginx và HTTPS. Ví dụ dùng Ubuntu 24.04 LTS, tên miền proxy.timecloud.vn, thư mục /srv/webapp và cổng nội bộ 8000. Thay tên miền bằng tên miền bạn quản lý trong mọi cấu hình bên dưới.
Luồng truy cập: Trình duyệt → Nginx (80/443) → Gunicorn (127.0.0.1:8000) → Flask.
sudo; xem hướng dẫn truy cập VPS Ubuntu.app.py chứa biến app, cùng templates/ và static/. Web dùng công nghệ khác cần lệnh chạy riêng.sudo apt update
sudo apt install -y python3 python3-venv python3-pip nginx curl ufw nano certbot python3-certbot-nginx
sudo systemctl enable --now nginx
Nếu dùng UFW, cho phép đúng cổng SSH trước khi bật firewall. Ví dụ dưới đây dùng SSH cổng 22; thay bằng cổng thực tế nếu khác.
sudo ufw allow 22/tcp
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status
Với lần cài mới, tạo tài khoản dịch vụ riêng:
sudo adduser --system --group --home /srv/webapp webapp
sudo install -d -o webapp -g webapp -m 0755 /srv/webapp
Dùng SFTP của Bitvise để tải mã nguồn vào thư mục tạm ~/webapp-upload của tài khoản SSH. Chỉ tải app.py, templates/, static/ và các module ứng dụng cần; không tải môi trường Python hoặc thư mục cache từ Windows. Sau đó:
sudo cp -r ~/webapp-upload/. /srv/webapp/
sudo chown -R webapp:webapp /srv/webapp
sudo -u webapp test -f /srv/webapp/app.py
Nếu mã đã nằm ở /root/code, có thể dùng sudo cp -r /root/code/. /srv/webapp/ thay bước sao chép trên. Sau đó vẫn cần gán quyền và tạo môi trường Python mới trên Ubuntu.
sudo -u webapp python3 -m venv /srv/webapp/.venv
sudo -u webapp /srv/webapp/.venv/bin/python -m pip install --upgrade pip
sudo -u webapp /srv/webapp/.venv/bin/python -m pip install Flask gunicorn 'requests[socks]'
Lệnh trên cài các thư viện cần cho app.py của website hiện tại. Nếu mã nguồn dùng thêm thư viện, bổ sung vào môi trường này. Không cài trực tiếp requirements.txt hiện tại trên Ubuntu: file có các gói Windows như pywin32, PyGetWindow và các gói desktop không cần cho web.
Dùng venv để tránh lỗi externally-managed-environment; không dùng sudo pip hoặc --break-system-packages. Khi đã kiểm tra hoạt động, lưu phiên bản để lần cài sau dùng lại:
sudo -u webapp /srv/webapp/.venv/bin/python -m pip freeze | sudo tee /srv/webapp/requirements-ubuntu.txt > /dev/null
cd /srv/webapp
sudo -u webapp /srv/webapp/.venv/bin/gunicorn --worker-class gthread --workers 1 --threads 8 --bind 127.0.0.1:8000 --timeout 120 --access-logfile - --error-logfile - app:app
app:app nghĩa là biến app trong module app.py. Cấu hình dùng gthread để xử lý đồng thời các yêu cầu và luồng kiểm tra proxy; số worker/thread cần điều chỉnh theo tải thực tế. Cách này không cần cài gevent.
Giữ terminal đang chạy, mở terminal SSH thứ hai để thử:
curl -I http://127.0.0.1:8000/docs
Nếu nhận HTTP 200, quay lại terminal đầu và nhấn Ctrl + C để dừng chạy thử trước khi tạo dịch vụ; tránh hai tiến trình cùng chiếm cổng 8000.
Mở file dịch vụ:
sudo nano /etc/systemd/system/webapp.service
[Unit]
Description=Gunicorn for Flask webapp
After=network.target
[Service]
User=webapp
Group=webapp
WorkingDirectory=/srv/webapp
Environment=PYTHONUNBUFFERED=1
ExecStart=/srv/webapp/.venv/bin/gunicorn --worker-class gthread --workers 1 --threads 8 --bind 127.0.0.1:8000 --timeout 120 --access-logfile - --error-logfile - app:app
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Trong nano: Ctrl + O, Enter để lưu, rồi Ctrl + X để thoát. Khởi động và kiểm tra:
sudo systemctl daemon-reload
sudo systemctl enable --now webapp
sudo systemctl status webapp --no-pager
curl -I http://127.0.0.1:8000/docs
Kết quả mong đợi: dịch vụ active (running) và HTTP 200. Khi sửa file service, dùng daemon-reload rồi restart; không cần daemon-reexec.
Mở file cấu hình (dùng tên miền thật của bạn):
sudo nano /etc/nginx/sites-available/proxy.timecloud.vn
server {
listen 80;
server_name proxy.timecloud.vn;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection "";
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 300s;
}
}
proxy_buffering off cho phép nhận kết quả SSE từng phần; proxy_read_timeout giới hạn khoảng chờ giữa các lần nhận dữ liệu từ ứng dụng. Nginx và Gunicorn đều dùng 8000.
Kích hoạt site một lần; nếu liên kết đã tồn tại, kiểm tra cấu hình hiện có trước khi thay:
sudo ln -s /etc/nginx/sites-available/proxy.timecloud.vn /etc/nginx/sites-enabled/proxy.timecloud.vn
sudo nginx -t
Chỉ khi kiểm tra báo thành công, nạp lại Nginx:
sudo systemctl reload nginx
curl -I -H 'Host: proxy.timecloud.vn' http://127.0.0.1/docs
curl -I http://proxy.timecloud.vn/docs
Cả hai lần thử cần trả HTTP 200 trước khi cấp SSL. Site mặc định có thể giữ lại khi tên miền khớp server_name. Không xóa cấu hình site khác đang phục vụ trên VPS.
Khi DNS đã trỏ đúng và cổng 80 truy cập được từ Internet, chạy Certbot, nhập email, đọc và chấp nhận điều khoản nếu đồng ý:
sudo certbot --nginx -d proxy.timecloud.vn --redirect
sudo certbot renew --dry-run
sudo systemctl list-timers --all | grep certbot
curl -I https://proxy.timecloud.vn/docs
Mong đợi HTTPS trả HTTP 200 và kiểm tra gia hạn thành công. Với cách cài Certbot qua apt ở trên, kiểm tra timer đã được lên lịch; nếu chưa, bật bằng sudo systemctl enable --now certbot.timer. Giữ cổng 80 hoạt động cho xác thực và gia hạn HTTP-01. Nếu DNS đi qua proxy/CDN, cần bảo đảm yêu cầu xác thực đến đúng VPS.
Sao lưu bản đang chạy, đưa mã mới lên, gán lại quyền nếu cần rồi khởi động lại ứng dụng. Nếu chỉ đổi Python hoặc template, không cần restart Nginx.
sudo systemctl restart webapp
sudo systemctl status webapp --no-pager
sudo journalctl -u webapp -n 100 --no-pager
sudo tail -n 100 /var/log/nginx/error.log
sudo ss -ltnp 'sport = :8000'
| Hiện tượng | Cách kiểm tra |
|---|---|
| 502 Bad Gateway | Kiểm tra webapp đang chạy và Nginx trỏ đúng 127.0.0.1:8000. |
| ModuleNotFoundError | Cài thư viện còn thiếu bằng Python trong /srv/webapp/.venv. |
| Permission denied / lỗi thư mục | Kiểm tra WorkingDirectory và quyền của tài khoản webapp. |
| Address already in use | Xem tiến trình bằng ss; dừng terminal chạy thử hoặc đúng dịch vụ đang chiếm cổng. Không dùng kill -9 khi chưa xác định tiến trình. |
| SSL thất bại | Kiểm tra DNS A/AAAA, cổng 80 và domain trong Nginx. |
| Kết quả kiểm tra không cập nhật dần | Kiểm tra buffering ở Nginx và proxy/CDN nếu có. |
Hoàn tất khi: webapp active, nginx -t thành công, HTTPS hoạt động và renew --dry-run thành công. Mở web và thử chức năng kiểm tra proxy để xác nhận ứng dụng hoạt động thực tế.