Перенос сайта на статику: мотивация, стоимость, работа

    Недавно мы сделали то, о чём грезят все программисты и дизайнеры — переписали всё с нуля: полный редизайн нашего сайта и написание «движка» с чистого листа. Ниже поведаем о мотивации и процессе миграции с онлайн-CMS на статику.







    Что за сайт-то?


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


    С контентом работают разные люди — маркетологи, HR-менеджеры, разработчики. У разных людей (и для разных задач) разные предпочтения в инструментах. Если фронтэндщику, верстающему новый кейс в портфолио, удобнее сделать git pull и работать в своём привычном окружении (vim, конечно), то HR'у, исправляющему опечатку в тексте вакансии, проще через веб-морду поменять одну букву, нажать кнопку «сохранить» и заниматься дальше своими делами.



    Как было


    Исторически сложилось так, что в предыдущей реинкарнации сайт был построен на самодельном движке, основанном на фреймворке Ruby on Rails. В качестве CMS использовался rails_admin. Для редактирования части контента был подключен WYSIWYG-редактор (CKEditor); остальное — в виде голого HTML (CodeMirror).



    Мотивация


    Зачем трогать, если работает?


    1. Проблема синхронизации данных в репозитории и данных в БД.


      
Банальная CMS на RoR использовала банальную СУБД sqlite (да, это один из тех случаев, когда sqlite идеально годится для продакшена). Соответственно, тот контент, который лежит в БД, не лежит в репозитории в git. Это неудобно для фронтенд-разработчиков, которым необходимо внести серьёзные изменения, например, в кейс портфолио. Дело в том, что первичная версия кейса, создаваемая разработчиком, лежит в виде шаблона в репозитории, а в момент деплоймента шаблон компилируется и пишется в БД, дабы иметь возможность его редактировать в CMS. Обратный процесс нетривиален (а в общем случае хоть и возможен, будет давать не исходный результат, что в итоге создаёт проблемы с патчами, например).
    2. Операционная стоимость.

      

Сайт не требует интерактива от пользователей, но при этом всё равно необходима относительно сложная инфраструктура — как минимум одна машина с реверс-прокси и app-сервер. Как известно, RoR любит покушать RAM, а это стоит денег.
    3. Производительность.

      

Значительно затрудняется оптимизация производительности. Необходимо кэшировать страницы / фрагменты. Кэш надо инвалидировать. Интеграция с CDN требует когнитивных усилий.
    4. Удобство работы.

      

