Появилась тут задача сделать сайт-справочник по основам блокчейна для проекта где я консультирую по безопасности.
Что требовалось:
быстро поднять базовую версию, чтобы начать публиковать контент
шустрая и плоская (flat-file) CMS, потому что для такой задачи БД не нужна
чтобы была админка для добавления контента
настроить бэкапы
чтобы все было безопасно
чтобы можно было дописывать всякий функционал если надо
Выбирал между:
Lektor CMS (python + jinja2)
Automad (PHP+ AML)
GravCMS (PHP+ twig)
Сначала тестил Lektor, т.к. python это мой основной язык и с jinja2 я отлично знаком. Но не понравилась убогая админка и мутноватая документация. Бывает так, что читаешь документацию и все понятно, а тут нужно прям рисерч делать, а на это времени нет (см. пункт 1 требований). Приятно, что можно хостить бесплатно на Github, но если так сделать, то серверные скрипты потребовали бы передеплоя на виртуальный хостинг или на VPS.
Вердикт: не подошло
Automad - новый интересный проект, выглядит очень нарядно и хочется его потестировать. Забраковал его, главным образом, из-за того, что поднятая в docker-е тестовая сборка заметно лагала. Возможно проблема на моей стороне, но разбираться и тестить некогда, надо дело делать а не в кроличьи норы прыгать. Плюс у них свой шаблонизатор AML (Automad Markup Language), его надо отдельно изучать, можно залипнуть надолго.
Вердикт: интересно, но нет времени разбираться
GravCMS - не новый, но проверенный проект на PHP + Twig. PHP не сильно люблю, но знаю. С twig работал еще на symfony. Очень подробная документация. Куча skeleton-проектов где можно потыкать и посмотреть как сделано. Хорошая админка.
Вердикт: подходит, берем
Рабочая среда
Разработкой занимаюсь в Linux Mint, считай та же убунта. Редактор кода VSCodium (форк MS VSCode но без телеметрии). И docker как основной инструмент изоляции среды.
В самом Grav для базового теста нет встроенного веб-сервера. Но он и не нужен, ведь можно dev-сервер на "голой" PHP запустить, выполнив из рута проекта:
php -S localhost:8000 system/router.php
Но в целом конечно надо запускать в докере, т.к. тогда никаких проблем с совместимостью не будет. Локально пишешь в докере, деплоишь в докере - красота.
Обычно для проектов на python я быстренько пишу docker-compose.yml файл с gunicorn, и все быстро запускается и работает. А вот с PHP все не так легко оказалось, там надо apache или nginx настраивать под нужный для Grav конфиг. Конфиг для nginx (мой выбор) заботливо поставляется в комплекте, но тоже своего рода запара.
Обычно такие решения уже кем-то сделаны и опубликованы в общем доступе, так что прежде чем тратить время на переизобретение велосипеда бывает полезно погуглить.
В итоге гугления я нашел готовый docker образ от довольно толковой команды https://docs.linuxserver.io/images/docker-grav/
Я привык запускать контейнеры через docker-compose, потому что обычно в достойном деплоя в докере проекте более одного контейнера. Сборщики образа заботливо предоставили готовый docker-compose.yml:
services:
grav:
image: lscr.io/linuxserver/grav:latest
container_name: grav
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
volumes:
- /path/to/grav/config:/config
ports:
- 80:80
restart: unless-stopped
Запустил образ, залез в папку config и порадовался тому, что туда вывели все что нужно: конфиги сервера, логи и те файлы самой CMS, которые как раз нужно бэкапить:
user/pages - контент
user/themes - шаблоны
user/config - конфиг сайта
user/plugins - плагины
user/data - всякие нестандартные загруженные данные
В моем случае используется слегка модифицированный шаблон learn2 и стандартный набор плагинов, поэтому я в схему моего кастомного деплоя и бэкапа включил user/pages и user/themes. Все прочее в случае чего восстанавливается за 20 минут.
Деплой и бэкап
Вот тут как раз я решил сделать бескомпромиссную схему backup-first, т.к. я как любой уважающий себя разработчик рационально ленив, а значит надо делать так, чтобы все нужное делалось само. Ну или в рамках общего workflow и нельзя было срезать угол.
А еще я умею в автоматизацию с помощью github actions, что делает меня чертовски эффективным лентяем. Я реализовал такую схему:
Локально у меня Grav висит в докере, аналогично тому как он задеплоен на VPS. В нем я пишу контент и вношу правки в шаблон.
Все это конечно же пушится в github-репозиторий.
В репозитории настроена CI/CD схема деплоя на VPS, которая при пуше в main:
подключается к серверу по SSH
идет в папку с репозиторием
пуллит свеженькие изменения из ветки main
копирует изменившиеся файлы в папку проекта
Почему отдельная папка для репозитория? Когда репо был в папке проекта, что-то капитально ломалось на стороне прода, и куда проще пуллить в отдельную папку и оттуда только нужное копировать, чем искать где там проблема.
Для любопытствующих привожу конфиг для github actions:
name: CI/CD Pipeline
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Exec remote cmd w. ssh key
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_IP }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT }}
script: |
cd project_git
GIT_SSH_COMMAND="ssh -i /home/user/.ssh/github_project" git stash
GIT_SSH_COMMAND="ssh -i /home/user/.ssh/github_project" git pull
cp -R ./config/www/user/pages/* /home/user/project_folder/config/www/user/pages/
cp -R ./config/www/user/themes/* /home/user/project_folder/config/www/user/themes/
Пояснительная бригада превентивно выехав поясняет, что параметр:
GIT_SSH_COMMAND="ssh -i /home/user/.ssh/github_project"
идущий перед привычным вызовом git - ни что иное, как указание какой ssh-ключ использовать для подключения к репо. Дело в том, что deploy key в разных репо не может быть один и тот же, а у меня на этом сервере несколько проектов автоматизированы аналогичным способом. Можно конечно написать в ~/.ssh/config инструкции на этот счет, но написать так в YAML-файл который управляет деплоем проще и быстрее.