Пару лет назад пробовал подружить php, otel collector с плагинов cumulative и prometheus. Помню что было не легко найти информацию как в коллектор подключаются плагины. А ещё cumulative как-то неправильно работал с метриками.
Да, меня тоже интересуют цифры: rps, latency, количество sku, количество и параметры машин эластика, количество шардов и реплик индекса.
Я тоже работаю с эластиком в качестве хранилища денормализованых данных каталога товаров. Время поиска конечно относительно неплохое, но не могу сказать что сопоставимо с редисом.
Это же не гайд в котором говорится, что так нужно делать. Имея систему с битриксом в составе вы сами решаете что для вас ценность и что для вас проблема.
В статье написано, что заказчик уже контейнеризировал битрикс, так что дальнейшие работы от подрядчика не создали тех проблем о которых вы говорите, если это вообще считать проблемой.
Скорее всего нет. Но когда проект дорастает до большой нагрузки и сильной кастомизации этого самого битрикса, самообновление начинает восприниматься наоборот как фактор угрозы. Обновление должно быть протестировано, в собственном коде должны быть сделаны соотвествующие правки, деплой должен быть выполнен через ci/cd, с возможностью отката.
Мне посчастливилось с этим столкнуться. Видимо на том проекте огромный трафик. Rolling update deployment'a занимает некоторое время и в течение нескольких минут запросы на домен сайта могут случайным о разом попадать как в новые поды, так и в старые.
Для решения проблемы сделал так:
Создал persistent volume для хранения ассетов, причём внутри делаю две папки - old и new
Перед обновлением подов, удаляю содержимое папки old, копирую все из new в old, удаляю все из new, копирую ассеты из нового образа в new
Перед приложением стоит nginx, который раздаёт файлы ассетов хитрым образом: сначала файл ищется в образе, если там нет, то в папке new, а если и там нет - то в папке old
Таким о разом получается не важно какая версия клиента в какой pod послала запрос - нужный файл так или иначе будет найден.
В именах файлов есть хэш от их содержимого, так что можно не волноваться что случайно подтянется файл не той версии.
Неплохо. А как решаете проблему того, что в момент релиза новой версии клиент может начать подгружать чанк для старой версии, и запрос на скачивание файла случайно пападает в новый pod (допустим мы деплоимся в кубер)?
Без схемы индекса (маппинга) эластик бесполезен. Просто нечёткий поиск можно и другими средствами сделать, дешевле.
Кроме того ваш код не рассчитан на большое количество данных: нельзя вешать индексацию на observer, потому что это не позволяет делать batch запросы на индексацию.
А в команде переиндексации не стоить делать Model::all(), потому что вам может не хватить памяти, нужно получать записи из бд постранично (чанками) , и каждый чанк отправлять в эластик batch запросом.
Если есть возможность (а в go она есть) метрики лучше отдавать на отдельном порту. Иначе есть опасность случайно сделать их доступным в интернете, а потом придумывать как их закрыть.
Завидую фронтендерам. На бэке, чтобы получить flame-graph нужно совершить некоторое количество нетривиальных телодвижений, а у вас просто нажимаешь F12 и получаешь всё что нужно.
Странные показатели. Пустой эндпоинт на octane+swoole отрабатывает за 2мс. C RR не работаю, один раз тестировал - цифра была похожая.
И да, пустой эндпоинт может работать на скорости 1к рпс.
Возможно у вас добавляется систематическая погрешность из-за того что клиент и сервер в разных странах.
Стоит ещё проверить параметры хостинга, а точнее точно ли вам выделено 100% CPU.
Чтобы сравнение было честным, стоит наверное явно задать количество потоков сервера. А чтобы оно было не абстрактными попугаями, а близким к реальности - включать opcache для cli.
Ну и в идеале, конечно, результаты тестирования лучше смотреть в динамике. Итоговая сводка в 80мс в теории может складываться как среднее из 10мс и 900мс. Тут как минимум нужно указывать min, max, avg, а лучше графики.
И нет. При наличии логики поведение будет отличаться очень сильно. Типичная для php сайтов io-bound нагрузка легко может увести разницу между обсуждаемыми технологиями в разряд погрешности, ато и вовсе "сравнять" их по применимости с php-fpm.
пхп лучше запускать от юзера 1000:1000 ведь не все разрабатывают на ликунсе, многие на винде + wsl
Я обычно решаю это так: разработчики клонируют репозиторий с конфигами для локального разворота, и там в конфигах есть докерфайл для сборки dev образа, куда через аргумент передаётся uid/gid пользователя которого надо создать в образе, чтобы далее при монтировании папки с исходниками, независимо от того где создаётся файл - на хосте или в контейнере, у него был один и тот же uid владельца.
Один из немногих, но существенный недостаток php - это его синхронность. Отправка логов/трейсов по сети снижает производительность приложения и создаёт опасность зависания, если принимающая сторона ляжет. Запись в файл выступает неблокирующим буфером.
Тут конечно можно порассуждать, что сайдкар vector врядли ляжет, а если и ляжет то это проблема всего пода. Можно ещё подумать что запись на диск не сильно медленее чем взаимодействие с вектором через локалхост. Да. Но у меня осадочек остался, а попытаться ещё раз пока руки не дошли.
Я обычно решаю проблему раздачи статики выделяя общий volume между контейнерами app и nginx, в которой app, при старте копирует то что должен раздавать nginx - js, css и прочие файлы изменяющиеся только при деплое новой версии приложения.
Кстати ещё нужные отдельные volume для storage/app и storage/logs
Спасибо что поделились, всегда интересно смотреть как это устроено у других. Удобно конечно когда весь проект в одном репозитории, хотя и возникают вопросики - а что делать когда придётся использовать второй/третий репозитории? Проекты имеют обыкновение расти и распиливаться на микросервисы. Либо волевым решением постановить что будет монорепо, либо выносить конфигурацию запуска в отдельный репозиторий.
Я выбрал второй вариант. Я работаю с проектами, где в каждом несколько десятков репозиториев с кодом - приложения и библиотеки. Ну и им необходим десяток сервисов - база, редис и т.д.
Чтобы это не превратилось к гигантский монолитный docker-compose.yml файл, созданы отдельные файлы под каждый компонент. Ну а чтобы облегчить типовые операции над приложениями написана утилита elc. А все конфиги необходимые для запуска приложений и сервисов проекта вынесены в репозиторий
Главная задача экономики заключается в создании товаров, которые экономят время потребителя и обеспечивают качество, превосходящее результаты его самостоятельного производства.
Сомнительные плюсы. Никто не запускает веб-приложения на php как однопоточный синхронный скрипт. Подавляющее большинство проектов используют php-fpm, кто-то использует асинхронные фреймворки вроде AmPHP или ReactPHP.
Механизм graceful shutdown.Если нам надо провести новую инициализацию (параметры подключения изменились, например) – есть возможность плавного перезапуска воркеров только после завершения текущих операций.
В узком смысле любой нормальный сервер умеет в graceful shutdown - и nginx и php-fpm не бросают обрабатываемый запрос при перечитывании конфигурации.
В широком смысле, такая фича на уровне приложения не очень-то и нужна, когда всё крутится в кубере и уже он обеспечивает плавное обновление подов.
Легкое горизонтальное масштабирование.При необходимости RoadRunner способен запустить дополнительные воркеры, обеспечивая тем самым горизонтальную масштабируемость.
Старый добрый php-fpm тоже может запускать дополнительные процессы.
Настоящая многопоточность. RoadRunner эффективно использует все предоставленные ядра процессора, тем самым повышая утилизацию текущих ресурсов.
Опять же, php-fpm запускает множество процессов, тем самым утилизируя больше одного ядра. А с точки зрения многопоточности в контексте приложения RR ничего нового не даёт - приложение продолжает синхронно работать в одном потоке. Просто как и в случае с php-fpm, у нас фактически запускается несколько практически не связанных друг с другом процессов.
Шестой пункт неправильно написан.
Пару лет назад пробовал подружить php, otel collector с плагинов cumulative и prometheus. Помню что было не легко найти информацию как в коллектор подключаются плагины. А ещё cumulative как-то неправильно работал с метриками.
40млн SKU. 50 RPS на поиск товаров.
Время ответа в перцентилях:
- 50 - 20мс
- 75 - 40мс
- 95 - 120мс
Кластер эластика: 3 мастер ноды (8cpu x 16GB), 4 дата ноды (24cpu x 48GB).
Индекс размером 200Gb, разделён на 8 шардов + по одной реплике.
Да, меня тоже интересуют цифры: rps, latency, количество sku, количество и параметры машин эластика, количество шардов и реплик индекса.
Я тоже работаю с эластиком в качестве хранилища денормализованых данных каталога товаров. Время поиска конечно относительно неплохое, но не могу сказать что сопоставимо с редисом.
Это же не гайд в котором говорится, что так нужно делать. Имея систему с битриксом в составе вы сами решаете что для вас ценность и что для вас проблема.
В статье написано, что заказчик уже контейнеризировал битрикс, так что дальнейшие работы от подрядчика не создали тех проблем о которых вы говорите, если это вообще считать проблемой.
Скорее всего нет. Но когда проект дорастает до большой нагрузки и сильной кастомизации этого самого битрикса, самообновление начинает восприниматься наоборот как фактор угрозы. Обновление должно быть протестировано, в собственном коде должны быть сделаны соотвествующие правки, деплой должен быть выполнен через ci/cd, с возможностью отката.
Мне посчастливилось с этим столкнуться. Видимо на том проекте огромный трафик. Rolling update deployment'a занимает некоторое время и в течение нескольких минут запросы на домен сайта могут случайным о разом попадать как в новые поды, так и в старые.
Для решения проблемы сделал так:
Создал persistent volume для хранения ассетов, причём внутри делаю две папки - old и new
Перед обновлением подов, удаляю содержимое папки old, копирую все из new в old, удаляю все из new, копирую ассеты из нового образа в new
Перед приложением стоит nginx, который раздаёт файлы ассетов хитрым образом: сначала файл ищется в образе, если там нет, то в папке new, а если и там нет - то в папке old
Таким о разом получается не важно какая версия клиента в какой pod послала запрос - нужный файл так или иначе будет найден.
В именах файлов есть хэш от их содержимого, так что можно не волноваться что случайно подтянется файл не той версии.
Неплохо. А как решаете проблему того, что в момент релиза новой версии клиент может начать подгружать чанк для старой версии, и запрос на скачивание файла случайно пападает в новый pod (допустим мы деплоимся в кубер)?
Без схемы индекса (маппинга) эластик бесполезен. Просто нечёткий поиск можно и другими средствами сделать, дешевле.
Кроме того ваш код не рассчитан на большое количество данных: нельзя вешать индексацию на observer, потому что это не позволяет делать batch запросы на индексацию.
А в команде переиндексации не стоить делать Model::all(), потому что вам может не хватить памяти, нужно получать записи из бд постранично (чанками) , и каждый чанк отправлять в эластик batch запросом.
Если есть возможность (а в go она есть) метрики лучше отдавать на отдельном порту. Иначе есть опасность случайно сделать их доступным в интернете, а потом придумывать как их закрыть.
Завидую фронтендерам. На бэке, чтобы получить flame-graph нужно совершить некоторое количество нетривиальных телодвижений, а у вас просто нажимаешь F12 и получаешь всё что нужно.
Странные показатели. Пустой эндпоинт на octane+swoole отрабатывает за 2мс. C RR не работаю, один раз тестировал - цифра была похожая.
И да, пустой эндпоинт может работать на скорости 1к рпс.
Возможно у вас добавляется систематическая погрешность из-за того что клиент и сервер в разных странах.
Стоит ещё проверить параметры хостинга, а точнее точно ли вам выделено 100% CPU.
Чтобы сравнение было честным, стоит наверное явно задать количество потоков сервера.
А чтобы оно было не абстрактными попугаями, а близким к реальности - включать opcache для cli.
Ну и в идеале, конечно, результаты тестирования лучше смотреть в динамике. Итоговая сводка в 80мс в теории может складываться как среднее из 10мс и 900мс. Тут как минимум нужно указывать min, max, avg, а лучше графики.
И нет. При наличии логики поведение будет отличаться очень сильно. Типичная для php сайтов io-bound нагрузка легко может увести разницу между обсуждаемыми технологиями в разряд погрешности, ато и вовсе "сравнять" их по применимости с php-fpm.
пхп лучше запускать от юзера 1000:1000 ведь не все разрабатывают на ликунсе, многие на винде + wsl
Я обычно решаю это так: разработчики клонируют репозиторий с конфигами для локального разворота, и там в конфигах есть докерфайл для сборки dev образа, куда через аргумент передаётся uid/gid пользователя которого надо создать в образе, чтобы далее при монтировании папки с исходниками, независимо от того где создаётся файл - на хосте или в контейнере, у него был один и тот же uid владельца.
Один из немногих, но существенный недостаток php - это его синхронность. Отправка логов/трейсов по сети снижает производительность приложения и создаёт опасность зависания, если принимающая сторона ляжет. Запись в файл выступает неблокирующим буфером.
Тут конечно можно порассуждать, что сайдкар vector врядли ляжет, а если и ляжет то это проблема всего пода. Можно ещё подумать что запись на диск не сильно медленее чем взаимодействие с вектором через локалхост. Да. Но у меня осадочек остался, а попытаться ещё раз пока руки не дошли.
Storage отдельно, потому что контентные файлы обычно хранятся в отдельном хранилище, например NFS или Ceph. Или S3, но тогда уже volume не нужен.
Volume для logs чтобы расшарить его с контейнером сборщика логов.
А кэши в memcached или в redis
Я обычно решаю проблему раздачи статики выделяя общий volume между контейнерами app и nginx, в которой app, при старте копирует то что должен раздавать nginx - js, css и прочие файлы изменяющиеся только при деплое новой версии приложения.
Кстати ещё нужные отдельные volume для storage/app и storage/logs
Спасибо что поделились, всегда интересно смотреть как это устроено у других.
Удобно конечно когда весь проект в одном репозитории, хотя и возникают вопросики - а что делать когда придётся использовать второй/третий репозитории? Проекты имеют обыкновение расти и распиливаться на микросервисы. Либо волевым решением постановить что будет монорепо, либо выносить конфигурацию запуска в отдельный репозиторий.
Я выбрал второй вариант. Я работаю с проектами, где в каждом несколько десятков репозиториев с кодом - приложения и библиотеки. Ну и им необходим десяток сервисов - база, редис и т.д.
Чтобы это не превратилось к гигантский монолитный docker-compose.yml файл, созданы отдельные файлы под каждый компонент. Ну а чтобы облегчить типовые операции над приложениями написана утилита elc. А все конфиги необходимые для запуска приложений и сервисов проекта вынесены в репозиторий
Спорное утверждение.
Сомнительные плюсы. Никто не запускает веб-приложения на php как однопоточный синхронный скрипт. Подавляющее большинство проектов используют php-fpm, кто-то использует асинхронные фреймворки вроде AmPHP или ReactPHP.
В узком смысле любой нормальный сервер умеет в graceful shutdown - и nginx и php-fpm не бросают обрабатываемый запрос при перечитывании конфигурации.
В широком смысле, такая фича на уровне приложения не очень-то и нужна, когда всё крутится в кубере и уже он обеспечивает плавное обновление подов.
Старый добрый php-fpm тоже может запускать дополнительные процессы.
Опять же, php-fpm запускает множество процессов, тем самым утилизируя больше одного ядра.
А с точки зрения многопоточности в контексте приложения RR ничего нового не даёт - приложение продолжает синхронно работать в одном потоке. Просто как и в случае с php-fpm, у нас фактически запускается несколько практически не связанных друг с другом процессов.