Известный факт: WYSIWYG работает не очень. Часто приходится в нём прибегать к кнопке «показать код» и редактировать HTML. А если этот HTML скомпилирован из шаблона, он бывает не очень красивым, и в окошке браузера работать с ним не комильфо.

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


    Другие бонусы решения на статике:


    • Мы в итоге захостились на GitHub Pages, который самостоятельно менеджит SSL-сертификаты от Let's Encrypt, интегрируется с Akamai CDN и не просит за это ни цента. Стоит отметить, что для интеграции с CDN необходимо использовать домен третьего уровня, так что требуется редирект с e-legion.com на www.e-legion.com. Подробнее об этом в документации GitHub'а. И немного на тему: www.yes-www.org.
    • Все изменения сохраняются в истории репо. Ранее часть изменений была в git log'е, а часть — в истории в Rails Admin.
    • Все изменения можно сперва проверить на тестовом инстансе, куда они автоматом попадают по пушу в репо. Если всё хорошо, то одной кнопкой всё деплоится в прод. Ранее постоянно синхронизировать весь контент между контурами и/или вносить все правки сперва на тест, потом на прод, было настолько неудобно (и необязательно), что никто этого, конечно, не делал. В какой-то момент тест вообще отключили, потому что, опять же, лишние бесполезные ресурсы.

    GitHub Pages

    Вы перешли только в 2019?


    Почему же с такими очевидными плюсами мы осуществили переход только сейчас? Не потому что программисты только из пещеры вылезли. Они уже на протяжении нескольких лет пытались продать начальству своё решение. Но помимо внепланового выделения бюджета, главным препятствием стоял вопрос удобства работы с контентом: как научить главных контентщиков (маркетологов) работать со всей этой историей? Если с редактированием «кода» они более-менее знакомы и привыкли, то обучить работать их с git'ом не всем представлялось целесообразным.


    Какое-то время назад была идея написать свой фронт с интуитивным интерфейсом, но реализация упиралась в трудоёмкость, то есть в ресурсы разработчиков.


    Шло время, и появились такие решения, как GitLab и встроенный в него Web IDE. И когда подошло время редизайна сайта, мы наконец создали небольшой прототип, представили его отделу маркетинга на ознакомление, проведя небольшой урок по работе с GitLab. На объяснение потребовалось менее часа. Маркетологам реализация понравилась, и процесс был запущен.



    GitLab IDE Interface

    Технические подробности


    Итак, пара слов о том, как в итоге получилось.


    > tree -aL 1 --dirsfirst -C
    .
    ├── .git
    ├── app
    ├── images
    ├── node_modules
    ├── pages
    ├── public
    ├── .gitignore
    ├── .gitlab-ci.yml
    ├── .jshintrc
    ├── README.markdown
    ├── gulpfile.js
    ├── makefile
    ├── package-lock.json
    └── package.json
    

    Что видно сразу:


    • генератор написан на Node.js;
    • используется таск-раннер gulp;
    • на верхнем уровне есть директории app (файлы «приложения», то есть шаблоны и исходники js и css), images (очевидно, картинки), pages (контент), public (директория, которая будет сервиться по http).

    > tree -aL 3 --dirsfirst -C pages/
    pages/
    ├── en
    ...
    └── ru
        ├── portfolio
        │   ├── projects
        │   └── index.pug
        ├── vacancies
        │   ├── klgd
        │   ├── msk
        │   ├── spb
        │   └── index.pug
        ├── 404.pug
        ├── about.pug
        ├── contacts.pug
        ├── education.pug
        ├── events.pug
        ├── faq.pug
        ├── index.pug
        └── process.pug
    

    Контент структурирован интуитивно понятным образом, так что (даже без знания английского языка) можно найти нужный файл, исходя из URL страницы. Используется шаблонизатор Pug (синтаксис проще и чище, чем HTML).


    Со сбором js, css и картинок всё банально. Посмотрим на недословную выжимку кода из gulpfile'а, собирающего странички:


    gulp.task('pug', () => {
      // Preload shared data only once.
      let sharedData = {};
      for (let lang of ["en", "ru"]) {
        sharedData[lang] = loadSharedData(lang);
      }
      // Load necessary data from other files for each file render.
      let dataGetter = (file) => {
        let content = frontmatter(String(file.contents));
        file.contents = new Buffer(content.body);
        let data = content.attributes;
        data.lang = file.relative.split(path.sep)[0];
        data.env = process.env.NODE_ENV;
        loadPageData(data, sharedData);
        return data;
      };
      // Remove html file extensions in URLs.
      let renamer = (filepath) => {
        if (filepath.basename === '404') {
          return;  // special case for Github Pages
        }
        if (filepath.basename !== 'index') {
          filepath.dirname = path.join(filepath.dirname, filepath.basename);
          filepath.basename = 'index';
        }
      };
      return gulp
        .src('./pages/**/*.pug')
        .pipe(data(dataGetter))
        .pipe(pug(pugOptions))
        .pipe(rename(renamer))
        .pipe(gulp.dest('./public'));
    });
    

    Для подгрузки данных при рендеринге шаблонов используется gulp-data. Метаданные файлов лежат в самих шаблонах в формате front-matter, откуда соответствующим пакетом подгружаются. «Смежные» данные, например, список кейсов для индексной страницы портфолио или список вакансий, подгружаются специальным дата-геттером, который собирает необходимый набор данных для каждой отдельной страницы.


    Помимо этого используется gulp-rename для облагораживания URL'ов — все странички помещаются в одноимённые директории под названием index.html. Таким образом, исходная страничка faq.pug доступна по URL /faq/, а не /faq.html.





    Второй интересный момент, который стоит рассмотреть, это конфигурация GitLab CI/CD:


    stages:
      - build
      - deploy
    
    build_sites:
      stage: build
      tags:
        - npm
      before_script:
        - make deps
      script:
        - make build
      variables:
        NODE_ENV: production
      artifacts:
        when: on_success
        expire_in: 7 days
        paths:
          - public
    
    deploy_staging:
      stage: deploy
      tags:
        - npm
      only:
        - master
      environment: staging
      dependencies:
        - build_sites
      script:
        - make deploy_server
      variables:
        SSH_USER: elegion
    
    deploy_production:
      stage: deploy
      when: manual
      tags:
        - npm
      only:
        - master
      environment: production
      dependencies:
        - build_sites
      script:
        - make -j2 deploy_ghpages
    

    Тут стоит обратить внимание на следующие вещи:


    • Сборка происходит при пуше в любую ветку. Так, при работе в feature-бренчах люди получают фидбек, если где-то накосячили на столько, что сломали билд.
    • При пуше в master, автоматом происходит деплой на тестовое окружение. Для деплоя используется примитивный rsync --archive --compress --delete --copy-links ./public ${SSH_USER}@${SSH_HOST}: (да, ещё один бонус сайта на статике — сверхшустрые и беспроблемные деплои).
    • Джоба на деплой в прод становится доступна после успешной сборки и деплоймента в тест и запускается по нажатию одной кпопки в UI.
      GitLab CI UI
    • Благодаря возможностям make, деплой сразу двух сайтов на двух языках (www.e-legion.com и www.e-legion.ru) делается параллельно.


    Заключение


    На разработку прототипа ушло 2 дня. Доведение движка до ума заняло ещё 3 дня. Настройка CI/CD заняла менее 1 дня. Остальное потраченное время было необходимо в любом случае — создание дизайна, рерайт контента, вёрстка. В итоге довольны все: разработчики, потому что simple is better than complex и поддержка стала в разы проще; админы, потому что они перестали быть нужны вообще, контентщики, потому что стало удобнее. Цитата маркетолога: «Знаю, что мне теперь не хочется закрыть глаза или убежать, когда нужно что-то на сайте поправить». Заодно и на хостинг теперь уходит 0 ₽ в месяц, примерно на 750 ₽ меньше, чем раньше.




    Если вы в своей IT-компании до сих пор не используете статику для сайтов-визиток, лендингов и подобных штук по причине того, что переживаете за способности ваших контентщиков, спешим переубедить вас и ваше начальство по опыту нашей истории успеха. Современный UI GitLab и подобных ему git-хостингов при должной конфигурации проекта более удобен для работы наших маркетологов и HR'ов, чем старая CMS на rails_admin. Даже если первые пару раз у людей будут возникать вопросы, помочь с ответом сможет любой подручный разработчик, знакомый с git, потому что всё максимально просто и понятно.

    e-Legion
    87,00
    Лидер мобильной разработки в России
    Поделиться публикацией

    Похожие публикации

    Комментарии 41

      +6

      хороший кейс, написано интересно.


      спасибо!

        –2
        Я тоже думал об этом)
          +7
          Достаточно появлению одного малейшего требования взаимодействия с бекендом и статика уже не поможет. Банально, говоря, например, только про раздел вакансии, ведь, например, это может захотеться уже завтра: кнопка (форма) отправить резюме, «оставьте емейл и мы перезвоним», лайки, просмотры.
          Это решение не масштабируемое, вы загнали себя в достаточно узкие рамки, выход из которых будет стоить вам полного рефакторинга структуры проекта.
          Если хотелось готового из коробки решения — есть wordpress с его историей редактирования. В вашем же случае, как по мне, было проще создать дополнительную таблицу с историей редактирования и кастомизировать WYSIWYG (это обычно не так сложно, BBCode+regex на бекенде), чтобы облегчить жизнь редакторам.
          В прочем, решение имеет право на жизнь, если вы, переходя на статику, просчитали вышеописанный момент и однозначно и на века, решили, что бекенд не нужен :)
            +5

            Если вдруг понадобится какое-нибудь взаимодействие, то есть, например, лямбды. Даже в статический сайт можно добавить динамику, как например staticman умеет добавлять комментарии, не говоря уже о всяких Disqus и Commento.
            Но если вдруг понядобится что-то более узкое, то можно взять и сделать виджет на ReactJS, который будет рендерить что-то с маленького и легкого бека.
            Зато статический сайт это действительно дешево (платишь только за домен), быстро (время уходит только на передачу контента) и надежно (GitHub с большим успехом переживет хабраэффект, чем любой купленный хостинг).
            Эти плюсы чаще всего с лихвой покрывают все неудобства.

              +2
              Несомненно, мы об этом подумали. Помимо того, что ответил вам Виталий vtvz_ru, хочу заметить, что на сайте уже есть формы подписки на уведомления о наших конференциях и школах. Они используют Google Forms на бэкенде, что тоже не добавляет дополнительной стоимости, потому что у нас уже есть корпоративный G Suite.
                +2
                Вместе в вордпрессом Компания получит пачку уязвимостей, возможно взломов и возможность все это лечить, фиксить и обновлять.

                А формы можно отправлять через гуглформс
                  –5
                  Можно пачку паследних уязвимостей wp ядра? Плагинов меньше юзать надо. )
                    +3
                    Можно пачку паследних уязвимостей wp ядра?

                    Выбирайте
                      –2
                      Последние два года по Code Execution — все связаны с авторизированными пользователями. 17 год уязвимость не ядра, а через сторонние темы или плагины. 16 год пусто. Дальше не проверял. Не плохо для системы, с миллионными копиями.
                      Заплатки безопасности устанавливаются без участия пользователя.

                      Более того, есть довольно неплохие бесплатные решения по безопасности для wp от сторонних разработчиков, обрабатывающих общие известные стратегии проникновения. Это как бы мастхэв изначально.

                      Не идеально, но и не сказать что прям ужас ужас решето.
                        0

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


                        Со статикой проблемы взлома просто не существует.

                          –1
                          Этого не лишен ни один программный продукт. Чем популярнее сайт тем больше он уходит с общих решений. Непопулярные сайты рискуют схватить от китайских ботов.Как я писал, общие стратегии проникновения закрывают решения типа wordfence. Никто не говорит, что статика плохо. Взлом сайта компании не самое страшное, что может случиться. Не все компании айтишные и воспринимают это как удар по репутации. Тем более мы говорим о вероятности, а не о том что wordpress ломают налево и направо.
                          0
                          Более того, есть довольно неплохие бесплатные решения по безопасности для wp от сторонних разработчиков, обрабатывающих общие известные стратегии проникновения. Это как бы мастхэв изначально.
                          Можете их перечислить?
                    –2
                    Да каммон, поднимаешь свою динамику где хочешь и взаимодействуешь по rest из статики.
                    0
                    у вас ошибка — yadi.sk/i/ghAJ4Lacoeo6ig
                      0
                      Не очень понимаю по картинке, в чём ошибка. Поясните, пожалуйста.
                        +1
                        … подхватываем вашегои приложения
                          0
                          Так и задумано. Тут эмоджи несёт смысловую нагрузку. Модно и молодёжно. :-) Тем не менее, спасибо за бдительность.
                            +2
                            отстаю от моды, старею )
                      +1
                      1. Что делать с картинками? Нужно выложить корпоратив галлереей. Фотки 4000px по ширине 6мб каждая. Где ресайзите оптимизируете и как делаете превьюшки?
                      2. Что делаете с переменными? К примеру у вас есть номер телефона где его нужно отображать в разных местах шаблонов. Есть ли бд с константами?
                      3. Sitemap чем генерируете? Если вы переименовываете файлы в папки индексхтмл
                      4. Странные у вас подход к оформлению вакансий. Выходит если надо где то поменять блок местами, зп над инфой надо править все вакансии. То есть у вас каждая вакансия это шаблон. А если нужно выводить вакансии карточками внизу на главной вдобавок? Нужна бд. Пока не нашел удобного визуального редактора к json файлу который кушает pug, правлю так и перегенерирую проект.
                      Вам следовало ваш sqlite конвертнуть в json и продолжить размечать им вьюхи как будто у вас RoR.
                        0
                        Gulp-data, не заметил. Но все равно как то у вас сложнее чем иметь json объект.

                        Сколько у вас страниц в итоге что минута на рендер уходит?
                          +1
                          1. Не, не нужно. :-) Для подобных вещей есть всякие инстаграмы, фейсбуки, фликры и прочие файлопомойки. Но если вдруг очень захотелось, ничто не мешает обрабатывать файлы в момент сборки точно так же, как вы бы их обрабатывали в момент аплоада на бэкенде.
                          2. Есть подключаемые файлы, переменные, миксыны. Кстати, номер телефона вместе с имейлом вынесены в такой файл, это вы верно подметили.
                          3. JavaScript'ом, например. Или ещё чем-нибудь, как удобнее. Из makefile'а можно вызвать любую утилиту.
                          4. Нет, не выходит. Есть шаблон. Есть контентные файлы. Есть наследование шаблонов. Есть переопределяемые блоки. Почитайте документацию pugjs.org. Если нужно на главной — не проблема. На главной уже есть карточки с кейсами портфолио, например. Задача уже решена и не вызвала трудностей.
                            0
                            4. Ага, открыл полный скриншот. С порядком проблем не будет.
                            C pug все хорошо только он требует все равно заглядывания в предыдущий файл, чтобы скопипастить все от туда. Все равно остается осадочек от необходимости минимального копипаста. Вот у вас .container не вынесен в шаблон а остался в контентном файле. У ul есть класс. Я стараюсь свести разметку в контентном файле к минимуму, к обычному html без спец классов, потому что в случае чего нужно бегать по всем файлам и искать не забыл ли где то исправить класс какой. Даже комментарии я бы удалил т.к title,description, vacancy_active понятные по названиям.

                            я имел ввиду, что есть вам требутся дублировать текст, те же вакансии размещать в двух местах с одним и тем же текстом? вы просто копипастите?
                              +1

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


                              У ul класс тоже умышленно сделан. Никто не мешает повесить стили на .page-content ul, но мы так делать не стали.


                              Комментарии вам не нужны, а HR-менеджерам могут быть нужны.


                              Но это всё мелкие детали, которые вы можете сделать по-своему, а мы сделали по нашему, потому что нам так оказалось удобнее. И в случае чего переделать — вообще не проблема. Всё максимально тривиально.


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

                                0
                                del
                              +1
                              1. Cloudinary вполне справляется с этой задачей. Бесплатного тарифа хватит за глаза и за уши.
                              2. Статический сайт тоже имеет исходники и собирается генераторами, которые вполне поддерживают переменные.
                              3. В популярных генераторах статики такие штуки уже давным давно сделаны другими людьми и обкатаны десятками тысяч проектов.
                              4. Опять же, данные храниться могут в любом формате, например в yaml, а как они будут отображаться — работа шаблонов и шаблонизатора.

                              Посмотрите в сторону Jekyll. Очень популярное решение для статики с кучей официальных и не очень плагинов вдогонку. И вопросы отпадут сами собой.

                                0
                                да это ясно, яж интересовался как у них.
                              0
                              а чем JAMStack не подошел?
                                +1

                                Тем что он тут не нужен совсем, например.

                                –1
                                Ребят, вам что жалко 750р/мес?)
                                  0
                                  почему не hugo?
                                    +1
                                    Мы рассматривали несколько движков и отвергли разные по разным причинам: где-то были проблемы с нашей двуязычностью, где-то было не сделать (либо слишком неочевидно, как) то, что нам требовалось. Многие движки заточены исключительно под блоги. В общем, потратив некоторое время на ознакомление с готовыми решениями, пришли к тому, что быстрее сделать своё, благо кода там две сотни строк, из них треть — gulp'овый boilerplate.
                                    +2
                                    В похожих случаях использую Grav, когда WordPress избыточен. Есть и плагин админки, ну если хочется (или необходим) визуальный вариант.
                                      +1

                                      Ребята, сорян, но мне кажется тут технологии ради технологий. Все это прекрасно, пока такие in-house решения вы не продаете клиентам.
                                      Из WordPress легко можно генерировать статику, если нужно — есть REST API, так что рендеринг можно делать любым удобным способом (React, Vue, Angular). Ну серьезно, WP используется на миллионах сайтов разного уровня, но нет, для очередного лендинга или визитки — это не серьезно. Вы упоминали про кривой WYSIWYG: все так, в 2019 отдавать клиенту сайт с возможностью редактирования только с помощью визуального редактора и пары чекбоксов — стыдно. Для WP есть по меньшей мере 5 великолепных плагинов, которые позволяют создавать интерфейсы любой сложности. Не любите плагины — используйте родные блоки Gutenberg, введенные в версии 5. Если умеете React, то написать новый блок не проблема, или можно сделать экстенд существующего(кастомные кнопки, листинг других постов, что угодно — клиент будет только мышкой тыкать).
                                      У меня такое ощущение, что на российском рынке разработки все что касается WordPress воспринимается исключительно негативно, даже при том, что инструментов разработки для WP сотни.

                                        +2
                                        Всё это прекрасно, только коммент не имеет отношения к посту. В посте нет ни слова про WordPress, так что не понятно, с чего вдруг вы пишете про негатив к нему. Алсо, в посте нет про продажу клиентам. Если бы вы прочитали заключение, то увидели, что речь идёт про сайты-визитки и прочие маркетинговые лендинги внутри IT-компаний.
                                          0

                                          Как раз наоборот, господин radist2s говорит, что WP может в пару кликов сгенерировать вам статический контент, который не будет жрать ресурсы бэка. А когда надо, вы снова правите контент силами WP, который также снова нагенерирует кучу статики. Статику отдавать не проблема вообще любому серверу, даже очень слабому. У вас ведь основная мотивация была в том, что Бэк для отрисовки жрёт кучу ресурсов, плюс тяжело оркестрировать контентом. Так вот, WP решает все ваши проблемы.

                                            +2
                                            Я не спорю с господином/госпожой radist2s, что WP это может. Мои возражения направлены на другие части комментария.

                                            Мы в курсе, что WP (и ряд других решений) так может. Но для нас это решение более тяжеловесное (WP тоже надо хостить где-то, хоть на локальной виртуалке) и менее поворотливое (кастомизировать WP намного сложнее). У нас уже есть гитлаб, в котором хостятся сотни других проектов и который предоставляет и UI для редактирования, и CI, и CD. И в итоге покрывает не половину нашей мотивации, как вы перечислили, а всю.
                                              0

                                              Моя защита WP была скорее направлена не как ответ вам, а просто общее замечание. По поводу Gitlab CI у нас как раз так все и работает. Деплоим проект через Trellis, в процессе сборки проекта проходятся всякие тесты и прочее. Но это тоже все не относится к делу, конечно.

                                        –2
                                        Прикол, чё. Все идут в сторону React, GraphQL, а пацаны взяли и перешли на статику 90-х годов, красавчики)
                                        И Bootstrap с jQuery есть)
                                        А вам фронты на удалёнку не нужны случайно?)
                                          0
                                          не вижу противоречий. О переходе на статику еще не одна статья будет, поверьте. SSR сам толкает к этому, тут Gatsby.js и другие есть
                                          +2

                                          вторая (иногда третяя) сильнейшая политическая партия чехии свой главный и десятки региональных сайтов давно сделала статическими (jekyll). к чему все эти вордпрессы?


                                          https://www.pirati.cz/

                                            0
                                            Может вы это и неплохо придумали, только что у вас за контора, когда экономия 10 USD в месяц на хостинге вызывает огромное желание забросить все дела и клиентов и писать несколько дней статику сугубо под себя.

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

                                            Самое читаемое