Когда происходит миграция базы данных при деплое?

Ответ

В Node.js проектах миграции базы данных обычно выполняются до деплоя нового кода, как часть пайплайна CI/CD. Вот как я это организую:

Типичный процесс деплоя с миграциями:

  1. Подготовительный этап (Pre-deployment)

    // package.json
    {
    "scripts": {
    "migrate:up": "knex migrate:latest",
    "migrate:down": "knex migrate:rollback",
    "migrate:status": "knex migrate:status"
    }
    }
  2. Пайплайн в CI/CD (например, GitHub Actions)

    # .github/workflows/deploy.yml
    jobs:
    deploy:
    steps:
      - name: Run database migrations
        run: npm run migrate:up
        env:
          DATABASE_URL: ${{ secrets.PRODUCTION_DB_URL }}
    
      - name: Verify migrations
        run: npm run migrate:status
    
      - name: Deploy application
        run: ./deploy.sh

Практический пример с Knex.js:

// migrations/20240101_create_users.js
exports.up = function(knex) {
  return knex.schema.createTable('users', (table) => {
    table.increments('id');
    table.string('email').unique().notNullable();
    table.string('password_hash').notNullable();
    table.timestamps(true, true);
  });
};

exports.down = function(knex) {
  return knex.schema.dropTable('users');
};

// Скрипт для безопасного применения миграций
const runMigrations = async () => {
  try {
    console.log('Starting database migrations...');

    // Бэкап перед миграциями (для production)
    if (process.env.NODE_ENV === 'production') {
      await backupDatabase();
    }

    // Применение миграций
    const [batchNo, log] = await knex.migrate.latest();

    console.log(`Migrations completed. Batch: ${batchNo}`);
    log.forEach((migration) => {
      console.log(`✓ ${migration}`);
    });

    return true;
  } catch (error) {
    console.error('Migration failed:', error);

    // В production можно автоматически откатиться
    if (process.env.NODE_ENV === 'production') {
      await knex.migrate.rollback();
      console.log('Rollback completed due to migration error');
    }

    throw error;
  }
};

Лучшие практики, которые я применяю:

  • Всегда делаю бэкап базы перед миграциями в production
  • Использую транзакции для миграций, изменяющих данные
  • Тестирую миграции на staging-окружении
  • Реализую откатываемые стратегии деплоя (blue-green)
  • Добавляю проверки целостности данных после миграций

Ответ 18+ 🔞

А, ну это же классика, ёпта! Слушай, с миграциями в Node.js проектах история такая же древняя, как и сама разработка, но до сих пор народ умудряется наступать на одни и те же грабли, ядрёна вошь.

Вот смотри, как у умных людей это устроено, чтобы не было потом волнения ебать на всю команду. Основной принцип — миграции обязательно гоняешь до того, как новый код попадёт на сервера. Иначе будет тебе хиросима и нигерсраки, когда твоё приложение начнёт искать колонку, которой нихуя нет.

Как это обычно в пайплайне выглядит:

  1. Сначала подготовка, чувак. Всё по-взрослому, в package.json скрипты забиваешь.

    // package.json
    {
      "scripts": {
        "migrate:up": "knex migrate:latest",
        "migrate:down": "knex migrate:rollback", // На всякий пожарный, если всё накрылось медным тазом
        "migrate:status": "knex migrate:status"   // Чтобы проверить, не обосрался ли кто
      }
    }
  2. А потом в CI/CD (типа GitHub Actions) это всё автоматически и запускается.

    # .github/workflows/deploy.yml
    jobs:
      deploy:
        steps:
          - name: Run database migrations
            run: npm run migrate:up
            env:
              DATABASE_URL: ${{ secrets.PRODUCTION_DB_URL }} # Ключи, конечно, в секретах, а то доверия ебать ноль к случайным строкам в коде
    
          - name: Verify migrations
            run: npm run migrate:status # Глянуть, что всё применилось
    
          - name: Deploy application
            run: ./deploy.sh # И только потом уже новый код выкатываешь

Вот тебе практический пример на Knex.js, чтобы было понятнее:

// migrations/20240101_create_users.js
exports.up = function(knex) {
  return knex.schema.createTable('users', (table) => {
    table.increments('id');
    table.string('email').unique().notNullable();
    table.string('password_hash').notNullable();
    table.timestamps(true, true);
  });
};

exports.down = function(knex) {
  return knex.schema.dropTable('users'); // Функция отката — святое дело, никогда про неё не забывай
};

// А это уже скрипт для безопасного применения, чтобы не бздеть в production
const runMigrations = async () => {
  try {
    console.log('Starting database migrations...');

    // Бэкап перед миграциями — это как презерватив: лучше иметь и не нуждаться, чем наоборот
    if (process.env.NODE_ENV === 'production') {
      await backupDatabase();
    }

    // Само применение миграций
    const [batchNo, log] = await knex.migrate.latest();

    console.log(`Migrations completed. Batch: ${batchNo}`);
    log.forEach((migration) => {
      console.log(`✓ ${migration}`);
    });

    return true;
  } catch (error) {
    console.error('Migration failed:', error);

    // В продакшене если что-то пошло не так — немедленно откатывайся, не жди у моря погоды
    if (process.env.NODE_ENV === 'production') {
      await knex.migrate.rollback();
      console.log('Rollback completed due to migration error');
    }

    throw error; // И падай с ошибкой, чтобы весь пайплайн остановился
  }
};

А теперь, бля, лучшие практики, без которых ты просто распиздяй:

  • Бэкап перед миграциями в production — это не обсуждается. Иначе сам от себя охуеешь, когда что-то пойдёт не так.
  • Транзакции — твой лучший друг для миграций, которые данные меняют. Чтобы либо всё применилось, либо нихуя.
  • Всё тестируй на staging-окружении. Выкатывать миграции сразу в прод — это игра в русскую рулетку с полным барабаном.
  • Готовь стратегии отката (blue-green). Чтобы если новый код — говно, можно было мгновенно откатиться на старую версию.
  • После миграций добавь проверки целостности. Убедись, что данные на месте и ни одна хитрая жопа из таблиц не сбежала.

Вот так-то, ебать копать. Соблюдаешь эти правила — спишь спокойно. Нет — готовься к ночным звонкам и крикам "всё упало!".