Skip to main content

Как (и зачем) перевести блог на статический сайт

Согласно статстике W3Tech 98.5% современных сайтов – это динамические сайты. Строго говоря – это программы (порой – большие и сложные программы), мощные многоэтажные системы, связаные со сложными системами хранения данных. Каждое посещение такого сайта – это выполнение кода, что требует ресурсов сервера и времени. При этом реально в большинстве случаев вся эта мощь не используется – большинство сайтов довольно компактные и редко обновляются. А значит – мощная, сложная, гибкая, многокомпонентная, что самое страшное – потенциально ненадежная система, в общем-то, не нужна.

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

Необязательная предыстория

Когда интернет только появился – все сайты были сверстаны статически. Каждый сайт был простым набором физических файлов. Эти файлы лежали на физическом диске. При обращении к URL веб-сервер транслировал относительный путь в путь в файловой системе, физически читал файл с диска и отдавал его клиенту. Такой подход очень просто программируется, написать простейший web-server на любом современном языке можно за пару часов. При статической верстке очень легко управлять кешированием - достаточно читать атрибуты файла и отправлять их в заголовок. Проблемы начались с ростом объема сайта. Если вы создаете страницы по очереди - вы можете писать их руками, по одной. Проблемы начинаются, когда нужно поменять что-то во всех страницах разом. Десять страниц можно поменять руками. 20 - сложно. 100 - очень сложно. На пятой тысяче вы сильно пожалеете, что вообще вписались в эту задачу. Так же все эти файлы физически лежат на диске и занимают прорву места. Причем большая часть этих файлов - совершенно одинаковая (верстка и художественное оформление у страниц общее, отличаются они только содержанием).

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

Работало (и работает до сих пор) концептуально это очень просто:

  • Запрос из сети вызывает выполнение кода на сервере (это и есть наш сайт, точнее - его часть).
  • При выполнении код выясняет, какие данные требуются пользователю.
  • Часть данных может быть прямо в коде (название сайта, имя автора, год публикации), а часть – лежит во внешнем хранилище, например - в базе данных.
  • Код лезет в базу данных и вытаскивает оттуда нужные данные.
  • Код загружает шаблон, на основе которого он будет рисовать страницу. Шаблон - это та же страница с версткой, дизайном и всеми атрибутами.В ней нет только реальных данных, вместо них там стоят метки вида {заголовок будет тут}.
  • Код вставляет в шаблон все необходимые данные и рисует страницу. Обычно готовая страница лежит в оперативной памяти сервера и ждет отдачи.
  • Сервер получает от кода сообщение о готовности и отрисованую страницу.
  • Сервер отдает страницу пользователю.

Современный сервер выполняет весь этот набор действий довольно быстро, но за конечное время. Обычно полный цикл занимает 100-200 миллисекунд + еще 50-100 миллисекунд требуется на доступ к базе. Проблемы начинаются на больших нагрузках. Либо резко кончаются ресурсы (каждый запрос обрабатывается независимым куском кода и в базу тоже ходит независимо). Либо сервер ставит запросы на обработку в очередь и сайт начинает тормозить (или вообще отваливается, если пользователь просто не успел дождаться своей очереди).

Динамические сайты – это фантастически удобно. Когда данные лежат во внешнем хранилище – можно организовать туда доступ из нескольких мест. Например, сделать зоомагазины для кошек и для собак, но товары держать в общей базе. Это позволит показывать на каждом сайте уникальные для именно этого сайта товары + общие товары для обеих групп покупателей. Базы позволяют делать “срезы” данных по любому направлению. Это позволяет показать пользователю любой набор информации из базы, ограничив его любыми критериями. Данные всегда забираются из базы, а значит, обновив данные в базе мы моментально обновим их на сайте, никаких задержек. Динамические сайты используют для отображения шаблоны, а значит – не нужно муторно перерисовывать каждую страницу, если в ней нашлась какая-то проблема. Одна правка в одном месте сразу поправит все.

По этой причине динамические сайты стали основой современного интернета. Статические сайты никто не делает, потому, что это неудобно и трудозатраты кажутся неадекватными. Однако, если собрать статистику по сайтам в интернете, выясняется, что:

  • Подавляющее большинство сайтов имеет очень маленький размер – единицы, в редком случае десятки, в очень редком - сотни страниц. К примеру, возьмем блог. Это будет хороший блог очень обязательного человека, который пишет пост раз в неделю. В году всего 52 недели, то есть за каждый год существования блог вырастет на 52 страницы. За 10 лет - 520 страниц. Много ли вы видели десятилетних блогов?
  • Подавляющее большинство сайтов очень редко обновляется, и им просто не нужна высокая легкость и скорость обновления.
  • Подавляющее большинство сайтов очень редко посещается. Тут нужно сделать небольшое отступление и рассказать про кеш.

