
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
ความต้องการเบื้องต้น
- VPS Ubuntu 20.04, 22.04 หรือ 24.04 LTS
- RAM อย่างน้อย 512 MB (แนะนำ 1 GB สำหรับ Flask + Nginx)
- สิทธิ์ sudo หรือ root
- Domain ที่ชี้ A Record มาที่ IP ของ VPS แล้ว (สำหรับ SSL)
- ความรู้ SSH พื้นฐาน — ดู คู่มือการใช้งาน SSH บน VPS
ขั้นตอนที่ 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.comCertbot จะแก้ 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 ต่อครั้ง |
| gevent | I/O-heavy, เรียก API ภายนอก | รองรับ 1000+ concurrent request ต่อ worker |
| gthread | Threading-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 ให้น้อยที่สุด:
- Pull โค้ดใหม่จาก Git Repository
- เปิดใช้งาน virtualenv และติดตั้ง dependencies ใหม่ถ้ามี
- รัน Database Migration ถ้าโค้ดใหม่มีการเปลี่ยน Schema
- 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:
- reload — ส่ง SIGHUP ให้ Gunicorn, Worker ใหม่เริ่มทำงาน, Worker เก่ารอ request ปัจจุบันเสร็จแล้วหยุด (graceful) ไม่มี Downtime
- restart — หยุด Gunicorn ทันทีแล้วเริ่มใหม่ มี Downtime ช่วงสั้น ใช้เมื่อ reload ไม่สำเร็จ
⚠️ 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_USERNAMEFlask 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 ตั้งให้อัตโนมัติ:
- Strict-Transport-Security — บังคับ HTTPS ป้องกัน downgrade attack
- Content-Security-Policy — ควบคุม resource ที่โหลดได้ ป้องกัน XSS
- X-Frame-Options — ป้องกัน Clickjacking
- X-Content-Type-Options: nosniff — ป้องกัน MIME type sniffing
- Referrer-Policy — ควบคุมข้อมูล Referrer ที่ส่งออก
⚠️ ทดสอบ CSP ก่อน Production: Content Security Policy ที่เข้มงวดเกินไปอาจทำให้ Script หรือ Style ของตัวเองโหลดไม่ได้ ใช้ content_security_policy_report_only=True ก่อนเพื่อดู violation โดยไม่บล็อก แล้วค่อยปรับจนถูกต้องก่อน apply จริง
ขั้นตอนถัดไป — ขยาย Flask App สำหรับ Production ขนาดใหญ่
เมื่อ Flask App ของคุณเติบโตขึ้น ควรพิจารณาเพิ่มเติม:
- Redis Cache — เก็บ session และ cache query ผล ลด load บน Database อย่างมีนัยสำคัญ ดู ติดตั้ง Redis Cache บน VPS
- Celery + Redis — ประมวลผล task แบบ Background เช่น ส่ง Email หรือ Generate Report โดยไม่บล็อก request
- Database Connection Pooling — ใช้ SQLAlchemy pool ป้องกัน connection leak และลด overhead การ connect ใหม่ทุก request
- Health Check Endpoint — เพิ่ม
/healthroute ที่ Load Balancer หรือ Uptime Monitor ใช้ตรวจว่าแอปทำงานปกติ - Docker Container — Containerize Flask + Gunicorn ด้วย Docker เพื่อ deploy ที่ reproducible และง่ายต่อการ scale
การวาง 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 ทั้งหมด →