← Назад к вопросам
Как минимизировать время когда приложение не работает при деплое?
3.0 Senior🔥 141 комментариев
#DevOps и инфраструктура
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Минимизация времени простоя при деплое
Это критически важный вопрос для production-систем. Существует несколько стратегий, которые позволяют достичь нулевого простоя (zero-downtime deployment).
1. Blue-Green Deployment
Это наиболее надёжный метод для Node.js приложений:
- Запускаешь две идентичные production-среды: Blue (текущая) и Green (новая версия)
- Деплоишь новую версию только в Green
- Прогоняешь полный набор smoke-тестов
- Переключаешь load balancer / reverse proxy с Blue на Green
- Blue становится резервной копией на случай, если нужен rollback
// Пример с Nginx
upstream app {
server blue-app:3000 weight=100;
server green-app:3000 weight=0; // Слушает, но не получает трафик
}
// При деплое меняешь веса через конфиг или systemctl restart
2. Rolling Deployment
Поступательное обновление инстансов приложения:
- Имеешь несколько инстансов (например, 4 server'а за load balancer'ом)
- Выводишь один инстанс из работы (drain connections)
- Деплоишь на него новую версию
- Возвращаешь в rotation
- Повторяешь для остальных
// graceful shutdown handler
let isShuttingDown = false;
server.on('SIGTERM', async () => {
if (isShuttingDown) return;
isShuttingDown = true;
// Перестаём принимать новые запросы
server.close(() => {
// Завершаем существующие подключения
console.log('Server closed gracefully');
process.exit(0);
});
// Timeout для насильственного завершения
setTimeout(() => process.exit(1), 30000);
});
3. Graceful Shutdown & Connection Draining
Это обязательная часть любой стратегии:
const gracefulShutdown = (server, db, redis) => {
const shutdown = async (signal) => {
console.log(`Received ${signal}, starting graceful shutdown`);
// Шаг 1: Выводим из load balancer
await removeFromLoadBalancer();
// Шаг 2: Даём время активным запросам завершиться
await new Promise(resolve => {
const timeout = setTimeout(resolve, 30000);
server.close(() => clearTimeout(timeout));
});
// Шаг 3: Закрываем БД и Redis соединения
await db.close();
await redis.disconnect();
process.exit(0);
};
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));
};
4. Database Migrations Strategy
Миграции — главный источник простоя:
- Expand-Contract Pattern: сначала добавляешь новую колонку, затем переводишь приложение на её использование, потом удаляешь старую
- Online Migrations: используешь инструменты типа Percona's pt-online-schema-change или Ghost для MySQL
- Separate Migration Step: запускаешь миграции ДО деплоя нового кода
-- ✅ Безопасно: добавление колонки
ALTER TABLE users ADD COLUMN email_verified_at TIMESTAMPTZ;
-- ❌ Опасно: удаление колонки (может заблокировать таблицу)
ALTER TABLE users DROP COLUMN old_field;
5. Health Checks & Load Balancer Configuration
// Health check endpoint
app.get('/health', (req, res) => {
res.json({
status: 'ok',
uptime: process.uptime(),
timestamp: Date.now()
});
});
// Readiness check (готовность принимать трафик)
app.get('/ready', async (req, res) => {
try {
await db.query('SELECT 1');
res.json({ ready: true });
} catch (err) {
res.status(503).json({ ready: false });
}
});
6. DNS & Sticky Sessions
- Минимизируй TTL перед деплоем (чтобы DNS быстро обновился)
- Если используешь sticky sessions, убедись что load balancer корректно перенаправляет сессии при переводе инстанса
Рекомендуемая практика для production
Blue-Green + Graceful Shutdown — это gold standard:
- Запускаешь Green окружение
- Прогоняешь все тесты (unit, integration, smoke)
- Выполняешь миграции БД (если нужны)
- Отправляешь трафик на Green (мгновенное переключение)
- Мониторишь логи и метрики на предмет ошибок
- Сохраняешь Blue как fallback на 30-60 минут
При таком подходе downtime составляет менее 1 секунды (время переключения load balancer'а).