Deploy Python Flask บน VPS ด้วย Gunicorn และ Nginx

Flask เป็น Python Web Framework น้ำหนักเบาที่นิยมใช้สร้าง REST API, Web Application และ Microservice แต่ Flask Development Server ที่รันด้วย flask run ไม่เหมาะกับ Production เพราะรับคนได้น้อยและไม่ Stable วิธีที่ถูกต้องคือนำ Gunicorn มาเป็น WSGI Server แล้วให้ Nginx ทำหน้าที่ Reverse Proxy ด้านหน้า บทความนี้จะพาตั้งค่าแบบ Production-ready บน VPS Ubuntu ตั้งแต่ต้นจนเสร็จ

สิ่งที่จะได้หลังอ่านบทความนี้: Flask App รันผ่าน Gunicorn → Nginx → Domain พร้อม Auto-start เมื่อ VPS Reboot และรองรับ HTTPS ด้วย Let's Encrypt

ความต้องการเบื้องต้น

ขั้นตอนที่ 1: อัปเดต Server และติดตั้ง Python 3

Ubuntu มี Python 3 ติดมาแล้ว แต่ควรอัปเดต Package ให้ล่าสุดก่อน:

sudo apt update && sudo apt upgrade -y sudo apt install python3 python3-pip python3-venv -y

ตรวจสอบ version:

python3 --version pip3 --version

ขั้นตอนที่ 2: สร้าง Flask App และ Virtual Environment

แนะนำให้ใช้ virtualenv แยก dependency ของแต่ละ project ออกจากกัน ป้องกัน Package conflict:

mkdir -p ~/myflaskapp && cd ~/myflaskapp python3 -m venv venv source venv/bin/activate

ติดตั้ง Flask และ Gunicorn ใน virtualenv:

pip install flask gunicorn

สร้างไฟล์ app.py:

cat > app.py << 'EOF' from flask import Flask, jsonify app = Flask(__name__) @app.route('/') def index(): return jsonify({"message": "Hello from AsiaGB Flask App!", "status": "ok"}) @app.route('/health') def health(): return jsonify({"status": "healthy"}) if __name__ == '__main__': app.run(debug=False) EOF

สร้างไฟล์ wsgi.py สำหรับ Gunicorn:

cat > wsgi.py << 'EOF' from app import app if __name__ == '__main__': app.run() EOF

ทดสอบว่า Gunicorn รันได้:

gunicorn --bind 0.0.0.0:8000 wsgi:app

เปิดเบราว์เซอร์ไปที่ http://YOUR_VPS_IP:8000 ควรเห็น JSON response แล้วกด Ctrl+C เพื่อหยุด

ขั้นตอนที่ 3: ตั้งค่า Gunicorn เป็น systemd Service

เพื่อให้ Flask App รันอัตโนมัติเมื่อ VPS เปิดหรือ Reboot ต้องสร้าง systemd unit file:

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

ใส่เนื้อหาดังนี้ (แก้ YOUR_USERNAME ให้ถูกต้อง):

[Unit] Description=Gunicorn instance to serve Flask App After=network.target [Service] User=YOUR_USERNAME Group=www-data WorkingDirectory=/home/YOUR_USERNAME/myflaskapp Environment="PATH=/home/YOUR_USERNAME/myflaskapp/venv/bin" ExecStart=/home/YOUR_USERNAME/myflaskapp/venv/bin/gunicorn \ --workers 3 \ --bind unix:myflaskapp.sock \ -m 007 \ wsgi:app [Install] WantedBy=multi-user.target

--workers 3 คืออะไร? Gunicorn จะสร้าง Worker Process 3 ตัวรับ Request พร้อมกัน สูตรแนะนำคือ 2 × CPU Cores + 1 VPS 1 Core ควรใช้ 3 Workers, VPS 2 Core ใช้ 5 Workers

เปิดใช้งาน Service:

sudo systemctl start myflaskapp sudo systemctl enable myflaskapp sudo systemctl status myflaskapp

ถ้าเห็น Active: active (running) แสดงว่า Gunicorn ทำงานแล้ว

ขั้นตอนที่ 4: ติดตั้งและตั้งค่า Nginx

ติดตั้ง Nginx:

sudo apt install nginx -y

สร้าง Config สำหรับ Flask App:

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

ใส่เนื้อหา (แก้ your-domain.com และ path ให้ถูกต้อง):

