So stellen Sie eine ASP.NET-Anwendung ohne Ausfallzeiten bereit

127

Um eine neue Version unserer Website bereitzustellen, gehen wir wie folgt vor:

  1. Komprimieren Sie den neuen Code und laden Sie ihn auf den Server hoch.
  2. Löschen Sie auf dem Live-Server den gesamten Live-Code aus dem IIS-Website-Verzeichnis.
  3. Extrahieren Sie die neue Code-Zip-Datei in das jetzt leere IIS-Verzeichnis

Dieser Vorgang ist vollständig skriptgesteuert und erfolgt recht schnell. Es kann jedoch zu einer Ausfallzeit von 10 bis 20 Sekunden kommen, wenn die alten Dateien gelöscht und die neuen Dateien bereitgestellt werden.

Irgendwelche Vorschläge zu einer 0-Sekunden-Ausfallzeitmethode?

Karl Glennon
quelle
Sollte dies nicht auf ServerFault sein?
Daniel Rodriguez
49
Vielleicht, aber ServerFault gab es im September 2008 nicht
Karl Glennon
3
Kann IIS auf einen Symlink-Ordner verweisen? Wird das Ändern des Symlinks dazu führen, dass der IIS-Prozess wiederverwendet wird?
Neil McGuigan
Gibt es eine endgültige Lösung mit einem vollständigen Beispiel für ein Quellcode-Skript?
Kiquenet
Ist es nicht möglich, mehrere App-Pools zu haben und den Datenverkehr von einem App-Pool auf einen anderen umzuschalten?
Luke

Antworten:

79

Sie benötigen 2 Server und einen Load Balancer. Hier ist in Schritten:

  1. Schalten Sie den gesamten Datenverkehr auf Server 2 ein
  2. Bereitstellung auf Server 1
  3. Testserver 1
  4. Schalten Sie den gesamten Datenverkehr auf Server 1 ein
  5. Bereitstellung auf Server 2
  6. Testserver 2
  7. Schalten Sie den Datenverkehr auf beiden Servern ein

Selbst in diesem Fall kommt es immer noch zu einem Neustart der Anwendung und zum Verlust von Sitzungen, wenn Sie "Sticky Sessions" verwenden. Wenn Sie Datenbanksitzungen oder einen Statusserver haben, sollte alles in Ordnung sein.

Sklivvz
quelle
4
Sie können den Load Balancer auch so konfigurieren, dass vorhandene Sitzungen für einen bestimmten Server bedient werden, neue jedoch nicht akzeptiert werden. So können Sie vermeiden, dass Sitzungen gelöscht werden. Diese Technik erfordert jedoch das Warten auf das Ende der Sitzungen, und im Allgemeinen möchten Sie dies als Skript ausführen.
35
Diese Methode neigt dazu, herunterzufallen, wenn die Code-Rolle strukturelle Änderungen an der Datenbank aufweist. Sobald Sie die Datenbank für Server 1 aktualisieren, explodiert Server 2. Jetzt können Sie die Datenbank zum Testen auf Server 1 sichern / wiederherstellen, aber dann haben Sie das Problem, die Daten zu sortieren, die sich in der Live-Datenbank geändert haben, während die parallele Kopie ausgeführt wurde.
EBarr
1
@AndreiRinea - Wie würde dies in einem OLTP-System mit hohem Volumen funktionieren? Entweder ist das System nicht mehr synchron und Sie verlieren Daten, wenn Sie umschneiden, oder Sie müssen die Dateneingabe unterbrechen und ein Skript schreiben, um die vorübergehenden Daten zu identifizieren und in die neue DB-Struktur zu migrieren.
EBarr
9
@EBarr: und technisch gesehen haben Sie in der ASP.NET-App immer noch keine Ausfallzeit - die Frage lautet nicht "Wie wird eine SQL Server-Datenbank ohne Ausfallzeit bereitgestellt?".
Sklivvz
6
Der Schlüssel liegt darin, sich so zu entwickeln, dass Ihre SQL-Änderungen nicht destruktiv sind. In der folgenden Version müssen Sie häufig destruktive SQL-Änderungen vornehmen, sobald diese nicht mehr verwendet werden. Es ist nicht schwer mit Übung zu tun.
Bealer
60

