Top.Mail.Ru
OSEN-НИЙ SAAALEСкидка 50% на виртуальный хостинг и VDS
до 30.11.2025 Подробнее
Выберите продукт

Zero‑downtime деплой на виртуальном хостинге: релизы через симлинки, atomic deploy и откаты

Практический гайд по деплою без простоя на виртуальном хостинге: структура релизов через симлинки, атомарное переключение, rsync, shared‑директории, миграции БД, откаты и ротация релизов. Подходит для PHP‑проектов и популярных CMS; даны готовые SSH‑команды и чек‑листы.
Zero‑downtime деплой на виртуальном хостинге: релизы через симлинки, atomic deploy и откаты

Zero‑downtime деплой на виртуальном хостинге — это вполне достижимая практика, даже если у вас нет root‑доступа и нельзя перезапускать веб‑сервер. Ключ — в релизных директориях, симлинке current и атомарном переключении на новый релиз. Такой подход минимизирует риски, ускоряет релизы и даёт гарантированный откат за секунды.

Что такое zero‑downtime деплой и почему это работает на виртуальном хостинге

Zero‑downtime деплой — это публикация новой версии без перерывов в обслуживании. Посетитель в любой момент получает ответ, даже когда вы выкатываете свежую сборку. На виртуальном хостинге это достигается за счёт подготовки релиза в отдельной директории и атомарного обновления симлинка, на который смотрит DocumentRoot вашего сайта. Переключение симлинка — операция уровня файловой системы, она выполняется мгновенно и не требует перезапуска сервисов.

Идея проста: у вас есть папка releases/ с датированными релизами, папка shared/ с общими данными (загрузки, кэш, конфиги) и симлинк current, который указывает на активный релиз. Веб‑сервер обслуживает current/public. Вы загружаете новую версию в releases/2025XXXXXX, прогреваете её, затем атомарно переключаете current — и всё.

Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Базовая структура проекта

~/app/
  releases/
    20250101123000/
      public/
      vendor/
      ...
  shared/
    storage/
    uploads/
    .env
  current -> releases/20250101123000

Что хранить в shared/:

  • uploads/, storage/, директории с пользовательским контентом и файлами кэша;
  • конфигурацию и секреты (.env), чтобы не класть их в каждый релиз;
  • долгоживущие кэши и сессии, если они файловые.

В каждом релизе создаются симлинки на эти общие директории, например releases/<ts>/storage -> ../../shared/storage, releases/<ts>/.env -> ../../shared/.env. Это позволяет менять код, не трогая данные.

Связываем веб‑корень с релизом

На большинстве виртуальных хостингов DocumentRoot — это public_html/ или www/. Идеально, если можно настроить путь на ~/app/current/public. Если нельзя — сделайте симлинк из public_html на ~/app/current/public. Обязательно проверьте, что на веб‑сервере разрешены симлинки (на Apache — Options FollowSymLinks), а права доступа позволяют чтение.

Совет: держите public/ как корень сайта, чтобы не светить исходники. Если у вас CMS без каталога public, используйте .htaccess для блокировки доступа к служебным файлам.

Rsync‑выгрузка релиза на сервер с инкрементальной синхронизацией

Пайплайн деплоя: шаг за шагом

1) Сборка артефакта

На локальной машине или в CI собираем артефакт релиза: зависимости, минифицированные ассеты, скомпилированный фронтенд. Для PHP‑проектов практично собирать vendor/ локально под совместимую версию ОС/архитектуры и загружать как есть. Это ускорит выкладку на виртуальном хостинге и исключит долгую установку пакетов на сервере.

2) Выгрузка на сервер rsync‑ом

Для передачи файлов используйте rsync. Он быстр, докачивает инкрементально и экономит трафик. Передаём в новую релизную директорию, названную по таймстемпу:

TS=$(date +%Y%m%d%H%M%S)
REL=releases/$TS
ssh user@host "mkdir -p ~/app/$REL"
rsync -az --delete \
  --exclude ".git" \
  --exclude "node_modules" \
  --exclude "storage" \
  ./ user@host:~/app/$REL

Опцию --delete используйте только при отправке в уникальную новую папку релиза, а не при обновлении shared/. Если нужно экономить место, задействуйте --link-dest к предыдущему релизу — неизменившиеся файлы будут хардлинкованы, но учтите, что на некоторых тарифах жёсткие ссылки могут быть ограничены.

3) Привязываем shared‑данные

После загрузки создайте внутри релиза симлинки на общие директории и конфиги:

ssh user@host "cd ~/app/$REL && \
  ln -s ../../shared/storage storage && \
  ln -s ../../shared/uploads uploads && \
  ln -s ../../shared/.env .env"

Если проект требует прав на запись, проверьте, что umask и права на shared/ корректны (обычно 755 для директорий и 644 для файлов достаточно; для записываемых директорий — 775/777 в зависимости от модели пользователя веб‑сервера).

