Hướng dẫn giám sát hiệu suất PHP-FPM với Prometheus: Theo dõi Active Processes, Idle Workers và Slow Requests để tối ưu Web Server

Monitoring tutorial - IT technology blog
Monitoring tutorial - IT technology blog

Hôm qua team mình nhận được ticket: “Website load chậm, khách hàng phàn nàn.” Mình SSH vào server, chạy ps aux | grep php-fpm, đếm tay từng process — mất gần 10 phút mới xác định được PHP-FPM đang cạn kiệt worker, request đang xếp hàng chờ. Nếu có dashboard giám sát sẵn, mình có thể phát hiện ra từ trước khi khách hàng nhận ra vấn đề.

Trước khi có monitoring, mình phải SSH vào từng server kiểm tra — bây giờ chỉ cần mở dashboard là thấy hết. Bài này mình sẽ chia sẻ cách thiết lập giám sát PHP-FPM bằng Prometheus, tập trung vào đúng 3 chỉ số hay gây vấn đề nhất: active processes, idle workers và slow requests.

Tại sao PHP-FPM cần được giám sát riêng?

Prometheus và Grafana giám sát được CPU, RAM, disk — nhưng đó là sức khỏe của server, không phải của ứng dụng PHP. PHP-FPM có thể đang nghẹt worker trong khi CPU vẫn nhàn hạ 20%. Hai thứ đó hoàn toàn độc lập.

PHP-FPM hoạt động theo mô hình process pool: mỗi request PHP được xử lý bởi một worker process. Pool có giới hạn số worker tối đa (pm.max_children). Khi tất cả worker đang bận, request mới phải đứng chờ trong queue. Nếu queue đầy, server trả về lỗi 502 hoặc 504.

Ba chỉ số cốt lõi cần theo dõi:

  • Active Processes: Số worker đang xử lý request tại thời điểm đó
  • Idle Workers: Số worker rảnh, sẵn sàng nhận request mới
  • Slow Requests: Request mất quá nhiều thời gian xử lý (theo ngưỡng request_slowlog_timeout)

Khi active processes tiệm cận pm.max_childrenidle workers về 0, đó là dấu hiệu PHP-FPM sắp nghẹt. Khi slow requests tăng đột biến, có thể do query database chậm hoặc external API timeout kéo dài.

Bật PHP-FPM Status Page

PHP-FPM có sẵn một endpoint trả về số liệu, chỉ cần bật lên. Mở file cấu hình pool (thường là /etc/php/8.x/fpm/pool.d/www.conf):

sudo nano /etc/php/8.2/fpm/pool.d/www.conf

Tìm và uncomment (hoặc thêm) các dòng sau:

; Bật status page
pm.status_path = /status

; Bật slow request log
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slow.log

Khởi động lại PHP-FPM:

sudo systemctl restart php8.2-fpm

Kiểm tra status page đang hoạt động (qua socket Unix):

sudo -u www-data php-fpm8.2 -d "error_log=/dev/null" 2>/dev/null
curl --unix-socket /run/php/php8.2-fpm.sock http://localhost/status

# Hoặc nếu dùng TCP port:
curl http://127.0.0.1:9000/status

Output trả về dạng text với các chỉ số như active processes, idle processes, slow requests

Nếu dùng Nginx, thêm location block để expose endpoint nội bộ:

