Развертывание (одного узла) веб-приложения Django с практически нулевым временем простоя на EC2

Вопрос: Каковы хорошие стратегии для достижения нулевого (или максимально близкого к нулю) времени простоя при использовании Django?

В большинстве ответов, которые я читал, говорится «использовать юг» или «использовать ткань», но это очень расплывчатый ответ ИМХО. На самом деле я использую оба, и все еще задаюсь вопросом, как добиться нулевого времени простоя, насколько это возможно.

Некоторые подробности:

У меня есть приложение Django приличного размера, которое я размещаю на EC2. Я использую South для переноса схемы и данных, а также fabric с boto для автоматизации повторяющихся задач развертывания/резервного копирования, которые запускаются набором Jenkins (сервер непрерывной интеграции). База данных, которую я использую, представляет собой стандартный экземпляр PostgreSQL 9.0.

У меня есть...

  1. Промежуточный сервер, который постоянно редактируется нашей командой со всем новым контентом и загружается последним и лучшим кодом и...

  2. действующий сервер, на котором постоянно меняются учетные записи и пользовательские данные — все это записано в PostgreSQL.

Текущая стратегия развертывания:

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

Начинается простой.

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

После завершения загрузки эластичные ips живого сервера заменяются на промежуточный клон (и, таким образом, он становится новым живым сервером). Живой экземпляр и экземпляр живого клона завершаются.

Время простоя заканчивается.

Да, это работает, но по мере роста данных мое «виртуальное» нулевое время простоя становится все дальше и дальше. Конечно, мне пришло в голову каким-то образом использовать репликацию и начать изучать репликацию PostgreSQL и «в конечном счете согласованные» подходы. Я знаю, что есть некоторая магия, которую я мог бы сделать с балансировщиками нагрузки, но проблема с учетными записями, созданными в то же время, делает это сложным.

Что бы вы посоветовали мне посмотреть?

Обновление:

У меня есть типичное приложение Django с одним узлом. Я надеялся найти решение, которое будет более подробно решать специфические проблемы django. Например, идея использования поддержки Django для нескольких баз данных с пользовательскими маршрутизаторами наряду с репликацией. пришло мне в голову. Есть вопросы, связанные с тем, что, я надеюсь, коснется ответа.


person rburhum    schedule 23.05.2012    source источник


Ответы (4)


На что может быть интересно взглянуть, так это на технику под названием Канарское высвобождение. Я видел великолепную презентацию Джеза Хамбла в прошлом году на конференции по программному обеспечению в Амстердаме; речь шла об релизах с низким уровнем риска, слайды находятся здесь.

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

person Wesley    schedule 23.05.2012
comment
Спасибо, Уэсли. Canary Release звучит очень похоже на то, что описано в книге Lean Startup. В моем случае у меня есть только два узла (действующий и промежуточный сервер), поэтому мне не имеет смысла возвращаться назад. Даже если бы я это сделал, мне пришлось бы подумать об обратной миграции схемы/данных, что в некоторых случаях нетривиально. Я посмотрю ссылки и спасибо за них! - person rburhum; 23.05.2012
comment
Я сам никогда не использовал канареечные выпуски, но я думаю, что идея состоит в том, чтобы выполнять только миграции баз данных, которые добавляют таблицы/столбцы, но никогда ничего не изменяют/удаляют. Таким образом, все версии могут работать с одной и той же самой последней схемой базы данных. Надеюсь, листы вдохновят вас! - person Wesley; 23.05.2012
comment
Я просмотрел слайды, и они очень хороши. Спасибо за них. Тем не менее, они не решают никаких проблем, характерных для Django. Например, у меня есть пользователи (django-auth) с целой кучей связанных данных. Тогда у меня есть другие модели. Все в одном и том же узле... выпуски canary не применяются ко мне, если я не начну балансировать нагрузку между несколькими узлами, что, я думаю, я мог бы сделать, но мне бы хотелось получить больше информации о конкретных шаблонах django. В любом случае, спасибо за ссылку! - person rburhum; 23.05.2012

Живой сервер не должен быть перенесен. Этот сервер должен быть доступен с двух промежуточных серверов, server0 и server1. Изначально server0 активен, и изменения вносятся в server1. Если вы хотите сменить программное обеспечение, переключитесь на живые серверы. Что касается нового контента, то его не должно быть на промежуточном сервере. Это должно быть на живом сервере. Добавьте в свои таблицы столбец с номером версии для таблиц содержимого и измените базу кода, чтобы использовать правильный номер версии содержимого. Разработайте программное обеспечение для копирования старых версий в новые строки с обновленными номерами версий по мере необходимости. Поместите номер текущей версии в файл settings.py на серверах server0 и server1, чтобы у вас было центральное место для программного обеспечения, на которое можно было бы ссылаться при выборе данных, или создайте приложение для доступа к базе данных, которое можно обновлять для получения правильных версий контента. Конечно, для файлов шаблонов они могут быть на каждом сервере и будут уместны.

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

person kd4ttc    schedule 25.05.2012

