ปรับ PHP-FPM ให้เร็ว: Worker, Pool และ OPcache บน VPS
PHP-FPM (FastCGI Process Manager) คือ Implementation ของ PHP ที่ออกแบบมาเพื่อ Performance สูง ใช้คู่กับ Nginx (หรือ Apache กับ mod_proxy_fcgi) ข้อดีของ PHP-FPM คือสามารถปรับจำนวน Worker Process ได้ตามปริมาณงาน ทำให้ตอบสนอง Request ได้มากกว่า mod_php ที่ผูกกับ Apache Process ต่อ Request การ Tune PHP-FPM อย่างถูกต้องอาจทำให้เว็บ WordPress ที่ใช้อยู่เร็วขึ้น 2-3 เท่าโดยไม่ต้องเพิ่ม RAM
ทำความเข้าใจ pm.max_children
ตัวแปรที่สำคัญที่สุดใน PHP-FPM คือ pm.max_children ซึ่งกำหนดจำนวน PHP Worker สูงสุดที่ทำงานพร้อมกันได้ ถ้าตั้งสูงเกินไปจะ OOM Killer เพราะ RAM หมด ถ้าต่ำเกินไป Request จะเข้าคิวรอจนเกิด Timeout
สูตรคำนวณเบื้องต้น:
# ดูว่า PHP-FPM Process แต่ละตัวใช้ RAM เฉลี่ยเท่าไหร่ (หน่วย MB)
ps aux | grep "php-fpm" | awk '{sum+=$6; count++} END {print sum/count/1024 " MB average"}'
# ตัวอย่าง: VPS RAM 2GB, ใช้โดย OS+Nginx ~300MB, เหลือ ~1700MB
# PHP Worker ใช้เฉลี่ย 50MB → max_children = 1700/50 = 34
| RAM VPS | pm.max_children (แนะนำ) | หมายเหตุ |
|---|---|---|
| 1 GB | 8–12 | เหมาะเว็บเล็ก traffic ต่ำ |
| 2 GB | 20–30 | WordPress 1-3 เว็บ |
| 4 GB | 40–60 | WordPress หลายเว็บหรือ WooCommerce |
| 8 GB | 80–120 | เว็บ traffic สูงหรือ PHP API |
ตำแหน่งไฟล์ Config PHP-FPM
# Ubuntu/Debian (PHP 8.2)
/etc/php/8.2/fpm/pool.d/www.conf
# AlmaLinux/Rocky Linux (PHP 8.2)
/etc/php-fpm.d/www.conf
# เปิดแก้ไข
nano /etc/php/8.2/fpm/pool.d/www.conf
ค่า Pool ที่แนะนำสำหรับ VPS 2 GB
[www]
user = www-data
group = www-data
listen = /run/php/php8.2-fpm.sock
listen.owner = www-data
listen.group = www-data
; Process Manager แบบ dynamic (ปรับ Worker ตาม Load)
pm = dynamic
pm.max_children = 25
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 10
pm.max_requests = 500 ; restart Worker หลัง 500 request (ป้องกัน memory leak)
; Timeout
request_terminate_timeout = 60s
request_slowlog_timeout = 10s
slowlog = /var/log/php/slow.log
เลือก pm Mode ให้เหมาะกับงาน
- pm = dynamic — ปรับจำนวน Worker อัตโนมัติตาม Load เหมาะเว็บทั่วไป
- pm = ondemand — สร้าง Worker เมื่อมี Request เข้า เหมาะ VPS RAM น้อย traffic ไม่สม่ำเสมอ
- pm = static — Worker คงที่ตลอดเวลา เหมาะ VPS RAM เยอะและ traffic สูงต่อเนื่อง ตอบสนองเร็วที่สุด
ตั้งค่า OPcache — เพิ่มความเร็วสูงสุด
OPcache คือ Bytecode Cache ที่แปลง PHP Script เป็น Bytecode ครั้งเดียวแล้ว Cache ไว้ใน Memory ทำให้ไม่ต้อง parse ไฟล์ PHP ซ้ำทุก Request WordPress เร็วขึ้นได้ 40-70% เพียงแค่เปิด OPcache
# แก้ไข /etc/php/8.2/fpm/conf.d/10-opcache.ini หรือ /etc/php/8.2/mods-available/opcache.ini
[opcache]
opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=256 ; RAM สำหรับ Bytecode Cache (MB)
opcache.interned_strings_buffer=16 ; Buffer สำหรับ String cache (MB)
opcache.max_accelerated_files=10000 ; จำนวนไฟล์ PHP ที่ Cache ได้
opcache.revalidate_freq=2 ; ตรวจสอบไฟล์เปลี่ยนแปลงทุก 2 วินาที
opcache.fast_shutdown=1
opcache.validate_timestamps=1 ; เปิดไว้ระหว่าง development
; opcache.validate_timestamps=0 ; เปิดบน Production เพื่อเร็วสูงสุด (ต้อง reload php-fpm เมื่อ deploy)
opcache.validate_timestamps=0 บน Production Server เพื่อข้ามการตรวจสอบไฟล์ทุก Request แต่ต้องทำ systemctl reload php8.2-fpm ทุกครั้งหลัง deploy โค้ดใหม่
ตั้งค่า realpath_cache — เร็วขึ้นอีกระดับ
# /etc/php/8.2/fpm/php.ini
[PHP]
realpath_cache_size = 4096K ; Cache path resolution ของไฟล์ PHP (WordPress มีไฟล์เยอะมาก)
realpath_cache_ttl = 600 ; Cache นาน 10 นาที
สร้าง Pool แยกต่างหากสำหรับแต่ละเว็บ
ถ้ามีหลายเว็บบน VPS เดียวกัน ควรสร้าง Pool แยกสำหรับแต่ละเว็บ เพื่อให้ไม่แย่ง Worker กันและตรวจสอบ performance ได้แม่นยำกว่า:
# สร้างไฟล์ใหม่
cp /etc/php/8.2/fpm/pool.d/www.conf /etc/php/8.2/fpm/pool.d/mysite.conf
# แก้ไขใน mysite.conf
[mysite] ; ชื่อ pool
user = mysite
group = mysite
listen = /run/php/php8.2-mysite.sock ; socket แยก
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 5
# แก้ Nginx ให้ใช้ socket ของ pool นี้
fastcgi_pass unix:/run/php/php8.2-mysite.sock;
Reload และตรวจสอบ
# ทดสอบ config ก่อน reload
php-fpm8.2 -t
# Reload PHP-FPM (ไม่ kill process ที่กำลังทำงาน)
systemctl reload php8.2-fpm
# ดูสถานะ
systemctl status php8.2-fpm
# ดูการใช้ resource แบบ real-time
watch -n 1 "ps aux | grep php-fpm | grep -v grep | wc -l"
ตรวจสอบ Performance ด้วย Status Page
# เพิ่มใน pool config เพื่อเปิด Status Page
pm.status_path = /fpm-status
ping.path = /fpm-ping
# เพิ่มใน Nginx config (เข้าได้เฉพาะ localhost)
location /fpm-status {
allow 127.0.0.1;
deny all;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# ดู Status
curl http://localhost/fpm-status?full
การปรับ PHP-FPM ที่ถูกต้องเป็นหนึ่งในวิธีที่คุ้มค่าที่สุดในการเพิ่มความเร็วเว็บโดยไม่ต้องเพิ่ม Hardware ถ้าคุณกำลังมองหา VPS ที่มี Performance ดีตั้งแต่ต้น VPS ไทยของ asiagb มีแผนหลายขนาดพร้อม NVMe SSD ที่ช่วยให้ PHP-FPM ทำงานได้เต็มประสิทธิภาพ
ตรวจสอบว่า PHP-FPM ทำงานถูกต้อง
# ตรวจสอบ config syntax
php-fpm8.2 -t
# ผลที่ต้องการ: NOTICE: configuration file /etc/php/8.2/fpm/php-fpm.conf test is successful
# ดูสถานะ service
systemctl status php8.2-fpm
# ดูจำนวน worker ที่รันอยู่แบบ real-time
watch -n 1 "ps aux | grep php-fpm | grep -v grep | wc -l"
# ตรวจสอบ OPcache via PHP
php -r "var_dump(opcache_get_status()['opcache_enabled']);"
# ต้องเป็น: bool(true)
pm.status_path = /fpm-status ใน pool config แล้ว curl http://localhost/fpm-status จะแสดง active connections, idle workers, slow requests แบบ real-time — มีประโยชน์มากเมื่อ traffic พุ่ง
แก้ปัญหาที่พบบ่อย
Error: "502 Bad Gateway" จาก Nginx
มักเกิดจาก socket path ใน Nginx config ไม่ตรงกับที่ PHP-FPM ใช้จริง:
# ตรวจ socket path ที่ PHP-FPM สร้าง
ls /run/php/
# ควรเห็น php8.2-fpm.sock
# ตรวจ Nginx config ว่าชี้ตรงไหม
grep fastcgi_pass /etc/nginx/sites-enabled/*.conf
# ต้องตรงกัน: unix:/run/php/php8.2-fpm.sock
# ตรวจ permission ของ socket
ls -la /run/php/php8.2-fpm.sock
# ต้องเขียนได้โดย www-data
Error: "WARNING: [pool www] seems busy (pid X, fpm_got_signal)" และ request หนาแน่น
pm.max_children ต่ำเกินไปสำหรับ traffic ที่ได้รับ — PHP worker หมด request ต้องรอ:
# ดูว่า worker ใช้เต็มไหม
curl http://localhost/fpm-status
# ดูค่า active processes เทียบกับ max children
# เพิ่ม max_children ใน pool config
nano /etc/php/8.2/fpm/pool.d/www.conf
# เพิ่ม pm.max_children
systemctl reload php8.2-fpm
Error: PHP-FPM start ไม่ขึ้น — "WARNING: Nothing matches the include pattern '/etc/php/8.2/fpm/pool.d/*.conf'"
ไฟล์ pool config อาจถูกลบหรือ directory ว่างอยู่ ต้องสร้าง pool ใหม่:
# ตรวจ directory pool
ls /etc/php/8.2/fpm/pool.d/
# ต้องมีไฟล์ .conf อย่างน้อย 1 ไฟล์
# ถ้าว่าง สร้าง www.conf พื้นฐาน
cp /etc/php/8.2/fpm/pool.d/www.conf.default /etc/php/8.2/fpm/pool.d/www.conf
# หรือ install ใหม่: apt install --reinstall php8.2-fpm
Error: WordPress ยัง slow อยู่แม้ตั้ง OPcache แล้ว
ตรวจสอบว่า OPcache โหลด bytecode ครบหรือยัง และ validate_timestamps ไม่ทำให้ต้อง recheck ทุก request:
# ดู OPcache stats
php -r "print_r(opcache_get_status());"
# ดู opcache_statistics: hits vs misses
# ตรวจ memory ที่ใช้
php -r "print_r(opcache_get_status()['memory_usage']);"
# ถ้า used_memory ใกล้ full ให้เพิ่ม opcache.memory_consumption
Error: pm.max_requests ทำให้ worker restart บ่อย แต่ช้าลง
pm.max_requests = 500 หมายถึง worker จะ restart ทุก 500 request เพื่อป้องกัน memory leak ถ้า restart บ่อยเกินให้เพิ่มค่า หรือหา memory leak ใน code แทน:
# ดูขนาด memory ของแต่ละ worker
ps aux | grep php-fpm | awk '{print $6/1024 " MB " $11}' | head
# ถ้า memory ไม่ leak ให้เพิ่ม max_requests เป็น 2000-5000
# pm.max_requests = 2000
คำถามที่พบบ่อย
จะเปลี่ยน PHP version บน server ยังไงโดยไม่ให้เว็บ down?
ติดตั้ง PHP version ใหม่คู่ขนานกับเวอร์ชันปัจจุบัน จากนั้นสร้าง pool ใหม่สำหรับ PHP version ใหม่ เปลี่ยน Nginx ของเว็บที่ต้องการให้ชี้ socket ใหม่ ทดสอบเว็บนั้นก่อน ค่อยย้ายเว็บอื่นทีละตัว — PHP version เก่าและใหม่รันพร้อมกันได้บน Ubuntu
ทุกเว็บต้องใช้ PHP-FPM pool แยกไหม?
ไม่บังคับ แต่แนะนำถ้ามีหลายเว็บบน VPS เดียวกัน pool แยกให้แต่ละเว็บมี worker ของตัวเอง ถ้าเว็บหนึ่งมี traffic พุ่งจะไม่กระทบเว็บอื่น และยัง run แต่ละ pool ด้วย user ที่ต่างกัน ช่วยด้าน security ด้วย
pm = dynamic กับ ondemand ต่างกันอย่างไร?
dynamic รัก worker ขั้นต่ำไว้ตลอดเวลา (pm.start_servers) ทำให้ตอบ request แรกได้เร็ว เหมาะ traffic สม่ำเสมอ ส่วน ondemand สร้าง worker เมื่อมี request เข้า แล้ว idle worker จะหายไป เหมาะ VPS RAM น้อยหรือ traffic ไม่สม่ำเสมอ แต่ request แรกหลัง idle จะช้ากว่าเล็กน้อย