Идея кеширования в том, что если пользователи часто запрашивают некие данные - их проще не готовить по всей цепочке, а сохранить в уже подготовленном виде и отдавать сразу. Вместо 300 мсек на запрос получаем 10 мсек + освобождаем ресурсы сервера (так, как данные не надо готовить и отрисовывать, можно отдать уже готовую отрисованную страницу). Основная проблема кеша – его срок жизни. В момент, когда он создается, указывается, сколько времени должен жить объект. Когда объект закончит свое существование и будет удален - первый пользователь, который пойдет в обход кеша - вызовет запуск кода (и создание нового объекта). Отсюда вытекает две проблемы:

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

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

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

Собственно, для решения этих проблем были придуманы генераторы статических сайтов. Это была попытка взять “лучшее из двух миров” – просту и легкость работы статического сайта и удобство работы с динамическими данными.

Что такое Jekyll

Jekyll – один из многих генераторов статических сайтов. Генераторы пытаются совмещать несовемстимое – удобство обслуживания сайта динамического и легкость сайта, сверстанного статически. Работает это следующим образом:

  • Данные для сайта сохраняются в виде текстовых файлов в специальном формате (он простой).
  • Внешний вид и верстка сайта оформляется шаблонами (используется движок liquid от shopify).
  • Команда jekyll build проверяет все данные и отрисовывает сайт по шаблону в виде набора статичных файлов. Готовые файлы сохраняются на диск.
  • Готовые статичные файлы можно положить на сервер и сделать доступными в сети.

Когда вам нужен jekyll (pros)

  • Если у вас небольшой сайт (PR-страница, личный или корпоративный блог).
  • Если ваш сайт обновляется сравнительно редко и делает это один человек (или несколько, но вы можете договорится о процедуре обновления).
  • Если вам нужно очень сильно сэкономить (хостинг статических файлов можно найти бесплатно).
  • Если сайт некрупный, но планируется очень большая нагрузка (статический сайт будет обрабатываться быстрее динамического).
  • Если вам очень важна безопасность, но нет средств на программиста, который будет поддерживать код вашего сайта (jekyll создает статические страницы, а на сервере никакой код не выполняется в принципе – ломать там нечего).

Когда вам не нужен jekyll (contras)

  • Если у вас по-настоящему большой сайт и счет страниц идет на десятки тысяч. В принципе jekyll может работать с большими объемами данных, но это будет неудобно.
  • Если ваш сайт обновляется очень часто (новостное агентство, форум).
  • Если работа вашего сайта зависит от поведения пользователя на сайте (социальная сеть, персонализированные новости, интернет-магазин).
  • Если ваш сайт обновляется многими и из многих точек (twitter, сервис блогов).
  • Если нагрузка на ваш сайт по-настоящему велика и вы знаете, как заставить ваш код работать быстрее примитивной выгрузки данных с диска (да, это возможно). Но тогда я не понимаю, зачем вы вообще читаете эту простыню.

Установка

Для работы jekyll вам потребуется ruby, способов его установки настолько много, что я не буду на этом сейчас останавливаться. После установки ruby ставим гем jekyll:

> gem install jekyll

Теперь переходим в папку, где будет лежать сайт. На всякий случай напомню, что это не тот сайт, который будет доступен в сети, а тот, который служит источником данных. Создадим “пустой” сайт:

> jekyll new ./testsite

Running bundle install in ~/testsite...
  Bundler: Fetching gem metadata from https://rubygems.org/...........
  Bundler: Fetching version metadata from https://rubygems.org/..
  Bundler: Fetching dependency metadata from https://rubygems.org/.
  Bundler: Resolving dependencies...
  Bundler: Fetching public_suffix 3.0.0
  Bundler: Installing public_suffix 3.0.0
  Bundler: Using bundler 1.15.2
  Bundler: Using colorator 1.1.0
  Bundler: Using ffi 1.9.18
  Bundler: Using forwardable-extended 2.6.0
  Bundler: Using rb-fsevent 0.10.2
  Bundler: Fetching kramdown 1.15.0
  Bundler: Installing kramdown 1.15.0
  Bundler: Using liquid 4.0.0
  Bundler: Using mercenary 0.3.6
  Bundler: Using rouge 1.11.1
  Bundler: Using safe_yaml 1.0.4
  Bundler: Fetching addressable 2.5.2
  Bundler: Installing addressable 2.5.2
  Bundler: Using rb-inotify 0.9.10
  Bundler: Using pathutil 0.14.0
  Bundler: Fetching sass-listen 4.0.0
  Bundler: Installing sass-listen 4.0.0
  Bundler: Using listen 3.0.8
  Bundler: Fetching sass 3.5.1
  Bundler: Installing sass 3.5.1
  Bundler: Using jekyll-watch 1.5.0
  Bundler: Using jekyll-sass-converter 1.5.0
  Bundler: Using jekyll 3.5.0
  Bundler: Using jekyll-feed 0.9.2
  Bundler: Using minima 2.1.1
  Bundler: Bundle complete! 4 Gemfile dependencies, 22 gems now installed.
  Bundler: Use `bundle info [gemname]` to see where a bundled gem is installed.
  Bundler: The latest bundler is 1.16.0.pre.2, but you are currently running 1.15.2.
  Bundler: To update, run `gem install bundler --pre`