4) Предпродовые шаги

  • Прогрев кэшей, генерация автозагрузки, оптимизация роутов — всё это делайте в каталоге нового релиза, ещё до переключения.
  • Миграции БД — только обратно совместимые. Вносите сначала добавочные изменения (колонки по умолчанию, новые таблицы), а разрушающие шаги откладывайте на следующий релиз.
  • Проверьте новый релиз через временный URL или симлинк, не задействующий current (например, current_canary для теста).

5) Atomic deploy: атомарное переключение

Критический момент — сменить current так, чтобы ни одна просьба посетителя не попала в «пустоту». Самый надёжный путь — создать новый симлинк и переименовать его поверх старого одной операцией rename(2):

ssh user@host "cd ~/app && \
  ln -s $REL current.new && \
  mv -Tf current.new current"

mv -T заставляет трактовать цель как файл, а не каталог; в связке с -f вы получаете атомарную замену старого симлинка на новый. На некоторых минималистичных окружениях mv без -T тоже корректно заменит симлинк, но поведение может отличаться.

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

6) Пост‑деплой и здоровье

  • Сделайте лёгкий health‑check (HTTP‑запрос к эндпойнту статуса, чтение версии из файла, простая SQL‑проверка).
  • Обновите ассеты с версионированием (хеш в имени файла), чтобы клиенты не тянули старый кэш.
  • OPcache: путь меняется вместе с релизом, поэтому кешированные скрипты не конфликтуют. На большинстве хостингов включён opcache.validate_timestamps, и новый путь автоматически загрузится. Подробнее — в статье про OPcache и оптимизации на шаред‑хостинге.

Откаты без боли

Zero‑downtime означает и мгновенный откат. Держите несколько прошлых релизов и переключайтесь назад тем же атомарным приёмом. Пример простого отката на предыдущий таймстемп:

ssh user@host "cd ~/app && \
  PREV=$(ls -1dt releases/* | sed -n '2p') && \
  [ -n \"$PREV\" ] && ln -s \"$PREV\" current.rollback && \
  mv -Tf current.rollback current"

Храните историю релизов (например, 5–10 шт.) и автоматически чистите старые, чтобы не упираться в квоту. Удалять можно смело после нескольких успешных выкладок и отсутствия ошибок в логах.

Откат релиза через переключение симлинка current на предыдущую версию

Детали rsync для безопасного деплоя

  • -a сохраняет права и симлинки; -z сжимает трафик; --info=progress2 помогает контролировать прогресс.
  • --delete используйте только в пределах нового релиза.
  • --link-dest=../<prev> для дедупликации неизменившихся файлов между релизами — полезно при частых релизах.
  • Чётко задайте --exclude для .git, node_modules, tests, локальных кэшей, чтобы не тянуть лишнее.

Миграции без простоя: базовые приёмы

  • Разделяйте миграции на «добавочные» и «разрушающие». Сначала добавочные: новые таблицы, колонки с дефолтом и NULL‑совместимостью, индексы.
  • Пишите код, совместимый со старой и новой схемой (feature flags, двусторонняя поддержка колонки в течение нескольких релизов).
  • Тяжёлые DDL‑операции выполняйте ночью или используйте онлайн‑алгоритмы MySQL (ALGORITHM=INPLACE, LOCK=NONE, когда возможно).
  • Делайте mysqldump --single-transaction как страховку; это не даёт zero‑downtime само по себе, но спасёт от потери данных.

Интеграция с Git и CI

Вместо того, чтобы держать git на проде, надёжнее собирать артефакты в CI и отправлять их rsync‑ом. Подход «build once, deploy many» гарантирует повторяемость. Минимальный сценарий в CI: заархивировать сборку, передать на сервер, распаковать в releases/<ts>, связать shared, сделать health‑check и переключить current. Результат: быстрый, предсказуемый и обратимый деплой.

Пример скрипта деплоя с откатом

# локально: ./deploy.sh user host ~/app
USER=$1
HOST=$2
ROOT=$3
TS=$(date +%Y%m%d%H%M%S)
REL=releases/$TS

ssh $USER@$HOST "mkdir -p $ROOT/$REL"
rsync -az --delete \
  --exclude ".git" \
  --exclude "node_modules" \
  --exclude "storage" \
  ./ $USER@$HOST:$ROOT/$REL

ssh $USER@$HOST "cd $ROOT/$REL && \
  ln -s ../../shared/storage storage && \
  ln -s ../../shared/uploads uploads && \
  ln -s ../../shared/.env .env && \
  cd $ROOT && ln -s $REL current.new && mv -Tf current.new current"