Если я правильно понимаю, проблема, похоже, в том, что ваше приложение не работает, а данные восстанавливаются в новую базу данных вместе со схемой.

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

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

  1. Добавьте столбец как принимающий значения NULL и заставьте django начать запись в этот столбец, чтобы новые записи имели правильные данные.
  2. Заполните существующие записи.
  3. Заставьте django также начать чтение с нового столбца.
person mpessas    schedule 23.05.2012
comment
Узел со всем новым содержимым (промежуточный сервер) также имеет целую кучу новых и обновленных медиафайлов. Чтобы решить проблемы с медиафайлами, я мог бы просто завершить развертывание с помощью rsync (без удаления старых файлов), а затем выполнить rsync с удалением, чтобы удалить ненужные файлы (это немного сложнее, потому что я развертываю с помощью облачного фронта, но теоретически это должно работать). Проблема в том, что если я урежу и загружу новый набор контента на работающий сервер, люди увидят ошибки, если попытаются использовать сайт во время загрузки нового контента. - person rburhum; 23.05.2012
comment
Если бы у меня была простая модель django с отметками базы данных, я мог бы выполнить настраиваемую синхронизацию репликации с низким бюджетом для этой модели. Но у меня есть 30 моделей со сложными отношениями между ними. Я мог бы выпустить новую версию всех моих моделей с привязанной к ним версией, но это далеко не тривиально, и должен быть более простой способ (тм) - person rburhum; 23.05.2012
comment
Вы имеете в виду статические файлы (в django медиафайлы загружаются пользователем)? Для этого есть django-staticfiles (проверьте CachedFileStorage или что-то в этом роде). Он будет делать то, что вы хотите. Но, TBH, я не могу понять, чего вы хотите добиться с помощью управления версиями. - person mpessas; 23.05.2012
comment
Синхронизация статических файлов (для папки мультимедиа) тривиальна с помощью rsync. Кроме того, я использую собственное хранилище для S3, так что это не проблема. Проблема в том, что модели django (плюс связанные данные) различаются в обеих версиях веб-приложения (действующая и готовящаяся к развертыванию). Развертывание кода и содержимого при синхронизации приводит к простоям. Вопрос в шаблонах для сокращения времени простоя. - person rburhum; 23.05.2012

Для достижения нулевого времени простоя у вас должно быть как минимум 2 сервера + балансировщик. И обновлять их последовательно. Если вы хотите обновить как базу данных, так и приложение, и иметь 0 простоев, у вас должно быть 2 сервера базы данных. Ни чудес, ни серебряной пули, и django не выберешься из проблем развертывания.

person Nikolay Fominyh    schedule 01.06.2012
comment
какой из узлов имеет учетные записи пользователей? - person rburhum; 01.06.2012
comment
Оба узла приложений имеют доступ к учетным записям пользователей. Или что вы имеете в виду? - person Nikolay Fominyh; 02.06.2012
comment
есть доступ к учетным записям пользователей, вы имеете в виду, что есть третий узел с базой данных? Таким образом, ваше решение фактически требует трех узлов. Если да, то все в порядке. Но что произойдет, если для запуска одного из узлов приложения потребуется южная миграция данных? - person rburhum; 03.06.2012
comment
Да, для моих решений всегда требуется как минимум один выделенный сервер базы данных. ЕСЛИ южная миграция занимает много времени (например, у вас есть обновление в таблице с 10 миллионами строк в ней), то вам необходимо реплицировать базу данных как минимум на 2 узла. Кстати, у меня здесь недостаточно опыта. Я знаю, что инстаграм для этой цели использовал снапшоты EC2, а для postgresql традиционный способ - использовать решение типа slony. - person Nikolay Fominyh; 04.06.2012
comment
Но тогда нет смысла иметь два экземпляра приложения EC2, если только один может работать одновременно с определенной (южной) версией схемы, не так ли? Я согласен с вами в том, что решение состоит в том, чтобы иметь два экземпляра и выполнять переключение, но мой первоначальный вопрос до сих пор не решен (например, как обращаться с объектами django-auth в этом сценарии). - person rburhum; 04.06.2012
comment
Какая проблема с объектами django-auth? Где вы храните сессию? - person Nikolay Fominyh; 04.06.2012
comment
создается учетная запись. где вы его храните? - person rburhum; 08.06.2012
comment
Когда мы создаем учетную запись пользователя, мы сохраняем ее в БД. - person Nikolay Fominyh; 12.06.2012
comment
Итак, один хозяин, другой раб. когда вы обновляете мастер с помощью миграции, которая занимает много времени, ведомый может предоставлять только доступ для чтения, но не функции записи. То же верно и для всех других объектов, требующих записи. Для любого нетривиального приложения это может означать время простоя (веб-приложению нужны определенные объекты в БД для записи, чтобы не отключаться) - person rburhum; 12.06.2012
comment
если миграция занимает много времени - то ее надо запускать отдельно. найти способ горячей замены мастера при работающем приложении. - person Nikolay Fominyh; 12.06.2012