Das Microsoft Web Deployment Tool unterstützt dies bis zu einem gewissen Grad:

Aktiviert die Unterstützung von Windows Transactional File System (TxF). Wenn die TxF-Unterstützung aktiviert ist, sind Dateivorgänge atomar. Das heißt, sie sind entweder erfolgreich oder scheitern vollständig. Dies stellt die Datenintegrität sicher und verhindert, dass Daten oder Dateien in einem "halben" oder beschädigten Zustand existieren. In MS Deploy ist TxF standardmäßig deaktiviert.

Es scheint, dass die Transaktion für die gesamte Synchronisierung gilt. Außerdem ist TxF eine Funktion von Windows Server 2008, sodass diese Transaktionsfunktion mit früheren Versionen nicht funktioniert.

Ich glaube, es ist möglich, Ihr Skript für 0-Ausfallzeiten mithilfe von Ordnern als Versionen und der IIS-Metabasis zu ändern:

  • für einen vorhandenen Pfad / eine vorhandene URL:
  • Kopieren Sie eine neue (oder geänderte) Website auf den Server unter
    • \ web \ app \ v2.1 \
  • Ändern Sie die IIS-Metabasis, um den Website-Pfad zu ändern
    • von \ web \ app \ 2.0 \
    • zu \ web \ app \ v2.1 \

Diese Methode bietet folgende Vorteile:

  • Falls bei einer neuen Version ein Problem auftritt, können Sie problemlos auf Version 2.0 zurücksetzen
  • Zum Bereitstellen auf mehreren physischen oder virtuellen Servern können Sie Ihr Skript für die Dateibereitstellung verwenden. Sobald alle Server über die neue Version verfügen, können Sie gleichzeitig die Metabasen aller Server mithilfe des Microsoft Web Deployment Tool ändern.
George Tsiokos
quelle
5
Ich habe diesen Ansatz implementiert, indem ich unsere Powershell-Bereitstellungsskripte angepasst habe. Sie können den Teil des Skripts sehen, der den IIS-Site-Ordner ändert: stackoverflow.com/questions/330608/… Vielen Dank für den Zeiger.
Karl Glennon
17
Leider berücksichtigt diese Methode keine strukturellen Änderungen an der Datenbank. Sobald Sie die Datenbank für Version 2.1 aktualisiert haben, explodiert Version 2.0.
EBarr
8
Die Verwendung von TxF ist hier übertrieben, IMO. Es schadet nichts, sowohl v2.0 als auch v2.1 gleichzeitig im Dateisystem zu haben. Die große Änderung tritt ein, wenn v2.1 online geht und zu diesem Zeitpunkt die TxF-Transaktion festgeschrieben wurde. Die Ausfallzeit von Null tritt tatsächlich aufgrund der Art und Weise auf, wie IIS von einem alten AppPool zu einem neuen wechselt, und nicht aufgrund von TxF.
RickNZ
5
Ein weiteres Problem besteht darin, dass eine große Menge von Benutzerdaten in Unterordnern der App-Ordner gespeichert ist.
Kenny Evitt
4
Dies ist keine 0-Sekunden-Bereitstellung, da die neue App gestartet werden muss.
usr
12

Sie können eine Bereitstellung ohne Ausfallzeiten auf einem einzelnen Server erreichen, indem Sie das Anwendungsanforderungsrouting in IIS als Softwarelastausgleich zwischen zwei lokalen IIS-Standorten an verschiedenen Ports verwenden. Dies wird als blaugrüne Bereitstellungsstrategie bezeichnet, bei der jeweils nur einer der beiden Standorte im Load Balancer verfügbar ist. Stellen Sie die Bereitstellung auf der Site bereit, die "down" ist, wärmen Sie sie auf und bringen Sie sie in den Load Balancer (normalerweise durch Bestehen einer Integritätsprüfung für Application Request Routing). Nehmen Sie dann die ursprüngliche Site, die aktiv war, aus dem "Pool" (erneut) indem der Gesundheitscheck fehlschlägt).

Ein vollständiges Tutorial finden Sie hier.

Kavun
quelle
7

