Лучшие практики для развертывания веб-приложений Java с минимальным временем простоя?
При развертывании большого Java-приложения (>100 МБ.war) в настоящее время я использую следующий процесс развертывания:
- Файл приложения.war расширяется локально на компьютере разработчика.
- Расширенное приложение rsync:ed из машины разработки в живую среду.
- Сервер приложений в рабочей среде перезапускается после rsync. Этот шаг не является строго необходимым, но я обнаружил, что перезапуск сервера приложений при развертывании позволяет избежать "java.lang.OutOfMemoryError: PermGen space" из-за частой загрузки классов.
Хорошие вещи об этом подходе:
- Функция rsync минимизирует объем данных, отправляемых с компьютера разработчика в оперативную среду. Загрузка всего.war-файла занимает более десяти минут, тогда как rsync занимает пару секунд.
Плохие вещи об этом подходе:
- Во время работы rsync контекст приложения перезапускается, поскольку файлы обновляются. В идеале перезапуск должен произойти после завершения rsync, а не когда он все еще работает.
- Перезапуск сервера приложений вызывает примерно две минуты простоя.
Я хотел бы найти процесс развертывания со следующими свойствами:
- Минимальное время простоя во время процесса развертывания.
- Минимальное время, затраченное на загрузку данных.
- Если процесс развертывания зависит от сервера приложений, то сервер приложений должен быть открытым.
Вопрос:
- Учитывая заявленные требования, каков оптимальный процесс развертывания?
18 ответов
Было отмечено, что 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 и разверните их отдельно на сервере Tomcat. common/lib
каталог; например, используя rsync
,
Обновить:
Так как этот ответ был впервые написан, появился лучший способ развертывания военных файлов на tomcat с нулевым временем простоя. В последних версиях tomcat вы можете включать номера версий в свои военные имена. Так, например, вы можете развернуть файлы ROOT##001.war
а также ROOT##002.war
в один и тот же контекст одновременно. Все после ##
tomcat интерпретирует как номер версии, а не часть пути к контексту. Tomcat будет поддерживать работоспособность всех версий вашего приложения и обслуживать новые запросы и сеансы до последней версии, которая будет полностью запущена, а также корректно завершать старые запросы и сеансы в той версии, с которой они начали. Указание номеров версий также можно выполнить с помощью менеджера tomcat и даже каталитических задач. Больше информации здесь.
Оригинальный ответ:
Rsync имеет тенденцию быть неэффективным для сжатых файлов, так как его алгоритм дельта-передачи ищет изменения в файлах и небольшое изменение несжатого файла, может резко изменить результирующую сжатую версию. По этой причине, возможно, имеет смысл использовать rsync несжатый файл войны, а не сжатую версию, если пропускная способность сети оказывается узким местом.
Что плохого в использовании приложения Tomcat Manager для развертывания? Если вы не хотите загружать весь war-файл напрямую в приложение менеджера Tomcat из удаленного местоположения, вы можете rsync (без сжатия по причинам, указанным выше) поместить его в производственную ячейку, перепаковать его в war и затем передайте его менеджеру на месте. Существует хорошая муравейная задача, которая поставляется вместе с Tomcat и позволяет создавать сценарии развертывания с помощью приложения менеджера Tomcat.
В вашем подходе есть еще один недостаток, который вы не упомянули: хотя ваше приложение частично развернуто (во время операции rsync), ваше приложение может находиться в несовместимом состоянии, когда измененные интерфейсы могут быть не синхронизированы, новые / обновленные зависимости могут быть недоступным и т. д. Кроме того, в зависимости от того, сколько времени занимает работа rsync, ваше приложение может перезапускаться несколько раз. Знаете ли вы, что вы можете и должны отключить режим прослушивания измененных файлов и перезапуска в Tomcat? Это на самом деле не рекомендуется для производственных систем. Вы всегда можете выполнить перезапуск приложения вручную или с помощью сценария ant, используя приложение менеджера Tomcat.
Конечно, ваше приложение будет недоступно пользователям во время перезапуска. Но если вы так обеспокоены доступностью, у вас наверняка есть резервные веб-серверы за балансировщиком нагрузки. При развертывании обновленного файла war вы можете временно настроить балансировщик нагрузки для отправки всех запросов на другие веб-серверы до завершения развертывания. Промойте и повторите для других веб-серверов.
В любой среде, где необходимо учитывать время простоя, вы, безусловно, используете какой-то кластер серверов для повышения надежности за счет избыточности. Я бы вынул хост из кластера, обновил его, а затем выбросил обратно в кластер. Если у вас есть обновление, которое не может работать в смешанной среде (например, для БД требуется несовместимое изменение схемы), вам придется закрыть весь сайт, по крайней мере, на мгновение. Хитрость заключается в том, чтобы вызвать процессы замены, прежде чем бросать оригиналы.
Используя tomcat в качестве примера - вы можете использовать CATALINA_BASE, чтобы определить каталог, в котором будут найдены все рабочие каталоги tomcat, отдельно от исполняемого кода. Каждый раз, когда я развертываю программное обеспечение, я развертываю в новый базовый каталог, чтобы у меня мог быть новый код, находящийся на диске рядом со старым кодом. Затем я могу запустить другой экземпляр tomcat, который указывает на новый базовый каталог, запустить и запустить все, а затем заменить старый процесс (номер порта) на новый в подсистеме балансировки нагрузки.
Если меня беспокоит сохранение данных сеанса через коммутатор, я могу настроить свою систему таким образом, чтобы у каждого хоста был партнер, которому он реплицирует данные сеанса. Я могу отбросить один из этих хостов, обновить его, вернуть его обратно, чтобы он восстановил данные сеанса, а затем переключить два хоста. Если у меня есть несколько пар в кластере, я могу отбросить половину всех пар, затем выполнить массовое переключение, или я могу сделать их по паре за раз, в зависимости от требований выпуска, требований предприятия и т. Д. Лично, однако, я предпочитаю просто позволить конечным пользователям страдать от очень частой потери активного сеанса, а не пытаться обновить его без изменений.
Это все компромисс между ИТ-инфраструктурой, сложностью процесса выпуска и усилиями разработчиков. Если ваш кластер достаточно большой и ваше желание достаточно сильное, то достаточно легко спроектировать систему, которая может быть заменена без каких-либо простоев для большинства обновлений. Большие изменения схемы часто приводят к фактическому простою, поскольку обновленное программное обеспечение обычно не может вместить старую схему, и вам, вероятно, не удастся скопировать данные в новый экземпляр БД, выполнить обновление схемы и затем переключить серверы на новый БД, поскольку вы будете пропускать любые данные, записанные в старую после того, как из нее будет клонирована новая БД. Конечно, если у вас есть ресурсы, вы можете поручить разработчикам модифицировать новое приложение, чтобы использовать новые имена таблиц для всех обновляемых таблиц, и вы можете установить триггеры на действительной базе данных, которая будет корректно обновлять новые таблицы с данными как он записывается в старые таблицы предыдущей версией (или может использовать представления для эмуляции одной схемы из другой). Поднимите ваши новые серверы приложений и поменяйте их местами в кластере. Существует множество игр, в которые вы можете играть, чтобы минимизировать время простоя, если у вас есть ресурсы для их создания.
Возможно, самый полезный механизм сокращения времени простоя при обновлении программного обеспечения - убедиться, что ваше приложение может работать в режиме только для чтения. Это предоставит пользователям необходимую функциональность, но предоставит вам возможность вносить изменения в масштабе всей системы, которые требуют изменений в базе данных и тому подобное. Переведите ваше приложение в режим только для чтения, затем клонируйте данные, обновите схему, подключите новые серверы приложений к новой базе данных, затем переключите балансировщик нагрузки для использования новых серверов приложений. Ваше единственное время простоя - это время, необходимое для перехода в режим только для чтения, и время, необходимое для изменения конфигурации вашего балансировщика нагрузки (большинство из которых может справиться с этим без какого-либо простоя вообще).
Мой совет - использовать rsync с разнесенными версиями, но развернуть файл войны.
- Создайте временную папку в живом окружении, где у вас будет взорванная версия веб-приложения.
- Rsync взорвал версии.
- После успешного rsync создайте файл war во временной папке на машине с действующей средой.
- Замените старую войну в каталоге развертывания сервера новой папкой из временной папки.
Рекомендуется заменить старую войну новой войной в контейнере JBoss (который основан на Tomcat), потому что это атомарная и быстрая операция, и он уверен, что при запуске развертывателя все приложение будет в развернутом состоянии.
Разве вы не можете сделать локальную копию текущего веб-приложения на веб-сервере, выполнить rsync для этого каталога, а затем, возможно, даже с помощью символических ссылок, одним махом, указать Tomcat на новое развертывание без особых простоев?
Горячее развертывание Java EAR для минимизации или устранения простоев приложения на сервере или "Горячее" развертывание зависимости от войны в Jboss с использованием плагина Jboss Tools Eclipse может иметь несколько вариантов.
Развертывание в кластере без простоев тоже интересно.
JavaRebel также имеет возможность быстрого развертывания кода.
Ваш подход к rsync извлеченной войны довольно хорош, а также к перезапуску, поскольку я считаю, что на рабочем сервере не должно быть включено горячее развертывание. Таким образом, единственным недостатком является время простоя, когда вам нужно перезагрузить сервер, верно?
Я предполагаю, что все состояние вашего приложения хранится в базе данных, поэтому у вас нет проблем с некоторыми пользователями, работающими на одном экземпляре сервера приложений, в то время как другие пользователи находятся на другом экземпляре сервера приложений. Если так,
Запустите два сервера приложений: Запустите второй сервер приложений (который прослушивает другие порты TCP) и разверните на нем свое приложение. После развертывания обновите конфигурацию Apache httpd (mod_jk или mod_proxy), чтобы он указывал на второй сервер приложений. Изящно перезапустите процесс Apache httpd. Таким образом, у вас не будет простоев, и новые пользователи и запросы будут автоматически перенаправлены на новый сервер приложений.
Если вы сможете использовать поддержку кластеризации и репликации сеансов сервера приложений, это будет даже гладко для пользователей, которые в данный момент вошли в систему, так как второй сервер приложений выполнит повторную синхронизацию при запуске. Затем, когда нет доступа к первому серверу, выключите его.
Это зависит от архитектуры вашего приложения.
Одно из моих приложений находится за прокси-сервером с балансировкой нагрузки, где я выполняю поэтапное развертывание, эффективно устраняя время простоя.
Если статические файлы являются большой частью вашей большой WAR (100Mo довольно большой), то размещение их вне WAR и развертывание их на веб-сервере (например, Apache) перед вашим сервером приложений может ускорить процесс. Кроме того, Apache обычно лучше обрабатывает статические файлы, чем движок сервлетов (даже если большинство из них добились значительных успехов в этой области).
Таким образом, вместо того, чтобы производить большую жирную ВОЙНУ, положите ее на диету и произведите:
- большой жирный ZIP со статическими файлами для Apache
- менее жирная WAR для двигателя сервлета.
При желании можно пойти дальше в процессе уменьшения WAR: если возможно, разверните Grails и другие JAR-файлы, которые не изменяются часто (что, вероятно, имеет место в большинстве из них), на уровне сервера приложений.
Если вам удастся создать более легкую WAR, я не стал бы беспокоиться о rsyncing каталогах, а не архивах.
Сильные стороны этого подхода:
- Статические файлы могут быть оперативно "развернуты" в Apache (например, использовать символическую ссылку, указывающую на текущий каталог, разархивировать новые файлы, обновить символическую ссылку и вуаля).
- WAR будет тоньше, и его развертывание займет меньше времени.
Слабость этого подхода:
- Есть еще один сервер (веб-сервер), так что это добавляет (немного) больше сложности.
- Вам нужно будет изменить сценарии сборки (не так уж важно IMO).
- Вам нужно будет изменить логику rsync.
На что установлен ваш PermSpace? Я ожидал бы увидеть этот рост также, но должен пойти вниз после сбора старых классов? (или ClassLoader все еще сидит без дела?)
Думая громко, вы можете rsync в отдельную директорию с именем или датой. Если контейнер поддерживает символические ссылки, не могли бы вы SIGSTOP корневого процесса, переключить корень файловой системы контекста через символическую ссылку, а затем SIGCONT?
Что касается раннего перезапуска контекста. Все контейнеры имеют параметры конфигурации для отключения автоматического повторного развертывания при изменении файла класса или статического ресурса. Вы, вероятно, не можете отключить автоматическое повторное развертывание изменений web.xml, поэтому этот файл обновляется последним. Поэтому, если вы отключите автоматическое повторное развертывание и обновите файл web.xml как последний, вы увидите, что контекст перезапустится после всего обновления.
Мы загружаем новую версию веб-приложения в отдельный каталог, а затем либо перемещаемся, чтобы заменить его работающей, либо используем символические ссылки. Например, у нас есть символическая ссылка в каталоге веб-приложений tomcat с именем "myapp", которая указывает на текущее веб-приложение с именем "myapp-1.23". Мы загружаем новое веб-приложение в "myapp-1.24". Когда все будет готово, остановите сервер, удалите символическую ссылку и создайте новую, указывающую на новую версию, затем снова запустите сервер.
Мы отключаем автоматическую перезагрузку на производственных серверах для повышения производительности, но даже в этом случае изменение файлов в веб-приложении неядерным способом может вызвать проблемы, поскольку статические файлы или даже страницы JSP могут изменяться так, что это приводит к неработающим ссылкам или, что еще хуже.
На практике веб-приложения на самом деле располагаются на общем устройстве хранения, поэтому кластерные серверы, серверы с балансировкой нагрузки и отказоустойчивые серверы имеют одинаковый код.
Основным недостатком вашей ситуации является то, что загрузка займет больше времени, поскольку ваш метод позволяет rsync передавать только измененные или добавленные файлы. Вы можете сначала скопировать старую папку webapp в новую, и rsync к ней, если это имеет существенное значение и если это действительно проблема.
Я не уверен, что это ответит на ваш вопрос, но я просто поделюсь процессом развертывания, который я использую или сталкиваюсь в нескольких проектах, которые я делал.
Похоже на то, что я никогда не вспоминаю о полном передислокации или обновлении войны. В большинстве случаев мои обновления ограничены несколькими jsp-файлами, возможно, библиотекой, некоторыми файлами классов. Я могу управлять и определять, какие артефакты затронуты, и обычно мы упаковывали это обновление в zip-файл вместе со скриптом обновления. Я буду запускать скрипт обновления. Сценарий выполняет следующие действия:
- Сделайте резервную копию файлов, которые будут перезаписаны, возможно, в папку с сегодняшней датой и временем.
- Распакуйте мои файлы
- Остановите сервер приложений
- Переместить файлы поверх
- Запустите сервер приложений
Если время простоя является проблемой, и обычно это так, мои проекты обычно представляют собой HA, даже если они не имеют общего состояния, но используют маршрутизатор, который обеспечивает маршрутизацию слипших сеансов.
Еще одна вещь, которая мне любопытна, была бы, почему нужно rsync? Вы должны знать, какие изменения необходимы, определяя их в своей среде подготовки / разработки, не выполняя дельта-проверки в режиме реального времени. В большинстве случаев вам придется настроить rsync так, чтобы он все равно игнорировал файлы, например, определенные файлы свойств, которые определяют ресурсы, используемые производственным сервером, такие как соединение с базой данных, сервер smtp и т. Д.
Я надеюсь, что это полезно.
Просто используйте 2 или более сервера Tomcat с прокси-сервером. Этот прокси может быть apache / nignix / haproxy.
Теперь в каждом из прокси-серверов есть "входящий" и "выходной" URL с настройками портов.
Сначала скопируйте свою войну в кота, не останавливая службу. Как только война развернута, она автоматически открывается движком кота.
Обратите внимание на перекрестную проверку unpackWARs="true" и autoDeploy="true" в узле "Host" внутри server.xml
Это выглядит так
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true"
xmlValidation="false" xmlNamespaceAware="false">
Теперь посмотрите логи tomcat. Если ошибки нет, значит, успешно.
Теперь нажмите все API для тестирования
Теперь зайдите на ваш прокси-сервер.
Просто измените фоновое отображение URL с именем новой войны. Поскольку регистрация на прокси-серверах, таких как apache / nignix / haProxy, заняла очень мало времени, вы почувствуете минимальное время простоя
Обратитесь - https://developers.google.com/speed/pagespeed/module/domains для сопоставления URL-адресов.
Вы используете Resin, Resin имеет встроенную поддержку для управления версиями веб-приложений.
http://www.caucho.com/resin-4.0/admin/deploy.xtp#VersioningandGracefulUpgrades
Обновление: это сторожевой процесс может помочь с проблемами permgenspace.
Tomcat 7 имеет замечательную функцию, называемую " параллельное развертывание", которая разработана для этого варианта использования.
Суть в том, что вы расширяете.war в каталог, либо непосредственно под webapps /, либо по символической ссылке. Последующие версии приложения находятся в каталогах с именем app##version
, например myapp##001
а также myapp##002
, Tomcat будет обрабатывать существующие сессии, переходя на старую версию, а новые сессии переходить на новую версию.
Загвоздка в том, что вы должны быть очень осторожны с утечками PermGen. Это особенно верно для Grails, который использует много PermGen. VisualVM твой друг.
Не "лучшая практика", а то, о чем я только что подумал.
Как насчет развертывания веб-приложения через DVCS, например, git?
Таким образом, вы можете позволить git выяснить, какие файлы передавать на сервер. У вас также есть хороший способ отойти от этого, если он окажется разоренным, просто сделайте возврат!
Я написал скрипт bash, который принимает несколько параметров и выполняет rsyncs файл между серверами. Ускоряет передачу rsync для больших архивов: