Задать вопрос
@PendalF89

Как понять, почему падает PHP-FPM?

Раз в несколько суток на VPS заканчивается оперативка и падает PHP-FPM, притом почему-то сам не перезапускается, несмотря на sudo systemctl enable php8.3-fpm, помогает только ручной перезапуск. Я хочу понять, какой из сайтов является "виновником торжества" и на каком запросе происходит падение.

На VPS крутится 7 сайтов, со следующей посещаемостью:
Сайт 1: 100 человек в сутки
Сайт 2: 5000 человек в сутки
Сайт 3: 200 человек в сутки
Сайт 4: 3000 человек в сутки
Сайт 5: 6500 человек в сутки
Сайт 6: 50 человек в сутки
Сайт 7: 2500 человек в сутки

Все сайты написаны на Yii2 + MySQL, кроме одного, на котором 50 человек в сутки - он на WordPress.

Вот характеристики VPS на Timeweb.cloud, тариф "Cloud 30":
1 x 3.3 ГГц CPU
2 Гб RAM + SWAP 2GB
30 Гб NVMe
ДЦ SPB-3


Вот скриншот потребления ресурсов за последние сутки:
65e02983e9f99113403669.png

Характеристики PHP-FPM:
pm.max_children = 30
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20


Общий конфиг Nginx:
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 768;
    # multi_accept on;
}