Ich habe dies kürzlich durchlaufen und die Lösung, die ich gefunden habe, bestand darin, zwei Standorte in IIS einzurichten und zwischen ihnen zu wechseln.

Für meine Konfiguration hatte ich ein Webverzeichnis für jede A- und B-Site wie folgt: c: \ Intranet \ Live A \ Schnittstelle c: \ Intranet \ Live B \ Schnittstelle

In IIS habe ich zwei identische Sites (dieselben Ports, Authentifizierung usw.) mit jeweils einem eigenen Anwendungspool. Eine der Sites läuft (A) und die andere wird gestoppt (B). Der Live-Host hat auch den Live-Host-Header.

Wenn es um die Bereitstellung zum Leben geht, veröffentliche ich einfach am Standort der STOPPED-Site. Da ich über den Port auf die B-Site zugreifen kann, kann ich die Site vorwärmen, damit der erste Benutzer keinen Anwendungsstart verursacht. Dann kopiere ich mit einer Batch-Datei den Live-Host-Header nach B, stoppe A und starte B.

Rob King
quelle
1
Dies hilft bei Ausfallzeiten aufgrund von Dateikopien, hat jedoch das gleiche Problem wie @Sklivvz - sobald die Code-Rolle strukturelle Änderungen an der Datenbank aufweist, boomt die Site.
EBarr
Dies schien mir auch der intuitive Weg zu sein, aber warum gibt es keinen einfachen, eingebauten Weg, dies zu tun?
Petrus Theron
3
@Ebarr dann rollen Sie keine destruktiven SQL-Änderungen aus. Wenn Sie beispielsweise eine Spalte entfernen müssen, tun Sie dies in der nächsten Version, wenn sie von A oder B nicht mehr verwendet wird.
Bealer
@Bealer - vereinbart (mit Vorbehalt). Es gibt eine ganze Reihe dieser Fragen zu "Ausfallzeiten während Code-Rollen". Ich habe noch keine gefunden, die wirklich die Realitäten der Entwicklung eines DB-Schemas beschreibt. Vorsichtsmaßnahme - Es gibt eine Vielzahl von Komplikationen, die mit zweiphasigen Änderungen des Schemas einhergehen. Ein Beispiel - viele der ORMs werden angezeigt, wenn sich die Tabellendefinition von der Definition nach ihrem Verständnis unterscheidet (neue oder fehlende Spalten).
EBarr
2
@Rob wie können Sie die Site "vorwärmen", wenn sie gestoppt wird?
Andrew Gee
7

Mit der ServerManager-Klasse von Microsoft.Web.Administration können Sie Ihren eigenen Bereitstellungsagenten entwickeln.

Der Trick besteht darin, den PhysicalPath des VirtualDirectory zu ändern, was zu einem atomaren Online-Wechsel zwischen alten und neuen Web-Apps führt.

Beachten Sie, dass dies dazu führen kann, dass alte und neue AppDomains parallel ausgeführt werden!

Das Problem ist, wie Änderungen an Datenbanken usw. synchronisiert werden.

Durch Abfragen der Existenz von AppDomains mit alten oder neuen PhysicalPaths kann festgestellt werden, wann die alten AppDomains beendet wurden und ob die neuen AppDomains gestartet wurden.

Um das Starten einer AppDomain zu erzwingen, müssen Sie eine HTTP-Anforderung stellen (IIS 7.5 unterstützt die Autostart-Funktion).

Jetzt benötigen Sie eine Möglichkeit, Anforderungen für die neue AppDomain zu blockieren. Ich verwende einen benannten Mutex, der vom Bereitstellungsagenten erstellt wird und dessen Eigentümer ist, auf den Application_Start der neuen Webanwendung wartet und der dann vom Bereitstellungsagenten freigegeben wird, sobald die Datenbankaktualisierungen vorgenommen wurden.

(Ich verwende eine Markierungsdatei in der Web-App, um das Mutex-Warteverhalten zu aktivieren.) Sobald die neue Web-App ausgeführt wird, lösche ich die Markierungsdatei.

Jack
quelle
6

OK, da alle die Antwort ablehnen, die ich 2008 geschrieben habe * ...

Ich werde Ihnen sagen, wie wir es jetzt im Jahr 2014 machen. Wir verwenden keine Websites mehr, da wir jetzt ASP.NET MVC verwenden.

