Оптимизируем работу с изображениями для статического сайта на базе Middleman и GitHub Pages

Запоздало поддавшись всеобщему тренду блоггинга и игры в писателей да технических специалистов, публикую свой первый пост! :)

В нём я расскажу об одном из подходов к работе с изображениями для случая статического сайта, сгенерированного движком Middleman и размещённого на GitHub Pages.

Почему GitHub Pages + Middleman + Images != ♥

Исходные данные просты:

  • новый блог / сайт на движке Middleman - нам ведь нужно что-то простое, но гиковское
  • размещаемся при этом на GitHub Pages - так как скорее всего у нас будет около десяти посетителей в месяц, и покупать отдельный хостинг для такого как-то глупо
  • из предыдущего пункта вытекает, что деплоимся мы через Git

Рано или поздно нам приходит идея всё красиво оформить: главную страницу с бэкграундом, фоточки с котятами в посты вставить.

Вроде таких:

Picture of lying cat

Полные энтузиазма мы всё сделали, локально закоммитили, пришло время пушить!

И первое, что сбивает наш запал - это скорость загрузки фоток на сервера GitHub.

На моём, по-сегодняшним меркам, древнем ADSL со 512 Кбит/c на отдачу, скорость заливки по SSH варьируется в пределах 65-120 Кбит/c, для варианта же с HTTPS всё ещё хуже, и составляет около 55-75 Кбит/c.

Picture of upload speed to GitHub

Это при том, что различные тесты показывают максимальную скорость загрузки до Сан-Франциско (именно там находятся основные сервера GitHub) в 490 Кбит/c. То есть в 4-8 раз меньше доступной.

На практике это означает, что в среднем фотографию в 1 МБ я заливаю примерно полторы минуты. А если их несколько?

Есть, правда, одна хорошая отмазка - за время заливки нескольких фотографий высокого качества можно сходить, чайку попить..

Закрываем на это глаза, пока не приходит осознание следующего логичного факта: так как размер удалённого репозитория будет расти пропорционально количеству залитой статики, то синхронизация с ним с другого компьютера, операции по слиянию веток с постами-заготовками и прочее - все эти процессы будут медленными и скучными.

Но и с этим можно смирится, ведь хостинг-то бесплатный! :)

Терпим, мучаемся, но вот масло в огонь подливает ещё и медленная загрузка нашего блога. Статического-то сайта!

Причём, даже если мы не обновляем его продожительное время, и браузер вроде как должен был успешно закэшировать все посещённые страницы, мы всё равно постоянно сталкиваемся с ожиданием загрузки страниц и фотографий.

Примерно в этот момент халявный уксус становится не таким уж и сладким, и мы задумываемся о выделении минимального бюджета на свой удалённый сервачок, планами по его настройке и прочих программистских радостях, забывая, что первоначальной идеей было “развернуть всё по-быстрому и уделять больше внимания наполнению”.

Далее апатия, потеря мотивации, писать оказалось не так интересно, домен продлевать не хочется…

Причины такой несправедливости

Настоящие пытливые умы так просто не сдаются, ведь правда? Решаем копнуть чуть глубже и вот что мы узнаём:

  • git push не так прост, как кажется на первый взгляд

Независимо от протокола передачи (SSH или HTTPS), происходит предварительное соединение, запрос на выполнение команд на удалённом сервере, обмен информацией о том, каких коммитов пока ещё нет на сервере, компрессинг данных перед их передачей и тд.

  • по-хорошему Git вообще не предназначен для бинарников, так как это противоречит его идее работы с дельтами разницы между текстовыми файлами

  • обход загрузки больших файлов есть и решается, как правило, через сторонние расширения к Git (типа LFS и git-annex) или отдельным сабмодулем (но всё это явно не наши варианты, так как объёмы данных не те)

  • несмотря на то, что GitHub Pages крутятся за собственным CDN, и статика кэшируется и раздаётся с ближайшего к адресу запроса сервера, есть один нюанс.

GitHub - это сервис для кода, GitHub Pages - сервис не для блогов, а для простых домашних страниц репозиториев. А главным требованием для таких страниц является частое обновления и синхронизация с кодовой базой репозитория, чтобы данные были актуальны. Ведь демо страница не должна использовать устаревший код.

