Comment déployer une application ASP.NET sans temps d'arrêt


127

Pour déployer une nouvelle version de notre site Web, nous procédons comme suit:

  1. Compressez le nouveau code et téléchargez-le sur le serveur.
  2. Sur le serveur actif, supprimez tout le code actif du répertoire du site Web IIS.
  3. Extrayez le nouveau fichier zip de code dans le répertoire IIS maintenant vide

Ce processus est entièrement scripté et se produit assez rapidement, mais il peut encore y avoir un temps d'arrêt de 10 à 20 secondes lorsque les anciens fichiers sont supprimés et les nouveaux fichiers déployés.

Des suggestions sur une méthode de temps d'arrêt de 0 seconde?


Cela ne devrait-il pas être sur ServerFault?
Daniel Rodriguez

49
Peut-être, mais ServerFault n'existait pas en septembre 2008
Karl Glennon

3
IIS peut-il pointer vers un dossier de lien symbolique? La modification du lien symbolique entraînera-t-elle le recyclage du processus IIS?
Neil McGuigan

une solution finale avec un exemple de script de code source complet?
Kiquenet

N'est-il pas possible d'avoir plusieurs pools d'applications et de transférer le trafic d'un pool d'applications à un autre?
Luke le

Réponses:


79

Vous avez besoin de 2 serveurs et d'un équilibreur de charge. Voici par étapes:

  1. Activez tout le trafic sur le serveur 2
  2. Déployer sur le serveur 1
  3. Test du serveur 1
  4. Activez tout le trafic sur le serveur 1
  5. Déployer sur le serveur 2
  6. Test du serveur 2
  7. Activez le trafic sur les deux serveurs

Le fait est que même dans ce cas, vous aurez toujours des redémarrages d'applications et des pertes de sessions si vous utilisez des «sessions persistantes». Si vous avez des sessions de base de données ou un serveur d'état, tout devrait bien se passer.


4
Vous pouvez également configurer l'équilibreur de charge pour qu'il traite les sessions existantes pour un serveur donné, mais n'accepte pas de nouvelles. Cela vous permet d'éviter d'abandonner des sessions. Cette technique nécessite cependant d'attendre la fin des sessions, et en général, vous voudrez créer un script.

35
Cette méthode a tendance à tomber lorsque le rouleau de code a des modifications structurelles de la base de données. Une fois que vous avez mis à niveau la base de données pour le serveur 1, le serveur 2 explose. Vous pouvez maintenant sauvegarder / restaurer la base de données pour le test sur le serveur 1, mais vous avez alors le problème de trier les données qui ont changé dans la base de données active pendant que la copie parallèle était en cours d'exécution.
EBarr

1
@AndreiRinea - comment pensez-vous que cela fonctionnerait dans un système OLTP à volume élevé? Soit le système se désynchronise et vous perdez des données lorsque vous coupez, soit vous devez interrompre la saisie des données et écrire un script pour identifier et migrer les données transitoires vers la nouvelle structure de base de données.
EBarr

9
@EBarr: et dans tous les cas techniquement, vous n'avez toujours aucun temps d'arrêt sur l'application ASP.NET - la question n'est pas "comment déployer sur une base de données de serveur SQL sans temps d'arrêt".
Sklivvz

6
Leur clé est de se développer de manière à ce que vos modifications SQL ne soient pas destructrices. Vous devez souvent effectuer des modifications SQL destructives dans la version suivante une fois qu'elle n'est plus utilisée. Ce n'est pas difficile à faire avec la pratique.
Bealer

60

L' outil de déploiement Web Microsoft prend en charge cela dans une certaine mesure:

Active la prise en charge de Windows Transactional File System (TxF). Lorsque la prise en charge TxF est activée, les opérations sur les fichiers sont atomiques; c'est-à-dire qu'ils réussissent ou échouent complètement. Cela garantit l'intégrité des données et empêche les données ou les fichiers d'exister dans un état «à mi-chemin» ou corrompu. Dans MS Deploy, TxF est désactivé par défaut.

