Bereitstellung ohne Ausfallzeiten

40

Ich versuche, Bereitstellungen ohne Ausfallzeiten zu erreichen, damit ich weniger während der Ausfallzeiten und mehr während der "langsameren" Stunden bereitstellen kann - oder theoretisch zu jeder Zeit.

Mein aktuelles Setup, etwas vereinfacht:

  • Webserver A (.NET App)
  • Webserver B (.NET App)
  • Datenbankserver (SQL Server)

Mein aktueller Bereitstellungsprozess:

  1. Beenden Sie die Sites auf Webserver A und B
  2. Aktualisieren Sie das Datenbankschema für die Version der App, die bereitgestellt wird
  3. Webserver aktualisieren A
  4. Aktualisieren Sie den Webserver B
  5. Alles wieder online bringen

Aktuelles Problem

Dies führt zu einer geringen Ausfallzeit pro Monat - etwa 30 Minuten. Ich mache das außerhalb der Geschäftszeiten, es ist also kein großes Problem - aber es ist etwas, von dem ich wegkommen möchte.

Auch - es gibt keinen Weg, wirklich "zurück" zu gehen. Ich erstelle im Allgemeinen keine Rollback-DB-Skripte, sondern nur Upgrade-Skripte.

Den Load Balancer nutzen

Ich würde gerne einen Webserver auf einmal aktualisieren können. Nehmen Sie Webserver A aus dem Load Balancer heraus, aktualisieren Sie ihn, stellen Sie ihn wieder online und wiederholen Sie den Vorgang für Webserver B.

Das Problem ist die Datenbank. Jede Version meiner Software muss für eine andere Version der Datenbank ausgeführt werden - ich stecke also irgendwie fest.

Mögliche Lösung

Eine aktuelle Lösung, die ich in Betracht ziehe, ist die Annahme der folgenden Regeln:

  • Löschen Sie niemals eine Datenbanktabelle.
  • Löschen Sie niemals eine Datenbankspalte.
  • Niemals eine Datenbankspalte umbenennen.
  • Ordnen Sie niemals eine Spalte neu an.
  • Jede gespeicherte Prozedur muss versioniert werden.
    • Bedeutung - 'spFindAllThings' wird zu 'spFindAllThings_2', wenn es bearbeitet wird.
    • Dann wird es 'spFindAllThings_3', wenn es erneut bearbeitet wird.
    • Dieselbe Regel gilt für Ansichten.

Dies scheint zwar ein bisschen extrem - ich denke, es löst das Problem. Jede Version der Anwendung wird die DB auf eine nicht unterbrechende Weise treffen. Der Code erwartet bestimmte Ergebnisse von den Ansichten / gespeicherten Prozeduren - und dies hält diesen "Vertrag" gültig. Das Problem ist - es sickert nur schlampig. Ich weiß, dass ich alte gespeicherte Prozeduren bereinigen kann, nachdem die App für eine Weile bereitgestellt wurde, aber es fühlt sich nur schmutzig an. Auch - es hängt von allen Entwicklern ab, die diese Regel befolgen, was meistens passieren wird, aber ich stelle mir vor, dass jemand einen Fehler machen wird.

Endlich - meine Frage

  • Ist das schlampig oder abgedreht?
  • Macht es sonst noch jemand so?
  • Wie lösen andere Leute dieses Problem?
