WordPress Sunucumuzda Yüksek CPU Kullanımının Suçlusunu Bulmak
Yayınlanan: 2020-05-07Birkaç gün önce barındırma sağlayıcımızdan (SiteGround) sitemizin zaten "izin verilen aylık CPU kullanımının %90'ına" sahip olduğunu ve %100'ü aştığımızda "web hizmetinin sınırlı olacağını" bildiren bir e-posta aldık. ve "ona erişmekte sorun yaşayabiliriz". Oldukça korkutucu, değil mi? Kesinlikle en kısa sürede düzeltmemiz gereken tamamen istenmeyen bir durumdu. Ama… nereden başlamalı?

Bugün, suçluyu belirlemek için ne yaptığımızı ve sorunu nasıl çözdüğümüzü açıklayarak web sitelerinde oldukça yaygın bir sorunla karşılaşma deneyimimizi sizinle paylaşmak istedik. Bu şekilde, benzer bir sorunla karşılaşırsanız, nasıl başlayacağınız konusunda bazı fikirleriniz olacak…
CPU kullanımının yüksek olmasının nedenleri
WordPress, PHP ile yazılmış bir içerik yönetim sistemidir. Bu, sunduğu içeriğin bir dizi PHP betiği tarafından dinamik olarak oluşturulduğu anlamına gelir: bir ziyaretçi web sitenize her geldiğinde, WordPress isteği işler ("lütfen bana ana sayfanızı gönderin" gibi bir şeydir) ve bir yanıt oluşturur (içinde). bu durumda ana sayfayı gönderir). Açıkçası, bir isteğe yanıt vermek, sunucu kaynaklarının belirli bir kullanımını gerektirir: İsteğin kendisine bakmak, ziyaretçinin neye erişmek istediğini belirlemek, onu veritabanından almak, HTML yanıtını oluşturmak vb.
Bir önbellek sisteminin web sitenizin yükleme süresini hızlandırmasının nedenlerinden biri artık oldukça açık olmalıdır: temelde bu işlem süresinden tasarruf sağlar. İlk kez belirli bir istek geldiğinde ("bana ana sayfanızı gönderin"), WordPress başlar ve yanıtı oluşturur. Bir önbellek varsa, söz konusu yanıtı ziyaretçiye gönderilmeden önce saklar. Bu şekilde, aynı kaynağa (bizim örneğimizde bir ana sayfa) gelecek istekler artık WordPress'in herhangi bir şeyi işlemesini gerektirmez; önbellek daha önce kaydettiği kopyayı geri gönderebilir, böylece zaman ve kaynak tasarrufu sağlar.
Bu performans göz önüne alındığında, sunucumuzda yüksek CPU kullanımı görmemizin nedenlerinin neler olduğunu hayal etmek zor değil:
- Çok fazla istek alıyorsunuz. Web sitenize aynı anda çok sayıda kullanıcı gelirse veya çok sayıda gayri meşru istek alırsanız (muhtemelen birisi sunucunuza saldırıyor), WordPress tüm bu istekleri işlemek zorunda kalacak ve bu nedenle sunucu kaynaklarının kullanımı artacaktır.
- İsteklerin çözülmesi yavaştır. Yüklü çok sayıda eklentiniz varsa veya eklentilerinizden bazıları herhangi bir nedenle verimsizse, aldığınız tüm istekler gereğinden uzun sürecektir, çünkü WordPress çok fazla verimsiz kod çalıştıracaktır.
Yani bir önbellek bu sorunlara karşı iyi bir koruma gibi görünüyor, değil mi? Ve gerçekten öyle. Ancak, lütfen önbelleğin sorunu "çözmediğini" unutmayın; sadece onu “gizler”. Ve bunu hatırlamak önemlidir, çünkü önbelleğe alınamayan ve bu nedenle her zaman WordPress'in çalışmasını gerektirecek WordPress işlevleri vardır:
- WP-Cron kullanılarak zamanlanan görevler. WP-Cron, gelecekte çalışacak görevleri zamanlamak için bir WordPress mekanizmasıdır. Örneğin, WordPress bunu planlanmış gönderileri yayınlamak için kullanır.
- WordPress'in REST API'si. REST API, JSON nesneleri gönderip alarak bir WordPress sitesiyle etkileşim kurmak için üçüncü taraf uygulamalardan kullanılabilecek bir arabirimdir. Bazı REST API istekleri önbelleğe alınabilir (yani, GET istekleri), ancak diğerleri değildir (POST ve PUT). Bu nedenle, genellikle önbelleğe alamayacağınız bir şeydir…
- WordPress'te AJAX İstekleri. WordPress'te REST API'sine sahip olmadan önce, dinamik web siteleri oluşturmak için AJAX API'sini kullanmamız gerekiyordu. Bu API, REST API'ye oldukça benzer, çünkü onu sunucuya bilgi göndermek ve sunucudan bilgi almak için de kullanabiliriz. Bu farklı bir sistemdir, ancak REST API ile aynı sınırlamalara tabidir.
Problemi analiz etmek
Öncelikle web sitemizde CPU kullanımının neden arttığını belirlememiz gerekiyor. Web sitemize gelen istek sayısı arttı mı? Bireysel istekleri yerine getirmek artık daha mı yavaş? Bu soruları yanıtlamak için sunucumuzda çok kullanışlı bir aracımız var: erişim günlüğü .
Erişim günlüğü, sunucunun aldığı her isteği, onlar hakkında yararlı bilgilerle birlikte günlüğe kaydettiği bir metin dosyasıdır. Özellikle, erişim günlüğü bize bir isteğin ne zaman alındığını (tarih ve saat), bunu kimin yaptığını (bir IP), hangi kaynağı istediğini (bir URL), isteğin başarılı olup olmadığını vb. söyler. İşte sunucumuzdan bir örnek :
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" ...Daha yakından bakalım:
-
66.249.83.82. Bu, isteği yapan cihazın IP'sidir. -
22/Apr/2020:14:04:59 +0200. Bu, talebin kesin tarihi ve saatidir. -
GET /es/blog/imagenes-gratuitas-para-tu-blog/ HTTP/1.0. Ardından, ziyaretçinin blogumuzdan (İspanyolca) bir gönderi istediğini (GET) görüyoruz. -
Mozilla/5.0 (Linux; Android ...Bu, tarayıcının Kullanıcı Aracısıdır ve bize istekte bulunan cihaz ve işletim sistemi hakkında bazı bilgiler verir.
İlkinden sonra sunucumuzun aynı IP'den ( 66.249.83.82 ) birden çok istek aldığına dikkat edin. Bu bir saldırı gibi görünebilir, ancak aslında öyle değildir: web sayfaları genellikle birkaç varlık (resimler, komut dosyaları, stiller) içerir ve sitenizdeki belirli bir web sayfasına erişen bir ziyaretçinin hepsini almak için birden fazla istekte bulunması tamamen normaldir. .

Bizim durumumuzda, gerçekten de alışılmadık derecede yüksek sayıda talebimiz olduğunu doğrulayabildik. Aslında son derece yüksek. Ve bunu biliyorduk çünkü günlük dosyası normalden çok daha büyüktü.
Olası bir açıklama, herhangi bir nedenle ziyaretlerin zirve yapması olabilir… ancak Google Analytics'e göre durum böyle değildi. Yani farklı bir şey oluyordu.
Erişim günlüğünün daha ayrıntılı bir analizi, şu gerçeği belirlememizi sağladı: Aldığımız tüm isteklerin %15'inden fazlası aynı IP'den geldi. Ve (davul rulosu) bu IP bizim kendi web sunucumuzdu!

Suçluyu belirlemek
Bu noktada, CPU kullanımında bir zirve oluşturacak kadar çok istekte bulunanın kendi sunucumuz olduğunu sonunda anladık. Ama neden? Bu neden oluyordu? Bu talepleri kim oluşturuyordu? Bunlar cevaplaması daha zor sorular.
İlk önce günlüklerimize tekrar baktık, IP'ye göre filtreleme yaptık ve elimizdeki soruna biraz ışık tutabilecek bir model belirlemeye çalıştık:
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" ... ve bir tane bulduk: tüm bu anormal isteklerin Kullanıcı Aracısı php-requests/1.7-3470169 . İlginç!
WordPress'in istekleri tetiklemek için birkaç işlevi vardır: wp_remote_request . Bu işlevlerin kaynak koduna bakarsanız, temelde WP_Http adlı bir sınıftan birkaç yöntemi paketlediklerini göreceksiniz. Bu sınıf ilginçtir, çünkü varsayılan olarak yaptığı tüm istekler User-Agent'ı “WordPress/version” olarak ayarlar, dolayısıyla bunun bizim sorunumuzla bir ilgisi olabilir. Ama olmadı… henüz.
WordPress'in kaynak kodunu incelemeye devam edersek, WP_Http istekleri yapmak için dahili olarak başka bir WordPress sınıfı kullandığını görürüz: Requests . Ve boy bu sınıf ilginç: tanımının en başında, değeri 1.7-3470169 olan VERSION adında bir sabit tanımladığını görüyoruz. Ve biraz sonra, günlüklerimizde bulduğumuz User-Agent'ı oluşturmak için bu sabiti kullanır: php-requests/1.7-3470169 .
Parlak! Aldığımız tüm bu tuhaf isteklerin WordPress sitemizden geldiğini doğruladık. Bu muhtemelen suçlunun bir eklenti olduğu anlamına gelir… ama hangisi?
Bunu çözmemiz gereken fikir oldukça basitti: User-Agent'ı , Requests sınıfını kullanan eklentinin adını içerecek şekilde değiştirirsek, sunucumuzun erişim günlüğünde eklentinin adını görürüz. Ve bunu başarmak aslında oldukça kolaydır. Tek yaptığımız, aşağıdaki kod parçasıyla Requests ' get_default_options işlevini düzenlemekti:
$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})";Adım adım ne yaptığını görelim:
- İlk önce
debug_backtraceile yürütme yığınını alıyoruz. Bu, geçerli olana ulaşmak için çağrılan tüm işlevleri ortaya çıkaran bir geri izleme oluşturan bir PHP işlevidir. - Yürütme yığınındaki her öğe için, çağrılan işlev, tanımlandığı dosya ve satır, çağrıldığı argümanlar vb. Gibi bilgilere sahibiz. Ancak odaklanmak istediğimiz şey, işlevin tanımlandığı dosya:
wp-content/plugins, bunun bir eklentide tanımlanmış bir işlev olduğundan eminiz. - Yığındaki tüm öğeleri işledikten sonra, bulduğumuz tüm eklentilerin adlarını (varsa) almamız ve bunları
useragentdeğişkenine eklememiz yeterlidir.
WordPress'i açıklandığı gibi genişlettikten sonra, suçlunun kim olduğunu hızla görmeye başladık:
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)" ...Sorunu çözmek
Soruna neden olan eklentinin adını öğrendikten sonra geliştiricisiyle iletişime geçip yardım istedik. Geri dönüş yaptılar ve uygun bir çözüm üzerinde çalışırken sorunun nasıl üstesinden gelineceğini anlattılar. Neyse ki bizim için işler çabucak düzeldi:

Gördüğünüz gibi, özgür yazılımla ilgili harika şeylerden biri, kullandığımız uygulamaların kaynak kodunu keşfedebilmemiz ve ihtiyaçlarımıza göre uyarlayabilmemizdir. Bu durumda, ihtiyaç duyduğumuz bazı bilgileri ortaya çıkarabilmesi için WordPress'in kendisini değiştirebildik.
Umarım belirli bir işlevi kimin çalıştırdığını bulmak için debug_backtrace kullanma hilesini beğenmişsinizdir. Elbette, ortodoks bir yöntem değil, ancak uygulanması hızlı ve şimdiye kadar her zaman son derece yararlı olduğu kanıtlandı.
Unsplash'ta Michal Mancewicz'in öne çıkan görseli.