server { listen 80; server_name your-domain.com www.your-domain.com; location / { include proxy_params; proxy_pass http://unix:/home/YOUR_USERNAME/myflaskapp/myflaskapp.sock; } location /static { alias /home/YOUR_USERNAME/myflaskapp/static; expires 30d; add_header Cache-Control "public, no-transform"; } }

เปิดใช้งาน Config และทดสอบ:

sudo ln -s /etc/nginx/sites-available/myflaskapp /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl restart nginx

ขั้นตอนที่ 5: เปิด Firewall

sudo ufw allow 'Nginx Full' sudo ufw allow OpenSSH sudo ufw enable sudo ufw status

⚠️ สำคัญ: ต้อง allow OpenSSH ก่อน enable ufw เสมอ ไม่งั้นจะถูก lock ออกจาก VPS ทาง SSH ทันที

ขั้นตอนที่ 6: ติดตั้ง SSL ด้วย Certbot (Let's Encrypt)

ติดตั้ง Certbot สำหรับ Nginx:

sudo apt install certbot python3-certbot-nginx -y

ขอ SSL Certificate (ต้องมี Domain ชี้มาที่ VPS ก่อน):

sudo certbot --nginx -d your-domain.com -d www.your-domain.com

Certbot จะแก้ Nginx Config ให้อัตโนมัติ เพิ่ม HTTPS Redirect และ SSL Settings ทดสอบ Auto-renewal:

sudo certbot renew --dry-run

ทดสอบ Auto-start หลัง Reboot

สั่ง Reboot แล้วตรวจสอบว่า Service ยังทำงานอยู่:

sudo reboot

หลัง SSH กลับเข้ามา:

sudo systemctl status myflaskapp sudo systemctl status nginx

ทั้งสองควรแสดงสถานะ active (running)

คำสั่ง Manage Service ที่ใช้บ่อย

# Restart Flask App หลังแก้โค้ด sudo systemctl restart myflaskapp # ดู Log ของ Gunicorn sudo journalctl -u myflaskapp -f # ดู Nginx Access/Error Log sudo tail -f /var/log/nginx/access.log sudo tail -f /var/log/nginx/error.log # Test Nginx Config หลังแก้ไข sudo nginx -t && sudo systemctl reload nginx

เพิ่มประสิทธิภาพ Gunicorn — Async Workers และ Connection Tuning

ค่า default ของ Gunicorn เหมาะกับแอปทั่วไป แต่ถ้า Flask App ของคุณมี I/O สูง (เรียก API ภายนอก, query DB บ่อย) ควรเปลี่ยนจาก sync workers เป็น Async Workers เพื่อรองรับ concurrent request ได้มากขึ้นโดยไม่เพิ่ม CPU:

pip install gevent

แก้ ExecStart ใน systemd unit file:

ExecStart=/home/YOUR_USERNAME/myflaskapp/venv/bin/gunicorn \ --workers 3 \ --worker-class gevent \ --worker-connections 1000 \ --timeout 60 \ --bind unix:myflaskapp.sock \ -m 007 \ wsgi:app
Worker Class เหมาะกับ ข้อสังเกต
sync (default)CPU-bound, แอปง่าย1 request ต่อ worker ต่อครั้ง
geventI/O-heavy, เรียก API ภายนอกรองรับ 1000+ concurrent request ต่อ worker
gthreadThreading-friendly codeใช้ Thread แทน Greenlet

Reload systemd และ restart service หลังแก้ unit file:

sudo systemctl daemon-reload sudo systemctl restart myflaskapp

ตั้งค่า Nginx Caching และ Rate Limiting

Nginx สามารถ cache response จาก Gunicorn ได้เพื่อลด load บนแอป และตั้ง rate limit ป้องกัน abuse เพิ่มบรรทัดต่อไปนี้ใน Nginx config:

# ด้านบน server block — กำหนด cache zone proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=flask_cache:10m max_size=100m inactive=60m; limit_req_zone $binary_remote_addr zone=api_limit:10m rate=30r/m; server { listen 443 ssl; server_name your-domain.com; # Cache static API responses 5 นาที location /api/ { limit_req zone=api_limit burst=10 nodelay; proxy_cache flask_cache; proxy_cache_valid 200 5m; proxy_cache_use_stale error timeout; add_header X-Cache-Status $upstream_cache_status; include proxy_params; proxy_pass http://unix:/home/YOUR_USERNAME/myflaskapp/myflaskapp.sock; } # ไม่ cache dynamic endpoint location / { proxy_no_cache 1; include proxy_params; proxy_pass http://unix:/home/YOUR_USERNAME/myflaskapp/myflaskapp.sock; } }

เคล็ดลับ: ตรวจ header X-Cache-Status: HIT / MISS ใน browser DevTools → Network tab เพื่อยืนยันว่า cache ทำงาน ถ้า HIT แปลว่า Nginx ตอบเองโดยไม่ส่งต่อไป Gunicorn

ตั้งค่า Environment Variables และ Flask Config ที่ปลอดภัย

การใส่ Secret Key, Database URI หรือ API Key ตรงในโค้ดเป็นความเสี่ยงด้านความปลอดภัย วิธีที่ถูกต้องคือใช้ Environment Variables ผ่าน systemd unit file:

[Service] ... EnvironmentFile=/etc/myflaskapp/env ExecStart=...

สร้างไฟล์ /etc/myflaskapp/env และกำหนดสิทธิ์:

sudo mkdir -p /etc/myflaskapp sudo nano /etc/myflaskapp/env

เนื้อหาใน env file:

FLASK_SECRET_KEY=your-very-long-random-secret-key-here DATABASE_URL=mysql+pymysql://user:password@localhost/dbname REDIS_URL=redis://127.0.0.1:6379/0 FLASK_ENV=production

กำหนดสิทธิ์ให้อ่านได้เฉพาะ root:

sudo chmod 600 /etc/myflaskapp/env sudo chown root:root /etc/myflaskapp/env

ใน app.py ดึง environment variable ผ่าน os.environ:

import os from flask import Flask app = Flask(__name__) app.secret_key = os.environ.get('FLASK_SECRET_KEY', 'fallback-dev-key') DATABASE_URL = os.environ.get('DATABASE_URL')

⚠️ ห้ามใส่ Secret ใน Git: เพิ่ม /etc/myflaskapp/env และ .env ใน .gitignore เสมอ ข้อมูลที่ leak ผ่าน Git repository เป็นสาเหตุของช่องโหว่ที่พบบ่อยที่สุดในแอป Production

ตั้งค่า Flask Logging และ Error Tracking บน Production

แอป Flask ที่รันบน Production ควรมีระบบ Logging ที่ดีเพื่อตรวจหาปัญหาได้รวดเร็ว ปรับ Logging ใน app.py ให้บันทึกไปยังไฟล์:

import logging from logging.handlers import RotatingFileHandler import os if not os.path.exists('logs'): os.mkdir('logs') # ตั้งค่า RotatingFileHandler — ไฟล์ละ 10MB เก็บ 5 ไฟล์ file_handler = RotatingFileHandler('logs/flask.log', maxBytes=10*1024*1024, backupCount=5) file_handler.setLevel(logging.INFO) file_handler.setFormatter(logging.Formatter( '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]' )) app.logger.addHandler(file_handler) app.logger.setLevel(logging.INFO) app.logger.info('Flask Application startup')

ดู Log แบบ Real-time ขณะทดสอบ:

sudo tail -f ~/myflaskapp/logs/flask.log

นอกจาก Log ไฟล์ ควรดู systemd journal ด้วยเสมอเมื่อเกิดปัญหา:

# ดู Log ทั้งหมดของ Service sudo journalctl -u myflaskapp --since "1 hour ago" # ดู Log แบบ follow (เหมือน tail -f) sudo journalctl -u myflaskapp -f # ดู Error ล่าสุด sudo journalctl -u myflaskapp -e -n 50

เคล็ดลับ Production: ตั้งค่า Log Rotation ใน /etc/logrotate.d/myflaskapp เพื่อไม่ให้ Log ไฟล์กินพื้นที่ Disk จนเต็ม โดยเฉพาะบน VPS ที่มี SSD จำกัด

Deploy Update โค้ด Flask โดยไม่ Downtime

เมื่อต้องอัปเดตโค้ด Flask ขั้นตอนที่ถูกต้องเพื่อลด Downtime ให้น้อยที่สุด:

  1. Pull โค้ดใหม่จาก Git Repository
  2. เปิดใช้งาน virtualenv และติดตั้ง dependencies ใหม่ถ้ามี
  3. รัน Database Migration ถ้าโค้ดใหม่มีการเปลี่ยน Schema
  4. Reload Gunicorn อย่างนุ่มนวลด้วย systemctl reload แทน restart
# Pull latest code cd ~/myflaskapp git pull origin main # เปิด virtualenv และอัป dependencies source venv/bin/activate pip install -r requirements.txt # Reload Gunicorn (graceful — รอ request ปัจจุบันเสร็จก่อน) sudo systemctl reload myflaskapp # ตรวจสอบสถานะหลัง reload sudo systemctl status myflaskapp

ข้อแตกต่างระหว่าง reload กับ restart:

⚠️ Database Migration: รัน Migration ก่อน reload เสมอ โค้ดใหม่ที่ต้องการ column ใหม่แต่ยังไม่มีใน Database จะทำให้เกิด Error ทันทีที่ Worker ใหม่เริ่มรับ Request

ปัญหาที่พบบ่อยและวิธีแก้

502 Bad Gateway

มักเกิดจาก Gunicorn ไม่ทำงาน ตรวจด้วย sudo systemctl status myflaskapp และดู Log ด้วย journalctl -u myflaskapp -e

Permission Denied บน Socket

ตรวจสอบว่า Nginx User (www-data) มีสิทธิ์อ่าน Socket File ใน Home Directory ของ User ตัวเอง ถ้าไม่ได้ให้เพิ่ม User www-data ลงใน Group ของ User นั้น:

sudo usermod -aG YOUR_USERNAME www-data sudo chmod 710 /home/YOUR_USERNAME

Flask Debug Mode ใน Production

ตรวจสอบว่าไม่มี debug=True ใน app.py หรือ environment variable FLASK_DEBUG=1 เพราะ Debug Mode เปิด Werkzeug Debugger ที่เป็นช่องโหว่ด้านความปลอดภัย

เพิ่มความปลอดภัยด้วย Flask-Talisman และ Security Headers

Flask App บน Production ควรส่ง Security Headers ที่ถูกต้องเพื่อป้องกัน XSS, Clickjacking และการ Sniff Content Type ใช้ library Flask-Talisman ซึ่งตั้งค่า Header ทั้งหมดให้อัตโนมัติ:

pip install flask-talisman

ใส่ใน app.py:

from flask_talisman import Talisman csp = { 'default-src': "'self'", 'script-src': ["'self'", "'unsafe-inline'"], 'style-src': ["'self'", "'unsafe-inline'"], } talisman = Talisman( app, content_security_policy=csp, strict_transport_security=True, strict_transport_security_max_age=31536000, frame_options='SAMEORIGIN', referrer_policy='strict-origin-when-cross-origin' )

Headers ที่ Flask-Talisman ตั้งให้อัตโนมัติ:

⚠️ ทดสอบ CSP ก่อน Production: Content Security Policy ที่เข้มงวดเกินไปอาจทำให้ Script หรือ Style ของตัวเองโหลดไม่ได้ ใช้ content_security_policy_report_only=True ก่อนเพื่อดู violation โดยไม่บล็อก แล้วค่อยปรับจนถูกต้องก่อน apply จริง

ขั้นตอนถัดไป — ขยาย Flask App สำหรับ Production ขนาดใหญ่

เมื่อ Flask App ของคุณเติบโตขึ้น ควรพิจารณาเพิ่มเติม:

การวาง Flask App บน VPS แบบ Production-ready ด้วย Gunicorn + Nginx + systemd ที่ทำในบทความนี้เป็นรากฐานที่แข็งแกร่งสำหรับระบบทุกขนาด ตั้งแต่ Startup ที่เพิ่ง launch ไปจนถึง SaaS ที่รองรับผู้ใช้หลายหมื่นคนต่อวัน สิ่งสำคัญคือ monitor อย่างสม่ำเสมอ ดู Log ทุกครั้งที่เกิดปัญหา และ test ทุก update ใน staging environment ก่อน deploy จริงเสมอ

นักพัฒนาหลายคนเลือก AsiaGB VPS สำหรับรัน Python Flask เพราะ Full Root Access ทำให้ติดตั้ง Library และปรับแต่ง Server ได้อิสระ ไม่มีข้อจำกัดเหมือน Shared Hosting ค่าบริการเริ่มต้น 500 บาท/เดือน เหมาะกับโปรเจกต์ทุกขนาดตั้งแต่ REST API ส่วนตัวไปจนถึง Web Application ที่ให้บริการลูกค้า VPS ของเราใช้ SSD ทำให้ Database Query และ File I/O เร็วกว่า HDD ทั่วไปหลายเท่า ซึ่งส่งผลโดยตรงต่อ Response Time ของ Flask App และประสบการณ์ผู้ใช้

หากคุณต้องการความช่วยเหลือในการตั้งค่าเริ่มต้น เช่น การ setup User, การ configure UFW หรือการ install Python และ Nginx สามารถติดต่อทีมงาน AsiaGB ได้ตลอด 24 ชั่วโมงผ่านระบบ Ticket เพื่อให้ผู้เชี่ยวชาญช่วยเดิน setup แรกให้เสร็จสรรพก่อนที่คุณจะ deploy โค้ดจริง ทำให้มั่นใจได้ว่า Flask App ของคุณพร้อม Production บน VPS ที่ตั้งค่าอย่างถูกต้องตั้งแต่วันแรก

สรุปขั้นตอน: Python 3 + virtualenv → Flask + Gunicorn → systemd Service → Nginx Reverse Proxy → UFW Firewall → Let's Encrypt SSL = Flask App พร้อม Production บน VPS

ต้องการ VPS สำหรับ Python Flask?

AsiaGB มี VPS Linux Ubuntu พร้อมใช้งาน เริ่มต้น 500 บาท/เดือน · Full Root Access · SSD · 1 Dedicated IP

ดู VPS ทั้งหมด →