New jekyll site installed in ~/testsite.

Самый простой способ убедится, что сайт создался и работает - собрать его. Команда jekyll serve обработает имеющиеся данные и соберет статический сайт, а затем запустит маленький веб-сервер, чтобы можно было посмотреть на сайт. Эту команду надо выполнить внутри папки сайта:

> cd ./testsite
> jekyll serve

Configuration file: /home/testsite/_config.yml
   Deprecation: The 'gems' configuration option has been renamed to 'plugins'. Please update your config file accordingly.
        Source: /home/testsite
   Destination: /home/testsite/_site
Incremental build: disabled. Enable with --incremental
  Generating...
                done in 0.812 seconds.
Auto-regeneration: enabled for '/home/testsite'
Server address: http://127.0.0.1:4000/
Server running... press ctrl-c to stop.

Теперь можно зайти браузером на http://127.0.0.1:4000 и полюбоваться на сайт:

![jekyll new]({{ site.baseurl }}{% link /assets/img/jekyll-new.png %})

Быстрый старт

Jekyll изначально - движок для блогов. Это не значит, что на нем можно писать только блоги, просто с блогами там работать легче всего. Все блог-посты, которые будут видны на сайте, jekyll ищет в каталоге _posts. Название у файла должно соответствовать шаблону YYYY-MM-DD-slug.md, где:

  • YYYY год в четырехсимвольном формате (2010)
  • MM месяц в двухсимвольном формате (03 – март).
  • DD в двухсимвольном формате (08 - 8 Марта).
  • slug - произвольное название статьи на английском языке. Регистр букв (большая-маленькая) – учитываться не будет. Реально slug нужен на тот случай, если у вас два сообщения в один день.
  • md - подсказка, что это формат markdown. Jekyll поддерживает и другие форматы, но в данном примере я буду рассматривать markdown, он очень простой.

Заголовок (шапка) файла тоже имеет определенные требования, вот пример:

---
layout: post
title: "Текст про 8 марта"
date: "2010-03-08 8:00:00+03:00"
---

Что тут написано:

  • layout: post указывает jekyll, какой шаблон нужно использовать для отрисовки (перевода) данного статического текста в html-страницу. Post - стандартный шаблон, который идет в комплекте, и если нет уверенности – проще всего брать его.
  • title - это заголовок страницы. Именно этот текст будет отображен в списке постов на главной странице и именно этот текст будет в шапке страницы, когда пользователь ее откроет.
  • date - дата и время публикации. Время указывается в 24-формате с точностью до секунды, через + (или -) указывается тайм-зона. Ее указывать не обязательно, но тогда будет использоваться та, что указана в настройках. Эти дата и время будут показаны на сайте. Если их не указать – дата публикации будет взята из названия файла (если указать - из этого поля, а дата из названия будет игнорироваться).

Пропускаем строку после заголовка и можно писать текст

Лирическое отступление: markdown

Markdown – это простой язык разметки. Он нужен для того, чтобы разделять в тексте заголовки и абзацы, делать текст жирным или курсивным. HTML, на котором написаны страницы, которые лежат в интернете – тоже язык разметки. Но HTML – язык сложный, мощный, гибкий и не прощающий ошибок. Markdown – напротив, специально сделан предельно простым, пусть и в ущерб гибкости.

Markdown прекрасно понимает переносы строк. Нужно отделить один абзац от другого – достаточно одной пустой строки между ними.

Для заголовков используется символ #. Ставится он только с одной стороны и заголвок считается только с новой строки. Пример:

  • # заголовок заголовок первого уровня (H1). Самый крупный.
  • ## заголовок поменьше - заголовок второго уровня (h2). Поменьше.
  • ### подзаголовок - заголовок третьего уровня. Самый маленький.

