Поширена помилка - міграції в команді старту контейнера:
CMD php artisan migrate --force && php-fpm
Проблеми:
- кілька реплік стартують одночасно - і одночасно запускають ті самі міграції. Конфлікти, блокування, наполовину застосовані зміни;
- падіння міграції - контейнер не стартує, оркестратор перезапускає його знову й знову;
- масштабування (новий контейнер через годину) теж запускає міграції;
- воркери черг і планувальник з того самого образу - теж мігрують.
Правильні варіанти:
1. Окремий крок деплою перед оновленням застосунку - одноразовий контейнер з тим самим образом:
docker run --rm --env-file .env ghcr.io/acme/app:sha-a1b2c3d php artisan migrate --force
У Kubernetes - Job перед оновленням Deployment; у Kamal - хук перед деплоєм; у CI - окремий крок пайплайну.
2. Якщо міграції все ж запускаються зі старту контейнера - з блокуванням:
php artisan migrate --force --isolated
--isolated бере атомарне блокування через кеш - міграції виконає лише один процес, інші пропустять. Потрібен спільний драйвер кешу з підтримкою блокувань (Redis, база даних).
Сумісність версій - найважливіше. Під час поступового деплою старий і новий код працюють одночасно з уже оновленою базою. Тому міграції мають бути сумісними з попередньою версією:
- додати колонку - безпечно (nullable чи зі значенням за замовчуванням);
- перейменувати чи видалити колонку - у кілька релізів: додати нову, писати в обидві, перенести дані, перевести читання, і лише потім видалити стару;
- важкі зміни великих таблиць (індекси, зміна типу) - без довгих блокувань (онлайн-DDL,
CREATE INDEX CONCURRENTLYу PostgreSQL), інакше застосунок «зависне» на час міграції.
Відкат: відкат коду не відкочує базу. План має враховувати, що попередня версія коду працюватиме з новою схемою - ще одна причина для сумісних міграцій.
Резервна копія перед міграцією на продакшені - обов'язковий крок для ризикованих змін.