การค้นหาสาเหตุของการใช้ CPU สูงในเซิร์ฟเวอร์ WordPress ของเรา
เผยแพร่แล้ว: 2020-05-07ไม่กี่วันที่ผ่านมาเราได้รับอีเมลจากผู้ให้บริการโฮสติ้งของเรา (SiteGround) แจ้งให้เราทราบว่าเว็บไซต์ของเรามี "90% ของการใช้งาน CPU รายเดือนที่ได้รับอนุญาต" และเมื่อเราเกิน 100% "บริการเว็บจะถูกจำกัด" และเราอาจมี "ปัญหาในการเข้าถึง" ค่อนข้างน่ากลัวใช่มั้ย? เป็นสถานการณ์ที่ไม่พึงปรารถนาอย่างยิ่งที่เราต้องแก้ไขโดยเร็วที่สุด แต่... จะเริ่มต้นที่ไหน

วันนี้ เราต้องการแบ่งปันประสบการณ์ของเราที่ประสบ ปัญหาที่พบบ่อย ในเว็บไซต์ อธิบายว่าเราทำอะไรเพื่อระบุตัวผู้ กระทำความผิด และวิธีที่เรา แก้ไข ปัญหา ด้วยวิธีนี้ หากคุณประสบปัญหาที่คล้ายกัน คุณจะมีแนวคิดในการเริ่มต้น...
เหตุผลที่คุณสามารถใช้ CPU สูงได้
WordPress เป็นระบบจัดการเนื้อหาที่เขียนด้วย PHP ซึ่งหมายความว่าเนื้อหาที่ให้บริการถูกสร้างขึ้นแบบไดนามิกโดยชุดสคริปต์ PHP ทุกครั้งที่มีผู้เข้าชมมาถึงเว็บไซต์ของคุณ WordPress จะดำเนินการตามคำขอ (ซึ่งคล้ายกับ "โปรดส่งหน้าแรกของคุณมาให้ฉัน") และสร้างการตอบกลับ (ใน กรณีนี้จะส่งโฮมเพจ) เห็นได้ชัดว่าการตอบสนองต่อคำขอหมายถึงการใช้ทรัพยากรเซิร์ฟเวอร์บางอย่าง: เราต้องพิจารณาคำขอนั้นเอง กำหนดสิ่งที่ผู้เยี่ยมชมต้องการเข้าถึง ดึงข้อมูลจากฐานข้อมูล สร้างการตอบสนอง HTML และอื่นๆ
สาเหตุหนึ่งที่ว่าทำไมระบบแคชเร่งความเร็วในการโหลดเว็บไซต์ของคุณจึงน่าจะชัดเจนอยู่แล้ว: โดยพื้นฐานแล้วจะช่วยประหยัดเวลาในการประมวลผล เมื่อมีคำขอที่เจาะจงมาถึงเป็นครั้งแรก (“ส่งหน้าแรกของคุณมาให้ฉัน”) WordPress จะเริ่มต้นและสร้างการตอบกลับ หากมีแคชอยู่ แคชจะจัดเก็บการตอบสนองดังกล่าวก่อนที่จะส่งไปยังผู้เยี่ยมชมจริง ด้วยวิธีนี้ คำขอในอนาคตที่ส่งไปยังแหล่งข้อมูลเดียวกันนั้น (ในตัวอย่างของเรา หน้าแรก) ไม่ต้องการให้ WordPress ดำเนินการใดๆ อีกต่อไป แคชสามารถส่งสำเนาที่บันทึกไว้ก่อนหน้านี้กลับคืนซึ่งช่วยประหยัดเวลาและทรัพยากร
เมื่อคำนึงถึงประสิทธิภาพนี้แล้ว จึงไม่ยากที่จะจินตนาการว่าเหตุใดเราจึงเห็นการใช้งาน CPU สูงบนเซิร์ฟเวอร์ของเรา:
- คุณได้รับคำขอมากเกินไป หากมีผู้ใช้จำนวนมากมาที่เว็บไซต์ของคุณพร้อมกัน หรือคุณได้รับคำขอที่ผิดกฎหมายจำนวนมาก (อาจมีผู้โจมตีเซิร์ฟเวอร์ของคุณ) WordPress จะต้องดำเนินการกับคำขอเหล่านั้นทั้งหมด ดังนั้น การใช้ทรัพยากรเซิร์ฟเวอร์จะเพิ่มขึ้น
- คำขอแก้ไขได้ช้า หากคุณมีปลั๊กอินติดตั้งจำนวนมากหรือปลั๊กอินบางตัวของคุณไม่มีประสิทธิภาพไม่ว่าด้วยเหตุผลใดก็ตาม คำขอทั้งหมดที่คุณได้รับจะใช้เวลานานกว่าที่จำเป็น เนื่องจาก WordPress จะเรียกใช้โค้ดที่ไม่มีประสิทธิภาพจำนวนมาก
ดูเหมือนว่าแคชจะป้องกันปัญหาเหล่านี้ได้ดีใช่ไหม และแท้จริงแล้วมันคือ อย่างไรก็ตาม โปรดทราบว่าแคชไม่ได้ "แก้ไข" ปัญหา มันแค่ "ซ่อน" มัน และนี่เป็นสิ่งสำคัญที่ต้องจำไว้ เพราะมีฟังก์ชันของ WordPress ที่ไม่สามารถแคชได้ ดังนั้น WordPress จึงต้องรันอยู่เสมอ:
- งานที่กำหนดเวลาไว้โดยใช้ WP-Cron WP-Cron เป็นกลไกของ WordPress สำหรับการตั้งเวลางานที่จะดำเนินการในอนาคต ตัวอย่างเช่น WordPress ใช้เพื่อเผยแพร่โพสต์ตามกำหนดเวลา
- REST API ของ WordPress REST API เป็นอินเทอร์เฟซที่สามารถใช้จากแอปพลิเคชันบุคคลที่สามเพื่อโต้ตอบกับไซต์ WordPress โดยส่งและรับวัตถุ JSON คำขอ REST API บางรายการอาจถูกแคชไว้ (กล่าวคือ คำขอ GET) แต่คำขออื่นๆ ไม่ใช่ (POST และ PUT) ดังนั้นจึงเป็นสิ่งที่คุณไม่สามารถแคชได้โดยทั่วไป...
- คำขอ AJAX ใน WordPress ก่อนที่เราจะมี REST API ใน WordPress เราต้องใช้ AJAX API เพื่อสร้างเว็บไซต์แบบไดนามิก API นี้ค่อนข้างคล้ายกับ REST API เพราะเราสามารถใช้เพื่อส่งและรับข้อมูลจากเซิร์ฟเวอร์ได้ เป็นระบบที่แตกต่างกัน แต่อยู่ภายใต้ข้อจำกัดเดียวกันกับ REST API
วิเคราะห์ปัญหา
อันดับแรก เราต้องระบุ สาเหตุที่ การใช้งาน CPU เพิ่มขึ้นในเว็บไซต์ของเรา จำนวนคำขอไปยังเว็บไซต์ของเราเพิ่มขึ้นหรือไม่? ตอนนี้ให้บริการคำขอแต่ละรายการช้าลงหรือไม่ เพื่อตอบคำถามเหล่านี้ เรามีเครื่องมือที่มีประโยชน์มากบนเซิร์ฟเวอร์ของเรา: บันทึกการเข้า ใช้
บันทึกการเข้าใช้เป็นไฟล์ข้อความที่เซิร์ฟเวอร์บันทึกทุกคำขอที่ได้รับพร้อมกับข้อมูลที่เป็นประโยชน์เกี่ยวกับสิ่งเหล่านี้ โดยเฉพาะอย่างยิ่ง บันทึกการเข้าถึงจะบอกเราว่าได้รับคำขอเมื่อใด (วันที่และเวลา) ใครเป็นคนสร้าง (IP) ทรัพยากรที่ร้องขอ (URL) คำขอนั้นสำเร็จหรือไม่ ฯลฯ นี่คือตัวอย่างจากเซิร์ฟเวอร์ของเรา :
66.249.83.82 - - [22/Apr/2020:14:04:59 +0200] "GET /es/blog/imagenes-gratuitas-para-tu-blog/ HTTP/1.0" 200 22325 "-" "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko; googleweblight) Chrome/38.0.1025.166 Mobile Safari/535.19" 66.249.83.84 - - [22/Apr/2020:14:05:02 +0200] "GET /es/wp-content/uploads/sites/3/2018/07/aziz-acharki-549137-unsplash-540x350.jpg HTTP/1.0" 200 10566 "https://neliosoftware.com/es/blog/imagenes-gratuitas-para-tu-blog/" "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko; googleweblight) Chrome/38.0.1025.166 Mobile Safari/535.19" 66.249.83.82 - - [22/Apr/2020:14:05:02 +0200] "GET /es/wp-content/uploads/sites/3/2018/07/Screenshot-of-Unsplah-website-768x520.png HTTP/1.0" 200 399577 "https://neliosoftware.com/es/blog/imagenes-gratuitas-para-tu-blog/" "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko; googleweblight) Chrome/38.0.1025.166 Mobile Safari/535.19" ... 188.79.17.218 - - [22/Apr/2020:14:06:14 +0200] "GET /es/blog/problemas-mas-comunes-de-wordpress/ HTTP/1.0" 200 110741 "https://www.google.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15" 188.79.17.218 - - [22/Apr/2020:14:06:16 +0200] "GET /es/wp-content/plugins/nelio-ab-testing/assets/dist/js/alternative-loader.js?version=52b0ff65c68ab39896d47d6ff673fd59 HTTP/1.0" 200 2763 "https://neliosoftware.com/es/blog/problemas-mas-comunes-de-wordpress/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15" 188.79.17.218 - - [22/Apr/2020:14:06:16 +0200] "GET /es/wp-includes/css/dist/block-library/style.min.css?ver=5.4 HTTP/1.0" 200 7627 "https://neliosoftware.com/es/blog/problemas-mas-comunes-de-wordpress/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15" ...มาดูกันดีกว่า:
-
66.249.83.82. นี่คือ IP ของอุปกรณ์ที่ส่งคำขอ -
22/Apr/2020:14:04:59 +0200. นี่คือวันที่และเวลาที่แน่นอนของคำขอ -
GET /es/blog/imagenes-gratuitas-para-tu-blog/ HTTP/1.0. จากนั้นเราเห็นผู้เยี่ยมชมร้องขอ (GET) โพสต์จากบล็อกของเรา (เป็นภาษาสเปน) -
Mozilla/5.0 (Linux; Android ...นี่คือ User-Agent ของเบราว์เซอร์และให้ข้อมูลบางอย่างเกี่ยวกับอุปกรณ์และระบบปฏิบัติการที่ส่งคำขอ
สังเกตว่าเซิร์ฟเวอร์ของเราได้รับคำขอหลายรายการจาก IP เดียวกัน ( 66.249.83.82 ) หลังจากคำขอแรก นี่อาจดูเหมือนเป็นการโจมตี แต่จริงๆ แล้วไม่ใช่: หน้าเว็บมักจะมีเนื้อหาหลายอย่าง (รูปภาพ สคริปต์ ลักษณะ) และเป็นเรื่องปกติอย่างสมบูรณ์ที่ผู้เยี่ยมชมที่เข้าถึงหน้าเว็บบางหน้าในเว็บไซต์ของคุณจะดำเนินการหลายคำขอเพื่อเรียกข้อมูลทั้งหมด .

ในกรณีของเรา เราสามารถยืนยันได้ว่า เรามีคำขอจำนวนมากผิดปกติ สูงมากจริงๆ และเรารู้ว่าเพราะล็อกไฟล์มีขนาดใหญ่กว่าปกติ
คำอธิบายที่เป็นไปได้คือมีการเข้าชมสูงสุดด้วยเหตุผลบางประการ… แต่จากข้อมูลของ Google Analytics นั่นไม่ใช่กรณี บางสิ่งที่แตกต่างกันจึงเกิดขึ้น
การวิเคราะห์บันทึกการเข้าถึงโดยละเอียดยิ่งขึ้นทำให้เราสามารถระบุข้อเท็จจริงต่อไปนี้: คำขอมากกว่า 15% ที่เราได้รับมาจาก IP เดียวกัน และ (drum roll) IP นั้นเป็นเว็บเซิร์ฟเวอร์ของเราเอง!

ระบุตัวผู้กระทำผิด
ณ จุดนี้ในที่สุดเราก็รู้ว่าเซิร์ฟเวอร์ของเราเป็นผู้ส่งคำขอจำนวนมากจนทำให้เกิดการใช้งาน CPU สูงสุด แต่ทำไม? ทำไมสิ่งนี้จึงเกิดขึ้น? ใครเป็นคนสร้างคำขอเหล่านั้น? นี่เป็นคำถามที่ยากกว่าที่จะตอบ
อันดับแรก เราดูบันทึกของเราอีกครั้ง กรองตาม IP และพยายามระบุรูปแบบที่สามารถให้ความกระจ่างเกี่ยวกับปัญหาที่เกิดขึ้นได้:
35.214.244.124 - - [22/Apr/2020:14:06:08 +0200] "GET /es HTTP/1.0" 301 - "https://neliosoftware.com/es" "php-requests/1.7-3470169" 35.214.244.124 - - [22/Apr/2020:14:06:08 +0200] "GET /es?..." "php-requests/1.7-3470169" 35.214.244.124 - - [22/Apr/2020:14:06:18 +0200] "GET /es?..." "php-requests/1.7-3470169" 35.214.244.124 - - [22/Apr/2020:14:07:21 +0200] "GET /es?..." "php-requests/1.7-3470169" 35.214.244.124 - - [22/Apr/2020:14:07:24 +0200] "GET /es?..." "php-requests/1.7-3470169" ... และเราพบสิ่งหนึ่ง: User-Agent ของคำขอที่ผิดปกติเหล่านี้คือ php-requests/1.7-3470169 น่าสนใจ!
WordPress มีฟังก์ชันหลายอย่างที่จะทริกเกอร์คำขอ: wp_remote_request หากคุณดูที่ซอร์สโค้ดของฟังก์ชันเหล่านี้ คุณจะเห็นว่าโดยทั่วไปแล้วมันรวมเมธอดสองสามวิธีจากคลาสที่เรียกว่า WP_Http คลาสนี้น่าสนใจเพราะโดยค่าเริ่มต้น คำขอทั้งหมดที่ทำให้ User-Agent เป็น “WordPress/version” ดังนั้นจึงเป็นไปได้ว่าสิ่งนี้อาจเกี่ยวข้องกับปัญหาของเรา แต่ก็ยังไม่...
หากเราตรวจสอบซอร์สโค้ดของ WordPress ต่อไป เราจะเห็นว่า WP_Http ใช้คลาส WordPress อื่นภายในเพื่อส่งคำขอจริง: Requests และคลาสนี้มีความน่าสนใจสำหรับเด็ก: ในตอนต้นของคำจำกัดความ เราเห็นว่ามันกำหนดค่าคงที่ที่ชื่อ VERSION ซึ่งมีค่าเท่ากับ 1.7-3470169 และอีกเล็กน้อยต่อมา จะใช้ค่าคงที่นี้เพื่อสร้าง User-Agent ที่ เราพบในบันทึกของเรา : php-requests/1.7-3470169
ฉลาดหลักแหลม! ตอนนี้เราได้ยืนยันแล้วว่าคำขอแปลก ๆ เหล่านี้ที่เราได้รับมาจากไซต์ WordPress ของเรา นี่อาจหมายความว่าผู้ร้ายเป็นปลั๊กอิน… แต่อันไหน?
แนวคิดที่เราต้องคิดออกนั้นค่อนข้างง่าย: หากเราแก้ไข User-Agent เพื่อให้รวมชื่อของปลั๊กอินที่ใช้คลาส Requests เราจะเห็นชื่อของปลั๊กอินในบันทึกการเข้าถึงเซิร์ฟเวอร์ของเรา และนี่เป็นเรื่องง่ายมากที่จะบรรลุ ทั้งหมดที่เราทำคือแก้ไขฟังก์ชัน get_default_options ของ Requests ด้วยข้อมูลโค้ดต่อไปนี้:
$trace = debug_backtrace(); $files = []; foreach ( $trace as $log ) { if ( false !== strpos( $log['file'], '/wp-content/plugins/' ) ) { $files[] = $log['file']; } } if ( empty( $files ) ) { $debug = 'no-plugin'; } else { $plugins = array_map( function( $x ) { return preg_replace( '/.*\/wp-content\/plugins\/([^\/]+)\/.*/', '$1', $x ); }, $files ); $plugins = array_unique( $plugins ); $debug = implode( ' ', $plugins ); } $defaults['useragent'] .= " (NelioDebug {$debug})";มาดูกันว่ามันทำอะไรทีละขั้นตอน:
- ขั้นแรกเราได้รับการดำเนินการกองกับ
debug_backtraceนี่คือฟังก์ชัน PHP ที่สร้าง backtrace เผยให้เห็นฟังก์ชันทั้งหมดที่ถูกเรียกให้ไปถึงฟังก์ชันปัจจุบัน - สำหรับแต่ละองค์ประกอบในกองการเรียกใช้งาน เรามีข้อมูลเช่น ฟังก์ชันที่ถูกเรียกใช้ ไฟล์และบรรทัดที่กำหนด อาร์กิวเมนต์ที่เรียกใช้ เป็นต้น อย่างไรก็ตาม สิ่งที่เราต้องการเน้นคือ ไฟล์ที่มีการกำหนดฟังก์ชัน: หากอยู่ใน
wp-content/pluginsเราทราบแน่นอนว่าเป็นฟังก์ชันที่กำหนดไว้ในปลั๊กอิน - เมื่อเราประมวลผลองค์ประกอบทั้งหมดในสแต็กแล้ว เราเพียงแค่ต้องได้รับชื่อ (ถ้ามี) ของปลั๊กอินทั้งหมดที่เราพบและรวมไว้ในตัวแปร
useragent
หลังจากขยาย WordPress ตามที่อธิบายไว้ เราก็เริ่มดูว่าใครคือ ผู้ร้าย :
35.214.244.124 - - [23/Apr/2020:10:59:08 +0200] "GET /es HTTP/1.0" 301 - "https://neliosoftware.com/es" "php-requests/1.7-3470169 (NelioDebug culprit)" 35.214.244.124 - - [23/Apr/2020:10:59:20 +0200] "GET /?..." "php-requests/1.7-3470169 (NelioDebug culprit)" 35.214.244.124 - - [23/Apr/2020:10:59:36 +0200] "GET /?..." "php-requests/1.7-3470169 (NelioDebug culprit)" 35.214.244.124 - - [23/Apr/2020:11:00:01 +0200] "GET /es?..." "php-requests/1.7-3470169 (NelioDebug culprit)" 35.214.244.124 - - [23/Apr/2020:11:05:21 +0200] "GET /es?..." "php-requests/1.7-3470169 (NelioDebug culprit)" ...การแก้ปัญหา
เมื่อเราทราบชื่อปลั๊กอินที่ละเมิดแล้ว เราก็ติดต่อผู้พัฒนาและขอความช่วยเหลือ พวกเขาตอบกลับและบอกเราถึงวิธีที่จะเอาชนะปัญหาในขณะที่พวกเขาหาทางแก้ไขที่เหมาะสม โชคดีสำหรับเรา สิ่งต่างๆ ดีขึ้นอย่างรวดเร็ว:

อย่างที่คุณเห็น สิ่งที่ยอดเยี่ยมอย่างหนึ่งเกี่ยวกับซอฟต์แวร์ฟรีก็คือ เราสามารถสำรวจซอร์สโค้ดของแอปพลิเคชันที่เราใช้และปรับให้เข้ากับความต้องการของเราได้ ในกรณีนี้ เราสามารถปรับเปลี่ยน WordPress เองได้ เพื่อให้สามารถเปิดเผยข้อมูลบางอย่างที่เราต้องการได้
ฉันหวังว่าคุณจะชอบเคล็ดลับของการใช้ debug_backtrace เพื่อค้นหาว่าใครเป็นผู้เรียกใช้ฟังก์ชันบางอย่าง แน่นอนว่าไม่ใช่วิธีการดั้งเดิม แต่นำไปใช้ได้อย่างรวดเร็ว และจนถึงตอนนี้ก็ได้รับการพิสูจน์แล้วว่ามีประโยชน์อย่างยิ่ง
ภาพเด่นโดย Michal Mancewicz บน Unsplash
