Les meilleures pratiques pour déployer des applications Web Java avec un temps d'immobilisation minimal?

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

Question

Lors du déploiement d'une application Web Java volumineuse (> 100 Mo .war), j'utilise actuellement le processus de déploiement suivant:

  • Le fichier .war de l'application est développé localement sur la machine de développement.
  • L’application étendue est rsync: ed de la machine de développement vers l’environnement réel.
  • Le serveur d'applications dans l'environnement en direct est redémarré après le rsync. Cette étape n'est pas strictement nécessaire, mais j'ai constaté que le redémarrage du serveur d'applications lors du déploiement évite "java.lang.OutOfMemoryError: PermGen space". en raison du chargement fréquent des classes.

Bonnes choses à propos de cette approche:

  • rsync minimise la quantité de données envoyées par la machine de développement à l'environnement en direct. Le téléchargement de l’ensemble du fichier .war prend plus de dix minutes alors qu’un rsync ne prend que quelques secondes.

Mauvaises choses à propos de cette approche:

  • Pendant l'exécution de rsync, le contexte de l'application est redémarré car les fichiers sont mis à jour. Idéalement, le redémarrage devrait avoir lieu une fois la procédure rsync terminée, et non lorsqu'elle est toujours en cours d'exécution.
  • Le redémarrage du serveur d'applications entraîne environ deux minutes d'indisponibilité.

J'aimerais trouver un processus de déploiement avec les propriétés suivantes:

  • Temps d'arrêt minimal pendant le processus de déploiement.
  • Temps minimal de téléchargement des données.
  • Si le processus de déploiement est spécifique au serveur d'applications, celui-ci doit être à source ouverte.

Question:

  • Étant donné les exigences énoncées, quel est le processus de déploiement optimal?
Était-ce utile?

La solution

Il a été noté que rsync ne fonctionnait pas correctement lors de la publication des modifications dans un fichier WAR. La raison en est que les fichiers WAR sont essentiellement des fichiers ZIP et sont créés par défaut avec des fichiers membres compressés. De petites modifications apportées aux fichiers membres (avant la compression) entraînent des différences importantes dans le fichier ZIP, ce qui rend l'algorithme de transfert delta de rsync inefficace.

Une solution possible consiste à utiliser jar -0 ... pour créer le fichier WAR d'origine. L'option -0 indique à la commande jar de ne pas compresser les fichiers membres lors de la création du fichier WAR. Ensuite, lorsque rsync compare les anciennes et les nouvelles versions du fichier WAR, l’algorithme de transfert delta devrait pouvoir créer de petites différences. Ensuite, arrangez-vous pour que rsync envoie les diffs (ou les fichiers originaux) sous forme compressée; par exemple. utilisez rsync -z ... ou un flux / transport de données compressé situé en dessous.

EDIT: Selon la structure du fichier WAR, il peut également être nécessaire d’utiliser jar -0 ... pour créer des fichiers JAR de composants. Cela s’appliquerait aux fichiers JAR fréquemment sujets à modification (ou qui sont simplement reconstruits), plutôt qu’aux fichiers JAR stables tiers.

En théorie, cette procédure devrait apporter une amélioration significative par rapport à l’envoi de fichiers WAR normaux. En pratique, je n’ai pas essayé cela, je ne peux donc pas promettre que cela fonctionnera.

L’inconvénient est que le fichier WAR déployé sera beaucoup plus volumineux. Cela peut entraîner des temps de démarrage plus longs pour les applications Web, bien que je suppose que l'effet serait marginal.

Une approche totalement différente consisterait à consulter votre fichier WAR pour voir si vous pouvez identifier les fichiers JAR de bibliothèque susceptibles de ne jamais (presque) jamais changer. Extrayez ces fichiers JAR du fichier WAR et déployez-les séparément dans le répertoire common / lib du serveur Tomcat; par exemple. en utilisant rsync .

Autres conseils

Mise à jour:

Depuis que cette réponse a été écrite pour la première fois, un meilleur moyen de déployer des fichiers war sur tomcat avec zéro temps d'indisponibilité est apparu. Dans les versions récentes de tomcat, vous pouvez inclure des numéros de version dans vos noms de fichiers war. Ainsi, par exemple, vous pouvez déployer simultanément les fichiers ROOT ## 001.war et ROOT ## 002.war dans le même contexte. Tout ce qui suit le ## est interprété comme un numéro de version par tomcat et ne fait pas partie du chemin de contexte. Tomcat maintiendra toutes les versions de votre application en cours d'exécution et servira les nouvelles demandes et sessions à la version la plus récente entièrement opérationnelle tout en complétant gracieusement les anciennes demandes et sessions sur la version avec laquelle elles ont démarré. Vous pouvez également spécifier les numéros de version via le gestionnaire de tomcat et même les tâches catalina ant. Plus d'infos ici .

