web-dev-qa-db-fra.com

Meilleures pratiques pour le déploiement de Java webapps avec un temps d'arrêt minimal?

Lors du déploiement d'une grande Java webapp (> 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 à l'environnement en direct.
  • Le serveur d'applications dans l'environnement en direct est redémarré après la 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: espace PermGen" en raison du chargement fréquent des classes.

Bonnes choses à propos de cette approche:

  • Le rsync minimise la quantité de données envoyées de la machine de développement vers l'environnement en direct. Le téléchargement de l'intégralité du fichier .war prend plus de dix minutes, tandis qu'une rsync prend 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 synchronisation rsync terminée, pas lorsqu'elle est toujours en cours d'exécution.
  • Le redémarrage du serveur d'applications provoque 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 passé à télécharger les données.
  • Si le processus de déploiement est spécifique au serveur d'applications, le serveur d'applications doit être open-source.

Question:

  • Compte tenu des exigences énoncées, quel est le processus de déploiement optimal?
55
knorv

Il a été noté que rsync ne fonctionne pas correctement lors de la transmission de modifications à 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 de grandes différences d'échelle dans le fichier Zip, rendant l'algorithme de transfert delta de rsync inefficace.

Une solution possible consiste à utiliser jar -0 ... pour créer le fichier WAR d'origine. Le -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 être capable de créer de petites différences. Ensuite, faites en sorte que rsync envoie les diffs (ou fichiers originaux) sous forme compressée; par exemple. utilisation rsync -z ... ou un flux/transport de données compressé 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 composant. Cela s'appliquerait aux fichiers JAR qui sont fréquemment sujets à changement (ou qui sont simplement reconstruits), plutôt qu'aux fichiers JAR tiers stables.

En théorie, cette procédure devrait apporter une amélioration significative par rapport à l'envoi de fichiers WAR classiques. En pratique, je n'ai pas essayé cela, donc je ne peux 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 des applications Web plus longs, même si je soupçonne que l'effet serait marginal.


Une approche entièrement différente serait de regarder votre fichier WAR pour voir si vous pouvez identifier les JAR de bibliothèque qui ne changeront probablement (presque) jamais. Retirez ces JAR du fichier WAR et déployez-les séparément dans le serveur Tomcat common/lib répertoire; par exemple. en utilisant rsync.

20
Stephen C

Mettre à jour:

Depuis que cette réponse a été écrite pour la première fois, une meilleure façon de déployer des fichiers de guerre sur Tomcat sans temps d'arrêt a émergé. Dans les versions récentes de Tomcat, vous pouvez inclure des numéros de version dans vos noms de fichiers de guerre. Ainsi, par exemple, vous pouvez déployer les fichiers ROOT##001.war et ROOT##002.war dans le même contexte simultanément. Tout après le ## est interprété comme un numéro de version par Tomcat et ne fait pas partie du chemin de contexte. Tomcat gardera toutes les versions de votre application en cours d'exécution et servira les nouvelles demandes et sessions à la dernière version entièrement opérationnelle tout en complétant gracieusement les anciennes demandes et sessions sur la version avec laquelle elles ont commencé. La spécification des numéros de version peut également être effectuée via le gestionnaire Tomcat et même les tâches catalina ant. Plus d'informations ici .

Réponse originale:

Rsync a tendance à être inefficace sur les fichiers compressés car son algorithme de transfert delta recherche les changements dans les fichiers et une petite modification d'un fichier non compressé peut radicalement modifier la version compressée résultante. Pour cette raison, il peut ê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.

Quel est le problème avec l'utilisation de l'application Tomcat Manager pour effectuer 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 resynchroniser (non compressé pour les raisons mentionnées ci-dessus) vers un emplacement d'espace réservé sur la boîte de production, le reconditionner dans une guerre et puis remettez-le au responsable localement. Il existe une tâche de fourmi Nice livrée avec Tomcat vous permettant de script des déploiements à l'aide de l'application de gestionnaire Tomcat.

Il y a une faille supplémentaire dans votre approche que vous n'avez pas mentionnée: alors que votre application est partiellement déployée (pendant une opération rsync), votre application peut être dans un état incohérent où les interfaces modifiées peuvent être désynchronisées, des dépendances nouvelles/mises à jour peuvent être indisponible, etc. En outre, en fonction de la durée de votre travail 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? Il n'est en fait pas recommandé pour les systèmes de production. Vous pouvez toujours effectuer un redémarrage manuel ou scripté de votre application à l'aide de l'application Tomcat Manager.