MattW
quelle
2
Wo ist dein Backout-Plan? Wie testest du, dass alles funktioniert und es keine Regressionen gibt?
Deer Hunter
3
Sie brauchen "nie" nicht: Sie müssen "nur" sicherstellen, dass jeweils zwei benachbarte Versionen gleichzeitig ausgeführt werden können. Dies schränkt Ihre Upgrade-Pfade ein, ist jedoch nicht so schwerwiegend, wie es niemals möglich ist, das DB-Schema signifikant zu ändern.
Joachim Sauer
Danke Joachim ... Ich spreche gerne absolut, damit die Grundidee klar ist - aber Sie haben Recht, wir könnten eine Politik der Abwärtskompatibilität durch N-Releases verfolgen, bei der wir unnötige DB-Objekte entfernen können.
MattW
2
Sie möchten einen Rollback-Plan haben. Eines Tages wirst du es brauchen.
Thorbjørn Ravn Andersen
1
Nach meiner Erfahrung ist Ihre mögliche Lösung für die meisten Websites schlechter als das Problem, das sie löst. Die Komplexität, die es hinzufügen wird, wird teurer sein, als Sie jetzt vorhersagen können. Vielleicht um ein Vielfaches mehr Zeit / Mühe, um Änderungen vorzunehmen und Funktionen hinzuzufügen. Ich würde es nur für Websites betrachten , die absolut nicht jede Ausfallzeit haben, jemals .
MGOwen

Antworten:

14

Dies ist ein sehr pragmatischer Ansatz für datenbankgestützte Software-Upgrades. Es wurde beschrieben von Martin Fowler und Pramod Sadalage in den Jahren 2003 und anschließend geschrieben in Evolutionary Datenbank - Design: Refactoring Datenbanken .

Ich kann sehen, was Sie meinen, wenn Sie sagen, dass es schlampig erscheint, aber wenn Sie dies absichtlich und mit Bedacht tun und sich die Zeit nehmen, ungenutzte Strukturen aus der Codebasis und der Datenbank zu refaktorisieren, wenn sie nachweislich nicht mehr verwendet werden, ist es viel robuster als Einfachere Lösungen basierend auf Upgrade- und Rollback-Skripten.

Mike Partridge
quelle
5

"Keine Ausfallzeit" ist nur einer von vielen möglichen Gründen für diese Vorgehensweise. Wenn Sie ein Datenmodell auf diese Weise abwärtskompatibel halten, können Sie viele verschiedene Probleme lösen:

  • Wenn Sie über eine Vielzahl von Softwarepaketen verfügen, die auf Ihre Datenbank zugreifen, müssen Sie diese nicht alle überprüfen, wenn sie von einer Schemaänderung betroffen sind (in größeren Organisationen mit mehreren Teams, die Programme schreiben, die alle auf dieselbe Datenbank zugreifen, können Schemaänderungen sehr schwierig werden)

  • Wenn Sie müssen, können Sie eine ältere Version eines Ihrer Programme auschecken und es wird höchstwahrscheinlich auf einer neueren Datenbank ausgeführt (sofern Sie nicht erwarten, dass das alte Programm neuere Spalten korrekt verarbeitet).

  • Der Import / Export von archivierten Daten in die aktuelle Datenbankversion ist wesentlich einfacher

Hier ist eine zusätzliche Regel für Ihre Liste

  • Jede neue Spalte sollte entweder NULL-fähig sein oder einen aussagekräftigen Standardwert enthalten

(Dies stellt sicher, dass auch ältere Programme, die die neuen Spalten nicht kennen, nichts kaputt machen, wenn sie neue Datensätze in Ihrer Datenbank erstellen.)

Natürlich hat dieser Ansatz einen echten Nachteil: Ihre Datenmodellqualität kann sich mit der Zeit verschlechtern. Wenn Sie die vollständige Kontrolle über alle Anwendungen haben, die auf Ihre Datenbank zugreifen, und Sie alle diese Anwendungen einfach umgestalten können, wenn Sie beispielsweise eine Spalte umbenennen, sollten Sie die Dinge möglicherweise auf eine sauberere Weise umgestalten.

Doc Brown
quelle
4

Es ist von Bereitstellung zu Bereitstellung unterschiedlich.

Sicher, Sie könnten niemals eine Tabelle oder eine Spalte löschen. Sie können niemals etwas ändern, das die Schnittstellenkompatibilität beeinträchtigt. Sie können jederzeit eine Abstraktionsebene hinzufügen. Aber dann müssen Sie diese Abstraktion und die Version die Versionierung versionieren.

Die Frage, die Sie sich stellen müssen, lautet: Ändert jede einzelne Version das Schema so, dass es nicht abwärtskompatibel ist?

