Деплой: что происходит

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

Зачем нужно

Многие разработчики нажимают «деплой» и ждут результата, не понимая что происходит внутри. Когда деплой ломается — они не знают где искать. Понимание пайплайна деплоя позволяет быстро локализовать проблему: сборка упала? Тесты провалились? Контейнер не стартует? Health check не проходит?

Где используется

  • Отладка упавшего деплоя в CI/CD
  • Понимание zero-downtime стратегий
  • Настройка собственного пайплайна деплоя

Основной контент

Полный жизненный цикл деплоя

1. git push → GitHub
      ↓
2. CI trigger: GitHub Actions запускается
      ↓
3. Lint + Tests (если падает → деплой отменяется)
      ↓
4. Build (npm run build / tsc / webpack)
      ↓
5. Docker build (собрать образ)
      ↓
6. Docker push (в registry)
      ↓
7. CD: подключение к серверу (SSH)
      ↓
8. Pull нового образа на сервере
      ↓
9. Запуск нового контейнера (или rolling update в K8s)
      ↓
10. Health check: ждём /health → 200
      ↓
11. Переключение трафика на новый контейнер
      ↓
12. Остановка старого контейнера
      ↓
13. Уведомление в Slack (success/failure)

Деплой с нулевым downtime

# Blue-Green деплой вручную (docker)
# Старый контейнер: api-blue на порту 3000
# Новый контейнер: api-green

# 1. Запустить новый контейнер на другом порту
docker run -d --name api-green -p 3001:3000 myapp:new

# 2. Проверить здоровье
curl http://localhost:3001/health   # ожидаем 200

# 3. Переключить Nginx на новый контейнер
# Изменить upstream в nginx.conf: 3000 → 3001
nginx -s reload

# 4. Остановить старый
docker stop api-blue && docker rm api-blue

# 5. Переименовать для следующего деплоя
docker rename api-green api-blue

Rolling Update в Kubernetes

# K8s автоматически делает rolling update при изменении image
kubectl set image deployment/my-api api=myuser/myapp:new-sha

# Следить за прогрессом
kubectl rollout status deployment/my-api

# Откат если что-то пошло не так
kubectl rollout undo deployment/my-api

Что значит «деплой завершён»

// server.js — graceful shutdown при смене контейнера
const server = app.listen(3000, () => {
  console.log('Server started on port 3000');
});

// При получении SIGTERM (docker stop, K8s pod termination):
process.on('SIGTERM', () => {
  console.log('SIGTERM received, starting graceful shutdown...');
  server.close(() => {
    // Закрыть соединения с БД
    db.end(() => {
      console.log('Graceful shutdown complete');
      process.exit(0);
    });
  });

  // Принудительно завершить через 30s если не успели
  setTimeout( => process.exit(1), 30000);
});

Миграции БД во время деплоя

# Важный вопрос: когда запускать миграции?

# Вариант 1: до деплоя нового кода (рекомендуется)
# Миграции должны быть совместимы со СТАРЫМ кодом
ssh server "cd /app && npm run db:migrate"
# → потом деплой нового кода

# Вариант 2: как часть старта нового контейнера
# CMD ["sh", "-c", "npm run db:migrate && node dist/index.js"]

# ОПАСНО: не делать destructive миграции (DROP COLUMN)
# пока старый код ещё работает и использует эту колонку

Скрипт деплоя на VPS

#!/bin/bash
# deploy.sh
set -e   # выход при любой ошибке

echo "=== Starting deployment ==="

# Переходим в директорию
cd /var/www/myapp

# Получаем изменения
git fetch origin main
git reset --hard origin/main

# Запускаем миграции
docker compose exec -T api npm run db:migrate

# Пересобираем и перезапускаем
docker compose up -d --build --no-deps api

# Ждём health check
echo "Waiting for health check..."
for i in {1..30}; do
  if curl -sf http://localhost:3000/health > /dev/null; then
    echo "Health check passed!"
    exit 0
  fi
  sleep 2
done

echo "Health check failed after 60s!"
exit 1

Частые ошибки

  • Нет graceful shutdown — при деплое активные запросы обрываются, пользователи видят ошибки
  • Деструктивные миграции во время деплоя (DELETE/DROP) — старый код падает, используя удалённые данные
  • Отсутствие rollback плана — при проблеме с новой версией нет быстрого пути вернуться назад
  • Деплоить без проверки health check — сервер «перезапустился», но приложение не стартовало из-за ошибки конфига

Связанные темы

Ресурсы