Votre application ne sera bien sûr pas accessible aux utilisateurs lors d'un redémarrage. Mais si vous êtes tellement préoccupé par la disponibilité, vous avez sûrement des serveurs Web redondants derrière un équilibreur de charge. Lors du déploiement d'un fichier war mis à jour, vous pouvez temporairement demander à l'équilibreur de charge d'envoyer toutes les demandes à d'autres serveurs Web jusqu'à la fin du déploiement. Rincez et répétez pour vos autres serveurs Web.

30
Asaph

Dans tout environnement où les temps d'arrêt sont une considération, vous exécutez sûrement une sorte de cluster de serveurs pour augmenter la fiabilité via la redondance. Je retirerais un hôte du cluster, le mettrais à jour, puis le rejetterais dans le cluster. Si vous avez une mise à jour qui ne peut pas s'exécuter dans un environnement mixte (changement de schéma incompatible requis sur la base de données, par exemple), vous allez devoir supprimer tout le site, au moins pour un moment. L'astuce consiste à faire apparaître les 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, séparés du code exécutable. Chaque fois que je déploie un logiciel, je déploie dans un nouveau répertoire de base afin que je puisse avoir un nouveau code résidant 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 fonctionner, puis échanger l'ancien processus (numéro de port) avec le nouveau dans l'équilibreur de charge.

Si je suis préoccupé par la préservation des données de session sur le commutateur, je peux configurer mon système de sorte que chaque hôte ait un partenaire vers lequel il réplique les données de session. Je peux supprimer l'un de ces hôtes, le mettre à jour, le restaurer pour qu'il récupère les données de session, puis changer les deux hôtes. Si j'ai plusieurs paires dans le cluster, je peux supprimer la moitié de toutes les paires, puis effectuer un changement de masse, ou je peux les faire une paire à la fois, en fonction des exigences de la version, des exigences de l'entreprise, etc. Personnellement, cependant, je préfère simplement permettre aux utilisateurs finaux de subir la perte très occasionnelle d'une session active plutôt que d'essayer de mettre à niveau avec des sessions intactes.

Tout cela est un compromis entre l'infrastructure informatique, la complexité du processus de publication et l'effort des développeurs. Si votre cluster est assez grand et votre désir suffisamment fort, il est assez facile de concevoir un système qui peut être remplacé sans aucun temps d'arrêt pour la plupart des mises à jour. Les changements de schéma importants forcent souvent des temps d'arrêt réels, car les logiciels mis à jour ne peuvent généralement pas accueillir l'ancien schéma et vous ne pouvez probablement pas vous en sortir avec la copie des données vers une nouvelle instance de base de données, la mise à jour du schéma, puis le basculement des serveurs vers la nouvelle base de données, car vous aurez manqué toutes les données écrites sur l'ancienne après que la nouvelle base de données en ait été clonée. 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 qui sont mises à jour, et vous pouvez mettre des déclencheurs en place sur la base de données en direct qui mettra à jour correctement les nouvelles tables avec des données comme il est écrit sur les anciennes tables par la version précédente (ou peut-être utiliser des vues pour émuler un schéma de l'autre). Affichez 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 disposez des ressources de développement pour les créer.

Le mécanisme le plus utile pour réduire les temps d'arrêt lors des mises à niveau logicielles est peut-être de s'assurer que votre application peut fonctionner en mode lecture seule. Cela fournira 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, mettez en place de nouveaux serveurs d'applications avec une 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 (dont la plupart peuvent le gérer sans aucun temps d'arrêt).

13
ideasculptor

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 une version éclatée de webapp.
  2. Versions éclatées de Rsync.
  3. Une fois rsync réussi, créez un fichier war dans le dossier temporaire de la machine de l'environnement en direct.
  4. Remplacez l'ancienne guerre dans le répertoire de déploiement du serveur par une nouvelle provenant du dossier temporaire.

Le remplacement de l'ancienne guerre par une nouvelle est recommandé dans le conteneur JBoss (basé sur Tomcat) car il s'agit d'une opération atomique et rapide et il est sûr que lorsque le déployeur démarrera, toute l'application sera à l'état déployé.

10
cetnar

