วิธีปิด WP-Cron และตั้งค่า System Cron บน WordPress
WP-Cron ทำงานเฉพาะเมื่อมีคนเข้าชมเว็บไซต์ ทำให้เกิดปัญหาคอขวดหรือการประมวลผลล่าช้า เรียนรู้วิธีตั้งค่า System Cron ผ่าน WP-CLI เพื่อเพิ่มประสิทธิภาพและตรวจสอบการทำงานจริง
wp-cron คืออะไร และเหตุใดจึงควรใช้ system cron แทน
WP-Cron คือตัวจัดตารางเวลาทำงานที่ติดตั้งมาพร้อมกับ WordPress โดยจะทำงานก็ต่อเมื่อมีผู้ใช้งานเรียกหน้าเว็บไซต์เท่านั้น ไม่มีกระบวนการใดภายใน WordPress ที่จะเริ่มทำงานได้ด้วยตัวเอง ในทุกคำขอที่ไม่ได้รับข้อมูลจากแคช WordPress จะอ่านรายการงานที่กำหนดเวลาไว้ หากมีงานใดถึงกำหนด ระบบจะส่งคำขอ HTTP ครั้งที่สองกลับไปยังตัวเองที่ /wp-cron.php เพื่อดำเนินการงานนั้น การย้ายงานดังกล่าวไปไว้ใน system cron จะช่วยให้คุณกำหนดเวลาการทำงานที่แน่นอนและคาดการณ์ได้ ไม่ว่าในขณะนั้นจะมีผู้เข้าชมเว็บไซต์เป็นพันคนหรือไม่มีเลยก็ตาม
มีเพียงสองบรรทัดที่ทำหน้าที่หลัก ได้แก่ การกำหนดค่า constant ใน wp-config.php และการเพิ่มรายการใน crontab ส่วนเนื้อหาที่เหลือในคู่มือนี้คือรายละเอียดที่บรรทัดทั้งสองไม่ได้ระบุไว้ เช่น การกำหนด user ที่ต้องใช้รันงาน วิธีตรวจสอบว่าเหตุการณ์ที่กำหนดเวลาไว้ทำงานจริงหรือไม่ และสาเหตุ 3 ประการที่ทำให้การตั้งค่านี้ล้มเหลวโดยไม่แสดงข้อความแจ้งเตือนใดๆ บนเว็บไซต์
ตัวอย่างในคู่มือนี้ใช้ /srv/www/example.com เป็นไดเรกทอรีของ WordPress และ www-data เป็น user ของเว็บเซิร์ฟเวอร์ โปรดแทนที่ด้วย path และ user ของคุณเองในทุกจุดที่เกี่ยวข้อง
ผู้เข้าชมรายใดเป็นผู้กระตุ้นค่าใช้จ่ายของ cron บนเว็บไซต์ที่มีการใช้งานสูง
ทุกคำขอที่ไม่ได้ถูกแคชจะต้องจ่ายค่าตรวจสอบ WordPress จะโหลดออปชัน cron เปรียบเทียบการประทับเวลา และเมื่อถึงกำหนดเวลา ระบบจะเรียก spawn_cron() ซึ่งจะส่งคำขอ loopback แบบไม่บล็อกไปยัง /wp-cron.php ผู้เข้าชมไม่ต้องรอผลลัพธ์ แต่ PHP worker จะต้องรอ ใน VPS ขนาดเล็กที่รัน PHP-FPM ด้วย pm.max_children = 5 งานตามกำหนดการที่ทำงานช้าเพียงงานเดียวอาจยึดครองความจุ PHP ของคุณไปถึง 1 ใน 5 นานเท่าที่งานนั้นจะเสร็จสิ้น และมีโอกาสสูงที่จะถูกกระตุ้นในช่วงนาทีที่มีการใช้งานสูงสุดของคุณ เนื่องจากเป็นช่วงเวลาที่มีการโหลดหน้าเว็บมากที่สุด
WordPress มีการจำกัดการทำงานซ้ำ โดยจะใช้การล็อกที่มีอายุการใช้งานเท่ากับ WP_CRON_LOCK_TIMEOUT ซึ่งโดยค่าเริ่มต้นคือ 60 วินาที เพื่อไม่ให้ผู้เข้าชมหลายรายเริ่มการทำงานพร้อมกัน การล็อกนี้ช่วยจำกัดการทำงานซ้ำ แต่ไม่ได้ย้ายภาระงานออกไปจากเส้นทางการประมวลผลของคำขอ
จงนับจำนวนครั้งที่งานนี้ทำงานบนเซิร์ฟเวอร์ของคุณก่อนที่จะตัดสินว่ามันเป็นปัญหาหรือไม่ คำขอ loopback แต่ละรายการจะปรากฏใน access log ของเว็บเซิร์ฟเวอร์:
sudo grep -c 'wp-cron.php' /var/log/nginx/access.logApache จะเขียนลงใน /var/log/apache2/access.log แทน หากจำนวนครั้งต่อวันสูงถึงหลักพัน นั่นถือเป็นค่าใช้จ่ายที่เกิดขึ้นจริง และเป็นตัวเลขที่คุณควรวัดผลบนเครื่องของคุณเองแทนที่จะอ่านจากบทความ เช่นเดียวกับที่คุณจะ ทำ benchmark VPS ก่อนและหลังการเปลี่ยนแปลงใดๆ
การใช้แคชจะเปลี่ยนสถานการณ์ไป หากแคชของหน้าเว็บสามารถตอบสนองคำขอส่วนใหญ่ด้วยไฟล์ HTML แบบคงที่ PHP จะไม่ทำงานสำหรับคำขอเหล่านั้น ดังนั้นการตรวจสอบ cron จึงไม่เกิดขึ้น เว็บไซต์ที่มีการใช้งานสูงและมีการใช้แคชอย่างหนักจะเริ่มมีพฤติกรรมเหมือนกับเว็บไซต์ที่มีการใช้งานน้อยตามที่ระบุไว้ด้านล่าง
สาเหตุที่ cron ทำงานไม่ตรงเวลาเมื่อไม่มีผู้เข้าชมเว็บไซต์
หากไม่มีผู้เข้าชม cron ก็จะไม่ทำงาน เว็บไซต์ที่มีผู้เข้าชมเพียงไม่กี่ครั้งต่อวันจะเรียกใช้งาน scheduled jobs ตามจำนวนครั้งที่มีผู้เข้าชม โดยจะทำงานในช่วงเวลาสุ่มตามจังหวะที่มีคนเข้ามายังเว็บไซต์
อาการที่พบจะมีลักษณะเดียวกันทั้งหมด โพสต์ที่ตั้งเวลาไว้ตอน 09:00 จะยังคงค้างอยู่ในรายการโพสต์โดยมีสถานะเป็น Missed schedule จนกว่าจะมีคนโหลดหน้าเว็บ ปลั๊กอินสำรองข้อมูลจะข้ามการทำงานในช่วงกลางคืน การตรวจสอบการอัปเดตจะล่าช้า ทำให้แดชบอร์ดไม่แสดงรายการที่ต้องอัปเดตแม้ว่าจะมี security release ออกมาแล้วก็ตาม อีเมลคำสั่งซื้อ การแจ้งเตือนการต่ออายุ และการแจ้งเตือนวันหมดอายุจะถูกส่งออกไปล่าช้า
เหตุการณ์ทั้งหมดนี้ไม่มีการบันทึกข้อผิดพลาดใดๆ ในมุมมองของ WordPress งานดังกล่าวไม่ได้ถือว่าล่าช้า เนื่องจากงานนั้นยังไม่เคยถูกเริ่มต้นขึ้นเลย
ขั้นตอนที่ 1: ปิดการทำงานของ visitor trigger ใน wp-config.php
เปิดไฟล์ /srv/www/example.com/wp-config.php แล้วเพิ่มค่าคงที่ดังนี้:
define( 'DISABLE_WP_CRON', true );ให้วางไว้ เหนือ บรรทัดที่มีข้อความ /* That's all, stop editing! Happy publishing. */ เนื่องจากบรรทัดที่อยู่ถัดจากคอมเมนต์นั้นจำเป็นต้องใช้ wp-settings.php และ wp-settings.php คือจุดที่ WordPress เชื่อมต่อ cron check เข้ากับ init หากกำหนดค่าคงที่หลังจากคำสั่ง require นั้น จะถือว่าสายเกินไปที่จะเปลี่ยนแปลงการทำงานใดๆ และไฟล์จะดูเหมือนถูกต้องในขณะที่ trigger ยังคงทำงานอยู่
ตรวจสอบให้แน่ใจว่าบรรทัดดังกล่าวอยู่ในตำแหน่งที่คุณต้องการ:
grep -n "DISABLE_WP_CRON\|stop editing" /srv/www/example.com/wp-config.phpค่าคงที่นี้ไม่ได้หยุดการกำหนดตารางเวลาของเหตุการณ์ต่างๆ ปลั๊กอินจะยังคงเพิ่มงานเข้าสู่คิวตามปกติเหมือนเดิม แต่จะหยุดไม่ให้การโหลดหน้าเว็บเรียกใช้งานคิวเหล่านั้น ซึ่งหมายความว่าคิวจะไม่ถูกประมวลผลจนกว่าคุณจะทำขั้นตอนที่ 3 เสร็จสิ้น
นอกจากนี้ ค่าคงที่นี้ไม่ได้บล็อกการร้องขอโดยตรงไปยัง /wp-cron.php ใครก็ตามยังสามารถเรียกใช้ URL นั้นได้ ซึ่งโดยปกติแล้วไม่มีอันตรายเนื่องจากไฟล์จะประมวลผลเฉพาะงานที่ถึงกำหนดเท่านั้น การบล็อกในไฟล์กำหนดค่าของเว็บเซิร์ฟเวอร์เป็นทางเลือกเสริม หากคุณบล็อกไฟล์ดังกล่าว กลไกสำรอง curl ที่ระบุไว้ในช่วงท้ายของคู่มือนี้จะหยุดทำงานไปด้วยเช่นกัน
ขั้นตอนที่ 2: ติดตั้ง WP-CLI
WP-CLI เป็นเครื่องมือบรรทัดคำสั่งอย่างเป็นทางการสำหรับ WordPress ซึ่งจำเป็นต้องใช้ PHP command line binary ซึ่งเป็นแพ็กเกจแยกต่างหากจาก PHP module ของเว็บเซิร์ฟเวอร์
php -v
sudo apt install -y php-cliติดตั้ง WP-CLI จากไฟล์ phar build ตามที่คู่มือการติดตั้งอย่างเป็นทางการแนะนำ:
cd /tmp
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
wp --infophp wp-cli.phar --info จะแสดงพาธของ PHP binary, เวอร์ชันของ PHP และเวอร์ชันของ WP-CLI หากแสดงข้อมูลครบทั้งสามรายการ แสดงว่าไฟล์ phar ทำงานได้ปกติ ณ เดือนสิงหาคม 2026 คู่มือการติดตั้งระบุว่า PHP 7.2.24 คือเวอร์ชันขั้นต่ำที่รองรับ และเนื่องจาก Ubuntu 24.04 มาพร้อมกับ PHP 8.3 เซิร์ฟเวอร์ในปัจจุบันจึงผ่านเกณฑ์นี้ไปไกลแล้ว คุณสามารถอัปเดตในภายหลังได้ด้วย sudo wp cli update
ให้รัน WP-CLI ในฐานะผู้ใช้ของเว็บไซต์เท่านั้น ห้ามรันในฐานะ root:
sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com core versionหากรันในฐานะ root ระบบจะปฏิเสธการทำงานของ WP-CLI:
Error: YIKES! It looks like you're running this as root.ระบบจะแนะนำให้ใช้ --allow-root แต่ห้ามใช้คำสั่งดังกล่าวในที่นี้ เนื่องจากเหตุผลจะถูกอธิบายไว้ในรูปแบบความผิดพลาดแรกด้านล่าง
นอกจากนี้ โปรดสังเกตว่า sudo -u www-data -i จะไม่ทำงาน เนื่องจาก login shell ของบัญชีนั้นคือ /usr/sbin/nologin และคุณจะได้รับ This account is currently not available. การส่งคำสั่งโดยตรงไปยัง sudo -u จะเป็นการข้าม login shell ทำให้คำสั่งทำงานได้ตามปกติ
ตอนนี้ให้ยืนยันว่า WordPress มองเห็นค่าคงที่จากขั้นตอนที่ 1 แล้ว:
sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com eval 'var_dump( DISABLE_WP_CRON );'คำสั่งนี้จะแสดงผลเป็น bool(true) หากเกิด fatal error เกี่ยวกับค่าคงที่ที่ไม่ได้กำหนด (undefined constant) แสดงว่าบรรทัด define() ยังไม่ถูกประมวลผล ซึ่งมักหมายความว่าบรรทัดดังกล่าวถูกวางไว้หลังคำสั่ง require
ขั้นตอนที่ 3: เพิ่มรายการ cron ในฐานะผู้ใช้ที่ถูกต้อง
ผู้ใช้ที่ถูกต้องคือผู้ใช้ที่เป็นเจ้าของไฟล์ที่ PHP เขียนข้อมูลลงไป ให้ตรวจสอบทั้งสองฝั่ง:
stat -c '%U %G' /srv/www/example.com/wp-content/uploads
grep -E '^(user|group) = ' /etc/php/8.3/fpm/pool.d/www.confในการติดตั้ง Ubuntu แบบค่าเริ่มต้น ทั้งสองค่าจะตอบกลับเป็น www-data หากคุณกำหนดให้เว็บไซต์มี PHP-FPM pool ของตนเองพร้อมผู้ใช้เฉพาะ ซึ่งเป็นจุดสิ้นสุดปกติของการตั้งค่ารายเว็บไซต์บน LAMP stack บน Ubuntu 24.04 ให้ใช้ผู้ใช้นั้นสำหรับทุกขั้นตอนด้านล่าง
สร้างไดเรกทอรีสำหรับเก็บ log ที่ผู้ใช้ดังกล่าวสามารถเขียนไฟล์ได้:
sudo install -d -o www-data -g www-data -m 750 /var/log/wp-cronแก้ไข crontab ของผู้ใช้นั้น:
sudo crontab -u www-data -eเพิ่มบรรทัดนี้ลงไป:
*/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-example.lock /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now >> /var/log/wp-cron/example.log 2>&1อธิบายทีละส่วน */5 สั่งให้รันทุกห้านาที flock -n ใช้ไฟล์ล็อกและจะยกเลิกการทำงานทันทีหากการรันครั้งก่อนหน้ายังคงถือครองล็อกนั้นอยู่ /usr/local/bin/wp คือพาธแบบสมบูรณ์ซึ่ง cron จำเป็นต้องใช้ --path ช่วยให้คำสั่งรันได้จากทุกไดเรกทอรีทำงาน --due-now สั่งให้รันเฉพาะเหตุการณ์ที่ถึงเวลาแล้วเท่านั้น แทนที่จะรันทุกเหตุการณ์ในคิว การ redirect จะส่งผลลัพธ์ปกติและข้อผิดพลาดไปรวมไว้ในไฟล์เดียวที่คุณสามารถอ่านได้
ในทางปฏิบัติ การ redirect นี้ไม่ใช่ทางเลือกเสริม เนื่องจาก cron จะส่งผลลัพธ์ของงานไปยังอีเมลของผู้ใช้ แต่ VPS ส่วนใหญ่ไม่มี mail transfer agent ติดตั้งไว้ ทำให้ cron บันทึก (CRON) info (No MTA installed, discarding output) และทิ้งผลลัพธ์เหล่านั้นไป การใช้ไฟล์จึงช่วยเก็บหลักฐานไว้ได้
ตรวจสอบไฟล์ที่บันทึกไว้:
sudo crontab -u www-data -lสำหรับหลายเว็บไซต์ ให้ใช้บรรทัดละหนึ่งรายการโดยเหลื่อมเวลากันเพื่อไม่ให้ทุกงานเริ่มทำงานพร้อมกัน:
*/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-one.lock /usr/local/bin/wp --path=/srv/www/one.example.com cron event run --due-now >> /var/log/wp-cron/one.log 2>&1
2-59/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-two.lock /usr/local/bin/wp --path=/srv/www/two.example.com cron event run --due-now >> /var/log/wp-cron/two.log 2>&1ไฟล์ log จะขยายขนาดขึ้นเรื่อยๆ หากไม่มีการหมุนเวียนไฟล์ (rotate) ให้เขียนไฟล์ /etc/logrotate.d/wp-cron:
/var/log/wp-cron/*.log {
weekly
rotate 4
missingok
notifempty
compress
create 640 www-data www-data
}ตรวจสอบว่าไฟล์ถูก parse อย่างถูกต้องโดยไม่มีการแก้ไขใดๆ: sudo logrotate --debug /etc/logrotate.d/wp-cron
ขั้นตอนที่ 4: ยืนยันว่าเหตุการณ์ที่ตั้งเวลาไว้ทำงานจริง
บรรทัดใน crontab ที่บันทึกสำเร็จไม่ได้เป็นเครื่องพิสูจน์ว่างานทำงานได้จริง ให้ตรวจสอบไล่ระดับจากวิธีที่ง่ายที่สุดไปจนถึงวิธีที่ยืนยันผลได้แน่นอน
อย่างแรก cron ได้เริ่มคำสั่งหรือไม่? cron จะบันทึก log ลงใน journal ภายใต้ unit ของตัวเอง:
journalctl -u cron.service --since "15 min ago" | grep wpรายการที่ปกติจะมีลักษณะดังนี้ (ตัด timestamp และชื่อโฮสต์ออก):
CRON[24913]: (www-data) CMD (/usr/bin/flock -n /run/lock/wp-cron-example.lock /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now >> /var/log/wp-cron/example.log 2>&1)บรรทัดนั้นหมายความว่า cron ได้เริ่มคำสั่งของคุณในฐานะ www-data แล้ว แต่มันไม่ได้บอกว่าคำสั่งนั้นทำงานสำเร็จหรือไม่
อย่างที่สอง WordPress ได้ดำเนินการอะไรไปบ้าง? ให้อ่านไฟล์ log:
sudo tail -n 20 /var/log/wp-cron/example.logWP-CLI จะพิมพ์หนึ่งบรรทัดต่อหนึ่งเหตุการณ์ แล้วตามด้วยผลรวม:
Executed the cron event 'wp_version_check' in 0.418s.
Success: Executed a total of 2 cron events.ข้อผิดพลาดจะถูกบันทึกไว้ในไฟล์เดียวกัน ซึ่งเป็นจุดประสงค์หลักของ 2>&1 การทำงานส่วนใหญ่ที่ไม่มีงานค้างจะเขียนข้อมูลลงไฟล์น้อยมาก ดังนั้นควรตรวจสอบไฟล์หลังจากช่วงเวลาที่คุณทราบว่ามีงานรออยู่
อย่างที่สาม พิสูจน์ผลลัพธ์แบบ end-to-end ให้ตั้งเวลาเหตุการณ์ตัวอย่างแล้วเฝ้าดูว่ามันหายไปหรือไม่:
sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event schedule wp_cli_cron_check now
sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event list --fields=hook,next_run_relative --format=csv | grep wp_cli_cron_checkรอให้ครบหนึ่งช่วงเวลา แล้วรันคำสั่งแสดงรายการอีกครั้ง หาก hook หายไปแสดงว่าทำงานสำเร็จ เพราะเหตุการณ์แบบครั้งเดียวจะถูกลบออกจากคิวเมื่อทำงานเสร็จสิ้น เนื่องจากไม่มีปลั๊กอินใดลงทะเบียน callback ไว้กับชื่อ hook นั้น การรันจึงไม่ส่งผลกระทบอื่นต่อเว็บไซต์ หากรายการ hook ยังคงอยู่หลังจากผ่านไปสองช่วงเวลา แสดงว่าคิวไม่ได้ถูกประมวลผล และการตรวจสอบสองขั้นตอนแรกจะบอกคุณได้ว่าปัญหาอยู่ที่ cron หรือ WP-CLI
ห้ามใช้ wp cron test สำหรับการตรวจสอบนี้ คำสั่งดังกล่าวใช้ตรวจสอบว่าการเรียกใช้งานผ่านผู้เข้าชม (visitor triggered spawning) ทำงานหรือไม่ และจะแสดงข้อผิดพลาดเมื่อ DISABLE_WP_CRON เป็นค่า true บนเซิร์ฟเวอร์ที่ตั้งค่าไว้อย่างถูกต้อง ข้อผิดพลาดนั้นคือผลลัพธ์ที่คาดหวัง ไม่ใช่ความผิดปกติของระบบ
ทางเลือกด้วย systemd timer
หากงานตามกำหนดการอื่นๆ บนเซิร์ฟเวอร์ทำงานผ่าน systemd services and timers อยู่แล้ว ให้ย้าย WordPress ไปไว้ที่นั่นด้วย การทำงานทุกครั้งจะปรากฏใน systemctl list-timers และผลลัพธ์จะถูกส่งไปยัง journal แทนการเขียนลงไฟล์ที่คุณต้องคอยจัดการหมุนเวียน (rotate)
สร้างไฟล์ /etc/systemd/system/wp-cron-example.service:
[Unit]
Description=Run due WordPress cron events for example.com
[Service]
Type=oneshot
User=www-data
Group=www-data
ExecStart=/usr/local/bin/wp --path=/srv/www/example.com cron event run --due-nowจากนั้นสร้าง /etc/systemd/system/wp-cron-example.timer:
[Unit]
Description=Run WordPress cron for example.com every 5 minutes
[Timer]
OnCalendar=*:0/5
AccuracySec=30s
Persistent=true
[Install]
WantedBy=timers.targetsudo systemctl daemon-reload
sudo systemctl enable --now wp-cron-example.timer
systemctl list-timers wp-cron-example.timer
journalctl -u wp-cron-example.service -n 20Systemd จะไม่รัน service เดียวกันซ้อนกันในเวลาเดียวกัน ดังนั้นเวอร์ชันนี้จึงไม่จำเป็นต้องใช้ flock ส่วน Persistent=true จะช่วยให้งานที่พลาดไปในช่วงที่เครื่องปิดอยู่กลับมาทำงานต่อได้ ซึ่งเป็นสิ่งที่ crontab ทำไม่ได้
ให้เลือกใช้ระหว่าง crontab หรือ timer อย่างใดอย่างหนึ่ง การรันทั้งสองอย่างกับเว็บไซต์เดียวกันจะทำให้คิวถูกประมวลผลสองครั้ง และลูกค้าของคุณอาจเห็นงานประเภทอีเมลหรือคำสั่งซื้อถูกประมวลผลซ้ำซ้อนได้
เหตุผลที่ cron job ไม่ควรทำงานด้วยสิทธิ์ root
นี่คือสาเหตุแรกจาก 3 ประการที่ทำให้การตั้งค่าล้มเหลว หากคุณใส่ job ไว้ใน crontab ของ root ตัว WP-CLI จะหยุดทำงานก่อนที่จะเริ่มดำเนินการใดๆ:
Error: YIKES! It looks like you're running this as root.คิวจะไม่ถูกประมวลผล และหากคุณไม่ได้เปลี่ยนเส้นทาง output (redirect) คุณจะไม่เห็นข้อความแจ้งเตือนใดๆ วิธีแก้ไขที่อันตรายคือการเพิ่ม --allow-root เพราะจะทำให้ทุกไฟล์ที่ปลั๊กอินเขียนขึ้นในระหว่างการทำงานนั้นกลายเป็นของ root ทันที เมื่อมีการเรียกใช้งานเว็บในครั้งถัดไปโดยใช้สิทธิ์ www-data ระบบจะไม่สามารถเขียนไฟล์ลงในไดเรกทอรีเหล่านั้นได้ และเว็บไซต์จะเริ่มแสดงข้อผิดพลาดดังนี้:
Unable to create directory wp-content/uploads/2026/08. Is its parent directory writable by the server?ให้แก้ไขความเป็นเจ้าของไฟล์ (ownership) ให้ถูกต้อง จากนั้นจึงย้าย job ไปไว้ในที่ที่เหมาะสม:
sudo chown -R www-data:www-data /srv/www/example.com/wp-contentcrontab ของ root และ crontab ของ www-data เป็นไฟล์แยกจากกัน ดังนั้นการลบบรรทัดคำสั่งออกจากที่หนึ่งจะไม่ส่งผลกระทบต่ออีกที่หนึ่ง ให้ตรวจสอบทั้งสองแห่ง:
sudo crontab -u root -l
sudo crontab -u www-data -lเหตุใด cron จึงรายงาน wp: not found
นี่เป็นความล้มเหลวครั้งที่สอง cron กำหนดค่า PATH ที่สั้นมากให้กับงานของผู้ใช้ คือ /usr/bin:/bin ซึ่ง WP-CLI ติดตั้งอยู่ใน /usr/local/bin ซึ่งไม่อยู่ในรายการดังกล่าว งานจึงเริ่มทำงานและล้มเหลวภายในเสี้ยววินาที โดยใน log จะปรากฏข้อความเพียงบรรทัดเดียว:
/bin/sh: 1: wp: not foundให้ตรวจสอบ environment ของ cron ด้วยตนเองแทนการคาดเดา โดยเพิ่มบรรทัดชั่วคราวเข้าไปดังนี้:
*/5 * * * * env > /tmp/cron-env.txt 2>&1อ่านไฟล์ /tmp/cron-env.txt หลังจากผ่านไปหนึ่งรอบการทำงาน แล้วจึงลบบรรทัดดังกล่าวออก ค่า PATH= ในไฟล์นั้นคือสิ่งที่งานของคุณได้รับจริง
การแก้ไขทำได้สองวิธี คือใช้ absolute path ของ /usr/local/bin/wp ตามขั้นตอนที่ 3 หรือกำหนดค่า PATH ไว้ที่ด้านบนสุดของ crontab เหนือบรรทัดงานทุกบรรทัด:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binกับดักเดียวกันนี้ยังเกิดขึ้นในระดับที่ลึกลงไปอีก ไฟล์ wp phar เริ่มต้นด้วย #!/usr/bin/env php ดังนั้น shell จึงต้องหา php ให้พบด้วยเช่นกัน หาก PHP ติดตั้งอยู่นอก /usr/bin ซึ่งมักเกิดขึ้นกับ custom build หรือ build จาก control panel คุณจะพบข้อผิดพลาด:
/usr/bin/env: 'php': No such file or directoryในกรณีนี้ให้เรียกใช้ interpreter โดยระบุเส้นทางให้ชัดเจน เช่น /usr/local/bin/php /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now
เหตุใดช่วงเวลา 1 นาทีจึงทำให้เกิดปัญหาเดิมซ้ำ
นี่เป็นความล้มเหลวครั้งที่ 3 การใช้ * * * * * ให้ความรู้สึกปลอดภัยกว่าการตั้งค่า 5 นาที แต่สำหรับเว็บไซต์ที่มีการใช้งานหนาแน่น การตั้งค่านี้จะทำให้คุณกลับไปสู่จุดเริ่มต้น หากการทำงานหนึ่งรอบใช้เวลานานกว่าช่วงเวลาที่กำหนด รอบถัดไปจะเริ่มทำงานในขณะที่รอบแรกยังไม่เสร็จสิ้น ผ่านไป 10 นาทีจะมีกระบวนการ PHP ทำงานค้างอยู่ 10 รายการ ซึ่งแต่ละรายการจะจองหน่วยความจำและการเชื่อมต่อฐานข้อมูลของตนเองไว้
ตรวจสอบการสะสมของกระบวนการได้โดยตรงดังนี้:
ps -eo etimes,user,args | grep '[c]ron event run'etimes คืออายุของกระบวนการในหน่วยวินาที หากมีเพียงบรรทัดเดียวถือว่าปกติ แต่หากมีหลายบรรทัดที่มีอายุมากกว่าช่วงเวลาที่คุณตั้งไว้ แสดงว่ามีการทำงานซ้อนทับกัน และบน VPS ขนาดเล็ก ปัญหานี้จะจบลงด้วยข้อผิดพลาด Too many connections ของ MySQL หรือการที่ kernel สั่งยุติการทำงานของ PHP เพื่อคืนหน่วยความจำ ซึ่งคุณสามารถตรวจสอบได้ด้วย sudo dmesg -T | grep -i 'killed process'
WP-CLI จะเรียกใช้ event callbacks โดยตรงแทนการร้องขอผ่าน wp-cron.php ดังนั้นกลไกการล็อก 60 วินาทีที่ WordPress ใช้เพื่อป้องกันการเรียกซ้ำจึงไม่มีผลในกรณีนี้ flock -n ในขั้นตอนที่ 3 คือสิ่งที่ป้องกันการซ้อนทับในปัจจุบัน การทำงานที่ถูกข้ามไปจะยุติการทำงานทันทีโดยไม่มีการแจ้งเตือนตามการออกแบบ ปัญหาการซ้อนทับเป็นสิ่งที่คุณต้องแก้ไขใน crontab ไม่ใช่สิ่งที่ kernel ของโฮสต์จะจัดการให้คุณ แม้แต่ การจัดวางงานที่รับรู้แคชซึ่งเพิ่มเข้ามาใน Linux kernel 7.2 ก็ทำได้เพียงตัดสินใจว่ากระบวนการจะไปทำงานบนคอร์ใด แต่ไม่สามารถควบคุมจำนวนกระบวนการที่คุณสั่งให้เริ่มทำงานได้
เลือกช่วงเวลาจากตารางงานที่สั้นที่สุดที่คุณจำเป็นต้องใช้จริง และวัดระยะเวลาการทำงานก่อน:
time sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now5 นาทีเป็นค่าเริ่มต้นที่เหมาะสม: โพสต์ที่ตั้งเวลาไว้ 09:00 จะถูกเผยแพร่ภายใน 09:05 ส่วน 15 นาทีก็เพียงพอสำหรับเว็บไซต์ที่ไม่มีงานสำคัญด้านเวลา สำหรับช่วงเวลา 1 นาทีนั้นเหมาะสำหรับร้านค้าและปลั๊กอินที่ทำงานผ่านคิวซึ่งจำเป็นต้องใช้จริงๆ และควรใช้ก็ต่อเมื่อคุณทราบแน่ชัดแล้วว่าการทำงานหนึ่งรอบใช้เวลาเพียงไม่กี่วินาทีเท่านั้น
หากคุณไม่สามารถติดตั้ง WP-CLI ได้
โฮสต์บางแห่งบล็อกการใช้งานเครื่องมือผ่านเชลล์ คุณสามารถส่งคำขอ HTTP แบบปกติไปยัง wp-cron.php เพื่อรันคิวเดียวกันได้ โดยคำขอนี้จะผ่านเว็บสแต็กทั้งหมด:
*/5 * * * * /usr/bin/curl -sS --max-time 120 "https://example.com/wp-cron.php?doing_wp_cron" > /dev/nullสิ่งที่คุณต้องยอมแลกมีดังนี้:
- การทำงานจะถูกจำกัดด้วยค่า timeout ของเว็บเซิร์ฟเวอร์และ PHP-FPM ดังนั้นงานที่ใช้เวลานานอาจถูกตัดจบกลางคัน
- ใบรับรองต้องถูกต้อง มิฉะนั้น
curlจะหยุดทำงานพร้อมกับข้อผิดพลาดSSL certificate problemดังนั้นต้องดูแลให้การต่ออายุใบรับรองทำงานได้ตามปกติด้วย Certbot บน nginx - ระบบแคชหน้าเว็บต้องไม่แคช
wp-cron.phpมิฉะนั้นคำขอ cron จะได้รับผลลัพธ์ที่ถูกแคชไว้และไม่มีงานใดถูกประมวลผล - คุณจะไม่ได้รับผลลัพธ์การทำงานแบบเรียลไทม์ ดังนั้นหลักฐานเดียวที่ยืนยันว่างานได้รันแล้วคือผลลัพธ์ที่เกิดขึ้นจริง
-sS ช่วยให้ curl ไม่แสดงผลลัพธ์เมื่อทำงานสำเร็จ แต่ยังคงแสดงข้อผิดพลาดหากมีปัญหา ซึ่งเป็นสิ่งที่ควรใช้สำหรับงาน cron job
สิ่งที่ควรอยู่ในตารางเวลาของเซิร์ฟเวอร์
เมื่อ system cron ดูแลคิวของ WordPress แล้ว ให้เก็บงานประจำอื่นๆ ของเซิร์ฟเวอร์ไว้ในที่เดียวกันเพื่อให้ง่ายต่อการตรวจสอบ การติดตั้ง security patch ของระบบปฏิบัติการควรจัดการผ่าน unattended upgrades แทนการใช้ cron line ที่คุณต้องดูแลเอง การอัปเดตปลั๊กอินและธีมของ WordPress เป็นการตัดสินใจที่ต่างออกไป การใช้ wp plugin update --all ใน crontab อาจทำให้เว็บไซต์ที่ใช้งานจริงล่มตอนตี 3 โดยไม่มีใครดูแล ดังนั้นควรดำเนินการด้วยความตั้งใจ หรือทำผ่านขั้นตอน staging และมีการสำรองข้อมูลก่อนเสมอ
FAQ
การปิด WP-Cron จะทำให้โพสต์ที่ตั้งเวลาไว้ไม่เผยแพร่หรือไม่?
ไม่ หากมีกระบวนการอื่นคอยรันคิวงานอยู่ DISABLE_WP_CRON เพียงแค่หยุดไม่ให้การโหลดหน้าเว็บเป็นตัวกระตุ้นคิวงานเท่านั้น เหตุการณ์ต่างๆ ยังคงถูกตั้งเวลาไว้เหมือนเดิมทุกประการ โพสต์ที่ตั้งเวลาไว้ 09:00 จะถูกเผยแพร่ในการรัน cron ครั้งแรกหลังจาก 09:00 ดังนั้นหากตั้งช่วงเวลาไว้ 5 นาที โพสต์จะถูกเผยแพร่ภายใน 09:05 หากคุณกำหนดค่า constant นี้ไว้แต่ไม่ได้เพิ่มรายการ cron เข้าไป โพสต์จะค้างอยู่ในรายการโดยมีสถานะเป็น Missed schedule จนกว่าจะมีกระบวนการอื่นมารันคิวงานนั้น
ควรใช้ผู้ใช้ใดในการรัน WordPress cron job?
ควรใช้ผู้ใช้ที่เป็นเจ้าของไฟล์ที่ PHP เขียน ซึ่งโดยปกติคือ www-data บนการติดตั้ง Ubuntu เริ่มต้น ให้ตรวจสอบด้วยคำสั่ง stat -c '%U %G' /srv/www/example.com/wp-content/uploads และเปรียบเทียบกับบรรทัด user = ในไฟล์ตั้งค่า PHP-FPM pool ของคุณ การรัน job ในฐานะ root จะทำให้ WP-CLI หยุดทำงานพร้อมข้อผิดพลาด YIKES และการบังคับรันด้วย --allow-root จะทำให้ไฟล์ภายใน wp-content ตกเป็นของ root ซึ่งเว็บเซิร์ฟเวอร์จะไม่สามารถเขียนทับได้ในภายหลัง
ควรตั้งค่า system cron ให้รัน WordPress cron บ่อยแค่ไหน?
ทุกๆ 5 นาทีเหมาะสมสำหรับเว็บไซต์ส่วนใหญ่ ให้ปรับช่วงเวลาให้ตรงกับตารางเวลาที่สั้นที่สุดที่คุณใช้งานจริง และรักษาช่วงเวลาให้มากกว่าระยะเวลาที่ใช้ในการรันหนึ่งครั้ง ซึ่งคุณสามารถวัดได้โดยใส่ time ไว้หน้าคำสั่ง WP-CLI การตั้งช่วงเวลา 1 นาทีอาจทำให้การรันซ้อนทับกันบนเว็บไซต์ที่มีผู้ใช้งานจำนวนมาก เว้นแต่จะมี flock คอยป้องกันไว้
ทำไม wp cron test ถึงล้มเหลวหลังจากที่ฉันปิด WP-Cron?
เพราะคำสั่งนั้นเป็นการทดสอบการกระตุ้นการทำงานผ่านผู้เข้าชม และจะรายงานข้อผิดพลาดเมื่อ DISABLE_WP_CRON ถูกตั้งค่าเป็น true ซึ่งเป็นผลลัพธ์ที่ถูกต้องบนเซิร์ฟเวอร์ที่กำหนดค่าในลักษณะนี้ ให้ตรวจสอบผ่าน system cron แทน โดยอ่านจาก /var/log/wp-cron/example.log หรือตั้งค่าเหตุการณ์ตัวบ่งชี้ด้วย wp cron event schedule แล้วตรวจสอบว่าเหตุการณ์นั้นหายไปจาก wp cron event list หลังจากรอบการรันถัดไปหรือไม่
ฉันจำเป็นต้องใช้ WP-CLI หรือการใช้ curl เรียก wp-cron.php ก็เพียงพอแล้ว?
การใช้ curl สามารถทำได้และเป็นคำตอบที่เหมาะสมเมื่อคุณไม่สามารถติดตั้ง WP-CLI ได้ แต่วิธีนี้จะช้ากว่าเพราะเป็นการโหลด WordPress ผ่านเว็บเซิร์ฟเวอร์และถูกจำกัดด้วย request timeout ในขณะที่ WP-CLI จะรันเหตุการณ์ในกระบวนการ PHP บนบรรทัดคำสั่งโดยไม่มีการจำกัดเวลาของเว็บ และจะแสดงผลลัพธ์หนึ่งบรรทัดต่อหนึ่งเหตุการณ์พร้อมระยะเวลาที่ใช้ ทำให้ log บอกคุณได้อย่างแม่นยำว่ามีอะไรทำงานไปบ้างและใช้เวลานานเท่าใด