Сравнение инструментов для генерации thumbnails: imgproxy, thumbor, imaginary, picfit, imageproxy и weserv/images

от автора

Предыстория

Жил был один новостной проект. Время шло, одни фичи добавлялись, вторые удалялись… Одной из важнейших фишек была генерация превьюшек к картинкам (thumbnails), а именно — быстрая генерация (до 5 минут) всех thumbnails. Все было хорошо, пока не начали поступать жалобы, что, иногда, генерация не успевает за 5 минут все сделать. Начали «копать» и обнаружили интересную вещь: мы генерим 112 превьюшек к одной картинке. Нашей «радости» не было предела. После небольших дискуссий было решено увеличить maxReplicas до 60 в HPA (проблема возникала когда загружалось больше 80 картинок), так как это самое быстрое и дешевое решение.

Наши дни

Пришёл новый проект и ему потребовались thumbnails. Сервер немного опережал фронт, поэтому у нас было время на R&D.

Первым был найден imgproxy, а потом уже все остальные (см заголовок). Так же нам на глаза попалась следующая статья. Она, конечно, очень смахивала на заказную (что в результатах и будет видно), но все равно очень интересно.

Перед тем как приступить к тестированию, нужно было определиться, что мы хотим в итоге:

  • приемлемый RPS (от 10)

  • возможность работы с S3 (Minio)

  • кеширование сгенерированных превьюшек

  • защита от URL tampering

Краткий обзор каждого инструмента

Imgproxy

Github (5k starts, latest release — 2021-09-07)

