Когда-то, в далёкие времена ковида и окончательного огосударствления ВК, посетила меня идея поднять свой бложик на Aegea. Поднимал его я на актуальном тогда Proxmox 5, ещё ого-го как живом CentOS 7 в LXC. Внутри была, страшно сказать, MariaDB 5 и несвежий апачик. Утекло с тех лет много воды: контейнер несколько раз менял хостовый Proxmox, но внутрянку я не трогал.
И тут зачесались руки привести историческую инфраструктуру в CI-ный вид: плейбук там, плейбук тут.
База — Debian 13, обновление с миграцией на свежую версию движка, потому что с 2020 года Aegea я обновлял всего один раз.
Вообще, это заметка должна была быть одой LLM и нытьём на тему того, что нейросети заменят админодевопсов нафиг.
И, в общем и целом, я был в восторге. Написав некоторую длинную портянку текста о том, что, как и почему, я получил длинный плейбук, который, без шуток, выполнился с первого захода. И это с учётом того, что исходный сервер настраивал админ с сильно меньшим пониманием жизни и профессии.
Отрефлексировать я это отрефлексировал тем, что ТЗ было составлено с кучей ограничений, которые человек без опыта ковыряния во всём этом просто не сформулировал бы. Машине скормили хороший запрос — машина выдала хороший результат. Плейбук был запущен, и через пару минут я получил рабочий блог.
Но рефлексия не удалась.
При попытке загрузить картинку в новый пост ловлю: «не удалось зарегистрировать загруженный файл».
В nginx access log:
POST /@ajax/file-upload/?entity=note&entity-id=247 HTTP/1.1" 200При этом в /var/www/blog/pictures появлялись временные файлы:
-rw------- 1 www-data www-data 0 upload-XXXXXXОчевидно: место, права, лимиты на аплоад php, просто загрузку — проверил. PHP корректно получал файл:
Движок в фришной версии обфусцирован, разбираться в коде — неблагодарное дело — мы холопоадмины, наше дело логи читать. Достаём BFG.
strace -ff $(ps aux | awk '/php-fpm: pool www/ && !/awk/ {printf "-p %s ", $2}') -o /tmp/egeya-upload.straceПроблема раз:
rename("/tmp/phpXXXX", "/var/www/blog/pictures/upload-XXXX") = -1 EXDEVОшибка:
EXDEV (Invalid cross-device link)означает попытку `rename()` между разными файловыми системами.
Смотрю на вывод df -T в исходной системе, и в target — и понимаю, что Debian (не)смог. /tmp теперь не реальный каталог, а временная фс в памяти tmpsfs
Схема проблемы:
/tmp
|
| tmpfs
|
X rename()
|
/var/www/blog/pictures
|
| root filesystem
PHP создавал временный upload-файл в /tmp.
Эгея использовала move_uploaded_file(), который внутри PHP может выполнить быстрый rename().
Но rename(), как оказалось, между разными файловыми системами невозможен.
Бахнул отдельный временный каталог на той же файловой системе, где и бложик.
mkdir -p /var/www/blog/tmp
chown www-data:www-data /var/www/blog/tmp
chmod 700 /var/www/blog/tmpВ PHP-FPM :
upload_tmp_dir = /var/www/blog/tmpПосле перезапуска:
systemctl restart php8.2-fpmцепочка стала:
/var/www/blog/tmp/phpXXXX
|
| rename()
v
/var/www/blog/pictures/upload-XXXX
Ошибка EXDEV исчезла — но аплоад не заработал.
Достаём BFG второй раз.
strace -ff $(ps aux | awk '/php-fpm: pool www/ && !/awk/ {printf "-p %s ", $2}') -o /tmp/egeya-upload.straceВидим, что Эгея хочет поделиться тайнами мироздания в лог, но не может.
touch /var/www/blog/user/logs/main.logПовторяем аплоад:
Process image: Done
Register upload: <, , add, Array>
Ajax file upload: couldn’t register uploadЯвно не фешный, не хватает параметров. Смотрим консоль отладки. Должны быть entity и entity-id
Смотрим в nginx. Конфиг нагенерила LLM по мотивам того, что было в сети — и он был рабочий...формально.
location / {
log_not_found off;
try_files $uri $uri/ /index.php?go=$uri;
}Запрос /@ajax/file-upload/?entity=note&entity-id=247 после try_files превращался в /index.php?go=/@ajax/file-upload/. Параметры говорили «Мұнда параметрлер жоқ қой, иттер»
Меняем чудо-LLM на рабочее:
location / {
log_not_found off;
rewrite ^(.*)$ /index.php?go=$1 last;
}Смысл изменения:
try_files при внутреннем перенаправлении формирует новый URI и может потерять исходную строку параметров.
rewrite ... last выполняет внутренний rewrite с сохранением исходных GET-параметров. Рестартуем Nginx.
Бинго!