Как правильно делать миграции в Kubernetes, чтобы новые поды стартовали после миграций, а старые не сломались?
Что проверяют
Знаете ли вы, почему в .NET не стоит просто вызывать Database.Migrate() при старте, и умеете ли развести применение схемы и выкладку кода. Уточнение почти всегда одно: что будет со старыми подами, если миграция удалила колонку, на которую ссылается их модель EF Core.
Как отвечать
- миграции отдельно от приложения: Microsoft не рекомендует применять миграции на старте в продакшене. Реплики стартуют одновременно, приложению нужны права на DDL, а упавшая миграция роняет все поды разом. В EF Core 9
Migrate()берёт блокировку против параллельного запуска, но остальные доводы остаются; - bundle или скрипт:
dotnet ef migrations bundleсобирает самодостаточный исполняемый файл, его запускают в KubernetesJobсо строкой подключения изSecret. Альтернатива:dotnet ef migrations script --idempotentи прогон SQL в пайплайне. С FluentMigrator или DbUp схема та же; - порядок: сначала Job (хук Helm
pre-upgradeилиPreSyncв Argo CD), при его успехе раскатываетсяDeploymentсо стратегиейRollingUpdate,maxUnavailable: 0и readiness probe наMapHealthChecks; - совместимость назад: пока идёт rolling update, старый код работает на новой схеме. Поэтому миграции только расширяющие: новая колонка nullable или с
defaultValueSql, удаление старой через релиз, когда её не читает ни один под; - тяжёлые операции: изменение большой таблицы или индекс выносят в отдельную миграцию и через
migrationBuilder.Sql()задают онлайн-вариант для своей СУБД.
Откат образа схему не возвращает, поэтому Down() на проде нужен редко, а совместимость всегда.
Где спотыкаются
Добавляют колонку NOT NULL без значения по умолчанию. Старые поды продолжают делать INSERT без неё, и запись ломается ещё до конца выкладки.
Вызывают context.Database.Migrate() в Program.cs и считают вопрос закрытым. Под с неудачной миграцией уходит в CrashLoopBackOff, а права на изменение схемы получает рабочий сервис.
Выкатывают образ с тегом latest и надеются на kubectl rollout undo. Откатываться некуда: непонятно, какой именно образ был до этого, а флаг --record для истории давно помечен устаревшим.