Ne pouvez-vous pas faire une copie locale de l'application Web actuelle sur le serveur Web, rsync dans ce répertoire et peut-être même en utilisant des liens symboliques, en une seule fois, pointer Tomcat vers un nouveau déploiement sans trop de temps d'arrêt?

8
jarnbjo

Votre approche de rsync de la guerre extraite est plutôt bonne, ainsi que le redémarrage car je pense qu'un serveur de production ne devrait pas avoir le déploiement à chaud activé. Donc, le seul inconvénient est le temps d'arrêt lorsque vous devez redémarrer le serveur, non?

Je suppose que tout l'état de votre application est conservé dans la base de données, vous n'avez donc aucun problème avec certains utilisateurs travaillant sur une instance de serveur d'application tandis que d'autres utilisateurs sont sur une autre instance de serveur d'application. Si c'est le cas,

Exécutez deux serveurs d'applications: Démarrez le deuxième serveur d'applications (qui écoute les autres TCP ports) et déployez votre application là-bas. Après le déploiement, mettez à jour la configuration d'Apache httpd ( mod_jk ou mod_proxy) pour pointer vers le deuxième serveur d'applications. Redémarrage en douceur du processus Apache httpd. De cette façon, vous n'aurez aucun temps d'arrêt et les nouveaux utilisateurs et demandes sont automatiquement redirigés vers le nouveau serveur d'applications.

Si vous pouvez utiliser la prise en charge de la mise en cluster et de la réplication de session du serveur d'applications, cela sera même fluide pour les utilisateurs actuellement connectés, car le deuxième serveur d'applications se resynchronisera dès son démarrage. Puis, lorsqu'il n'y a pas d'accès au premier serveur, arrêtez-le.

4
mhaller

Cela dépend de l'architecture de votre 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.

4
Matt
4
elhoim

Si les fichiers statiques sont une grande partie de votre gros WAR (100Mo est assez gros), alors les mettre en dehors du WAR et les déployer sur un serveur Web (par exemple Apache) devant votre serveur d'applications peut accélérer les choses. En plus de cela, Apache fait généralement un meilleur travail pour servir les fichiers statiques qu'un moteur de servlet (même si la plupart d'entre eux ont fait des progrès significatifs dans ce domaine).

Donc, au lieu de produire une grosse GUERRE, mettez-la au régime et produisez:

  • un gros Zip avec des fichiers statiques pour Apache
  • une guerre moins lourde pour le moteur de servlet.

Éventuellement, allez plus loin dans le processus de réduction du WAR: si possible, déployez des Grails et d'autres JAR qui ne changent pas fréquemment (ce qui est probablement le cas de la plupart d'entre eux) au niveau du serveur d'applications.

Si vous réussissez à produire un WAR plus léger, je ne me soucierais pas de resynchroniser les répertoires plutôt que les archives.

Points forts de cette approche:

  1. Les fichiers statiques peuvent être "déployés" à chaud sur Apache (par exemple, utilisez un lien symbolique pointant sur le répertoire courant, décompressez les nouveaux fichiers, mettez à jour le lien symbolique et voilà).
  2. Le WAR sera plus mince et il faudra moins de temps pour le déployer.