Il semble que la transaction concerne toute la synchronisation. De plus, TxF est une fonctionnalité de Windows Server 2008, donc cette fonctionnalité de transaction ne fonctionnera pas avec les versions antérieures.

Je crois qu'il est possible de modifier votre script pour un temps d'arrêt 0 en utilisant des dossiers comme versions et la métabase IIS:

  • pour un chemin / URL existant:
  • Copiez le nouveau site Web (ou modifié) sur le serveur sous
    • \ web \ app \ v2.1 \
  • Modifier la métabase IIS pour changer le chemin du site Web
    • depuis \ web \ app \ 2.0 \
    • vers \ web \ app \ v2.1 \

Cette méthode offre les avantages suivants:

  • Dans le cas où la nouvelle version a un problème, vous pouvez facilement revenir à la v2.0
  • Pour déployer sur plusieurs serveurs physiques ou virtuels, vous pouvez utiliser votre script pour le déploiement de fichiers. Une fois que tous les serveurs ont la nouvelle version, vous pouvez modifier simultanément les métabases de tous les serveurs à l'aide de l'outil de déploiement Web Microsoft.

5
J'ai implémenté cette approche en adaptant nos scripts de déploiement PowerShell. Vous pouvez voir la partie du script qui modifie le dossier du site IIS ici: stackoverflow.com/questions/330608/ ... Merci pour le pointeur.
Karl Glennon

17
Malheureusement, cette méthode ne tient pas compte des modifications structurelles de la base de données. Une fois que vous mettez à niveau la base de données pour la version 2.1, la version 2.0 explose.
EBarr

8
Utiliser TxF est excessif ici, IMO. Cela ne fait pas de mal d'avoir à la fois v2.0 et v2.1 dans le système de fichiers en même temps. Le grand changement se produit lorsque la v2.1 est en ligne, et à ce moment-là, la transaction TxF a été validée. Le temps d'arrêt nul se produit vraiment en raison de la façon dont IIS passe d'un ancien AppPool à un nouveau, et non à cause de TxF.
RickNZ

5
Un autre problème avec cela est si une grande quantité de données utilisateur est stockée dans les sous-dossiers des dossiers d'application.
Kenny Evitt le

4
Il ne s'agit pas d'un déploiement de 0 seconde car la nouvelle application doit démarrer.
usr

12

Vous pouvez obtenir un déploiement sans temps d'arrêt sur un seul serveur en utilisant le routage des demandes d'application dans IIS en tant qu'équilibreur de charge logiciel entre deux sites IIS locaux sur des ports différents. C'est ce qu'on appelle une stratégie de déploiement bleu-vert où un seul des deux sites est disponible dans l'équilibreur de charge à un moment donné. Déployez sur le site qui est "arrêté", préchauffez-le et insérez-le dans l'équilibreur de charge (généralement en passant une vérification de l'état du routage des demandes d'application), puis retirez le site d'origine qui était en place du "pool" (à nouveau en faisant échouer son bilan de santé).

Un tutoriel complet peut être trouvé ici.


7

Je suis passé par là récemment et la solution que j'ai trouvée était d'avoir deux sites mis en place dans IIS et de basculer entre eux.

Pour ma configuration, j'avais un répertoire web pour chaque site A et B comme ceci: c: \ Intranet \ Live A \ Interface c: \ Intranet \ Live B \ Interface

Dans IIS, j'ai deux sites identiques (mêmes ports, authentification, etc.) chacun avec son propre pool d'applications. L'un des sites est en cours d'exécution (A) et l'autre est arrêté (B). celui en direct a également l'en-tête d'hôte en direct.