Réponse d'origine:

Rsync a tendance à être inefficace sur les fichiers compressés car son algorithme de transfert delta recherche les modifications apportées aux fichiers et une petite modification sur un fichier non compressé peut altérer considérablement la version compressée résultante. Pour cette raison, il pourrait être judicieux de rsync un fichier war non compressé plutôt qu'une version compressée, si la bande passante du réseau s'avère être un goulot d'étranglement.

Qu'est-ce qui ne va pas avec l'application de gestion Tomcat pour vos déploiements? Si vous ne souhaitez pas télécharger l'intégralité du fichier war directement dans l'application Tomcat Manager à partir d'un emplacement distant, vous pouvez le rsync (non compressé pour les raisons mentionnées ci-dessus) dans un emplacement réservé de la zone de production, le reconditionner en mode guerre et remettez-le ensuite au responsable local. Il existe une belle tâche ant livrée avec Tomcat qui vous permet d’écrire des déploiements à l’aide de l’application Tomcat Manager.

Il existe une faille supplémentaire dans votre approche que vous n'avez pas mentionnée: lorsque votre application est partiellement déployée (au cours d'une opération rsync), votre application peut se trouver dans un état incohérent où les interfaces modifiées risquent d'être désynchronisées. les dépendances mises à jour peuvent ne pas être disponibles, etc. En outre, selon le temps que prend votre tâche rsync, votre application peut en fait redémarrer plusieurs fois. Savez-vous que vous pouvez et devez désactiver le comportement d'écoute des fichiers modifiés et de redémarrage dans Tomcat? En fait, il n'est pas recommandé pour les systèmes de production. Vous pouvez toujours effectuer un redémarrage manuel ou avec un script ant de votre application à l’aide de l’application Tomcat Manager.

Bien entendu, votre application ne sera pas disponible pour les utilisateurs lors d’un redémarrage. Mais si la disponibilité vous préoccupe tellement, vous avez sûrement des serveurs Web redondants derrière un équilibreur de charge. Lors du déploiement d'un fichier war mis à jour, l'équilibreur de charge peut temporairement envoyer toutes les demandes à d'autres serveurs Web jusqu'à la fin du déploiement. Rincez et répétez pour vos autres serveurs Web.

Dans tous les environnements où les temps d'arrêt sont un facteur important, vous exécutez sûrement une sorte de cluster de serveurs afin d'accroître la fiabilité via la redondance. Je prendrais un hôte hors du cluster, le mettrais à jour et le jetterais dans le cluster. Si vous avez une mise à jour qui ne peut pas être exécutée dans un environnement mixte (modification du schéma incompatible requise sur la base de données, par exemple), vous devrez supprimer tout le site, au moins un instant. L'astuce consiste à mettre en place des processus de remplacement avant de déposer les originaux.

En utilisant tomcat comme exemple - vous pouvez utiliser CATALINA_BASE pour définir un répertoire dans lequel tous les répertoires de travail de tomcat seront trouvés, indépendamment du code exécutable. Chaque fois que je déploie un logiciel, je le déploie dans un nouveau répertoire de base afin que le nouveau code réside sur le disque à côté de l'ancien code. Je peux ensuite démarrer une autre instance de tomcat qui pointe vers le nouveau répertoire de base, tout démarrer et s'exécuter, puis permuter l'ancien processus (numéro de port) par le nouveau dans l'équilibreur de charge.

Si je souhaite préserver les données de session sur le commutateur, je peux configurer mon système de sorte que chaque hôte ait un partenaire avec lequel il réplique les données de session. Je peux supprimer un de ces hôtes, le mettre à jour, le restaurer afin qu'il récupère les données de la session, puis basculer les deux hôtes. Si j'ai plusieurs paires dans le cluster, je peux supprimer la moitié de toutes les paires, puis effectuer un basculement de masse ou à la fois, en fonction des exigences de la version, des exigences de l'entreprise, etc. Personnellement, cependant, je préfère laisser les utilisateurs finaux subir la perte très occasionnelle d’une session active plutôt que de tenter de mettre à niveau avec des sessions intactes.