Wir brauchen dafür sicherlich keinen Load Balancer und zwei Server. Das ist in Ordnung, wenn Sie 3 Server für jede Website haben, die Sie pflegen, aber es ist für die meisten Websites ein totaler Overkill.

Außerdem verlassen wir uns nicht auf den neuesten Assistenten von Microsoft - zu langsam, zu viel versteckte Magie und zu anfällig für Namensänderungen.

So machen wir das:

  1. Wir haben einen Post-Build-Schritt, der generierte DLLs in einen 'bin-pub'-Ordner kopiert.

  2. Wir verwenden Beyond Compare (was ausgezeichnet ist **), um geänderte Dateien (über FTP, da dies weitgehend unterstützt wird) bis zum Produktionsserver zu überprüfen und zu synchronisieren

  3. Wir haben eine sichere URL auf der Website, die eine Schaltfläche enthält, die alles in 'bin-pub' nach 'bin' kopiert (zuerst ein Backup erstellen, um ein schnelles Rollback zu ermöglichen). Zu diesem Zeitpunkt startet sich die App neu. Dann prüft unser ORM, ob Tabellen oder Spalten hinzugefügt werden müssen, und erstellt sie.

Das sind nur Millisekunden Ausfallzeit. Der Neustart der App kann ein oder zwei Sekunden dauern, aber während des Neustarts werden Anforderungen gepuffert, sodass praktisch keine Ausfallzeiten auftreten.

Der gesamte Bereitstellungsprozess dauert zwischen 5 Sekunden und 30 Minuten, je nachdem, wie viele Dateien geändert und wie viele Änderungen überprüft werden müssen.

Auf diese Weise müssen Sie nicht eine gesamte Website in ein anderes Verzeichnis kopieren, sondern nur den Ordner bin. Sie haben auch die vollständige Kontrolle über den Prozess und wissen genau, was sich ändert.

** Wir werfen immer einen kurzen Blick auf die Änderungen, die wir bereitstellen - als Last-Minute-Überprüfung, damit wir wissen, was zu testen ist, und wenn etwas kaputt geht, sind wir bereit. Wir verwenden Beyond Compare, weil Sie damit problemlos Dateien über FTP unterscheiden können. Ich würde das niemals ohne BC tun, Sie haben keine Ahnung, was Sie überschreiben.

