Перенос базы данных на производство django

От кого-то, у кого есть приложение django в нетривиальной производственной среде, как вы справляетесь с миграциями баз данных? Я знаю, что есть south, но похоже, что это пропустит довольно много, если будет что-то существенное.

Другие два варианта (которые я могу придумать или использовали) делают изменения в тестовой базе данных, а затем (в автономном режиме с приложением) и импортируют этот SQL-экспорт. Или, возможно, более рискованный вариант, внося необходимые изменения в производственную базу данных в режиме реального времени, и если что-то пойдет не так, как в резервную копию.

Как обычно вы обрабатываете изменения в вашей базе данных и изменения схемы?

Ответ 1

Я думаю, что есть две части этой проблемы.

Сначала выполняется управление схемой базы данных, и она изменяется. Мы делаем это с помощью Юга, сохраняя как рабочие модели, так и файлы миграции в нашем репозитории SCM. Для безопасности (или паранойи) мы берем дамп базы данных раньше (и если мы действительно боимся, после), выполняем какие-либо миграции. Юг был адекватен всем нашим требованиям.

Во-вторых, развертывание изменения схемы, которое выходит за рамки простого запуска файла миграции, созданного на Юге. По моему опыту, для изменения базы данных обычно требуется изменение развернутого кода. Если у вас даже небольшая веб-ферма, сохранение развернутого кода в синхронизации с текущей версией вашей схемы базы данных может быть не тривиальным - это ухудшается, если вы рассматриваете разные уровни кеширования и эффект для уже активного пользователя сайта. Различные сайты обрабатывают эту проблему по-разному, и я не думаю, что есть ответ на один размер.


Решение второй части этой проблемы не обязательно прямолинейно. Я не верю, что существует подход, основанный на одном уровне, и недостаточно информации о вашем веб-сайте и среде, чтобы предложить решение, наиболее подходящее для вашей ситуации. Однако, я думаю, есть несколько соображений, которые можно иметь в виду, чтобы помочь в развертывании в большинстве ситуаций.

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

Будьте осторожны, если вы внесете изменения в копию своей основной базы данных. Основная проблема здесь заключается в том, что ваш сайт по-прежнему жив, и предположительно принимает записи в базу данных. Что происходит с данными, записанными в основную базу данных, когда вы заняты миграцией клона для последующего использования? Ваш сайт должен либо сработать, либо временно приостановить какое-либо состояние только для чтения, иначе вы потеряете его.

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

Я видел/слышал, как пару других способов хорошо работают.

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

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

Но я думаю, что в этот момент Google станет вашим другом, потому что, как я уже сказал, решение очень специфично для контекста, и я волнуюсь, что этот ответ начнет становиться бессмысленным... Найдите что-то вроде "нулевого времени развертывания", Я получаю результаты, такие как это с большим количеством идей...

Ответ 2

Я использую Юг для производственного сервера с кодовой базой ~ 40 тыс. Строк, и до сих пор у нас не было проблем. Мы также прошли через пару крупных рефакторингов для некоторых наших моделей, и у нас были проблемы с нулем.

У нас также есть контроль версий на наших моделях, который помогает нам возвращать любые изменения, которые мы делаем для моделей со стороны программного обеспечения, а Юг - больше для фактических данных. Мы используем Django Reversion

Ответ 3

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

Короче говоря, я позволяю коду улавливать исключение, возникающее из старой схемы, и применять соответствующее обновление схемы. Я не ожидаю, что это будет принятым ответом - в некоторых случаях это приемлемо (и некоторые могут спорить никогда). Но я думаю, что у него есть уродливая утенка.

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

Затем я возвращаю изменение схемы и снова тестирую новый код. Я улавливаю полученные ошибки, выполняю модернизацию схемы, а затем повторю попытку ошибочного запроса.

Функция обновления должна быть записана так, чтобы она "не наносила вреда", так что, если она вызывалась несколько раз (как это может произойти при вводе в производство), она действует только один раз.

Фактический код python - я поставил это в конце моих настроек.py, чтобы проверить концепцию, но вы, вероятно, захотите сохранить ее в отдельном модуле:

from django.db.models.sql.compiler import SQLCompiler
from MySQLdb import OperationalError

orig_exec = SQLCompiler.execute_sql
def new_exec(self, *args, **kw):
    try:
        return orig_exec(self, *args, **kw)
    except OperationalError, e:
        if e[0] != 1054: # unknown column
            raise
        upgradeSchema(self.connection)
        return orig_exec(self, *args, **kw)
SQLCompiler.execute_sql = new_exec

def upgradeSchema(conn):
    cursor = conn.cursor()
    try:
        cursor.execute("alter table users add phone varchar(255)")
    except OperationalError, e:
        if e[0] != 1060: # duplicate column name
            raise

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

Вам нужно будет адаптировать класс исключений (MySQLdb.OperationalError в моем случае) и числа (1054 "неизвестный столбец"/1060 "дублирующий столбец" в моем случае) к вашему движку базы данных и изменению схемы, но это должно быть легко.

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

Одна из моих любимых вещей о python - это одна из возможностей легко переопределить системные методы во время выполнения, как это. Он обеспечивает такую гибкость.

Ответ 4

Юг не используется везде. Как и в моей организации, мы имеем 3 уровня тестирования кода. Одна из них - локальная среда для разработчиков, одна - среда разработки, а третья - работа с производством.

Местный Дев находится на руках разработчиков, где он может играть в соответствии с его потребностями. Затем идет этап разработки, который поддерживается идентично производству, например, до изменения db на реальном сайте, где мы сначала выполняем изменения db при постановке, и проверяем, все ли работает нормально, а затем мы вручную меняем производство db делает его идентичным повторной постановке.

Ответ 5

Если ваша база данных нетривиальна, и Postgresql у вас есть целая куча отличных вариантов SQL-мудрый, в том числе:

  • снимки и откат
  • прямая репликация на резервный сервер
  • пробное обновление, а затем жить

Вариант пробного обновления хорош (но лучше всего сделать в сотрудничестве с моментальным снимком)

su postgres
pg_dump <db> > $(date "+%Y%m%d_%H%M").sql
psql template1
# create database upgrade_test template current_db
# \c upgradetest
# \i upgrade_file.sql
...assuming all well...
# \q
pg_dump <db> > $(date "+%Y%m%d_%H%M").sql # we're paranoid
psql <db>
# \i upgrade_file.sql

Если вам нравится описанная выше компоновка, но вы беспокоитесь о времени, которое требуется для выполнения обновления дважды, вы можете заблокировать db для записи, а затем, если обновление до upgradeetest будет хорошо, вы можете переименовать db в dbold и upgradeetest в db. Есть много вариантов.

Если у вас есть файл SQL с перечислением всех изменений, которые вы хотите сделать, чрезвычайно удобная команда psql \set ON_ERROR_STOP 1. Это останавливает скрипт обновления в своих дорогах в тот момент, когда что-то пойдет не так. И, с большим количеством тестирования, вы можете убедиться, что ничего не делает.

Существует целый ряд различных схем доступа к базам данных с номером, указанным в этом ответе StackOverflow. Но это в основном довольно легко сделать вручную...

pg_dump --schema-only production_db > production_schema.sql
pg_dump --schema-only upgraded_db > upgrade_schema.sql
vimdiff production_schema.sql upgrade_schema.sql
or
diff -Naur production_schema.sql upgrade_schema.sql > changes.patch
vim changes.patch (to check/edit)