Tout cela est un compromis entre l’infrastructure informatique, la complexité des processus de publication et les efforts des développeurs. Si votre cluster est assez grand et votre désir assez fort, il est assez facile de concevoir un système pouvant être échangé sans interruption pour la plupart des mises à jour. Les modifications de schéma volumineuses imposent souvent des temps d'arrêt réels, car les logiciels mis à jour ne peuvent généralement pas prendre en charge l'ancien schéma et vous ne pouvez probablement pas vous permettre de copier les données dans une nouvelle instance de base de données, en effectuant la mise à jour du schéma, puis en basculant les serveurs sur la nouvelle base de données, car vous aurez oublié toutes les données écrites dans l'ancien après le clonage de la nouvelle base de données. Bien sûr, si vous avez des ressources, vous pouvez demander aux développeurs de modifier la nouvelle application pour utiliser de nouveaux noms de table pour toutes les tables mises à jour, et vous pouvez également mettre en place des déclencheurs sur la base de données dynamique, qui mettront correctement à jour les nouvelles tables il est écrit dans les anciennes tables par la version précédente (ou peut-être utiliser des vues pour émuler un schéma de l'autre). Amenez vos nouveaux serveurs d'applications et échangez-les dans le cluster. Il existe une tonne de jeux auxquels vous pouvez jouer afin de minimiser les temps d'arrêt si vous avez les ressources de développement pour les construire.

Le mécanisme le plus utile pour réduire les temps d'arrêt au cours des mises à niveau logicielles consiste à s'assurer que votre application peut fonctionner en mode lecture seule. Cela apportera certaines fonctionnalités nécessaires à vos utilisateurs mais vous laissera la possibilité d’apporter des modifications à l’échelle du système qui nécessitent des modifications de la base de données, etc. Placez votre application en mode lecture seule, puis clonez les données, mettez à jour le schéma, affichez les nouveaux serveurs d'applications contre la nouvelle base de données, puis basculez l'équilibreur de charge pour utiliser les nouveaux serveurs d'applications. Votre seul temps d'arrêt est le temps nécessaire pour passer en mode lecture seule et le temps nécessaire pour modifier la configuration de votre équilibreur de charge (la plupart d'entre eux peuvent le gérer sans aucun temps d'arrêt).

Mon conseil est d'utiliser rsync avec des versions éclatées mais de déployer un fichier war.

  1. Créez un dossier temporaire dans l'environnement en direct où vous aurez explosé la version de webapp.
  2. Versions éclatées de Rsync.
  3. Après avoir réussi rsync, créez un fichier war dans un dossier temporaire de la machine à environnement réel.
  4. Remplacez l'ancienne guerre dans le répertoire de déploiement du serveur par une nouvelle du dossier temporaire.

Il est recommandé de remplacer l’ancienne guerre par une nouvelle dans le conteneur JBoss (basé sur Tomcat), car c’est une opération atomique et rapide et il est certain que lorsque l’applicateur démarrera, l’application entière sera déployée.

Ne pouvez-vous pas créer une copie locale de l’application Web actuelle sur le serveur Web, rsync dans ce répertoire, puis peut-être même en utilisant des liens symboliques, pointez "aller" vers un nouveau déploiement sans temps d’interruption?

Votre approche de la guerre extraite par rsynchronisation est plutôt bonne, tout comme le redémarrage, car j'estime qu'un déploiement à chaud ne devrait pas être activé pour un serveur de production. Ainsi, le seul inconvénient est le temps d'arrêt au moment où vous devez redémarrer le serveur, n'est-ce pas?

Je suppose que tous les états de votre application sont conservés dans la base de données. Par conséquent, certains utilisateurs ne travaillent pas sur une instance de serveur d'applications alors que d'autres utilisateurs se trouvent sur une autre instance de serveur d'applications. Si oui,