Так вот, GitHub CDN кэширует статику только на 10 минут. Достигается это стадартным заголовком Cache-Control="max-age=600". По этой причине, каждые 10 минут ВСЯ статика (css/js/images/fonts) с вашего сайта будет инвалидирована (даже если она не менялась), а вам и вашим читателям придётся грузить всё заново.

И если минифицированные файлы js/сss + шрифты весят немного (120 КБ в моём случае), то изображения выкачиваются в полном размере!

Отсюда и малая скорость загрузки при повторном посещении - просто потому что оно на самом деле как первое! :)

Идеи

Несколько самых простых вариантов, сразу приходящих в голову:

  • завернуть наш блог за другой сторонний CDN, в котором вручную настроить политику кэширования и время жизни контента.

Отличный вариант (не считая времени на поиск, регистрацию, настройку и перенос DNS записей).

Однако он не решает проблемы маленькой скорости загрузки картинок в репозиторий, его неизменно растущий размер и возможность прослыть доморощенным любителем использовать инструменты не по-назначению.

  • осознав беду Git c большими файлами, принимаем однозначное решение не комитить фотографии, а хранить их на внешних ресурсах. Таких как Flickr и его аналогах, а на страницы вставлять абсолютные пути к загруженным файлам.

Тоже хорошо: теперь сам деплой проходит быстрее, репозиторий не растёт, а наличие распределённой сети серверов у всех крупных фотохостингов ускоряет работу нашего сайта.

Единственый минус лишь в увеличении подготовительный работы: отобрать фотографии, протестировать их локально (используя относительные пути), потом залить на фотохостинг в публичный альбом (что не всегда удобно, если аккаунт там для других целей), получить удалённые ссылки, заменить ими локальные, всё перепроверить и только потом собирать проект и деплоиться.

В общем неудобно..

Решение

Лично я не смог терпеть компромиссы между скоростью и удобством, именно поэтому на скорую руку был запилен proof-of-concept гем для MiddleMan - Middleman Image Uploader Tag.

Принцип работы сводится к автоматизации рутинной работы по заливке изображений на фотохостинг, подмены ссылок и гарантированном использовании CDN с большим временем жизни кэша.

Как установить и настроить можно почитать на странице гема, ничего сложного там нет, а я лишь расскажу как происходит процесс:

  • локально, в специальной папке хранятся все картинки больших размеров, которые планируется загружать. Папка попадает в .gitignore, таким образом на сервер мы её не пушим (как минус меньше пьём чаю).
  • во время проверки и разработки (mode :development) используются обычные относительные пути Middleman, только с другим хэлпером.

К примеру, remote_image_tag 'cats.jpg' сам найдёт картинку в нашей папке и подставит её в обычный image_tag.

  • подготовив пост, ничего не меняя, мы просто собираем проект.

В это время, все вызовы хелпера remote_image_tag сделают немного магии: изображения будут залиты на выбранный вами фотохостинг / CDN (на данный момент поддерживается только Cloudinary, но при желании можно добавить любой другой).

  • по-окончанию загрузки, автоматически получается удалённая ссылка и подставляется в виде теперь уже абсолютного пути в image_tag хэлпер, с чем наш проект и отправляется во внешний мир.

По-итогу, не меняя нашего подхода к работе и подготовке новых постов мы получаем изображения, загруженные не на GitHub, а на отдельный быстрый CDN с длительным сроком хранения статики.

Деплой происходит намного быстрее, а скорость повторной загрузки страницы по прошествию 10 минут в моём случае уменьшилось c 6.6 сек до 1.1!

И понятное дело, что чем больше будет изображений, тем ощутимее будет разница.

УСПЕХ!

Для любопытных

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

Поэтому данная возможность по-умолчанию отключена (да и нет необходимости в передаче публично доступной фотки по шифрованному каналу). Однако эту опцию можно вернуть через аргумент в вызове хэлпера.

Ещё один момент заключается в размещении серверов GitHub Pages и Cloudinary.

Так, пинг до первого у меня составляет 142 ms, до второго же около 26 ms. Можно предположить, что у Аkamai, мощности которого использует Cloudinary, лучшее распределение серверов по миру.

И как небольшой бонус, Google PageSpeed добавил балл за эту маленькую оптимизацию :D


На этом всё! Пользуюсь сам и надеюсь, что кому-нибудь также пригодится.

И репозитории будут небольших размеров, а страницы с котиками будут быстро открываться! :)

1
2
3
4
5
6
  /\___/\
 ( o   o )
 (  =^=  )
 (        )
 (         )
 (          )))))))))))