Quand il s'agit de déployer pour vivre, je publie simplement à l'emplacement du site STOPPED. Étant donné que je peux accéder au site B en utilisant son port, je peux préchauffer le site afin que le premier utilisateur ne provoque pas le démarrage d'une application. Ensuite, en utilisant un fichier batch, je copie l'en-tête de l'hôte en direct sur B, j'arrête A et je démarre B.


1
Cela aide avec les temps d'arrêt dus à la copie de fichiers, mais présente le même problème que @Sklivvz - dès que le rouleau de code a des changements structurels dans la base de données, le site est en plein essor.
EBarr

Cela me paraissait également intuitif, mais pourquoi n'y a-t-il pas un moyen simple et intégré de le faire?
Petrus Theron

3
@Ebarr alors ne déployez pas de changements SQL destructeurs. Par exemple, si vous devez supprimer une colonne, faites-le dans la prochaine version lorsqu'elle ne sera plus utilisée par A ou B.
Bealer

@Bealer - d'accord (avec mise en garde). Il y a toute une série de ces questions sur les "temps d'arrêt pendant les rôles de code". Je n'en ai pas encore trouvé un qui traite vraiment des réalités de l'évolution d'un schéma DB. Mise en garde - il existe une variété de complications qui accompagnent des modifications en deux phases du schéma. Un exemple - de nombreux ORM barf si la définition de la table diffère de la définition telle qu'elle la comprend (colonnes nouvelles ou manquantes).
EBarr

2
@Rob comment pouvez-vous "préchauffer" le site s'il est arrêté?
Andrew Gee

7

En utilisant la classe ServerManager de Microsoft.Web.Administration, vous pouvez développer votre propre agent de déploiement.

L'astuce consiste à modifier le PhysicalPath de VirtualDirectory, ce qui entraîne un basculement atomique en ligne entre les anciennes et les nouvelles applications Web.

Sachez que cela peut entraîner l'exécution en parallèle d'anciens et de nouveaux AppDomains!

Le problème est de savoir comment synchroniser les modifications apportées aux bases de données, etc.

En interrogeant l'existence d'AppDomains avec des PhysicalPaths anciens ou nouveaux, il est possible de détecter quand les anciens AppDomain (s) se sont terminés et si les nouveaux AppDomain (s) ont démarré.

Pour forcer le démarrage d'un AppDomain, vous devez effectuer une requête HTTP (IIS 7.5 prend en charge la fonctionnalité de démarrage automatique)

Vous avez maintenant besoin d'un moyen de bloquer les demandes pour le nouvel AppDomain. J'utilise un mutex nommé - qui est créé et détenu par l'agent de déploiement, attendu par Application_Start de la nouvelle application Web, puis publié par l'agent de déploiement une fois que les mises à jour de la base de données ont été effectuées.

