На дворе не 2005 год, и уже давно пора перестать заливать обновления сайта архивами или отдельными файлами через FTP. Как же доставлять обновления?
Современный подход к развёртыванию веб-приложения подразумевает использование Docker'а вкупе с Kubernetes, а тестирование и сборка приложения перед этим происходит в пайплайнах (pipeline) какой-нибудь системы непрерывной интеграции (CI).
Но зачем все эти ресурсоёмкие сложности, если у Вас простой сайт на обычном хостинге, на котором поддержка докера не предусмотрена?
В основе предлагаемого метода лежит использование команды git worktree, дающей возможность работать с несколькими ветками git-репозитория в отдельных папках. Также подразумевается, что кодовая база вашего проекта уже давно ютится в git-репозитории.
Почему git worktree?
Если вы привычным образом склонируете репозиторий в public_html, то рискуете оставить папку с именем .git общедоступной (а это исходный код вашего приложения). На неё придётся прописывать правила в .htaccess или прибегать к иным костылям. В любом случае это небезопасный путь.
С git worktree клон репозитория может находится в укромном местечке, а в отделённой ветке будет файл с именем .git, в котором будет указан только путь до клона.
Кроме того, имея всего один клон репозитория, вы можете выделить из него разные ветки на несколько сайтов. Например, ветка test на тестовую версию сайта, а ветка production на боевую версию.
Настройка хостинга и первичная установка приложения
Папка bin
Этот шаг необязательный, но я рекомендую первым делом создать папку bin в корне, куда будут помещены исполняемые файлы, и добавить её в переменную окружения PATH. Вводим в консоль:
mkdir $HOME/bin export PATH=$HOME/bin:$PATH
В корневой папке вашего профиля следует создать файл с именем .bash_profile с содержимым:
export PATH=$HOME/bin:$PATH
Вы можете сделать это с помощью следующей консольной команды:
echo "export PATH=\$HOME/bin:\$PATH" > $HOME/.bash_profile
Содержимое .bash_profile будет исполняться SSH-клиентом по умолчанию при подключении.
PHP
Если в своём проекте вы используете PHP, то учтите, что по умолчанию в консоли обычно используется PHP 5.3 (введите php --version, чтобы узнать версию). Для удобства использования в консоли PHP той версии, которая вам нужна, добавьте в папку bin ссылку. На примере PHP 7.2:
cd $HOME/bin; ln /opt/php7.2/bin/php php -s
Теперь php --version вернёт PHP 7.2.15 (примечание: минорная версия может отличаться).
Также разместите в папке bin последнюю версию Composer.
Важно: не забывайте о правах на чтение/запуск исполняемых файлов в папке bin.
Сертификаты
Если ваш проект находится в закрытом git-репозитории, то хосту понадобятся права доступа.
Можно использовать логин и пароль от учётной записи с правами, но мы поступим правильно и сгенерируем Deploy key (ключ, дающий доступ к репозиторию только на чтение).
В консоли пишем:
ssh-keygen -t rsa -f id_rsa
Для каждого сервера (gitlab, bitbucket, github...) можно сгенерировать свой ключ, меняя значение id_rsa параметра -f.
В домашней папке появится папка с именем .ssh, в которой будут находиться сгенерированные приватные и публичные ключи. Создадим файл ~/.ssh/config со следующим содержимым:
Для gitlab:
Host gitlab.com RSAAuthentication yes IdentityFile ~/.ssh/id_rsa
Для bitbucket:
Host bitbucket.org IdentityFile ~/.ssh/id_rsa
Для разных хостов записи с одинаковыми или разными ключами можно совместить в одном файле. Пример:Публичный ключ (содержимое файла id_rsa.pub) необходимо добавить в список ключей развёртывания (Deploy Keys) репозитория.Форма для добавления ключа развёртывания в GitLab
GIT
На многих хостингах рунета по сей день крутится древний Centos с версией git 1.8.3. Такая старая версия git'a абсолютно нам не подходит. Команда git worktree появилась ещё в 2015 году в git 2.5, а в git 2.7 значительно расширилась по функционалу.
Но не беспокойтесь, на машинах Timeweb предустановлен git 2.7.4.
Подготовим папку ~/git для размещения в ней клонов git-репозиториев и перейдём в неё:
mkdir $HOME/git cd $HOME/git
Клонируем репозиторий project-name в папку project-dir:
Перед выполнением команды замените путь до git-репозитория и имена ветки и проекта на свои.
git clone --mirror -b master git@gitlab.com:contributor/project-name.git project-dir
где "master" – имя ветки, на которую будет переключен клон по умолчанию (если не указывать, то будет выбрана «основная ветка»).
ВАЖНО: следует указать ту ветку, которая не будет участвовать в разворачиваемых приложениях, т.к. дерево worktree нельзя создать из активной ветки. Например, если у вас есть ветки master, test, production, то следует выбрать master, а test и production будут вскоре развёрнуты.
Как и на многих хостингах, в Timeweb применяется практика с использованием папки public_html в качестве корневой папки сайта (веб-папки проекта). Если у вас мультисайтовый аккаунт (более одного сайта на хосте), то и папок public_html несколько. Например:
- ~/www-project-name/public_html - для боевого приложения,
- ~/www-test-project-name/public_html - для тестов.
Названия папок берутся из названий сайтов в админской панели и приведены здесь для примера.
Обычно веб-папка проекта не всегда совпадает с корневой папкой приложения: в веб-папке принято размещать только публичные файлы — JS-скрипты, картинки, стили, шрифты; а также файл скрипта для инициализации приложения. Весь рабочий код остаётся вне веб-папки. Эта практика приводит к единственному решению — public_html должна быть ссылкой на веб-папку проекта. Сам проект можно разместить рядом, в папке branch.
Развернём ветку production. На этом этапе уже пора добавить в админской панели свой сайт www-project-name и вместе с тем создадутся папки ~/www-project-name/public_html. Папку public_html со страницей-заглушкой нужно сразу удалить. Теперь создаём worktree в папке ~/www-project-name/branch:
cd $HOME/git/project-dir git worktree add $HOME/www-project-name/branch production
Создаём символьную ссылку веб-папки на месте папки public_html, которую вы ранее удалили. В моём примере веб-папка является папкой web в корне проекта, у вас она может отличаться.
ln -s $HOME/www-project-name/branch/web $HOME/www-project-name/public_html
Поскольку в моём приложении есть и ветка test, то таким же образом я разверну и её:
cd $HOME/git/project-dir git worktree add $HOME/www-test-project-name/branch test ln -s $HOME/www-test-project-name/branch/web $HOME/www-test-project-name/public_html
Установка приложения
На текущем этапе все файлы нужных веток репозитория проекта размещены в соответствующих каталогах:
~/www-project-name/branch – production-ветка
~/www-test-project-name/branch – test-ветка
Осталось установить и настроить приложение. Переходим в корневую папку приложения:
cd $HOME/www-project-name/branch
Настройте конфигурационные файлы, которые, кстати, должны быть предварительно добавлены в .gitignore.
Если вы используете пакетные менеджеры, то подтяните необходимые пакеты. Применительно к Composer:
composer install --no-dev
Установите само приложение. Например, если это Yii2:
php yii init php yii migrate
Обновление
Приложение развёрнуто. Осталось настроить обновление приложения — вы ведь будете его поддерживать?
Для этого в папке bin создадим скрипт, который будет подгружать изменения из git-репозитория и применять их. На моём сервере это будет файл ~/bin/hi (с правами на запуск):
~/bin/hi
Содержимое файла в моём случае:
echo "Привет!" echo "";echo "Синхронизация репозитория"; cd $HOME/git/project-dir;git fetch; echo "";echo "Обновление production"; # переход к файлам приложения cd $HOME/www-project-name/branch; # сброс и подгрузка изменений git reset --hard;git pull origin production; # установка нужных версий зависимостей /opt/php7.2/bin/php $HOME/bin/composer install —no-dev; # к php нужно прописывать полные пути! # моё приложение поддерживает миграции с бэкапами /opt/php7.2/bin/php core migrate/up --all --backup; # для Yii2 это будет ...php yii migrate --interactive=0 echo "";echo "Обновление test"; cd $HOME/www-test-project-name/branch; git reset --hard;git pull origin test; /opt/php7.2/bin/php $HOME/bin/composer install --no-dev; /opt/php7.2/bin/php core migrate/up --all;
Таким образом, я буду заходить на сервер и здороваться с ним командой hi, а он поздоровается со мной, подгрузит с гита и установит обновления моего приложения.
Такой способ предоставляет обновление в полуавтоматическом режиме. Однако можно настроить и полностью автоматическое обновление приложений, например, сразу после пуша изменений в соответствующую ветку. В этом вам может помочь, например, опция web-push в настройках удалённого репозитория.
Примечание:
Вы, наверно, заметили, что перед подтягиванием обновлений вызывается команда
git reset --hard
Поэтому пользовательские файлы и файлы конфигурации должны быть помещены в .gitignore, чтобы git их не удалил при обновлении.
Комментарии
А че с гитхабом примеров нет?
Создание двух веток внутри домена будет считаться за два сайта? Или это в рамках 1го?
Получается БД у теста и продакшена будет одна и таже?
Что-то тут не так с терминологией.
"Ветка" относится к системе контроля версий. С доменом (доменным именем) это не пересекается.
Если под "сайтом" подразумевается единица измерения, относящаяся к хостингу и его тарифам, то один сайт это одна публичная папка. Количество БД прописано в тарифе. На тарифе Optimo+, например, неограниченное кол-во БД на 10 сайтов.
На один такой сайт может вести как один домен, так и несколько.
Если под "сайтом" подразумевается определённая версия веб-морды и контента, то тут уже другие рассуждения. В том случае, когда код приложения учитывает разные домены, определяя для каждого домена свою логику, конфигурацию БД и прочий контент, то можно на одной ветке разместить несолько сайтов. Разварачивая такой "мультисайт" на тарифе под один сайт, можно быстро упереться в лимиты тарифа.
> А че с гитхабом примеров нет?
Чемодан не занесли.
Речь идет про создание двух директорий с проектом. Следовательно к одной привязывается "боевое" доменное имя, а к другой тестовое. Например, поддомен staging.example.com или test.example.com
В контексте данной статьи я так понимаю одна база, но всегда можно попробовать на тестовой версии изменить настройки подключения и выгрузить дамп с боевой базы на тестовую.
Вот, кстати, фраза, которая намекает, что каждая ветка приложения может быть индивидуально настроена:
> Настройте конфигурационные файлы, которые, кстати, должны быть предварительно добавлены в .gitignore.
Хотите одну БД на тест и прод - пожалуйста, но я не вижу в этом смысла, т.к. тестовая среда не должна влиять на продуктовую. Разве что использовать префиксы для таблиц или приложение работает в режиме ReadOnly.
Если только это не preproduction :)
Иными словами новый код, но работающий с боевой базой, чтобы проверить как оно на production будет.
Но лучше новую функциональность тестировать отдельно. Тут я полностью согласен.
Директив `RSAAuthentication` для файла ~/.ssh/config в случае Gitlab уже устарела и вместо неё следует использовать `PubkeyAuthentication`
Все же немного не понятно ваше "Важное" замечание:
>> ВАЖНО: следует указать ту ветку, которая не будет участвовать в разворачиваемых приложениях, т.к. дерево worktree нельзя создать из активной ветки. Например, если у вас есть ветки master, test, production, то следует выбрать master, а test и production будут вскоре развёрнуты.
На практике, у меня никаких конфликтов не возникало при использовании имени ветки master в качестве "production". Т.е. следующие команды не вызвали никаких конфликтов (более полу-года уже работает):
$ git clone --mirror -b master git@gitlab.com:contributor/project-name.git project-dir
$ cd $HOME/git/project-dir
$ git worktree add $HOME/www-project-name/branch master
Может есть какие-то подводные камни, которых я не вижу?...
А про постгрю скажу так: приложение посерьёзнее блога/лендоса лучше сразу хостить на VDS в контейнерах. Там будет и постгря и всё остальное.
Первым делом :
$ git --version
git version 2.17.1
расходимся пацаны...