Плюсы:

  • самая «живая» из всех (на момент написания статьи — последний релиз 8 дней назад)

  • подробная документация

  • большой список возможностей (имеется PRO версия со своими фичами)

  • есть защита от URL tampering (есть примеры генерации сигнатур для разных ЯП — https://github.com/imgproxy/imgproxy/tree/master/examples)

  • может «читать» оригиналы картинок не только по http, а и по S3, Google Cloud Storage и Azure Blob Storage

  • есть готовый helm chart

Минусы:

  • нет готового механизма кеширования (это принципиальная позиция: imgproxy does one thing — resizing remote images — and does it well. It works great when you need to resize multiple images on the fly to make them match your application design without preparing a ton of cached resized images or re-doing it every time the design changes.)

thumbor

Github (8.6k starts, latest release — 2020-02-27)

Плюсы:

  • больше всего звезд на гитхабе

  • есть разные плагины

  • есть защита от URL tampering

  • есть встроенный механизм кеширования

Минусы:

  • может «читать» с S3, но как выяснилось — плагин для S3 написан на Python 2.7, а мы выбрали Release 7.0.0a5 который использует Python 3. В итоге эти штуки не взлетели вместе

  • единственное кеширование, которое работает — file_storage, что не есть хорошо, если говорить в контексте k8s

imaginary

Github (3.8k starts, latest release — 2020-08-12)

Плюсы:

  • есть защита от URL tampering

  • очень прост в настройке

  • есть интересная фишка: запросы GET | POST на /info — «Returns the image metadata as JSON»

Минусы:

  • нет S3

  • нет кеширования

Отдельное слово хочу добавить про генерацию сигнатуры (защита от URL tampering). Все что есть в документации: Enable URL signature (URL-safe Base64-encoded HMAC digest). Хорошо если ты пишешь на Go, можно зайти в исходники и найти это дело: https://github.com/h2non/imaginary/blob/master/middleware.go#L173

У нас же был PHP…

Пришлось потратить много времени, что бы отыскать это.

Если вкратце, то параметры в URL должны быть в определенном порядке (отсортированы по abc): height=240&url=XXX&width=320&sign=YYY. Если вы написали следующее: width=320?height=240&url=XXX&sign=YYY, то сигнатура в этом случае не совпадет и вместо картинки вы увидите текст об ошибке.

picfit

Github (1.5k starts, latest release — 2020-09-11)

Плюсы:

  • есть защита от URL tampering

  • прост в настройке (все настройки в json файле)

  • есть кеширование в Amazon S3, DigitalOcean S3, Google Cloud Storage.

Минусы:

  • нет готового докер образа в docker hub (но в репе есть Dockerfile)

  • как оказалось позже, кеширование работает напрямую с Amazon (те. нельзя сделать s3_endpoint=http://minio:9000). Кеширование на файлах есть, но нам не подходит

imageproxy

Github (2.6k starts, latest release — 2020-04-03)

Плюсы:

  • есть защита от URL tampering

  • есть кеширование в S3 и много куда еще (и даже in-memory LRU cache)

Минусы:

  • нет механизма upscale (если у вас картинка 1920×1080, то вы никак не сможете получить 3840×2160). Upd: @garfik подсказал параметр scaleUp, после его добавления — все взлетело.

weserv/images

Github (908 starts, latest release — 2021-09-07)

Плюсы:

  • простой и быстрый

  • есть встроенное кеширование на уровне nginx proxy

Минусы:

  • нет защиты от URL tampering

  • нет S3

Тестирование

Был собран docker-compose стенд (там можно найти все исходники), все что могло работать с кешом — было запущено в отдельных контейнерах:

  • thumbor настроен с файловым кешом

  • picfit настроен с файловым кешом

  • imageproxy настроен с in-memory кешом

Полный docker-compose.yaml:

version: '3.8'  services:   imgproxy:     image: darthsim/imgproxy:v2.17     ports:       - 8080:8080     restart: always     deploy:       resources:         limits:           cpus: 1     environment:       - IMGPROXY_CONCURRENCY=1       - IMGPROXY_USE_ETAG=true    thumbor:     image: minimalcompact/thumbor:7.0.0a5     ports:       - 8081:80     restart: always     deploy:       resources:         limits:           cpus: 1    thumbor-cache:     image: minimalcompact/thumbor:7.0.0a5     ports:       - 8082:80     restart: always     deploy:       resources:         limits:           cpus: 1     environment:       - RESULT_STORAGE_EXPIRATION_SECONDS=3600       - RESULT_STORAGE_FILE_STORAGE_ROOT_PATH=/tmp/thumbor/result_storage       - RESULT_STORAGE_STORES_UNSAFE=True       - RESULT_STORAGE=thumbor.result_storages.file_storage    imaginary:     image: h2non/imaginary:1.2.4     command: -enable-url-source -cpus 1     ports:       - 8083:80     restart: always     deploy:       resources:         limits:           cpus: 1     environment:       PORT: 80    picfit:     build:       context: .       dockerfile: ./.docker/picfit/Dockerfile     ports:       - 8084:3001     restart: always     deploy:       resources:         limits:           cpus: 1     environment:       - PICFIT_CONFIG_PATH=/config.json    picfit-cache:     build:       context: .       dockerfile: ./.docker/picfit/Dockerfile     ports:       - 8085:3001     restart: always     deploy:       resources:         limits:           cpus: 1     environment:       - PICFIT_CONFIG_PATH=/config_cache.json    imageproxy:     image: willnorris/imageproxy:latest     ports:       - 8086:8080     restart: always     deploy:       resources:         limits:           cpus: 1     environment:       - IMAGEPROXY_SCALEUP=true    imageproxy-cache:     image: willnorris/imageproxy:latest     ports:       - 8087:8080     environment:       - IMAGEPROXY_CACHE=memory:100:24h       - IMAGEPROXY_SCALEUP=true    weserv-images:     image: ghcr.io/weserv/images:5.x     ports:       - 8088:80     restart: always     deploy:       resources:         limits:           cpus: 1    weserv-images-without-cache:     build:       context: .       dockerfile: ./.docker/weserv-images/Dockerfile     ports:       - 8089:80     restart: always     deploy:       resources:         limits:           cpus: 1  networks:   default:     driver: bridge     ipam:       config:         - subnet: 172.10.10.0/24 

Результаты

Выводы

Серебряной пули нет. Кто-то хорош в одном, кто-то в другом. Где-то есть кеширование, где-то его нет и тд.

На данный момент мы остановились на Imgproxy. Да, в ней нет кеширования готовых превьюшек, но там есть ETag/Cache-Control/Expires HTTP headers. Плюс есть возможность добавить watermarks и много чего еще. Да и обновляется регулярно.

P.S. Несколько слов про чтение оригиналов с S3 и по http: так как мы разворачивали Minio S3 рядом с генераторами превьюшек — разницы в производительности не было, от слова совсем.

PPS. Добавил weserv/images по просьбе @mazaxakon.


ссылка на оригинал статьи https://habr.com/ru/articles/578282/