Для оформления текста внутри абзаца:

  • **жирный** - две звездочки сделают текст жирным (bold).
  • *курсивный* - одна звездочка сделает текст наклонным (italic)
  • ***жирный и курсивный*** - три звездочки сделают текст и жирным и курсивным сразу

Для ссылок:

  • [ссылка обычная](https://yandex.ru) - создаст обычную ссылку. Текст будет взят из [], а вести она будет по адресу ().
  • ![картинка](https://yandex.ru/image.jpg) - на странице появится картинка. Обратите внимание на ! в начале.

Чтобы создать список - начинайте строку с символа - или * и пробела за ним. Если у вас группа строк идет без разрыва, одна за другой – это будет воспринято как список:

- первый пункт
- второй
- третий

тут немного текста

- первый пункт второго списка
- второй пункт второго списка

Полное описание стандарта можно найти вот тут (на английском языке). Существует огромное количество текстовых редакторов с поддержкой markdown, которые прямо во время редактирования показывают, каким будет оформление текста. Лично я пишу этот текст в прекрасном редакторе Sublime3.

Настройки сайта

Глобальные настройки сайта находятся в файле _config.yml. Он довольно очевидный и легко читается. Что я рекомендую в нем поменять:

  • title - заголовок сайта. Отображается на каждой странице
  • description - описание сайта. Отображается на каждой странице стандартного шаблона внизу экрана
  • email, twitter_username, github_username - необязательные поля. Если есть - отображаются внизу экрана.
  • url - адрес вашего сайта в сети. Важен для внутренних ссылок.
  • baseurl - нужен в том случае, если jekyll является не основным сайтом, а только его частью и расположен в отдельной локации (к пример - по адресу /blog/). Baseurl будет автоматически добавляется перед началом каждого адреса, который есть в jekyll. Это довольно редкая настройка.
  • timezone - временная зона по умолчанию, задается в стандартном виде Регион/Город. Типичная настройка для европейской части России - Europe/Moscow.

Обычно настройки достаточно поменять один раз.

Дополнительные страницы

Обычно в блоге кроме собственно постов есть статические страницы, например – описание сайта или страница контактов. Jekyll позволяет создать такие страницы. Эти страницы не отображаются в списке постов на главной, но зато они видны в шапке сайта (сверху). Такие страницы нужно создавать прямо в корне (папке, где находятся все файлы jekyll) и в их названии не обязательно должна быть дата, достаточно просто имени и расширения md. Имя должно содержать только английские буквы, точку и тире (в противном случае могут быть сюрпризы). У страницы в заголовке должен быть обязательный атрибут permalink – это указание jekyll, по какому адресу эта страница должна быть видна на сайте. В остальном такие файлы ничем не отличаются от файлов блог-постов, которые я описывал выше.

Генерация и выкладка

Итак, текст написан, можем выкладывать. Чтобы собрать сайт из страниц-заготовок - используйте команду jekyll build. Ответ будет выглядеть примерно так:

> jekyll build
Configuration file: /home/testsite/_config.yml
            Source: /home/testsite
       Destination: /home/testsite/_site
 Incremental build: disabled. Enable with --incremental
      Generating...
                    done in 1.683 seconds.
 Auto-regeneration: disabled. Use --watch to enable.

В нормальном состоянии jekyll не должен как-то ругаться, выбрасывать предупреждения или ошибки. Если они все же есть – рекомендую их внимательно прочитать. Обычно прямо в тексте ошибки есть указание на то, как ее исправить.

Готовые к выкладке файлы будут ждать в папке _site. Теперь достаточно просто скопировать их на хостинг (подойдет совершенно любой хостинг, который позволяет загружать на него файлы) - и сайт доступен в интернете. На хостинг нужно отправлять только содержимое папки _site, ничего больше отправлять не нужно! Для размещения сайта можно, например, использовать совершенно бесплатный github pages, но в принципе – найти хостинг бесплатный или очень дешевый хостинг - несложно. Благо, все, что от него требуется – возможность хоть в каком-то виде отправлять на него файлы.

Добавление-удаление контента

В обновлении сайта нет ничего сложного, но на всякий случай я опишу его тезисно:

  • пишем новый текст. Файл кладем в _posts, если это запись в блоге или в корень сайта на жестком диске, если это – простая страница
  • выполняем jekyll build, чтобы пересобрать сайт
  • загружаем содержимое папки _site на хостинг. Если какие-то файлы на хостинге уже есть с прошлого раза – перезаписываем.
  • сайт обновлен и готов к использованию