Bind-mount, который выключил OPcache
Сервис php монтировал apps/. Каждый запрос делал stat() дерева через virtiofs. Hits в OPcache были шумом.
Локальный TTFB каталога был 80 мс. Тот же образ на VPS, те же данные, держался 40 мс, пока я не скопировал Compose и не примонтировал apps/ ради хотфикса. TTFB прыгнул к 400 мс. CPU в контейнере php был user time на stat, не на SQL. opcache_get_status() показывал тысячи misses и десятки hits.
opcache.validate_timestamps был 1, revalidate_freq 2. Для ноутбука так и надо. На virtiofs каждый include проверяет mtime через VM. LaraBoom плюс host App это много файлов. Воркер платил этот налог на каждый запрос, потом уже шёл в листинг.
Проблема была в bind-mount, который превратил OPcache в цикл stat. Посетители ждали 400 мс HTML, который раньше занимал 40. Нужен COPY в образе, validate_timestamps 0 и пересборка на правки PHP.
Два Compose
Публичный сервис php копирует vendor, packages/laraboom и apps/ в образ. validate_timestamps 0. opcache.preload грузит фреймворк и host App. Правка PHP это новый образ. Сервис vue по-прежнему биндит frontend/, потому что SSR это Node, и Vue мы правим там.
- Dev compose оставляет bind-mount и timestamps. Для локальной правки так и задумано.
- opcache.file_cache на томе выглядел умно. Он всё равно делает stat() исходников, если timestamps включены.
- nginx и php делят только том с unix-сокетом, не дерево PHP.
TTFB на VPS вернулся к числу без mount. Хотфикс это пересборка образа php. Это медленнее bind. Это и есть процесс, в котором OPcache делает свою работу.
Какой опыт из этого
opcache_get_status() hits против misses рассказывает быстрее профайлера. Тысячи misses на прогретом воркере это timestamps плюс mount.
Dev и prod Compose должны расходиться по дереву PHP. Скопировать ноутбучный mount на VPS это как 40 мс становятся 400.
Хотфикс PHP это пересборка образа. Это цена OPcache, который реально кэширует.