Faiblesse de cette approche:

  1. Il y a un serveur de plus (le serveur Web) donc cela ajoute (un peu) plus de complexité.
  2. Vous devrez changer les scripts de construction (ce n'est pas un gros problème IMO).
  3. Vous devrez changer la logique rsync.
2
Pascal Thivent

Quant au début du contexte redémarre. Tous les conteneurs ont des options de configuration pour désactiver le redéploiement automatique sur le fichier de classe ou les modifications de ressources statiques. Vous ne pouvez probablement pas désactiver les redéploiements automatiques sur les modifications web.xml, ce fichier est donc le dernier à mettre à jour. Donc, si vous désactivez le redéploiement automatique et mettez à jour le fichier web.xml en dernier, vous verrez le contexte redémarrer après toute la mise à jour.

1
toomasr

Vous utilisez Resin, Resin a intégré la prise en charge de 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 avec les problèmes de permgenspace.

1
danieljimenez

À quoi votre PermSpace est-il réglé? Je m'attendrais à voir cela grandir aussi mais devrait descendre après la collecte des anciennes classes? (ou le ClassLoader reste-t-il toujours là?)

En pensant à outloud, vous pouvez rsync vers un répertoire distinct de version ou de date. Si le conteneur prend en charge les liens symboliques, pourriez-vous SIGSTOP le processus racine, basculer la racine du système de fichiers du contexte via un lien symbolique, puis SIGCONT?

1
Jé Queue

Nous téléchargeons la nouvelle version de l'application Web dans un répertoire séparé, puis nous la déplaçons pour l'échanger avec celle en cours d'exécution, ou utilisons des liens symboliques. Par exemple, nous avons un lien symbolique dans le répertoire des applications Web Tomcat nommé "myapp", qui pointe vers l'application Web 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 des raisons de performances, mais même ainsi, le fait de modifier les fichiers de l'application Web de manière non atomique peut causer des problèmes, car les fichiers statiques ou même les pages JSP peuvent changer de manière à provoquer des liens rompus ou pire.

En pratique, les applications Web sont en fait situées sur un périphérique de stockage partagé, de sorte que les serveurs en cluster, à charge équilibrée et de basculement ont tous le même code disponible.

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 d'abord copier l'ancien dossier de l'application Web dans le nouveau, puis rsync sur celui-ci, si cela fait une différence significative et si c'est vraiment un problème.

1
Kief

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

Comme vous, je ne me souviens jamais avoir effectué un redéploiement ou une mise à jour de guerre complète. La plupart du temps, mes mises à jour sont limitées à quelques fichiers jsp, peut-être une bibliothèque, certains 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 emballé ces mises à jour dans un fichier Zip, avec un script de mise à jour. Je vais exécuter le script de mise à jour. Le script effectue les opérations suivantes:

  • Sauvegardez les fichiers qui seront écrasés, peut-être dans un dossier avec la date et l'heure d'aujourd'hui.
  • Décompresser mes fichiers
  • Arrêtez le serveur d'applications
  • Déplacez les fichiers
  • Démarrez le serveur d'applications

Si les temps d'arrêt sont un problème, et ils le sont généralement, mes projets sont généralement HA, même s'ils ne partagent pas l'état mais utilisent un routeur qui fournit un routage de session permanent.

Une autre chose que je suis curieuse serait, pourquoi le besoin de rsync? Vous devriez être en mesure de savoir quelles sont les modifications requises, en les déterminant dans votre environnement de préparation/développement, sans effectuer de vérifications delta avec live. Dans la plupart des cas, vous devrez régler votre rsync pour ignorer les fichiers de toute façon, comme certains fichiers de propriétés qui définissent les ressources qu'un serveur de production utilise, comme la connexion à la base de données, le serveur smtp, etc.

J'espère que ceci est utile.

1
Kent Lai

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

Maintenant, dans chacun des serveurs proxy, il y a une URL "in" et "out" avec des ports configurés.

Copiez d'abord votre guerre dans le Tomcat sans arrêter le service. Une fois la guerre déployée, elle est automatiquement ouverte par le moteur Tomcat.

Notez la vérification croisée unpackWARs = "true" et autoDeploy = "true" dans le nœud "Host" dans server.xml

Il ressemble à ça

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

Maintenant, regardez les logs de Tomcat. Si aucune erreur n'est là, cela signifie qu'il est en place avec succès.

Maintenant, appuyez sur toutes les API pour les tests

Maintenant, venez sur votre serveur proxy.

Changez simplement le mappage d'URL d'arrière-plan avec le nom de la nouvelle guerre. Étant donné que l'enregistrement auprès des serveurs proxy comme Apache/nignix/haProxy a pris très moins de temps, vous ressentirez un temps d'arrêt minimum.

Consultez - https://developers.google.com/speed/pagespeed/module/domains pour mapper les URL

1
ravi ranjan

Tomcat 7 a une fonctionnalité Nice appelée " déploiement parallèle " qui est conçue pour ce cas d'utilisation.

L'essentiel est que vous développez le .war dans un répertoire, soit directement sous webapps/soit en 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 vers l'ancienne version et les nouvelles sessions vers la nouvelle version.

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

1
craigforster

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

https://Gist.github.com/3985742

0
Kehan

Pas une "meilleure pratique" mais quelque chose auquel je viens de penser.

Que diriez-vous de déployer l'application Web via un DVCS tel que git?

De cette façon, vous pouvez laisser git déterminer les fichiers à transférer vers le serveur. Vous avez également un bon moyen de vous en sortir s'il s'avère que vous êtes cassé, faites simplement un retour!

0
John Nilsson