Django: Как синхронизировать код и базу данных при развертывании на Heroku?

Допустим, у нас есть проект Django под названием Alpha. Разработчики работают над Alpha в своем dev-окружении перед развертыванием проекта Django на Heroku. Procfile может выглядеть примерно так:

release: python manage.py migrate
web: python -m gunicorn wsgi:application

Когда разработчик пытается развернуть новую версию Alpha, проект поставляется вместе с файлами миграций (как рекомендуется). В конце развертывания Heroku выполнит оператор release python manage.py migrate для внесения всех соответствующих изменений в базу данных. Поскольку release является частью общего процесса развертывания, если он завершится неудачно, то новый код не будет развернут.

Однако... В то время как код будет возвращен к тому, что было до развертывания (как и ожидалось), потенциальные изменения в базе данных Heroku будут пермаментными. Например, предположим, что в новой версии есть три новые миграции:

  • 0011
  • 0012
  • 0013

Первые две миграции выполняются правильно, и база данных изменяется соответствующим образом. Они также добавляются в таблицу базы данных django_migrations. Последняя, однако, содержит проблему (например, она определяет ограничение, которое не соблюдается данными этапа).

В этом сценарии код на Heroku (как /migrations/, так и models.py) и база данных на Heroku теперь сильно рассинхронизированы: фактически, база данных отражает изменения, миграции которых даже не присутствуют в репозитории кода. Это может породить всевозможные проблемы.

Как предотвратить попадание в этот предикат и гарантировать, что код и данные всегда синхронизированы на 100% как на stage, так и на prod?

Post Scriptum

Heroku имеет следующий фрагмент как часть документации:

Используйте транзакции для миграции базы данных

.

При выполнении миграции базы данных всегда используйте транзакции. A транзакция обеспечивает успешное выполнение всех операций миграции до фиксации изменений в базе данных, сводя к минимуму возможность неудачной частичной миграции на этапе выпуска. Если миграция базы данных миграция базы данных не удалась на этапе выпуска (т.е. команда миграции завершается с ненулевым статусом), новый релиз не будет развернут. Если транзакции не использовались, это может оставить базу данных в частично мигрированной. Мы рекомендуем использовать heroku run, а не фазу выпуска, для внесения исправлений в схему/данные.

Как говорится в документации, которую вы процитировали, выполнение миграций в транзакции - хорошая идея. Это настолько хорошая идея, что Django делает это по умолчанию с базами данных, которые это поддерживают.

Но это поможет только в рамках одной миграции: если миграция 0013 завершится неудачей, как в вашем примере, изменения, внесенные ею, будут откачены. Изменения, внесенные миграциями 0011 и 0012, не будут отменены.

Для обратного перехода 0011 и 0012 вам придется откатываться назад вручную, например

python manage.py migrate myapp 0010

Но поскольку ваше приложение будет работать с предыдущим выпуском, если ваше развертывание не удастся, вы не можете просто heroku run это сделать - у Heroku нет файлов миграции для 0011 и 0012.

Скорее всего, по крайней мере частично, именно поэтому в документации Heroku говорится

Мы предлагаем использовать heroku run, а не фазу выпуска, для внесения исправлений в схему/данные.

Если вы удалите команду migration release phase, вы сможете начать развертывание следующим образом:

git push heroku main
heroku run python manage.py migrate

Если миграция не удалась, просто отмените две последние миграции, а затем вернитесь к предыдущему релизу :

heroku run python manage.py migrate myapp 0010
heroku releases:rollback

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

Вернуться на верх