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

StackOverflow https://stackoverflow.com/questions/1640333

Вопрос

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

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

Хорошие стороны такого подхода:

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

Плохие стороны такого подхода:

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

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

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

Вопрос:

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

Решение

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

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

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

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

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


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

Другие советы

Обновить:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • большой толстый ZIP-файл со статическими файлами для Apache
  • менее ожесточенная ВОЙНА для движка сервлетов.

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

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

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

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

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

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

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

Как и вы, я никогда не припомню, чтобы производил полную передислокацию войск или обновление.Большую часть времени мои обновления ограничены несколькими файлами jsp, возможно, библиотекой, некоторыми файлами классов.Я могу управлять и определять, какие артефакты затронуты, и обычно мы упаковываем эти обновления в zip-файл вместе со сценарием обновления.Я запущу скрипт обновления.Скрипт выполняет следующее:

  • Создайте резервную копию файлов, которые будут перезаписаны, возможно, в папку с сегодняшней датой и временем.
  • Распакуйте мои файлы
  • Остановите сервер приложений
  • Переместите файлы поверх
  • Запустите сервер приложений

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

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

Я надеюсь, что это полезно.

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

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

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

Мы загружаем новую версию веб-приложения в отдельный каталог, затем либо меняем ее местами с запущенной, либо используем символические ссылки.Например, у нас есть символическая ссылка в каталоге tomcat webapps с именем "myapp", которая указывает на текущее веб-приложение с именем "myapp-1.23".Мы загружаем новое веб-приложение в "myapp-1.24".Когда все будет готово, остановите сервер, удалите символическую ссылку и создайте новую, указывающую на новую версию, затем запустите сервер снова.

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

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

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

В Tomcat 7 есть приятная функция под названием "параллельное развертывание" это разработано для данного варианта использования.

Суть в том, что вы расширяете .war в каталог, либо непосредственно в webapps /, либо с символической ссылкой.Последовательные версии приложения находятся в каталогах с именем app##version, например myapp##001 и myapp##002.Tomcat будет обрабатывать существующие сеансы, переходящие к старой версии, и новые сеансы, переходящие к новой версии.

Загвоздка в том , что вы должны быть очень будьте осторожны с утечками PermGen.Это особенно верно для Grails, которые используют много PermGen.VisualVM - ваш друг.

Просто используйте 2 или более серверов tomcat с прокси-сервером поверх него.Этот прокси-сервер может принадлежать apache / nignix /haproxy.

Теперь на каждом прокси-сервере есть URL "in" и "out" с настроенными портами.

Сначала скопируйте свой war в tomcat, не останавливая работу сервиса.Как только war развернут, он автоматически открывается движком tomcat.

Примечание перекрестная проверка 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.

Не "лучшая практика", а то, о чем я только что подумал.

Как насчет развертывания веб-приложения через DVCS, такой как git?

Таким образом, вы можете позволить git определить, какие файлы передавать на сервер.У вас также есть хороший способ отказаться от него, если окажется, что он сломан, просто сделайте возврат!

Я написал скрипт bash, который принимает несколько параметров и повторно синхронизирует файл между серверами.Значительно ускоряет передачу rsync для больших архивов:

https://gist.github.com/3985742

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top