Wenn nur sehr wenige Releases das Schema auf diese Weise ändern, ist das Datenbankproblem stummgeschaltet. Führen Sie einfach eine rollierende Bereitstellung der Anwendungsserver durch.

Die beiden Dinge, die mir bei der Bereitstellung mit minimalen Ausfallzeiten am meisten geholfen haben, sind:

  1. Streben Sie nach Abwärtskompatibilität - zumindest innerhalb einer einzelnen Version. Sie werden es nicht immer schaffen, aber ich wette, Sie können es bei 90% oder mehr Ihrer Veröffentlichungen erreichen, besonders wenn jede Veröffentlichung klein ist.
  2. Haben Sie ein Pre-Release- und Post-Release-Datenbankskript. Auf diese Weise können Sie Umbenennungen oder Änderungen an der Benutzeroberfläche vornehmen, indem Sie vor der Bereitstellung Ihres Anwendungscodes ein neues Objekt erstellen und das alte Objekt nach der Bereitstellung des Anwendungscodes löschen. Wenn Sie eine neue Spalte hinzufügen, die nicht nullwertfähig ist, können Sie sie in Ihrem Vorabversionsskript mit einem Trigger, der einen Standardwert ausfüllt, als nullwertfähig hinzufügen. Dann können Sie in Ihrer Post-Release den Auslöser fallen lassen.

Hoffentlich kann der Rest Ihrer Bereitstellungen für Wartungsfenster gespeichert werden.

Weitere Ideen zur Bewältigung der wenigen Bereitstellungen, für die Ausfallzeiten erforderlich sind:

  • Können Sie Abwärtskompatibilität in Ihren Code integrieren? Kann Ihr Code beispielsweise mehrere Arten von Ergebnismengen unterstützen? Wenn Sie eine Spalte von einem int in ein double ändern müssen, könnte Ihr Anwendungscode sie als Zeichenfolge lesen und analysieren. Ein bisschen abgedreht, aber wenn es sich um temporären Code handelt, um sich selbst durch den Veröffentlichungsprozess zu bringen, ist es möglicherweise nicht das Ende der Welt.
  • Gespeicherte Prozeduren können dabei helfen, den Anwendungscode vor Schemaänderungen zu isolieren. Das kann nur so weit gehen, hilft aber ein bisschen.
Brandon
quelle
2

Sie könnten es möglicherweise für ein bisschen mehr Aufwand so machen.

  1. Sichern Sie die Datenbank, indem Sie einen Export durchführen
  2. Importieren Sie das Backup, aber benennen Sie es mit einer Release-Version um, z. B. myDb_2_1
  3. Führen Sie die Datenbankfreigabe auf myDB_2_1 aus
  4. Beenden Sie den App-Pool auf Webserver A oder entfernen Sie ihn aus dem Load Balancer
  5. Aktualisieren Sie Webserver A, führen Sie Tests nach der Implementierung aus und führen Sie bei Bedarf ein Rollback durch
  6. In der Sitzung wird Webserver B entlüftet und Webserver A erneut in die Schleife geschaltet
  7. Aktualisieren Sie Webserver B und stellen Sie ihn wieder in den Lastenausgleich

Natürlich benötigen die Web-Updates neue Konfigurationseinträge, um auf das neue Db-Schema zu verweisen. Wenn Sie Releases einmal im Monat durchführen und es ein kleines Team ist, wie viele DB-Änderungen nehmen Sie wirklich vor, die nicht abwärtskompatibel sind? Wenn Sie dies durch Testen kontrollieren können, können Sie eine automatisierte Bereitstellung ohne Ausfallzeit oder im schlimmsten Fall nur 5 Minuten Ausfallzeit anstreben.

LeoLambrettra
quelle
1
Was ist, wenn (wann) die App auf Server A in ihre Datenbank schreibt, nachdem Sie die Sicherung gespeichert, aber bevor Sie Server A gestoppt haben? Es gibt immer ein "Fenster der Verwundbarkeit". Diese Schreibvorgänge gehen verloren, was möglicherweise nicht akzeptabel ist.
sleske