Ich habe jetzt ein mittelgroßes Projekt, das kurz vor dem Ende der Phase "Schlampige koffeinhaltige Prototypen für Kundendemos" steht und in die Phase "Über die Zukunft nachdenken" übergeht. Das Projekt besteht aus Linux-basierten Geräten mit Software und Firmware sowie einem zentralen administrativen Webserver. Derzeit gibt es 10 Prototypen. Die Produktion wird voraussichtlich in der Größenordnung von 1000 liegen.
Da ich mich nicht mit der Kunst der automatischen Aktualisierung auskannte und wenig Zeit hatte, hatte ich schnell meine eigene Strategie für die Bereitstellung / automatische Aktualisierung von Software entwickelt, und ehrlich gesagt ist das scheiße. Es besteht derzeit aus folgenden:
- Ein gehostetes Git-Repo (GitLab) mit einem Zweig für Produktionsversionen (beachten Sie, dass sich die Webserverquelle ebenso wie einige andere Dinge in demselben Repo befindet).
- Eine Schaltfläche "Update bereitstellen" auf der Weboberfläche, die:
- Ruft die neueste Version aus dem Produktionsfreigabezweig in einen lokalen Repo-Bereich und kopiert sie auch in einen temporären Staging-Bereich für die Paketvorbereitung.
- Führt im Staging-Bereich ein Bereinigungsskript (im Repo gespeichert) aus, um nicht verwandte Quelldateien (z. B. Serverquelle, Firmwarequelle usw.) und Git-Dateien zu entfernen.
- Schreibt den aktuellen Git-Hash in eine Datei im Update-Paket (Zweck wird unten klar).
- Wenn alles gut gegangen ist, wird es komprimiert und bereit zum Servieren, indem das vorherige komprimierte Paket mit einer gleichnamigen Datei überschrieben wird. Anschließend wird der Staging-Bereich gelöscht.
- Beachten Sie, dass sich jetzt zwei Kopien der aktuellen Gerätesoftware auf dem Server befinden, von denen erwartet wird, dass sie synchron sind: Ein vollständiges lokales Git-Repo in der neuesten Produktionsbranche und ein sofort einsatzbereites GZIP-Paket, von dem jetzt angenommen wird, dass es dies darstellt gleiche Version.
- Die Software auf dem Gerät ist in einem Verzeichnis mit dem Namen enthalten
/opt/example/current
, das einen Symlink zur aktuellen Version der Software darstellt. - Eine automatische Aktualisierungsfunktion auf dem Gerät, die beim Booten:
- Überprüft,
do_not_update
ob eine Datei vorhanden ist, und ergreift keine weiteren Maßnahmen, falls vorhanden (für Entwicklungsgeräte siehe unten). - Liest den aktuellen Commit-Hash aus der oben genannten Textdatei.
- Stellt eine HTTP-Anforderung an den Server mit diesem Hash als Abfrageparameter. Der Server antwortet entweder mit einem 304 (Hash ist die aktuelle Version) oder stellt das gzipped-Update-Paket bereit.
- Installiert das Update-Paket, falls eines empfangen wurde, in
/opt/example
:- Extrahieren der aktualisierten Softwareinformationen in einen Ordner mit dem Namen
stage
. - Ausführen eines Skripts nach der Installation aus dem Update-Paket, das beispielsweise die erforderlichen lokalen Änderungen für dieses Update usw. vornimmt.
- Kopieren des aktuellen Software-Stammordners in
previous
(löschtprevious
zuerst den vorhandenen , falls vorhanden). - Kopieren des
stage
Ordners inlatest
(löschtlatest
zuerst den vorhandenen Ordner , falls vorhanden). current
Stellen Sie sicher, dass der Symlink auf zeigtlatest
.- Neustart des Geräts (Firmware-Updates, falls vorhanden, werden beim Neustart angewendet).
- Extrahieren der aktualisierten Softwareinformationen in einen Ordner mit dem Namen
- Überprüft,
Es gibt auch das Problem der erstmaligen Bereitstellung auf neu erstellten Geräten. Die Geräte basieren derzeit auf SD-Karten (haben ihre eigenen Probleme, die hier nicht berücksichtigt werden). Dieser Prozess besteht also aus:
- Es ist ein SD-Image vorhanden, auf dem sich eine stabile frühere Version der Software befindet.
- Aus diesem Bild wird eine SD-Karte erstellt.
- Beim ersten Start werden verschiedene gerätespezifische (seriennummernbasierte) Initialisierungen zum ersten Mal durchgeführt. Anschließend greift der Auto-Updater wie gewohnt auf die neueste Produktionsversion der Software zu und installiert sie.
Außerdem brauchte ich Unterstützung für Entwicklungsgeräte. Für Entwicklungsgeräte:
- Auf dem Gerät wird ein vollständiges lokales Git-Repo verwaltet.
- Der
current
Symlink verweist auf das Entwicklungsverzeichnis. - Es ist eine lokale
do_not_update
Datei vorhanden, die verhindert, dass der automatische Updater den Entwicklungscode mit einem Produktionsupdate wegbläst.
Der Bereitstellungsprozess sollte theoretisch wie folgt aussehen:
- Sobald der Code für die Bereitstellung bereit ist, verschieben Sie ihn in den Release-Zweig.
- Klicken Sie auf dem Server auf die Schaltfläche "Update bereitstellen".
- Das Update ist jetzt live und die Geräte werden bei der nächsten Überprüfung automatisch aktualisiert.
In der Praxis gibt es jedoch eine Menge Probleme:
- Der Webserver-Code befindet sich im selben Repo wie der Gerätecode, und der Server verfügt über ein lokales Git-Repo, das ich ausführe. Der neueste Webservercode befindet sich nicht in derselben Verzweigung wie der neueste Gerätecode. Die Verzeichnisstruktur ist problematisch. Wenn die Schaltfläche "Update bereitstellen" die neueste Version aus dem Produktionszweig abruft, wird sie in ein Unterverzeichnis des Servercodes abgerufen. Dies bedeutet, dass ich bei der Bereitstellung auf einem Server von Grund auf dieses Unterverzeichnis manuell "säen" muss, indem ich den Geräteproduktionszweig darin greife, da ich wahrscheinlich aufgrund eines Git-Benutzerfehlers meinerseits versuche, dies nicht zu tun Rufen Sie den Gerätecode aus dem Webserver- Zweig des übergeordneten Verzeichnisses ab . Ich denke, dies ist lösbar, indem der Staging-Bereich kein Unterverzeichnis des lokalen Git-Repos des Servers ist.
- Der Webserver verwaltet den Git-Hash der Gerätesoftware derzeit nicht dauerhaft. Beim Start des Servers führt es ein
git rev-parse HEAD
Software-Repo in seinem lokalen Gerät durch, um den aktuellen Hash abzurufen. Aus Gründen, die ich nicht in den Kopf bekommen kann, verursacht dies auch eine Menge logischer Fehler, die ich hier nicht beschreiben werde. Es genügt zu sagen, dass ein Neustart des Servers manchmal Probleme verursacht, insbesondere wenn der Server brandneu ist und keine Produktion aufweist Branch Repo wurde noch gezogen. Ich würde gerne die Quelle für diese Logik teilen, wenn ich dazu aufgefordert werde, aber dieser Beitrag wird lang. - Wenn das Desinfektionsskript (serverseitig) aus irgendeinem Grund fehlschlägt, bleibt dem Server ein aktuelles Repo, aber ein nicht synchrones / fehlendes Update-Paket
git rev-parse HEAD
übrig , und es wird ein Hash zurückgegeben, der nicht mit dem übereinstimmt, was tatsächlich vorhanden ist auf den Geräten bereitgestellt, und Probleme hier müssen manuell in der Server-Befehlszeile behoben werden. Das heißt, der Server weiß nicht, dass das Update-Paket nicht korrekt ist, sondern setzt dies lediglich immer im reinen Glauben voraus. In Kombination mit den vorherigen Punkten ist der Server in der Praxis äußerst zerbrechlich. - Eines der größten Probleme ist : Derzeit wird kein separater Updater-Daemon auf dem Gerät ausgeführt. Aufgrund von Komplikationen, die auf den WLAN-Internetzugang warten, und einigen Last-Minute-Hackerangriffen ist es die Hauptgerätesteuerungssoftware selbst, die das Gerät überprüft und aktualisiert. Dies bedeutet, dass, wenn eine schlecht getestete Version es irgendwie in die Produktion schafft und die Steuerungssoftware nicht gestartet werden kann, alle vorhandenen Geräte im Wesentlichen blockiert sind, da sie sich nicht mehr selbst aktualisieren können. Dies wäre ein absoluter Albtraum in der Produktion. Gleiches gilt für ein einzelnes Gerät, wenn es zu einem unglücklichen Zeitpunkt an Strom verliert.
- Das andere Hauptproblem ist : Inkrementelle Updates werden nicht unterstützt. Wenn ein Gerät beispielsweise eine Weile nicht eingeschaltet ist, muss es beim nächsten Update eine Reihe von Release-Versionen überspringen und ein direktes Update zum Überspringen von Versionen durchführen können. Die Folge dieser aktualisierten Bereitstellung ist ein Albtraum, sicherzustellen, dass jedes Update zusätzlich zu einer bestimmten früheren Version angewendet werden kann. Da Git-Hashes eher zur Identifizierung von Versionen als von Versionsnummern verwendet werden, ist ein lexikografischer Vergleich von Versionen zur Erleichterung inkrementeller Aktualisierungen derzeit nicht möglich.
- Eine neue Anforderung, die ich derzeit nicht unterstütze, besteht darin, dass einige Gerätekonfigurationsoptionen (Schlüssel / Wert-Paare) vorhanden sind, die auf der Seite des Verwaltungsservers konfiguriert werden müssen. Es würde mir nichts ausmachen, diese Geräteoptionen in derselben HTTP-Anforderung wie das Software-Update an das Gerät zurückzusenden (möglicherweise könnte ich sie in HTTP-Headern / Cookies einkapseln), obwohl ich mir darüber keine allzu großen Sorgen mache, wie ich kann Machen Sie es immer zu einer separaten HTTP-Anfrage.
- Es gibt eine leichte Komplikation aufgrund der Tatsache, dass zwei (und in Zukunft mehr) Versionen der Hardware existieren. Die aktuelle Version der Hardware wird tatsächlich als Umgebungsvariable auf dem ursprünglichen SD-Image gespeichert (sie können sich nicht selbst identifizieren), und die gesamte Software ist so konzipiert, dass sie mit allen Versionen der Geräte kompatibel ist. Firmware-Updates werden basierend auf dieser Umgebungsvariablen ausgewählt und das Update-Paket enthält Firmware für alle Versionen der Hardware. Ich kann damit leben, obwohl es ein bisschen klobig ist.
- Derzeit gibt es keine Möglichkeit, ein Update manuell auf das Gerät hochzuladen (kurz gesagt, diese Geräte verfügen über zwei WLAN-Adapter, einen für die Verbindung zum Internet und einen im AP-Modus, mit dem der Benutzer das Gerät konfiguriert Ich beabsichtige, der lokalen Weboberfläche des Geräts eine "Software aktualisieren" -Funktion hinzuzufügen. Dies ist keine große Sache, hat jedoch einige Auswirkungen auf die Installationsmethode des Updates.
- Eine Menge anderer Frustrationen und allgemeiner Unsicherheit.
Also ... das war lang. Aber meine Frage läuft darauf hinaus:
Wie mache ich das richtig und sicher? Gibt es kleine Anpassungen, die ich an meinem bestehenden Prozess vornehmen kann? Gibt es eine bewährte Strategie / ein vorhandenes System, das ich nutzen kann, damit ich nicht mein eigenes beschissenes Update-System rollen muss ? Oder wenn ich meine eigenen rollen muss, welche Dinge müssen zutreffen, damit ein Bereitstellungs- / Aktualisierungsprozess sicher und erfolgreich ist? Ich muss auch in der Lage sein, Entwicklungsgeräte in den Mix aufzunehmen.
Ich hoffe die Frage ist klar. Mir ist klar, dass es ein bisschen verschwommen ist, aber ich bin zu 100% sicher, dass dies ein Problem ist, das zuvor angegangen und erfolgreich gelöst wurde. Ich weiß einfach nicht, was die derzeit akzeptierten Strategien sind.