* Scrollen Sie nach unten, um es zu sehen :( Übrigens würde ich Websites nicht mehr empfehlen, da sie langsamer zu erstellen sind und mit halb kompilierten temporären Dateien schwer abstürzen können. Wir haben sie in der Vergangenheit verwendet, weil sie Datei für Datei agiler waren Bereitstellung. Sehr schnell, um ein kleines Problem zu beheben, und Sie können genau sehen, was Sie bereitstellen (wenn Sie natürlich Beyond Compare verwenden - ansonsten vergessen Sie es).

mike nelson
quelle
Es kommt jedoch immer noch zu Ausfallzeiten, da der App-Pool recycelt wird.
Testmuster
Nein, keine Ausfallzeit, da Anforderungen während des Neustarts der App automatisch von IIS gepuffert werden
Mike Nelson
5

Die einzige Methode ohne Ausfallzeiten, die ich mir vorstellen kann, ist das Hosting auf mindestens 2 Servern.

Sam Meldrum
quelle
1

Ich würde Georges Antwort für einen einzelnen Server ein wenig wie folgt verfeinern:

  1. Verwenden Sie ein Web Deployment-Projekt, um die Site in eine einzelne DLL vorkompilieren zu können
  2. Komprimieren Sie die neue Site und laden Sie sie auf den Server hoch
  3. Entpacken Sie es in einen neuen Ordner in einem Ordner mit den richtigen Berechtigungen für die Site, damit die entpackten Dateien die Berechtigungen korrekt erben (möglicherweise e: \ web, mit Unterordnern v20090901, v20090916 usw.).
  4. Verwenden Sie den IIS-Manager, um den Namen des Ordners zu ändern, der die Site enthält
  5. Bewahren Sie den alten Ordner noch eine Weile auf, damit Sie bei Problemen darauf zurückgreifen können

In Schritt 4 wird der IIS-Arbeitsprozess recycelt.

Dies ist nur dann keine Ausfallzeit, wenn Sie keine InProc-Sitzungen verwenden. Verwenden Sie stattdessen den SQL-Modus, wenn Sie können (noch besser, vermeiden Sie den Sitzungsstatus vollständig).

Natürlich ist es etwas komplizierter, wenn es mehrere Server und / oder Datenbankänderungen gibt ....

RickNZ
quelle
1
Gleiches Problem wie @Sklivvz - Diese Methode fällt aus, sobald die Code-Rolle strukturelle Änderungen an der Datenbank aufweist.
EBarr
3
Aus diesem Grund habe ich gesagt, dass es bei DB-Änderungen mehr Aufwand gibt ... Die Einführung von Code mit strukturellen Änderungen an der DB ist nicht nur ein Bereitstellungsproblem. Es muss auch Unterstützung im Code geben, und wahrscheinlich auch in der Datenbank.
RickNZ
1

Um die Antwort von sklivvz zu erweitern, die auf einer Art Load Balancer (oder nur einer Standby-Kopie auf demselben Server) beruhte.

  1. Leiten Sie den gesamten Datenverkehr zu Site / Server 2
  2. Warten Sie optional etwas, um sicherzustellen, dass möglichst wenige Benutzer ausstehende Workflows für die bereitgestellte Version haben
  3. Stellen Sie es auf Site / Server 1 bereit und wärmen Sie es so weit wie möglich auf
  4. Führen Sie Datenbankmigrationen transaktional aus (bemühen Sie sich, dies zu ermöglichen)
  5. Leiten Sie den gesamten Datenverkehr sofort zu Site / Server 1
  6. Bereitstellung auf Site / Server 2
  7. Direkter Datenverkehr zu beiden Standorten / Servern

Es ist möglich, ein bisschen Rauch zu testen, indem Sie einen Datenbank-Snapshot / eine Datenbank-Kopie erstellen. Dies ist jedoch nicht immer möglich.

Verwenden Sie nach Möglichkeit und Bedarf "Routing-Unterschiede", z. B. unterschiedliche Mieter-URLs (customerX.myapp.net) oder unterschiedliche Benutzer, um sie zuerst für eine unwissende Gruppe von Meerschweinchen bereitzustellen. Wenn nichts fehlschlägt, geben Sie es an alle weiter.

Da es sich um Datenbankmigrationen handelt, ist ein Zurücksetzen auf eine frühere Version häufig nicht möglich.

Es gibt Möglichkeiten, Anwendungen in diesen Szenarien besser zu spielen, z. B. die Verwendung von Ereigniswarteschlangen und Wiedergabemechanismen. Da es sich jedoch um die Bereitstellung von Änderungen an etwas handelt, das gerade verwendet wird, gibt es wirklich keinen narrensicheren Weg.

Gliljas
quelle
1

So mache ich es:

Absolute Mindestsystemanforderungen:
1 Server mit

  • 1 Load Balancer / Reverse Proxy (z. B. Nginx) wird auf Port 80 ausgeführt
  • 2 ASP.NET-Core / Mono-Reverse-Proxy- / Fastcgi-Chroot-Jails oder Docker-Container, die 2 verschiedene TCP-Ports abhören
    (oder sogar nur zwei Reverse-Proxy-Anwendungen auf 2 verschiedenen TCP-Ports ohne Sandbox)

Arbeitsablauf:

Starten Sie die Transaktion 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 
Stefan Steiger
quelle
-7

Ich würde vorschlagen, die alten Dateien dort zu belassen und sie einfach zu überschreiben. Auf diese Weise ist die Ausfallzeit auf Überschreibzeiten für einzelne Dateien beschränkt, und es fehlt immer nur eine Datei.

Ich bin mir nicht sicher, ob dies in einer "Webanwendung" hilft (ich glaube, Sie sagen, dass Sie das verwenden), weshalb wir immer "Websites" verwenden. Auch bei der Bereitstellung von "Websites" wird Ihre Website nicht neu gestartet und alle Benutzersitzungen werden gelöscht.

mike nelson
quelle
Hallo Mike, vielleicht möchtest du diese Antwort entfernen.
Sohail Ahmed