Exécuter deux serveurs d'applications : démarrez le deuxième serveur d'applications (qui écoute sur d'autres ports TCP) et déployez votre application à cet emplacement. Après le déploiement, mettez à jour la configuration d'Apache httpd (mod_jk ou mod_proxy) pour qu'elle pointe vers le deuxième serveur d'applications. Redémarrage gracieux du processus Apache httpd. De cette façon, vous n'aurez plus de temps d'arrêt, les nouveaux utilisateurs et les demandes seront automatiquement redirigés vers le nouveau serveur d'applications.

Si vous pouvez utiliser la prise en charge du clustering et de la réplication de session du serveur d'applications, les utilisateurs actuellement connectés se verront appliquer une configuration fluide, car le second serveur d'applications se resynchronise dès qu'il démarre. Ensuite, s'il n'y a pas d'accès au premier serveur, fermez-le.

Cela dépend de votre architecture d'application.

L'une de mes applications se trouve derrière un proxy d'équilibrage de charge, où j'effectue un déploiement échelonné, ce qui élimine efficacement les temps d'arrêt.

Si les fichiers statiques constituent une part importante de votre gros WAR (100 Mo est assez gros), il peut être plus rapide de les placer en dehors du WAR et de les déployer sur un serveur Web (Apache, par exemple) devant votre serveur d'applications. De plus, Apache sert généralement mieux les fichiers statiques que le moteur de servlet (même si la plupart d'entre eux ont fait des progrès significatifs dans ce domaine).

Ainsi, au lieu de produire une grosse guerre, mettez-la dans votre régime et produisez:

  • un gros fichier ZIP contenant des fichiers statiques pour Apache
  • Une WAR moins épaisse pour le moteur de servlet.

