← Все записи
Kamal / Docker / Диагностика

Деплой сломался: диагностика и откат с Kamal

Определяем, какая версия обслуживает сайт, проверяем healthcheck и логи, затем возвращаем рабочий релиз без потери причины сбоя.

Красный workflow не всегда означает, что сайт уже сломан. Сборка могла упасть до публикации образа, новый контейнер мог не пройти healthcheck, а проверка HTTPS — завершиться ошибкой уже после переключения трафика.

Разберём восстановление на примере статического сайта из первой статьи. Здесь нет базы данных или миграций: возвращаем предыдущий контейнер с HTML и Nginx. Все домены и адреса вымышлены, значения ключей и идентификаторы релизов в условном выводе скрыты как ********.

1. Не запускаем второй деплой поверх первого

Если работает автодеплой из GitHub Actions, сначала остановите новые автоматические публикации на время восстановления:

gh workflow disable deploy.yml --repo your-github-user/hello-kamal
gh run list --repo your-github-user/hello-kamal --workflow deploy.yml --limit 5

Отключение workflow не отменяет уже созданные запуски. Проверьте выполняющиеся и ожидающие задания. Не начинайте ручной откат параллельно активному kamal deploy: сначала выясните его состояние и дождитесь завершения либо осознанно остановите его.

Состояние блокировки можно посмотреть так:

kamal lock status

Не освобождайте блокировку только потому, что увидели сообщение «locked»: другой деплой может ещё работать.

2. Фиксируем текущее состояние

Из каталога проекта выполните:

kamal app version
kamal app details
kamal app logs --lines 100

Эти команды отвечают на разные вопросы: какая версия запущена, какие контейнеры видны и что пишет приложение. Проверьте также последний успешный и первый неудачный шаг Actions.

Сохраните время сбоя и версии локально. Если делитесь выводом, заранее уберите адреса, пользовательские данные и секреты. Условная запись для публикации:

service: hello-kamal
host: example.com
version: ********
registry_password: ********
request_authorization: ********

Полный docker inspect может содержать переменные окружения, а конфигурация Kamal — секреты. Не прикладывайте такой вывод к публичному issue без проверки.

3. Находим границу сбоя

Где остановилось Что проверить
Сборка Docker Dockerfile, COPY, зависимости, текст ошибки сборки
Публикация в GHCR Права токена и доступ workflow к пакету
SSH Нужный пользователь, ключ, known_hosts, доступность порта
Запуск контейнера Логи, команда запуска, конфигурация приложения
Healthcheck Порт, путь /up, фактический HTTP-ответ
Проверка после деплоя Какая версия активна, DNS, HTTPS, содержимое страницы

Если ошибка произошла до переключения трафика, старая версия может продолжать работать. Сначала подтвердите это через kamal app version и внешний запрос; ненужный откат только добавит изменений.

4. Проверяем healthcheck внутри контейнера

Для нашего Nginx-примера:

kamal app exec --reuse 'nginx -t'
kamal app exec --reuse 'wget -qO- http://127.0.0.1:8080/up'

--reuse выбирает работающий контейнер, а не создаёт новый для команды. Эти проверки показывают состояние именно текущего приложения. Если неудачный новый контейнер уже остановлен, отдельно изучите его логи и версию; успешный /up старого контейнера ничего не доказывает о новом. Опции доступны в командах приложения Kamal.

В конфигурации должны совпадать порт Nginx и app_port:

proxy:
  app_port: 8080
  healthcheck:
    path: /up

Прокси ждёт готовности по настроенному endpoint, но статический 200 на /up не проверяет текст главной страницы. Поэтому после деплоя нужна и внешняя проверка. Механизм описан в документации прокси.

5. Выбираем рабочую версию

Посмотрите сохранённые контейнеры:

kamal app containers -q

Найдите последний заведомо рабочий релиз и его версию в имени контейнера. В статье идентификатор намеренно скрыт:

hello-kamal-web-********

Не подставляйте маску буквально. Используйте реальное значение из собственного вывода и сверяйте его с успешным релизом. До завершения восстановления не запускайте очистку старых контейнеров и образов.

6. Откатываем и проверяем результат

Присвойте PREVIOUS_VERSION реальную версию. Здесь оставлена только маска:

PREVIOUS_VERSION='********'
kamal rollback "$PREVIOUS_VERSION"

Kamal поддерживает возврат к предыдущему релизу; нужная версия должна оставаться доступной. Команда и выбор контейнера разобраны в руководстве rollback.

После завершения:

kamal app version
curl --fail --silent --show-error --location --max-time 20 https://example.com --output /dev/null

Проверьте не только статус: откройте страницу и убедитесь, что вернулось ожидаемое содержимое. Для проблемы с DNS используйте сравнение с curl --resolve, сохраняя проверку TLS.

7. Исправляем Git, затем возвращаем автодеплой

Откат контейнера не отменяет плохой коммит в main. Следующий запуск workflow может снова опубликовать неисправную версию.

Подготовьте исправление или revert проблемного коммита через pull request. Если workflow временно отключён, включите его для проверок, удерживая изменения main под контролем:

gh workflow enable deploy.yml --repo your-github-user/hello-kamal

После успешных проверок слейте исправление и дождитесь нового деплоя. Запишите причину инцидента и проверку, которая должна поймать её в следующий раз: например, Nginx config test, запрос главной страницы или проверку обязательного текста.

Где заканчивается этот способ

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

Цель восстановления — вернуть рабочую версию и сохранить информацию о причине сбоя. Перезапуски, снятие блокировок и очистка Docker без понимания текущего состояния могут помешать обоим действиям.