server {
    listen 127.0.0.1:9001;  # Chỉ cho phép truy cập nội bộ

    location /status {
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}
sudo nginx -t && sudo systemctl reload nginx
curl http://127.0.0.1:9001/status

Cài đặt php-fpm_exporter

Prometheus không đọc được format text của PHP-FPM, cần có exporter để chuyển đổi. Mình dùng php-fpm_exporter của hipages (Go binary, nhẹ, không cần runtime).

# Tải về binary mới nhất
wget https://github.com/hipages/php-fpm_exporter/releases/download/v2.2.0/php-fpm_exporter_2.2.0_linux_amd64.tar.gz
tar xzf php-fpm_exporter_2.2.0_linux_amd64.tar.gz
sudo mv php-fpm_exporter /usr/local/bin/
sudo chmod +x /usr/local/bin/php-fpm_exporter

Tạo systemd service để chạy tự động:

sudo nano /etc/systemd/system/php-fpm-exporter.service
[Unit]
Description=PHP-FPM Exporter for Prometheus
After=network.target php8.2-fpm.service

[Service]
Type=simple
User=www-data
ExecStart=/usr/local/bin/php-fpm_exporter \
    --phpfpm.scrape-uri="tcp://127.0.0.1:9000/status" \
    --web.listen-address=":9253"
Restart=on-failure

[Install]
WantedBy=multi-user.target

Nếu PHP-FPM dùng Unix socket thay vì TCP:

ExecStart=/usr/local/bin/php-fpm_exporter \
    --phpfpm.scrape-uri="unix:///run/php/php8.2-fpm.sock;/status" \
    --web.listen-address=":9253"
sudo systemctl daemon-reload
sudo systemctl enable --now php-fpm-exporter

# Kiểm tra metrics đã export chưa
curl http://localhost:9253/metrics | grep phpfpm

Output sẽ thấy các metrics như:

phpfpm_active_processes 3
phpfpm_idle_processes 7
phpfpm_max_children_reached_total 0
phpfpm_slow_requests_total 12
phpfpm_listen_queue 0
phpfpm_max_listen_queue 5

Cấu hình Prometheus scrape PHP-FPM

Thêm job mới vào prometheus.yml:

scrape_configs:
  # ... các job hiện có ...

  - job_name: 'php-fpm'
    static_configs:
      - targets: ['localhost:9253']
        labels:
          server: 'web01'
          pool: 'www'
    scrape_interval: 15s

Reload Prometheus:

sudo systemctl reload prometheus

# Kiểm tra target đã up chưa
curl http://localhost:9090/api/v1/targets | python3 -m json.tool | grep -A5 php-fpm

Tạo Dashboard và Alert trên Grafana

Tạo dashboard mới, thêm các panel với PromQL queries:

Panel 1 — Active vs Idle Workers (Graph):

# Active processes
phpfpm_active_processes{job="php-fpm"}

# Idle processes
phpfpm_idle_processes{job="php-fpm"}

# Max children (giới hạn tối đa)
phpfpm_max_active_processes{job="php-fpm"}

Panel 2 — Slow Requests (Rate per minute):

rate(phpfpm_slow_requests_total{job="php-fpm"}[5m]) * 60

Panel 3 — Listen Queue (số request đang chờ):

phpfpm_listen_queue{job="php-fpm"}

Panel 4 — Worker Utilization % (quan trọng nhất):

phpfpm_active_processes / (phpfpm_active_processes + phpfpm_idle_processes) * 100

Khi con số này vượt 80% là cần chú ý, vượt 95% là PHP-FPM sắp nghẹt.

Cấu hình Alert Rule trong Grafana (hoặc Alertmanager) cho trường hợp queue bắt đầu tích lũy:

# alerting_rules.yml
groups:
  - name: php-fpm
    rules:
      - alert: PHPFPMHighWorkerUtilization
        expr: phpfpm_active_processes / (phpfpm_active_processes + phpfpm_idle_processes) * 100 > 85
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "PHP-FPM worker sắp hết trên {{ $labels.server }}"
          description: "Worker utilization đang ở {{ $value | printf \"%.1f\" }}%, nguy cơ request bị queue"

      - alert: PHPFPMListenQueueFull
        expr: phpfpm_listen_queue > 0
        for: 30s
        labels:
          severity: critical
        annotations:
          summary: "PHP-FPM queue đang tích lũy request chờ"
          description: "Có {{ $value }} request đang chờ worker trống trên {{ $labels.server }}"

Đọc số liệu thực tế như thế nào?

Một vài pattern mình hay gặp khi nhìn vào dashboard:

  • Active tăng đột biến rồi về bình thường: Traffic spike tạm thời, pool đủ lớn để xử lý — không cần lo.
  • Active duy trì cao, Idle gần 0 kéo dài: Pool không đủ worker. Tăng pm.max_children lên (nhưng phải tính RAM: mỗi PHP-FPM worker tốn ~30-50MB).
  • Slow requests tăng đột biến vào một khung giờ cố định: Có thể cron job nặng hoặc report query chạy định kỳ. Cần optimize query hoặc tách sang queue riêng.
  • Listen queue > 0: Đây là lúc người dùng đang cảm thấy website chậm. Cần xử lý ngay.

Mình thường set pm.max_children = RAM trống (MB) / 50. Ví dụ server còn 2GB RAM cho PHP-FPM thì đặt tối đa 40 workers.

Kết luận

Giám sát PHP-FPM với Prometheus không phức tạp — chỉ cần bật status page, chạy thêm một exporter nhỏ là có đầy đủ số liệu. Cái quan trọng là biết đọc con số: worker utilization cao liên tục mới đáng lo, còn spike ngắn là bình thường.

Khi đã có dashboard, mình có thể phân biệt được “website chậm do PHP-FPM không đủ worker” với “website chậm do database query chậm” — hai vấn đề có giải pháp hoàn toàn khác nhau. Không có monitoring thì đôi khi tăng worker lên mà vẫn chậm vì gốc rễ là ở chỗ khác.

Share: