Nhật ký Sysadmin

TỐI ƯU WORDPRESS BỊ CHẬM

Vừa rồi tôi được giao nhiệm vụ kiểm tra và tối ưu System cho blog WordPress của doanh nghiệp trong lĩnh vực Internet ở Việt Nam với lượng truy cập tương đối lớn. Website gặp tình trạng thường bị load chậm hoặc quá tải khi chạy các chiến dịch marketing. Dù cho khách hàng có đội dev riêng rất giỏi, đã tối ưu rất tốt từ mặt coding đến database, caching

OK, Challenge Accepted, bắt tay vào việc thôi!

KIỂM TRA SƠ BỘ TRƯỚC KHI TỐI ƯU WORDPRESS

Trước tiên, cần đo lường thời gian xử lý trang index.php trước đã. Lưu lại để làm xong còn đối chiếu xem mình tối ưu wordpress có hiệu quả hay không. Áp dụng phương pháp tương tự đã trình bày trong bài: “Tôi đã tối ưu WordPress nhanh hơn 18 lần như thế nào”, tôi check xử lý PHP trực tiếp không đi qua Web Server bằng lệnh:

$ time php index.php

Thời gian xử lý dao động vào khoảng 30s-40s. Quá chậm, như này mà không xử lý được thì toang thật.

Kiểm tra tài nguyên server:

  • RAM: còn dư nhiều.
  • Load Average: Load cao hơn số core của VPS hiện có => đang bị quá tải (thiếu CPU)
  • IO ổ cứng thì ổn, đạt chuẩn tốc độ SSD.
  • VPS chạy mô hình Reverse Proxy, trong đó sử dụng Apache chạy mod_php để xử lý PHP. Lệnh “top” cho thấy process httpd thường xuyên chiếm 100% CPU.
  • Kết quả thống kê CPU của lệnh top cho thấy CPU được phân bổ vào %us (user) và %sy (system) => Có thể loại bỏ nguyên nhân chậm do IO (vì %iowait không cao) và các vấn đề khác liên quan interrupt (do %si thấp).

TỔNG QUAN

Sau 30s làm quen, nắm tình hình tổng quan hệ thống cần tối ưu wordpress của khách hàng như sau:

  • Hệ thống đang bị qúa tải CPU, chủ yếu là ở phần xử lý ở user space và các system call của hệ thống. Nhiệm vụ ta cần phải phân tích và tìm cách tối ưu chỗ này.
  • Hệ thống sử dụng mod_php (Apache2Handler), mỗi request đến file PHP sẽ được handle bởi 1 process httpd. Trường hợp nếu chạy chiến dịch marketing, traffic đổ về nhiều và CÙNG LÚC thì sẽ có nhiều process httpd được sinh ra => dẫn đến tình trạng hết RAM => server xử lý chậm => task dồn vào queue nhiều => load cao, delay cao.
  • Process httpd khi handle request của người dùng cắn 100% CPU và thời gian xử lý khá lâu. Đây có lẽ là nút thắt quan trọng nhất cần phải gỡ. Vì khi httpd handle request người dùng nhanh sẽ giúp hạn chế số lượng process httpd được sinh ra. Điều này giúp giảm lượng RAM, CPU sử dụng và giảm delay của người dùng. 

Như đã phân tích ở trên, nhiệm vụ của httpd hiện tại là load mod_php lên và xử lý các file PHP. Suy cho cùng cái chúng ta cần tập trung ở đây là xem xét cách server xử lý các file PHP như thế nào.

PHÂN TÍCH CÁCH SERVER XỬ LÝ PHP

Sau khi khoanh vùng, tôi tiến hành xem chi tiết cách hệ thống đang xử lý 1 file PHP. Kiểm tra mặt system call trước, chạy strace để thống kê sơ bộ khi xử lý 1 file PHP thì các system call được gọi như thế nào:

$ strace -c php index.php

Kết quả khá rõ ràng, system call gettimeofday được gọi đến 1,3 triệu lần và chiếm 96,96%. Đây rõ ràng là điểm quá bất thường. Vậy gettimeofday là gì, vì sao lại được gọi nhiều đến thế? Theo man page của Linux thì gettimeofday sẽ trả về: “number of seconds and microseconds since the Epoch”

Vậy system call này liên quan đến việc lấy thời gian hiện tại, số lần system call này được gọi rất lớn, đến 1,3tr lần … lẽ nào là do thằng W3 Total cache không nhỉ? Có lẽ nào W3 Total Cache gọi hàm này để check xem các file cache có hết hạn hay chưa để update file mới, site to thế này nên việc có nhiều file cache sẽ khiến system call này được gọi nhiều lần, hợp lý quá mà.

Nhảy ngay vào source code grep “gettimeofday” xác nhận lại xem sao. Kết qủa trả về chỉ có plugins “w3total_cache” sử dụng hàm này.

Hàm gettimeofday() được gọi trong hàm getmicrotime(). Và hàm getmicrotime() được gọi trong hàm debug()

Nếu debug Level > 0 thì hàm getmicrotime() sẽ được gọi, và qua đó gettimeofday() sẽ được gọi. Quá rõ ràng, game hôm nay dễ thế, end game thôi. Vào wp-admin tắt debug của w3total cache là xong!

KẾT QUẢ BẤT NGỜ