http {

    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    types_hash_max_size 2048;
    server_tokens off;

    # server_names_hash_bucket_size 64;
    # server_name_in_redirect off;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ##
    # SSL Settings
    ##

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;

    ##
    # Logging Settings
    ##

    # access_log /var/log/nginx/access.log;
    # error_log /var/log/nginx/error.log;
    access_log off;
    error_log off;

    ##
    # Gzip Settings
    ##

    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_min_length 256;
    gzip_types
      application/atom+xml
      application/geo+json
      application/javascript
      application/x-javascript
      application/json
      application/ld+json
      application/manifest+json
      application/rdf+xml
      application/rss+xml
      application/xhtml+xml
      application/xml
      font/eot
      font/otf
      font/ttf
      image/svg+xml
      text/css
      text/javascript
      text/plain
      text/xml;

    ##
    # Virtual Host Configs
    ##

    include /etc/nginx/conf.d/*.conf;
}


Вот конфиг для одного из сайтов (остальные по аналогии):
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}
server {
    listen 80;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}
server {
    listen 443 ssl;
    http2 on;
    server_name www.example.com;
    
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    
    include ssl_params;
    
    return 301 https://example.com$request_uri;
}
server {
    listen 443 ssl;
    http2 on;
    server_name example.com;
    
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    
    include ssl_params;
    
    root   /var/www/example.com/frontend/web;
    index  index.php;
    error_log  /var/log/nginx/example.com.error.log;

    # Bots blocking
    include /etc/nginx/bot_block.conf;
    
    location ~* ^.+\.(rss|atom|jpg|jpeg|gif|png|ico|rtf|js|css|svg)$ {
        expires max;
    }

    # Защита директорий Antibot.cloud
    location ~ /antibot/(adm/|code/|data/|lang/) {
        deny all;
    }

    location / {
    rewrite ^([^.]*[^/])$ $1/ permanent;
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }
    
    location ~ /.well-known {
        allow all;
    }
}


Вот лог PHP-FPM:
[27-Feb-2024 08:35:12] NOTICE: fpm is running, pid 82470
[27-Feb-2024 08:35:12] NOTICE: ready to handle connections
[27-Feb-2024 08:35:12] NOTICE: systemd monitor interval set to 10000ms
[28-Feb-2024 07:27:00] NOTICE: Terminating ...
[28-Feb-2024 07:27:02] NOTICE: exiting, bye-bye!
[28-Feb-2024 07:27:03] NOTICE: fpm is running, pid 114928
[28-Feb-2024 07:27:03] NOTICE: ready to handle connections
[28-Feb-2024 07:27:03] NOTICE: systemd monitor interval set to 10000ms
[29-Feb-2024 01:02:06] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 8 children, there are 0 idle, and 21 total children
[29-Feb-2024 01:02:07] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 16 children, there are 0 idle, and 26 total children
[29-Feb-2024 01:02:08] WARNING: [pool www] server reached pm.max_children setting (30), consider raising it
[29-Feb-2024 01:02:29] WARNING: [pool www] child 114932 exited on signal 9 (SIGKILL) after 63326.182841 seconds from start
[29-Feb-2024 01:02:29] NOTICE: [pool www] child 143523 started
[29-Feb-2024 01:02:31] WARNING: [pool www] child 114935 exited on signal 9 (SIGKILL) after 63328.149700 seconds from start
[29-Feb-2024 01:02:31] NOTICE: [pool www] child 143524 started
[29-Feb-2024 02:33:35] WARNING: [pool www] server reached pm.max_children setting (30), consider raising it
[29-Feb-2024 06:08:37] NOTICE: Terminating ...
[29-Feb-2024 06:08:38] NOTICE: exiting, bye-bye!
[29-Feb-2024 06:09:01] NOTICE: fpm is running, pid 653
[29-Feb-2024 06:09:01] NOTICE: ready to handle connections
[29-Feb-2024 06:09:01] NOTICE: systemd monitor interval set to 10000ms
[29-Feb-2024 06:09:06] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 8 children, there are 4 idle, and 15 total children
[29-Feb-2024 06:09:07] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 16 children, there are 4 idle, and 16 total children
[29-Feb-2024 06:09:08] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 32 children, there are 3 idle, and 17 total children
[29-Feb-2024 07:49:06] NOTICE: Terminating ...
[29-Feb-2024 07:49:06] NOTICE: exiting, bye-bye!


Сейчас добавил в конфиг PHP-FPM отслеживание slowlog, поставил на 10 секунд. Что ещё можно сделать?

UPD 1.
Добавил pm.max_requests = 1000

UPD 2. 2024-03-17
Перешёл на более дорогой тариф:
Было:
Cloud 30
1 x 3.3 ГГц CPU • 2 Гб RAM • 30 Гб NVMe
Стало:
Cloud 50
2 x 3.3 ГГц CPU • 4 Гб RAM • 50 Гб NVMe

Падения по исчерпанию памяти продолжились.
Сделал отдельный PHP-FPM pool для каждого сайта, в результате для каждого сайта получился вот такой отдельный конфиг:
[example.com]

user = www-data
group = www-data

listen = /run/php/php8.3-fpm-$pool.sock
listen.owner = www-data
listen.group = www-data

pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 2
pm.max_requests = 1000

slowlog = /var/log/php-$pool.log.slow
request_slowlog_timeout = 10s


Не помогло. Всё жрёт и жрёт память.
65f68bca9889b124898721.png

Вот скриншот htop
65f68cd8d9b1e832753673.png

Я думал, что дело может быть в MySQL, но как видно на скриншоте, даже если сайт не использует БД, то памяти он жрёт неприлично много.

Такое ощущение, что pm.max_requests не влияет на сброс памяти у процессов PHP-FPM. Хотя по идее ведь процессы перезапускаются, соответственно и памяти после перезапуска должны потреблять меньше в случае возможных утечек.
  • Вопрос задан
  • 733 просмотра
Подписаться 1 Средний Комментировать
Решения вопроса 1
@PendalF89 Автор вопроса
Кажется, я нашёл временное решение. Наблюдая за htop, я заметил, что pm.max_requests перезапускает php-fpm процесс и как только процесс перезапустится, памяти он потребляет примерно 10MB, но через несколько секунд - сразу 180MB. Понятия не имею почему так происходит, но предполагаю, что дело в master-процессе. Значит - надо перезапускать master-процесс. Вот как это сделать в "лайтовом" режиме, без downtime, чтобы текущие запросы были обработаны, и только после их обработки произошёл перезапуск:
kill -USR2 $(cat /var/run/php/php8.3-fpm.pid)

Вот результат:
65f6aa19ca4e9260440179.png

Можно поставить задание в CRON. Я поставил на 3 утра каждые сутки:
0 3 * * * kill -USR2 $(cat /var/run/php/php8.3-fpm.pid)
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 4
yesbro
@yesbro
Думаю, помогаю думать
Можно для каждого сайта сделать свой pool. Тогда сразу будет понятно кто виновник.
Ответ написан
idmrty
@idmrty
Попробуйте для начала добавить pm.max_requests = 2000
Ответ написан
ThunderCat
@ThunderCat Куратор тега PHP
{PHP, MySql, HTML, JS, CSS} developer
А у вас случайно бэкап сервера не настроен? Настораживает динамика потребления оперативки, но чтение-запись прям тонко намекают на создание большого файла из кучи маленьких...
Ответ написан
SilenceOfWinter
@SilenceOfWinter Куратор тега PHP
та еще зажигалка...
по моему опыту, дело в поисковых ботах, которые имеют обыкновение создавать нагрузку единовременно.
логирование запросов есть как для субд так и для http серверов.
опять же никто не мешает повесить обработчики фиксирующие нагрузки или просто подрубить что-то вроде grafana
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы