จัดการ Background Process บน VPS ด้วย Supervisor
Supervisor เป็น process control system ที่เขียนด้วย Python ออกแบบมาเพื่อ monitor และ control background processes บน Unix/Linux ใช้บ่อยสำหรับ Laravel Queue Worker, Celery worker, Python bots หรือ service ที่ไม่มี systemd unit file บทความนี้แสดงวิธีตั้ง Supervisor จัดการ process ที่ต้องรันตลอดเวลา
ติดตั้ง Supervisor
sudo apt update
sudo apt install supervisor -y
sudo systemctl enable --now supervisor
# ตรวจสถานะ
sudo supervisorctl status
เพิ่ม Program Config
สร้างไฟล์ config ใน /etc/supervisor/conf.d/ แยกต่อ program:
sudo nano /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/myapp/artisan queue:work redis --sleep=3 --tries=3 --timeout=90
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=4
redirect_stderr=true
stdout_logfile=/var/log/supervisor/laravel-worker.log
stopwaitsecs=3600
# Python worker ตัวอย่าง
[program:celery-worker]
command=/home/ubuntu/myapp/venv/bin/celery -A myapp worker --loglevel=info
directory=/home/ubuntu/myapp
user=ubuntu
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/celery.log
stderr_logfile=/var/log/supervisor/celery-error.log
คำสั่ง supervisorctl
# โหลด config ใหม่
sudo supervisorctl reread
sudo supervisorctl update
# ดูสถานะทั้งหมด
sudo supervisorctl status
# start/stop/restart
sudo supervisorctl start laravel-worker:*
sudo supervisorctl stop laravel-worker:*
sudo supervisorctl restart laravel-worker:*
# ดู logs real-time
sudo supervisorctl tail -f laravel-worker:laravel-worker_00
numprocs=4 สร้าง process 4 ตัว ชื่อ laravel-worker_00 ถึง laravel-worker_03 ใช้ wildcard laravel-worker:* เพื่อ control ทุก process พร้อมกัน
Supervisor Web Interface
sudo nano /etc/supervisor/supervisord.conf
# เพิ่มใน [inet_http_server]
[inet_http_server]
port = 127.0.0.1:9001 # bind localhost เท่านั้น
username = admin
password = SecurePassword123
sudo supervisorctl reload
# เข้า http://localhost:9001 ผ่าน SSH tunnel
ตรวจสอบว่า Supervisor ทำงานถูกต้อง
# ตรวจ supervisord รัน
systemctl status supervisor
# ตรวจ process ทั้งหมด
sudo supervisorctl status
# ดู PID ของ process
sudo supervisorctl pid laravel-worker:laravel-worker_00
# ดู log
sudo tail -f /var/log/supervisor/laravel-worker.log
startretries=3 (default) หลังจาก retry ครบจะเป็น FATAL
แก้ปัญหาที่พบบ่อย
supervisorctl status แสดง STOPPED หรือ FATAL
Command ใน config ผิด หรือ user ไม่มีสิทธิ์รัน ตรวจ log เพื่อหา error:
# ดู log ของ process
sudo tail -50 /var/log/supervisor/laravel-worker.log
# ทดสอบรัน command โดยตรง
sudo -u www-data php /var/www/myapp/artisan queue:work redis --once
Config ใหม่ไม่ถูก load
ต้อง reread + update เสมอหลังแก้ config ไม่ใช่แค่ reload:
sudo supervisorctl reread # อ่าน config ใหม่
sudo supervisorctl update # apply การเปลี่ยนแปลง
sudo supervisorctl status # ตรวจผล
Process กิน memory มากขึ้นเรื่อยๆ (memory leak)
ตั้ง max_memory_mb ใน config เพื่อ restart อัตโนมัติเมื่อ memory เกิน (ต้องใช้ superlance plugin):
# หรือตั้ง restart ทุกกี่ request (สำหรับ Laravel queue)
command=php artisan queue:work --max-jobs=500 # restart หลัง 500 jobs
# หรือ
command=php artisan queue:work --max-time=3600 # restart ทุก 1 ชั่วโมง
Process ไม่ stop gracefully
signal ที่ส่งเมื่อ stop คือ TERM โดย default บาง process ต้องการ signal อื่น:
[program:myworker]
...
stopsignal=INT # ส่ง SIGINT (Ctrl+C) แทน SIGTERM
stopwaitsecs=60 # รอ 60 วินาทีก่อน SIGKILL
คำถามที่พบบ่อย
Supervisor กับ systemd ต่างกันอย่างไร ควรใช้อะไร?
systemd เป็น init system ของ Linux ทำงานได้กับทุก service Supervisor เป็น Python application เหมาะกับการจัดการ multiple similar processes (เช่น worker หลาย copy) ที่ config ง่ายกว่า systemd สำหรับ service หลักเดี่ยวๆ ใช้ systemd ดีกว่า สำหรับ worker pool ใช้ Supervisor สะดวกกว่า
Supervisor รับรู้ process crash ได้เร็วแค่ไหน?
Supervisor ตรวจด้วย event loop ซึ่งรับรู้ทันทีเมื่อ process exit (ไม่ต้อง polling) ดังนั้น restart เกิดเกือบ instant ภายใน 1-2 วินาที
จะ run Supervisor process หลัง deploy code ใหม่อย่างไร?
สำหรับ queue worker ต้อง restart หลัง deploy เสมอเพราะ code เดิมยัง load ใน memory ใส่ใน deploy script: supervisorctl restart laravel-worker:* หรือสำหรับ Laravel ใช้ php artisan queue:restart แล้ว Supervisor จะ detect ว่า worker exit แล้ว restart เอง