Рекомендации по развертыванию веб-приложений Java с минимальным временем простоя? - PullRequest
55 голосов
/ 29 октября 2009

При развертывании большого веб-приложения Java (> 100 МБ .war) в настоящее время я использую следующий процесс развертывания:

  • Файл приложения .war локально разворачивается на компьютере разработчика.
  • Расширенное приложение rsync: ed из машины разработки в живую среду.
  • Сервер приложений в рабочей среде перезапускается после rsync. Этот шаг не является строго обязательным, но я обнаружил, что перезапуск сервера приложений при развертывании позволяет избежать «java.lang.OutOfMemoryError: PermGen space» из-за частой загрузки классов.

Что хорошего в этом подходе:

  • rsync минимизирует объем данных, отправляемых с компьютера разработчика в оперативную среду. Загрузка всего файла .war занимает более десяти минут, тогда как rsync занимает пару секунд.

Недостатки этого подхода:

  • Во время работы rsync контекст приложения перезапускается, так как файлы обновляются. В идеале перезапуск должен произойти после завершения rsync, а не когда он все еще работает.
  • Перезапуск сервера приложений вызывает примерно две минуты простоя.

Я бы хотел найти процесс развертывания со следующими свойствами:

  • Минимальное время простоя во время процесса развертывания.
  • Минимальное время загрузки данных.
  • Если процесс развертывания зависит от сервера приложений, то сервер приложений должен быть с открытым исходным кодом.

Вопрос:

  • С учетом заявленных требований, каков оптимальный процесс развертывания?

Ответы [ 18 ]

30 голосов
/ 29 октября 2009

Обновление:

С тех пор, как этот ответ был впервые написан, появился лучший способ развертывания военных файлов на tomcat с нулевым временем простоя. В последних версиях tomcat вы можете включать номера версий в свои военные имена. Например, вы можете одновременно развернуть файлы ROOT##001.war и ROOT##002.war в одном и том же контексте. Все, что после ## интерпретируется tomcat как номер версии, а не как часть контекста. Tomcat будет поддерживать все версии вашего приложения и обслуживать новые запросы и сеансы до последней версии, которая будет полностью запущена, при этом изящно завершая старые запросы и сеансы в той версии, с которой они начали. Указание номеров версий также можно выполнить с помощью менеджера tomcat и даже каталитических задач. Подробнее здесь .

Оригинальный ответ:

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

Что не так с использованием приложения Tomcat Manager для развертывания? Если вы не хотите загружать весь файл war напрямую в приложение менеджера Tomcat из удаленного местоположения, вы можете rsync (без сжатия по причинам, указанным выше) поместить его в производственную ячейку, перепаковать его в war и затем передайте его менеджеру на месте. Существует хорошая муравейная задача, которая поставляется вместе с Tomcat, позволяя вам создавать сценарии развертывания с помощью приложения Tomcat Manager.

В вашем подходе есть еще один недостаток, который вы не упомянули: хотя ваше приложение частично развернуто (во время операции rsync), ваше приложение может находиться в несогласованном состоянии, когда измененные интерфейсы могут быть не синхронизированы, new / обновленные зависимости могут быть недоступны и т. д. Кроме того, в зависимости от того, сколько времени занимает работа rsync, ваше приложение может перезапускаться несколько раз. Знаете ли вы, что вы можете и должны отключить режим прослушивания измененных файлов и перезапуска в Tomcat? На самом деле это не рекомендуется для производственных систем. Вы всегда можете выполнить перезапуск приложения вручную или с помощью сценария ant, используя приложение менеджера Tomcat.

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

20 голосов
/ 29 октября 2009

Было отмечено, что rsync работает неправильно при отправке изменений в файл WAR. Причина этого заключается в том, что WAR-файлы по сути являются ZIP-файлами и по умолчанию создаются со сжатыми файлами-членами. Небольшие изменения в файлах-членах (перед сжатием) приводят к большим различиям в ZIP-файле, что делает алгоритм дельта-передачи rsync неэффективным.