Những tưởng quá trình tối ưu WordPress hôm nay đến đây là kết thúc. Tuy nhiên khi báo khách hàng vào tắt debug của W3 Total Cache, tôi thử lại và kết quả vẫn như cũ. Tại sao lại như vậy nhỉ? Nghi ngờ khách hàng tắt chưa đúng, tôi xin account admin vào để tự tay mình tắt cho chắc chắn rồi thử lại. Kết quả vẫn như cũ: system call gettimeofday vẫn được gọi 1,3tr lần khi xử lý PHP, performance không được cải thiện và delay vẫn như cũ. Điên máu, tôi move luôn thư mục w3total cache ra khỏi plugins để tạm thời disable nó. Kết quả vẫn không thay đổi! Tại sao? 

Tôi chợt nhận ra, có vẻ mình đã bị lừa, hàm gettimeofday() trong plugin W3 Total Cache của PHP chưa chắc đã gọi system call gettimeofday của kernel. Tôi đã quá vội vàng và xử lý theo suy nghĩ cảm tính của mình trong khi chưa xem xét kỹ các bằng chứng kỹ thuật khác. Quay lại strace process PHP và xem xét kỹ kết quả, tôi chợt nhận ra:

$ strace -o output.txt php index.php

gettimeofday được gọi mỗi khi chuẩn bị mở file mới hoặc cấp phát một vùng nhớ mới cho process (system call open và brk). Đặc điểm này giống hệt với cách ghi debug log, kiên nhẫn xem kỹ hơn tôi thấy sự xuất hiện của một nhân tố mới: NewRelic.

NEWRELIC

NewRelic là một APM (Application Performance Monitoring), trong thường hợp hiện tại, NewRelic sẽ thọc sâu vào xử lý của PHP và đưa ra các chỉ số đo lường để chúng ta đánh giá, theo dõi ứng dụng PHP như: sự liên kết giữa các file, thời gian xử lý từng file … vậy nên việc gọi gettimeofday sẽ đóng vai trò quan trọng trong việc NewRelic gửi metrics về monitor server, vì các giải pháp monitoring sẽ dụng Timeseries Database với metric có dạng: “(time, key => value)”, trong đó time thường là dạng seconds since epoch, rất liên quan tới gettimeofday. Đây cũng là 1 điểm đáng phải cân nhắc!

Lần theo dấu vết của NewRelic, tôi phát hiện thêm điểm nghi vấn khác: Xdebug. Điểm này rất đáng để xem xét, vì theo những thông tin thu thập được, tôi khá chắc chắn rằng gettimeofday có liên quan mật thiết đến tính năng debug nào đó.

XDEBUG

Tìm thêm thông tin về Xdebug:

Xdebug sẽ “record” tất cả function call và variable assignment. Nghe có vẻ đúng như thứ ta đang tìm kiếm rồi. Tiếp tục tìm thêm thông tin:

Khá nhiều report liên quan đến việc Xdebug làm giảm performance cũng như liên quan đến việc gettimeofday được gọi quá nhiều lần. Tôi đã nhìn thấy hình ảnh hệ thống hiện tại trong đấy rồi :)). Tắt extension này và thử lại:

Bingo! gettimeofday hoàn toàn biến mất khỏi top 1. CPU usage khi xử lý PHP cũng giảm xuống đáng kể . Thời gian xử lý đã rút ngắn xuống chỉ còn vài giây.

CHƯA ĐƯỢC PHÉP DỪNG LẠI

Nút thắt quan trọng nhất trong nhiệm vụ tối ưu wordpress đã được gỡ. Tôi thở phào nhẹ nhõm và chuẩn bị tinh thần để thông báo tin vui đến khách. Tuy nhiên, kết quả trên vẫn chưa cho phép tôi dừng lại khi thấy strace thống kê “lstat”, “open” và “fstat” là những system call top đầu được gọi. Vì điều này cho thấy việc truy cập file của hệ thống chưa được tối ưu. Tình huống lúc này tương tự như bài “Tôi đã tối ưu WordPress nhanh hơn 18 lần như thế nào”

Kiểm tra nhanh thì thấy hệ thống đang sử dụng PHP 5.6 và chưa được triển khai Opcache. Tôi tiến hành nâng cấp lên PHP 7.2 kèm theo Opcache cho họ. Kết quả

Đến đây. thời gian xử lý site chỉ còn lại vỏn vẹn 0.5s. Tôi nghĩ mình có thể dừng lại tại đây được rồi!

Sau 1 hồi hì hục thì kết quả đạt được:

  • Giảm thời gian xử lý từ 40s xuống 0.5s
  • Giảm được “1,351,675” lệnh thừa khi xử lý PHP.
  • Hệ thống đã ổn định và tin cậy, tự tin phục vụ cho các chiến dịch marketing.

BÀI HỌC RÚT RA KHI TỐI ƯU WORDPRESS

  1. Khi hệ thống có vấn đề, điều quan trọng nhất là cùng nhìn về mục tiêu chung: giải quyết được vấn đề đang gặp. Hãy cố gắng hết sức trong khả năng của mình thay vì mang suy nghĩ: site chậm là do bên code, báo dev xem lại code. Nếu suy nghĩ đó xảy ra trong tình huống này, tôi khá chắc chắn là vấn đề sẽ không bao giờ được giải quyết triệt để, thay vào đó sẽ nghĩ đến phương án nâng cấp thêm CPU cho server hoặc triển khai giải pháp load balancing để phân tải.
  2. Kinh nghiệm, phán đoán là tốt, nhưng cần phải kết hợp với các bằng chứng kỹ thuật được thể hiện trên hệ thống. Đừng để kinh nghiệm đánh lừa mình, hãy nhìn vào bản chất sự việc và tìm hiểu xem việc gì đang xảy ra, mọi thứ đang diễn ra như thế nào.
  3. Tham gia vào Group Chat Telegram của nhóm để được chia sẻ và học hỏi từ các thành viên khác.