Vous pouvez éventuellement aller plus loin dans le processus de WAR afin de le rendre plus mince: si possible, déployez des Grails et autres JAR qui ne changent pas souvent (ce qui est probablement le cas de la plupart d'entre eux) au niveau du serveur d'applications.

Si vous parvenez à produire un fichier WAR plus léger, je ne me ferais pas la peine de resynchroniser des répertoires plutôt que des archives.

Points forts de cette approche:

  1. Les fichiers statiques peuvent être "hot" déployés " sur Apache (par exemple, utilisez un lien symbolique pointant sur le répertoire en cours, décompressez les nouveaux fichiers, mettez à jour le lien symbolique et voilà).
  2. La guerre sera plus fine et son déploiement prendra moins de temps.

Faiblesse de cette approche:

  1. Il existe un serveur supplémentaire (le serveur Web), ce qui ajoute un peu plus de complexité.
  2. Vous aurez besoin de changer les scripts de construction (pas une grosse affaire OMI).
  3. Vous devrez modifier la logique rsync.

Je ne sais pas si cela répond à votre question, mais je ne parlerai que du processus de déploiement que j'utilise ou que je rencontre dans les quelques projets que j'ai réalisés.

De la même manière que vous, je ne me souviens jamais avoir procédé à un redéploiement ou à une mise à jour complète de la guerre. La plupart du temps, mes mises à jour sont limitées à quelques fichiers jsp, peut-être une bibliothèque, des fichiers de classe. Je suis capable de gérer et de déterminer quels sont les artefacts affectés, et généralement, nous avons regroupé ces mises à jour dans un fichier zip, avec un script de mise à jour. Je vais lancer le script de mise à jour. Le script effectue les opérations suivantes:

  • Sauvegardez les fichiers qui seront écrasés, dans un dossier contenant la date et l'heure du jour.
  • Déballez mes fichiers
  • Arrêtez le serveur d'applications
  • Déplacez les fichiers sur
  • Démarrer le serveur d'applications

Si les temps d'arrêt vous préoccupent, comme c'est généralement le cas, mes projets sont en haute disponibilité, même s'ils ne partagent pas l'état, mais qu'ils utilisent un routeur fournissant un routage de session permanent.

Une autre chose qui me rend curieux serait, pourquoi le besoin de rsync? Vous devriez pouvoir savoir quels sont les changements requis en les déterminant dans votre environnement de transfert / de développement, sans effectuer de vérifications delta avec live. Dans la plupart des cas, vous devrez quand même ajuster votre rsync pour ignorer les fichiers, tels que certains fichiers de propriétés qui définissent les ressources utilisées par un serveur de production, tels que la connexion à une base de données, le serveur smtp, etc.

J'espère que cela vous sera utile.

A quoi est défini votre PermSpace? Je m'attendrais à ce que cela se développe également, mais devrait descendre après la collecte des anciennes classes? (ou le ClassLoader reste-t-il toujours en place?)

En pensant trop fort, vous pouvez rsync dans un répertoire séparé, nommé avec la version ou la date. Si le conteneur prend en charge les liens symboliques, pouvez-vous SIGSTOP le processus racine, basculer sur la racine du système de fichiers du contexte via un lien symbolique, puis sur SIGCONT?

En ce qui concerne le contexte initial redémarre. Tous les conteneurs ont des options de configuration pour désactiver le redéploiement automatique sur les modifications de fichiers de classe ou de ressources statiques. Vous ne pouvez probablement pas désactiver le redéploiement automatique sur les modifications web.xml, ce fichier est donc le dernier à mettre à jour. Donc, si vous désactivez le redéploiement automatique et que vous mettez à jour le fichier web.xml en dernier, le contexte redémarre après après la mise à jour complète.

Nous téléchargeons la nouvelle version de l'application Web dans un répertoire distinct, puis nous passons à la remplacer par celle en cours d'exécution ou utilisons des liens symboliques. Par exemple, nous avons un lien symbolique dans le répertoire tomcat webapps nommé "myapp", qui pointe vers la Webapp actuelle nommée "myapp-1.23". Nous téléchargeons la nouvelle application Web sur "myapp-1.24". Lorsque tout est prêt, arrêtez le serveur, supprimez le lien symbolique et créez-en un nouveau pointant vers la nouvelle version, puis redémarrez le serveur.

Nous désactivons le rechargement automatique sur les serveurs de production pour améliorer les performances, mais néanmoins, le fait de modifier de manière non atomique les fichiers contenus dans l'application Web peut être source de problèmes, car les fichiers statiques ou même les pages JSP pourraient être modifiés de manière à provoquer des liens rompus ou pire.

En pratique, les applications Web sont en réalité situées sur un périphérique de stockage partagé. Les serveurs en cluster, à charge équilibrée et à basculement disposent tous du même code.

Le principal inconvénient de votre situation est que le téléchargement prendra plus de temps, car votre méthode permet à rsync de ne transférer que les fichiers modifiés ou ajoutés. Vous pouvez copier l’ancien dossier webapp dans le nouveau, puis rsync, si cela fait une différence significative et si c’est vraiment un problème.

Tomcat 7 dispose d'une fonctionnalité intéressante appelée & déploiement parallèle " conçu pour ce cas d'utilisation.

En résumé, vous développez le fichier .war dans un répertoire, directement sous webapps / ou par lien symbolique. Les versions successives de l'application se trouvent dans les répertoires nommés app ## version , par exemple myapp ## 001 et myapp ## 002 . Tomcat gérera les sessions existantes de l'ancienne version et les nouvelles sessions de la nouvelle version.

Le problème, c’est que vous devez être très prudent avec les fuites de PermGen. Cela est particulièrement vrai avec Grails qui utilise beaucoup de PermGen. VisualVM est votre ami.

Utilisez simplement 2 serveurs tomcat ou plus avec un proxy dessus. Ce proxy peut être apache / nignix / haproxy.

Il y a maintenant "& in" dans chacun des serveurs proxy. et " out " les URL avec les ports sont configurés.

Commencez par copier votre guerre dans le tomcat sans arrêter le service. Une fois que la guerre est déployée, elle est automatiquement ouverte par le moteur tomcat.

Remarquez la vérification croisée unpackWARs = " true " et autoDeploy = " true " dans le noeud " Host " dans server.xml

Cela ressemble à ceci

  <Host name="localhost"  appBase="webapps"
        unpackWARs="true" autoDeploy="true"
        xmlValidation="false" xmlNamespaceAware="false">

Maintenant, consultez les journaux de tomcat. Si aucune erreur n’existe, cela signifie qu’elle fonctionne correctement.

Appuyez maintenant sur toutes les API pour les tester

Maintenant, venez sur votre serveur proxy.

Modifiez simplement le mappage d'URL d'arrière-plan avec le nom de la nouvelle guerre. Comme l'enregistrement avec les serveurs proxy tels qu'apache / nignix / haProxy a pris très peu de temps, vous vous sentirez moins en arrêt

Voir - https://developers.google.com/speed/pagespeed/module / domain pour mapper les URL

Vous utilisez Resin, qui possède un support intégré pour la gestion des versions d’applications Web.

http://www.caucho.com/resin-4.0/ admin / deploy.xtp # VersioningandGracefulUpgrades

Mise à jour: son processus de surveillance peut également aider à résoudre les problèmes d'espace permanent.

N'est pas une "meilleure pratique" mais quelque chose que je viens de penser.

Pourquoi ne pas déployer l'application Web via un DVCS tel que git?

De cette façon, vous pouvez laisser à git le soin de déterminer quels fichiers transférer sur le serveur. Vous avez également un bon moyen de revenir en arrière si cela s'avère être une erreur, faites simplement un retour en arrière!

J'ai écrit un script bash qui prend quelques paramètres et rsyncise le fichier entre les serveurs. Accélère beaucoup le transfert de rsync pour les grandes archives:

https://gist.github.com/3985742

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top