Одним из возможных решений является использование jar -0 ... для создания исходного файла WAR. Параметр -0 указывает команде jar не сжимать файлы элементов при создании файла WAR. Затем, когда rsync сравнивает старую и новую версии файла WAR, алгоритм дельта-передачи должен иметь возможность создавать небольшие различия. Затем сделайте так, чтобы rsync отправлял diff-файлы (или оригинальные файлы) в сжатом виде; например используйте rsync -z ... или сжатый поток данных / транспорт под ним.

РЕДАКТИРОВАТЬ: В зависимости от того, как структурирован файл WAR, может также потребоваться использовать jar -0 ... для создания компонентных файлов JAR. Это относится к файлам JAR, которые часто подвергаются изменениям (или просто перестраиваются), а не к стабильным файлам JAR сторонних производителей.

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

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


Другой совершенно иной подход заключается в том, чтобы взглянуть на файл WAR, чтобы определить, можете ли вы определить библиотечные JAR-файлы, которые, вероятно, (почти) никогда не изменятся. Извлеките эти JAR-файлы из файла WAR и разверните их отдельно в каталоге common/lib сервера Tomcat; например используя rsync.

13 голосов
/ 05 ноября 2009

В любой среде, где учитывается время простоя, вы наверняка используете какой-нибудь кластер серверов для повышения надежности за счет избыточности. Я бы вынул хост из кластера, обновил его, а затем выбросил обратно в кластер. Если у вас есть обновление, которое не может работать в смешанной среде (например, для БД требуется несовместимое изменение схемы), вам придется закрыть весь сайт, по крайней мере, на мгновение. Хитрость заключается в том, чтобы вызвать процессы замены, прежде чем бросать оригиналы.

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

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

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

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

10 голосов
/ 01 ноября 2009

Мой совет - использовать rsync с разнесенными версиями, но развернуть файл войны.

  1. Создайте временную папку в рабочей среде, в которой у вас будет взорванная версия веб-приложения.
  2. Rsync взорванные версии.
  3. После успешного rsync создайте военный файл во временной папке на машине с действующей средой.
  4. Заменить старую войну в каталоге развертывания сервера новой папкой из временной папки.

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

8 голосов
/ 29 октября 2009

Разве вы не можете сделать локальную копию текущего веб-приложения на веб-сервере, выполнить rsync для этого каталога, а затем, возможно, даже с использованием символических ссылок, одним махом "указать" Tomcat на новое развертывание без больших простоев?

4 голосов
/ 06 ноября 2009

Это зависит от архитектуры вашего приложения.

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

4 голосов
/ 04 ноября 2009

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

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

Запуск двух серверов приложений : Запустите второй сервер приложений (который прослушивает другие порты TCP) и разверните там свое приложение. После развертывания обновите конфигурацию Apache httpd (mod_jk или mod_proxy), чтобы он указывал на второй сервер приложений. Изящно перезапустите процесс Apache httpd. Таким образом у вас не будет простоев, и новые пользователи и запросы будут автоматически перенаправлены на новый сервер приложений.

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

2 голосов
/ 05 ноября 2009

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

Итак, вместо того, чтобы производить большую жирную ВОЙНУ, положите ее на диету и произведите:

  • большой толстый ZIP со статическими файлами для Apache
  • менее жирная WAR для двигателя сервлета.

При желании можно пойти дальше в процессе уменьшения WAR: если возможно, разверните Grails и другие JAR-файлы, которые не изменяются часто (что, вероятно, имеет место в большинстве из них), на уровне сервера приложений.

Если вам удастся создать более легкую WAR, я бы не стал беспокоиться о каталогах rsyncing, а не об архивах.

Сильные стороны этого подхода:

  1. Статические файлы могут быть оперативно «развернуты» на Apache (например, использовать символическую ссылку, указывающую на текущий каталог, распаковать новые файлы, обновить символическую ссылку и вуаля).
  2. WAR будет тоньше, и его развертывание займет меньше времени.

Слабость этого подхода:

  1. Существует еще один сервер (веб-сервер), так что это добавляет (немного) больше сложности.
  2. Вам нужно будет изменить сценарии сборки (не так уж важно IMO).
  3. Вам нужно изменить логику rsync.
1 голос
/ 06 ноября 2009

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

...