Деплой без простоя: как переключать релизы через symlink
Символическая ссылка позволяет переключить сайт на уже подготовленный релиз одним атомарным действием. Но она не обновит код внутри работающего процесса и не защитит от несовместимой миграции базы: разбираем, где метод действительно сокращает простой, а где оставляет риск.

При обновлении сайта файлы можно скопировать поверх текущей версии — и получить запросы, которые одновременно читают старый и новый код. Переключение символической ссылки на готовый каталог релиза убирает эту гонку на уровне файлового пути, но само по себе не гарантирует нулевой простой: процесс приложения, база и фоновые задачи могут продолжить работать по старым правилам.
Что именно переключает символическая ссылка
Для каждого релиза создают отдельный каталог: например, releases/2025-03-08. В нём лежит целостная версия приложения. Путь current указывает на активный каталог, а веб-сервер и приложение обращаются к файлам через него.
Новый релиз собирают отдельно и проверяют до переключения. Затем создают временную ссылку рядом с current и атомарно заменяют ею активную. На Linux переименование в пределах одной файловой системы не оставляет промежутка, когда целевой путь отсутствует: читатель увидит старую или новую ссылку. Сам релиз при этом должен быть готов до переключения.
Термин. Атомарное переименование — операция, которая для других процессов выглядит как единый переход, а не как несколько видимых шагов. Оно не делает атомарными изменения в базе или перезапуск приложения.
Ссылка переключает указатель на файлы, а не весь работающий сервис. Нулевой простой зависит от того, переживут ли переключение одновременно старые и новые процессы, данные и запросы.
Переключение на Linux: минимальная последовательность
Пример рассчитан на GNU/Linux и GNU coreutils 9.5 или новее. Каталоги релизов и ссылка должны находиться на одном файловом разделе; временная ссылка создаётся в том же каталоге, что и current. Подставьте реальные пути и идентификатор уже собранного релиза. Если деплои могут запускаться параллельно, сначала настройте их сериализацию: два переключения одновременно способны сделать активным не тот релиз, который вы ожидали.
set -eu
APP_ROOT=/srv/myapp
RELEASE_ID=replace-with-built-release-id
RELEASE="$APP_ROOT/releases/$RELEASE_ID"
NEXT="$APP_ROOT/.current.next-$RELEASE_ID"
# Не переключаемся на каталог, которого нет.
test -d "$RELEASE"
# Временная ссылка создаётся рядом с current.
ln -s "$RELEASE" "$NEXT"
# GNU mv заменяет ссылку current одним переименованием.
mv -Tf "$NEXT" "$APP_ROOT/current"
# Проверяем, куда теперь указывает current.
readlink "$APP_ROOT/current"
Не запускайте копирование файлов прямо в каталог, на который уже указывает current: тогда старый и новый код снова смешаются. Каталог релиза после публикации лучше считать неизменяемым. Если сборка не прошла проверку, не создавайте ссылку на неё.
Что метод гарантирует — и чего не обещает
При правильном переключении новые обращения к пути current получают новый релиз, а уже открытые файлы могут оставаться доступны старым запросам. Но поведение зависит от того, как приложение загружает код. Долгоживущий процесс может держать уже загруженные модули, рабочий каталог или файловые дескрипторы. Одной замены ссылки недостаточно, чтобы заставить такой процесс перечитать приложение.
В PHP, Node.js и других средах детали различаются: проверьте, когда именно ваш рантайм загружает код и кэширует файлы, а затем решите, нужен ли мягкий перезапуск процессов после переключения. Перезапуск тоже требует проверки: если остановить старые процессы до готовности новых, пауза появится уже на этом шаге.
Есть и риск за пределами файловой системы. Старый запрос может начаться до переключения, а завершиться после него. В это время запрос, фоновая задача или соседний процесс уже могут работать с новой версией. Поэтому интерфейсы между версиями приложения и схемой базы должны оставаться совместимыми в переходный период.
Где ломается релизная схема
- Миграция удаляет поле сразу. Старый процесс ещё обращается к нему, а новая схема уже не позволяет выполнить запрос. Сначала добавляйте совместимые изменения, переводите приложение на новую схему и только в отдельном релизе удаляйте старое.
- Загрузки лежат внутри релиза. После переключения пользователи могут видеть разные наборы файлов в зависимости от пути и процесса. Пользовательские загрузки и другие данные, которые должны переживать выкладку, храните отдельно от каталогов релизов.
- Новый релиз не содержит старые статические файлы. Открытая страница или кэш браузера ещё запрашивают прежний файл, которого в новом каталоге уже нет. Сохраняйте совместимость URL ресурсов на время жизни старых страниц и кэшей.
- Проверили только сам факт смены ссылки. Это доказывает, что изменился путь, но не что приложение отвечает. Проверяйте реальный HTTP-ответ, ошибки приложения и доступность зависимостей после переключения.
- Очистили предыдущий релиз слишком рано. Старые процессы и длительные запросы ещё могут обращаться к его файлам. Не удаляйте его сразу после переключения: дождитесь, пока старые процессы завершатся и проверка нового релиза пройдёт.
Как организовать безопасный откат
Откат по той же схеме — это переключение ссылки на предыдущий релиз. Оно быстро вернёт старые файлы, но не отменит автоматически миграцию базы, уже отправленные сообщения или изменения пользовательских данных. До выкладки проверьте, что предыдущий каталог сохранён и старая версия может работать с текущей схемой данных.
- Соберите релиз в новом каталоге, не меняя активный.
- Проверьте комплектность файлов, конфигурацию и доступность внешних зависимостей.
- Выполните только совместимые изменения схемы и данных; отдельно оцените, можно ли безопасно повторить миграцию.
- Переключите ссылку атомарной заменой и проверьте ключевые HTTP-маршруты и фоновые задачи.
- Если проверка провалилась, переключите ссылку обратно, затем разберите логи и состояние данных. Не удаляйте неудачный релиз до выяснения причины.
Сравнение способов выкладки
| Подход | Что происходит | Основной риск |
|---|---|---|
| Копирование поверх текущих файлов | Файлы заменяются по одному | Запрос может увидеть смешанный набор старой и новой версии |
| Ссылка на каталог релиза | Путь переключается на заранее подготовленный каталог | Не обновляет автоматически код в долгоживущих процессах и не решает совместимость базы |
| Blue-green | Параллельно работают два окружения, трафик переключается между ними | Нужно поддерживать дополнительное окружение и согласовывать общие данные |
Символические ссылки подходят, когда приложение читает файлы с общего диска, релиз можно целиком собрать заранее, а короткое переключение пути безопасно для запросов. Если нужно постепенно переводить трафик, проверять новую версию на части запросов или независимо откатывать окружение, рассматривайте blue-green или другой способ маршрутизации.
Что измерять в проде
Без замеров нельзя утверждать, что переключение прошло без простоя. Сравните показатели до и после выкладки при одинаковом способе наблюдения: долю ошибочных HTTP-ответов, задержку ключевых запросов, число перезапусков процессов, ошибки фоновых задач и состояние соединений с базой. Отдельно отметьте время начала переключения и момент, когда проверки нового релиза прошли.
Проверяйте не только главную страницу. Выберите маршруты, которые обращаются к базе, загружают ресурсы и используют авторизацию, а затем проверьте их вместе с очередями и фоновыми задачами. Если в ходе выкладки растут ошибки или задержки, откат ссылки имеет смысл лишь тогда, когда старая версия совместима с уже изменёнными данными.
Что делать дальше
Сначала проверьте схему на тестовом окружении: подготовьте два соседних релиза, выполните переключение и убедитесь, что предыдущий каталог остаётся доступен работающим процессам. Затем отработайте откат после миграции и добавьте автоматическую проверку HTTP-ответа, ошибок приложения и фоновых задач. Именно совместимость этих частей, а не скорость команды переименования, определяет, будет ли деплой действительно незаметным для пользователей.
Логотип и фирменный стиль: Три концепции разного характера, палитра с обоснованием, проверка на читаемость в малом размере и в один цвет.
Спросить за 60 ₽Источники: GNU Coreutils: mv invocation, Linux man-pages: rename(2)
Частые вопросы
Замена ссылки действительно проходит без простоя?
Что делать, если у меня не GNU/Linux или другая версия coreutils?
Можно ли откатиться одной заменой ссылки?
Нужно ли перезапускать приложение после переключения?
Можно ли удалять старый каталог сразу после деплоя?
Почему после переключения сайт всё ещё отдаёт ошибки?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.