# откат на предыдущий релиз
# ssh $USER@$HOST "cd $ROOT && PREV=$(ls -1dt releases/* | sed -n '2p'); \
#   [ -n \"$PREV\" ] && ln -s \"$PREV\" current.rollback && mv -Tf current.rollback current"

Скрипт предельно простой: создаёт релиз, синхронизирует файлы, привязывает shared, переключает current. Откат — симметричный.

Оптимизация ассетов и кэшей

Чтобы уменьшить задержки при первом хите, прогревайте кэши до переключения. Для PHP‑фреймворков это может быть «компиляция» контейнера, предзагрузка маршрутов и конфигов. Статические ассеты следует версионировать по хешу файла, чтобы исключить коллизии кешей CDN/браузеров после релиза.

Лимиты и особенности виртуального хостинга

  • Квота диска: держите только ограниченное число релизов и включайте дедупликацию через --link-dest, если доступна.
  • Симлинки: проверьте, что веб‑сервер следует симлинкам и что симлинки внутри DocumentRoot разрешены.
  • Оптимизация по времени: крупные каталоги (например, vendor/) лучше собирать заранее и передавать целиком, чем устанавливать пакеты на сервере.
  • Ограничения по CPU/IO: избегайте тяжёлых операций во время пикового трафика.

Если задачам уже тесно на шаред‑хостинге (root, фоновые воркеры, очереди, кастомные демоны) — рассмотрите перенос на VDS. Подробный план миграции — в статье как переехать со shared на VDS.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Диагностика и логирование

Сохраняйте номер релиза в файле REVISION и отдавайте его через скрытый эндпойнт для быстрой диагностики. При проблемах по логам веб‑сервера и PHP‑FPM быстро понятно, на каком релизе возникла ошибка. Логи тоже можно положить в shared/, чтобы не терять историю при переключениях.

Мини‑чек‑лист перед релизом

  • Свежий бэкап БД и файлов shared/.
  • Релиз собирается в CI, зависимости зафиксированы (composer.lock, package-lock.json).
  • Релизная папка создана, shared привязан симлинками.
  • Критичные миграции либо онлайн, либо отложены.
  • Health‑check успешен на canary‑путь.
  • Переключение current — через mv -Tf.
  • План отката понятен: какой релиз «предыдущий» и как его вернуть.

FAQ

Нужно ли перезапускать веб‑сервер для подхвата нового кода?

Нет. Мы переключаем путь, а не переписываем файлы на месте. OPcache загрузит скрипты из нового пути, и они не конфликтуют со старыми.

Можно ли делать деплой без SSH?

Технически можно через SFTP, но без SSH сложно обеспечить атомарность и пост‑деплой шаги. Для надёжного zero‑downtime понадобится именно SSH.

Как безопасно чистить старые релизы?

Периодически удаляйте всё, что старше N последних релизов, кроме того, на который указывает current. Убедитесь, что нет активных откатов.

Что с миграциями, блокирующими таблицы?

Ищите онлайн‑варианты, разбивайте на шаги или переносите на низкую нагрузку. В крайнем случае используйте короткое техническое окно.

Как хранить секреты?

Вынесите их в shared/.env и ссылайтесь симлинком. Не включайте секреты в git и релизные архивы.

Итоги

Деплой через релизные директории и симлинки — простой и надёжный способ получить zero‑downtime на виртуальном хостинге. Вы готовите релиз «в стороне», проверяете его, затем «атомно» переключаете current одной операцией. В довесок получаете мгновенные откаты и контролируемый жизненный цикл релизов. Такой подход одинаково хорошо работает для PHP‑фреймворков и популярных CMS — нужен лишь SSH и дисциплина в миграциях.

Поделиться статьей

Вам будет интересно

Автобэкапы сайта и БД в S3: restic/borg, шифрование и проверка восстановления OpenAI Статья написана AI Fastfox

Автобэкапы сайта и БД в S3: restic/borg, шифрование и проверка восстановления

Настроим надежные автобэкапы сайта и баз данных в S3/Object Storage: restic и borg, клиентское шифрование, cron-задачи, дампы чере ...
Wildcard SSL для мультисайтов и SaaS: выпуск через DNS‑01, автоматизация и подводные камни OpenAI Статья написана AI Fastfox

Wildcard SSL для мультисайтов и SaaS: выпуск через DNS‑01, автоматизация и подводные камни

Подробный гайд для админов и DevOps: когда нужен wildcard SSL в мультисайтах и SaaS, почему выбирают DNS‑01, как автоматизировать ...
WAF на VDS: ModSecurity + OWASP CRS для Nginx — установка и тюнинг под популярные CMS OpenAI Статья написана AI Fastfox

WAF на VDS: ModSecurity + OWASP CRS для Nginx — установка и тюнинг под популярные CMS

Разворачиваем WAF на Nginx с ModSecurity и OWASP CRS на VDS: установка, запуск в DetectionOnly, подключение правил и безопасные ис ...