(J'utilise un fichier de marqueurs dans l'application Web pour activer le comportement d'attente mutex) Une fois que la nouvelle application Web est en cours d'exécution, je supprime le fichier de marqueurs.


6

OK, puisque tout le monde vote contre la réponse que j'ai écrite en 2008 * ...

Je vais vous dire comment nous le faisons maintenant en 2014. Nous n'utilisons plus de sites Web car nous utilisons maintenant ASP.NET MVC.

Nous n'avons certainement pas besoin d'un équilibreur de charge et de deux serveurs pour le faire, c'est bien si vous avez 3 serveurs pour chaque site Web que vous gérez, mais c'est exagéré pour la plupart des sites Web.

De plus, nous ne nous appuyons pas sur le dernier assistant de Microsoft - trop lent, trop de magie cachée et trop enclin à changer de nom.

Voici comment nous procédons:

  1. Nous avons une étape de post-construction qui copie les DLL générées dans un dossier «bin-pub».

  2. Nous utilisons Beyond Compare (ce qui est excellent **) pour vérifier et synchroniser les fichiers modifiés (via FTP car cela est largement pris en charge) jusqu'au serveur de production

  3. Nous avons une URL sécurisée sur le site Web contenant un bouton qui copie tout ce qui se trouve dans «bin-pub» vers «bin» (en prenant d'abord une sauvegarde pour permettre une restauration rapide). À ce stade, l'application redémarre d'elle-même. Ensuite, notre ORM vérifie s'il y a des tables ou des colonnes qui doivent être ajoutées et les crée.

Ce ne sont que des millisecondes d'arrêt. Le redémarrage de l'application peut prendre une seconde ou deux, mais pendant le redémarrage, les demandes sont mises en mémoire tampon afin qu'il n'y ait effectivement aucun temps d'arrêt.

L'ensemble du processus de déploiement prend entre 5 secondes et 30 minutes, selon le nombre de fichiers modifiés et le nombre de modifications à examiner.

De cette façon, vous n'avez pas à copier un site Web entier dans un répertoire différent, mais uniquement dans le dossier bin. Vous avez également un contrôle total sur le processus et savez exactement ce qui change.

** Nous faisons toujours un rapide tour d'horizon des changements que nous déployons - comme une double vérification de dernière minute, afin que nous sachions quoi tester et si quelque chose se brise, nous sommes prêts. Nous utilisons Beyond Compare car il vous permet de comparer facilement des fichiers via FTP. Je ne ferais jamais cela sans BC, vous n'avez aucune idée de ce que vous écrasez.

* Faites défiler vers le bas pour le voir :( BTW, je ne recommanderais plus les sites Web car ils sont plus lents à construire et peuvent se bloquer avec des fichiers temporaires à moitié compilés. Nous les avons utilisés dans le passé car ils permettaient un fichier par fichier plus agile Déploiement très rapide pour résoudre un problème mineur et vous pouvez voir exactement ce que vous déployez (si vous utilisez Beyond Compare bien sûr - sinon oubliez-le).


Mais, vous aurez toujours des temps d'arrêt car le pool d'applications se recycle.
testpattern

Non, pas de temps d'arrêt car les demandes sont automatiquement mises en tampon par IIS lors du redémarrage de l'application
Mike Nelson

5

Les seules méthodes sans temps d'arrêt auxquelles je pense impliquent l'hébergement sur au moins 2 serveurs.


1

J'affinerais un peu la réponse de George, comme suit, pour un seul serveur:

  1. Utilisez un projet de déploiement Web pour précompiler le site en une seule DLL
  2. Compressez le nouveau site et téléchargez-le sur le serveur
  3. Décompressez-le dans un nouveau dossier situé dans un dossier avec les bonnes autorisations pour le site, afin que les fichiers décompressés héritent correctement des autorisations (peut-être e: \ web, avec les sous-dossiers v20090901, v20090916, etc.)
  4. Utiliser IIS Manager pour modifier le nom du dossier contenant le site
  5. Conservez l'ancien dossier pendant un certain temps pour pouvoir y revenir en cas de problème

L'étape 4 entraînera le recyclage du processus de travail IIS.

Il ne s'agit que d'un temps d'arrêt nul si vous n'utilisez pas de sessions InProc; utilisez plutôt le mode SQL si vous le pouvez (mieux encore, évitez complètement l'état de session).

Bien sûr, c'est un peu plus compliqué lorsqu'il y a plusieurs serveurs et / ou changements de base de données ...


1
Même problème que @Sklivvz - Cette méthode tombe en panne dès que le rouleau de code a des changements structurels dans la base de données.
EBarr

3
C'est pourquoi j'ai dit que c'était plus impliqué quand il y avait des changements de DB ... Le déploiement de code avec des changements structurels dans la DB n'est pas seulement un problème de déploiement; il doit également y avoir un support dans le code, et probablement aussi dans la base de données.
RickNZ

1

Pour développer la réponse de sklivvz, qui reposait sur une sorte d'équilibreur de charge (ou simplement une copie de secours sur le même serveur)

  1. Diriger tout le trafic vers le site / serveur 2
  2. Si vous le souhaitez, attendez un peu pour vous assurer que le moins d'utilisateurs possible ont des workflows en attente sur la version déployée
  3. Déployez sur le site / serveur 1 et préchauffez-le autant que possible
  4. Exécuter les migrations de bases de données de manière transactionnelle (efforcez-vous de rendre cela possible)
  5. Diriger immédiatement tout le trafic vers le site / serveur 1
  6. Déployer sur le site / serveur 2
  7. Trafic direct vers les deux sites / serveurs

Il est possible d'introduire un peu de test de fumée, en créant un instantané / copie de base de données, mais ce n'est pas toujours faisable.

Si possible et nécessaire, utilisez des «différences de routage», telles que des URL de locataires différentes: s (customerX.myapp.net) ou différents utilisateurs, pour déployer d'abord sur un groupe inconnu de cobayes. Si rien n'échoue, relâchez à tout le monde.

Étant donné que les migrations de bases de données sont impliquées, il est souvent impossible de revenir à une version précédente.

Il existe des moyens de rendre les applications plus agréables dans ces scénarios, comme l'utilisation de files d'attente d'événements et de mécanismes de lecture, mais puisque nous parlons de déployer des modifications sur quelque chose qui est en cours d'utilisation, il n'y a vraiment pas de méthode infaillible.


1

Voici comment je fais:

Configuration système minimale absolue:
1 serveur avec

  • 1 équilibreur de charge / proxy inverse (par exemple nginx) s'exécutant sur le port 80
  • 2 chroot-jails ou docker-containers ASP.NET-Core / mono reverse-proxy / fastcgi écoutant sur 2 ports TCP différents
    (ou même juste deux applications de reverse-proxy sur 2 ports TCP différents sans aucune sandbox)

Flux de travail:

démarrer la transaction myupdate

try
    Web-Service: Tell all applications on all web-servers to go into primary read-only mode 
    Application switch to primary read-only mode, and responds 
    Web sockets begin notifying all clients 
    Wait for all applications to respond

    wait (custom short interval)

    Web-Service: Tell all applications on all web-servers to go into secondary read-only mode 
    Application switch to secondary read-only mode (data-entry fuse)
    Updatedb - secondary read-only mode (switches database to read-only)

    Web-Service: Create backup of database 
    Web-Service: Restore backup to new database
    Web-Service: Update new database with new schema 

    Deploy new application to apt-repository 
    (for windows, you will have to write your own custom deployment web-service)
    ssh into every machine in array_of_new_webapps
    run apt-get update
    then either 
    apt-get dist-upgrade
    OR
    apt-get install <packagename>
    OR 
    apt-get install --only-upgrade <packagename>
    depending on what you need
    -- This deploys the new application to all new chroots (or servers/VMs)

    Test: Test new application under test.domain.xxx
    -- everything that fails should throw an exception here
    commit myupdate;

    Web-Service: Tell all applications to send web-socket request to reload the pages to all clients at time x (+/- random number)
    @client: notify of reload and that this causes loss of unsafed data, with option to abort 

    @ time x:  Switch load balancer from array_of_old_webapps to array_of_new_webapps 
    Decomission/Recycle array_of_old_webapps, etc.

catch
        rollback myupdate 
        switch to read-write mode
        Web-Service: Tell all applications to send web-socket request to unblock read-only mode
end try 

-7

Je suggérerais de conserver les anciens fichiers là-bas et de simplement les écraser. De cette façon, le temps d'arrêt est limité aux heures d'écrasement d'un seul fichier et il n'y a qu'un seul fichier manquant à la fois.

Je ne suis pas sûr que cela aide dans une "application Web" (je pense que vous dites que c'est ce que vous utilisez), c'est pourquoi nous utilisons toujours des "sites Web". De plus, avec les "sites Web", le déploiement ne redémarre pas votre site et ne supprime pas toutes les sessions utilisateur.


Bonjour Mike, Vous voudrez peut-être supprimer cette réponse.
Sohail Ahmed
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.