
Irgendwie habe ich einmal beschlossen, einen Artikel über die Bereitstellung in Form von Docker-Containern und deb-Paketen zu schreiben, aber als ich anfing, wurde ich seltsamerweise in die fernen Zeiten der ersten Personal Computer und sogar Taschenrechner zurückgezogen. Statt trockener Vergleiche zwischen Docker und deb sind so diese Überlegungen zur Evolution entstanden, die ich Ihnen präsentiere.
Jedes Produkt, egal welches, muss auf irgendeine Weise zu den Produktservern gelangen, muss konfiguriert und gestartet werden. Darum wird es in diesem Artikel gehen.
Ich werde im historischen Kontext nachdenken, „was ich sehe, darüber singe ich“, was ich gesehen habe, als ich begonnen habe, Code zu schreiben, und was ich jetzt beobachte, was wir derzeit selbst verwenden und warum. Der Artikel erhebt keinen Anspruch auf eine umfassende Untersuchung, einige Aspekte sind ausgelassen, es ist mein persönlicher Blick auf das, was damals war und was heute ist.
Also, in den guten alten Zeiten... die früheste Art der Bereitstellung, die ich erlebt habe, waren Kassetten von Kassettenrecordern. Ich hatte einen BK-0010.01 Computer...
Die Ära der Taschenrechner
Nein, es gab einen noch früheren Moment, es gab einen anderen Taschenrechner und .
Also, als ich den hatte , der Weg, um das Programm zu übertragen, war ein gewöhnliches kariertes Blatt Papier, auf dem das Programm geschrieben war. Um es bei Bedarf manuell zu starten, wurde es in den Taschenrechner eingegeben. Wenn man spielen wollte (ja, ja, sogar auf diesem uralten Taschenrechner gab es Spiele) — setzte man sich hin und gab das Programm in den Taschenrechner ein. Natürlich ging das Programm beim Ausschalten des Taschenrechners in das Nichts verloren. Neben den handschriftlichen Codes auf Papier wurden Programme auch in den Zeitschriften „Radio“ und „Technik der Jugend“ veröffentlicht sowie in Büchern der damaligen Zeit gedruckt.
Die nächste Modifikation war der Taschenrechner , der bereits eine Art von nichtflüchtiger Datenspeicherung hatte. Jetzt musste das Spiel oder Programm nicht mehr manuell eingegeben werden; nach einigen magischen Knopfdrückern wurde es automatisch geladen.
Das größte Programm im Taschenrechner hatte 105 Schritte, und der Speicher des MK-52 betrug 512 Schritte.
Übrigens, falls es Fans dieser Taschenrechner gibt, die diesen Artikel lesen — während des Schreibens habe ich einen Emulator für Android und Programme dafür gefunden. Vorwärts in die Vergangenheit!
Eine kleine Abweichung zu MK-52 (aus Wikipedia)
MK-52 flog ins All mit dem Raumschiff „Sojus TM-7“. Er sollte verwendet werden, um die Landebahn zu berechnen, falls der Bordcomputer ausfällt.
MK-52 mit dem Erweiterungsmodul „Elektronik-Astro“ wurde seit 1988 auf Schiffe der Marine im Rahmen des Navigator-Rechnungssetzes geliefert.
Die ersten Personal Computer
Lass uns zu den Zeiten zurückkehren . Es versteht sich von selbst, dass auch der Speicher größer wurde und es überhaupt nicht mehr möglich war, den Code von Papier abzutippen (obwohl ich anfangs genau das tat, weil es einfach kein anderes Medium gab). Das Hauptmittel zur Speicherung und Lieferung von Software wurden Audiokassetten für Kassettenrekorder.
Die Speicherung auf Kassette bestand normalerweise aus einer oder zwei Binärdateien, alles andere war im Inneren enthalten. Die Zuverlässigkeit war sehr gering, sodass man 2-3 Kopien des Programms aufbewahren musste. Auch die Ladezeiten waren enttäuschend; Enthusiasten experimentierten mit verschiedenen Frequenzcodierungen, um diese Mängel zu beheben. Ich selbst beschäftigte mich zu der Zeit noch nicht professionell mit Softwareentwicklung (abgesehen von einfachen Programmen in Basic), daher kann ich leider nicht im Detail erzählen, wie alles im Inneren funktionierte. Die Tatsache, dass im Computer hauptsächlich nur RAM vorhanden war, bestimmte in hohem Maße die Einfachheit des Datenspeicherschemas.
Das Erscheinen zuverlässiger und großer Speicherträger
Später erscheinen Disketten, was den Kopiervorgang vereinfacht und die Zuverlässigkeit erhöht.
Die Situation ändert sich jedoch grundlegend, als ausreichend große lokale Speicher in Form von HDDs verfügbar werden.
Der Typ der Bereitstellung ändert sich grundlegend: es entstehen Installationsprogramme, die den Konfigurationsprozess des Systems steuern und die Bereinigung nach der Deinstallation übernehmen, da die Programme nicht einfach in den Speicher geladen werden, sondern bereits in den lokalen Speicher kopiert werden, aus dem unnötige Dateien bei Bedarf entfernt werden müssen.
Gleichzeitig steigt die Komplexität der bereitgestellten Software.
Die Anzahl der Dateien in der Bereitstellung steigt von einer Einheit auf Hunderte und Tausende, es beginnt zu Konflikten mit Versionen von Bibliotheken und anderen Herausforderungen, wenn verschiedene Programme dieselben Daten verwenden.
Zu dieser Zeit war mir die Existenz von Linux noch unbekannt, ich lebte in der Welt von MS DOS und später Windows und programmierte in Borland Pascal und Delphi, manchmal schielte ich in Richtung C++. Viele verwendeten InstallShield zur Bereitstellung von Produkten in jener Zeit. , das alle gestellten Anforderungen an die Bereitstellung und Konfiguration von Software erfolgreich löste.
Das Zeitalter des Internets
Die Komplexität von Softwaresystemen nimmt weiterhin zu, da der Übergang von monolithischen und Desktop-Anwendungen zu verteilten Systemen, Thin Clients und Mikrodiensten erfolgt. Jetzt müssen nicht nur einzelne Programme konfiguriert werden, sondern eine gesamte Suite, und zwar so, dass sie alle zusammenarbeiten.
Das Konzept hat sich grundlegend geändert, das Internet ist gekommen, und die Ära der Cloud-Dienste hat begonnen. Zunächst nur in Form von Websites, und ernsthaft mit Diensten, davon hat damals noch niemand geträumt. Doch das war ein Wendepunkt in der Industrie, sowohl in der Entwicklung als auch in der Bereitstellung von Anwendungen.
Ich habe für mich festgestellt, dass zu diesem Zeitpunkt ein Generationenwechsel unter den Entwicklern stattfand (oder war es nur in meinem Umfeld so?), und es hatte sich das Gefühl eingestellt, dass alle alten, bewährten Methoden der Bereitstellung in einem Moment vergessen wurden und alles von neuem begann: Die gesamte Bereitstellung wurde durch einfache Skripte übernommen und erhielt den stolzen Namen „Continuous Delivery“. Tatsächlich begann eine Phase des Chaos, in der das Alte vergessen und nicht mehr verwendet wurde, während es einfach nichts Neues gab.
Ich erinnere mich an Zeiten, als es in dem Unternehmen, in dem ich damals arbeitete (ich werde keinen Namen nennen), statt des Builds über Ant (Maven war damals noch nicht populär oder überhaupt nicht vorhanden) üblich war, dass die Leute einfach JAR-Dateien in der IDE erstellten und sie bedenkenlos in SVN committeten. Entsprechend bestand das Deployment darin, die Datei aus SVN zu holen und sie per SSH auf die benötigte Maschine zu kopieren. So einfach und grob war das.
Zur gleichen Zeit wurde die Bereitstellung einfacher Websites in PHP auf eine ganz primitive Weise durchgeführt, nämlich durch einfaches Kopieren der korrigierten Datei über FTP auf die Zielmaschine. Manchmal gab es sogar das nicht — der Code wurde live auf dem Produktionsserver bearbeitet, und es war etwas Besonderes, wenn irgendwo Backups vorhanden waren.
RPM- und DEB-Pakete
Andererseits gewannen UNIX-ähnliche Systeme mit der Entwicklung des Internets immer mehr an Beliebtheit. Besonders in dieser Zeit entdeckte ich RedHat Linux 6, etwa um das Jahr 2000. Natürlich gab es auch dort bestimmte Mittel zur Bereitstellung von Software. Laut Wikipedia erschien RPM als Hauptpaketmanager bereits 1995 mit der Version 2.0 von RedHat Linux. Und seitdem wird das System in Form von RPM-Paketen bereitgestellt und hat sich erfolgreich entwickelt und weiterentwickelt.
Die Debian-Familie hat einen ähnlichen Weg eingeschlagen und liefert Software in Form von deb-Paketen, was bis heute unverändert geblieben ist.
Paketmanager ermöglichen die Bereitstellung der Softwareprodukte, die Konfiguration während der Installation, die Verwaltung der Abhängigkeiten zwischen verschiedenen Paketen, die Deinstallation von Produkten sowie die Bereinigung überflüssiger Dateien im Prozess der Deinstallation. Das heißt, im Wesentlichen ist das alles, was benötigt wird, weshalb sie sich mehrere Jahrzehnte fast ohne Änderungen gehalten haben.
Cloud-Computing hat zu den Paketmanagern die Möglichkeit hinzugefügt, nicht nur von physischen Medien, sondern auch aus Cloud-Repositories zu installieren, aber im Grunde hat sich wenig geändert.
Es ist erwähnenswert, dass es derzeit einige Bestrebungen gibt, sich von deb zu entfernen und auf snap-Pakete umzusteigen, aber dazu später mehr.
Dieses neue Generation von Cloud-Entwicklern, die weder DEB noch RPM gekannt haben, hat ebenfalls langsam Erfahrung gesammelt, Produkte wurden komplexer, und es wurden vernünftigere Methoden zur Bereitstellung benötigt als FTP, Bash-Skripte und ähnliche studentische Lösungen.
Und hier kommt Docker ins Spiel, eine Mischung aus Virtualisierung, Ressourcenteilung und Bereitstellungsmechanismen. Das ist momentan angesagt und modern, aber braucht man das für alles? Ist das die universelle Lösung?
Meinen Beobachtungen zufolge wird Docker oft nicht als kluge Wahl angeboten, sondern einfach, weil es in der Community besprochen wird und viele, die es empfehlen, nur damit vertraut sind. Im Gegensatz dazu wird über die alten, bewährten Verpackungssysteme meist geschwiegen – sie existieren, leisten still ihren Dienst und fallen nicht auf. In so einer Situation bleibt wenig Spielraum – die Wahl ist offensichtlich – Docker.
Ich möchte meine Erfahrungen teilen, wie bei uns die Implementierung von Docker verlief und was letztendlich dabei herauskam.
Selbstgeschriebene Skripte
Ursprünglich hatten wir Bash-Skripte, die JAR-Dateien auf die entsprechenden Maschinen depolten. Jenkins steuerte diesen Prozess. Das funktionierte erfolgreich, da die JAR-Datei selbst bereits eine Sammlung ist, die Klassen, Ressourcen und sogar Konfigurationen enthält. Wenn man alles nach Möglichkeit hineintut – ist es nicht die schwierigste Aufgabe, es mit einem Skript zu entpacken.
Aber Skripte haben einige Nachteile:
- Skripte werden oft hastig geschrieben und sind daher so primitiv, dass sie nur ein einziges erfolgreiches Szenario enthalten. Dies wird dadurch begünstigt, dass der Entwickler an einer schnellen Lieferung interessiert ist, während ein normaler Skript ein beträchtliches Investieren an Ressourcen erfordert.
- Infolgedessen enthalten Skripte keine Deinstallationsverfahren.
- Es gibt kein festgelegtes Upgrade-Verfahren.
- Wenn ein neues Produkt erscheint, muss ein neuer Skript geschrieben werden.
- Es gibt keine Unterstützung für Abhängigkeiten.
Natürlich kann man einen komplexen Skript schreiben, aber wie bereits erwähnt — das erfordert Entwicklungszeit, und diese ist bekanntlich immer knapp.
All dies schränkt den Anwendungsbereich dieser Bereitstellungsmethode auf die einfachsten Systeme ein. Es ist Zeit für Veränderungen.
Docker
Irgendwann kamen frische Mid-Level-Kollegen zu uns, die voller Ideen strotzten und von Docker träumten. Na dann, legen wir los! Es gab zwei Versuche. Beide waren gescheitert – sagen wir mal, aufgrund großer Ambitionen, aber fehlender praktischer Erfahrung. Hätte man es mit aller Kraft vorantreiben und abschließen sollen? Wahrscheinlich nicht – das Team sollte evolutionär auf das erforderliche Niveau wachsen, bevor es die entsprechenden Werkzeuge nutzen kann. Darüber hinaus hatten wir bei der Verwendung von vorgefertigten Docker-Images häufig das Problem, dass das Netzwerk nicht korrekt funktionierte (was möglicherweise auch mit der Unreife von Docker selbst zusammenhing) oder es schwierig war, fremde Container zu erweitern.
Mit welchen Unannehmlichkeiten hatten wir zu kämpfen?
- Netzwerkprobleme im Bridge-Modus
- Unbequem, die Logs im Container anzusehen (sofern sie nicht separat im Dateisystem der Hostmaschine ausgegeben werden)
- Gelegentlich merkwürdiges Einfrieren von ElasticSearch innerhalb des Containers, die Ursache konnten wir nicht feststellen, der Container ist offiziell
- Unangenehm, die Shell innerhalb des Containers zu nutzen – alles ist stark eingeschränkt, es fehlen gewohnte Werkzeuge
- Große Größe der erstellten Container – teuer im Speicher
- Aufgrund der großen Größe der Container ist es schwierig, mehrere Versionen zu unterstützen.
- Längere Build-Zeit im Vergleich zu anderen Methoden (Skripte oder deb-Pakete).
Ist ein Spring-Service als Jar-Archiv wirklich schlechter zu deployen als über dasselbe deb? Ist Ressourcenauswahl wirklich notwendig? Sollten wir auf nützliche Betriebssystem-Tools verzichten, indem wir den Service in einen stark eingeschränkten Container packen?
Wie die Praxis gezeigt hat, ist das in der Realität nicht nötig; ein deb-Paket reicht in 90 % der Fälle aus.
Wann funktioniert das gute alte deb einmal nicht, und wann benötigen wir tatsächlich Docker?
Für uns war das der Einsatz von Services auf Python. Viele Bibliotheken, die für maschinelles Lernen benötigt werden und in der Standardinstallation des Betriebssystems fehlen (und die vorhandenen sind nicht die richtigen Versionen), Hacks mit Konfigurationen und die Notwendigkeit, verschiedene Versionen für verschiedene Services zu haben, die auf demselben Host-System laufen, führten dazu, dass Docker die einzige sinnvolle Lösung für die Bereitstellung dieser komplexen Mischung war. Der Aufwand für die Erstellung des Docker-Containers war geringer als der Versuch, alles in separate deb-Pakete mit Abhängigkeiten zu packen — tatsächlich würde niemand in seinem gesunden Verstand dafür übernehmen.
Ein zweiter Punkt, an dem Docker eingesetzt werden soll, ist die Bereitstellung von Services nach dem Blue-Green-Deployment-Schema. Hier möchte ich jedoch eine schrittweise Erhöhung der Komplexität erreichen: Zuerst werden deb-Pakete erstellt, und dann wird aus diesen ein Docker-Container erstellt.
Snap-Pakete
Kommen wir zurück zu den Snap-Paketen. Sie wurden erstmals offiziell in Ubuntu 16.04 eingeführt. Im Gegensatz zu herkömmlichen deb-Paketen und rpm-Paketen enthalten Snap-Pakete alle Abhängigkeiten. Dies vermeidet einerseits Konflikte zwischen Bibliotheken, führt aber andererseits zu größeren Paketgrößen. Außerdem kann dies auch die Sicherheit des Systems beeinflussen: Bei der Bereitstellung eines Snap-Pakets muss der Entwickler, der das Paket erstellt, alle Änderungen der verwendeten Bibliotheken im Auge behalten. Insgesamt ist die Situation nicht so eindeutig, und das allgemeine Glück über ihre Verwendung tritt nicht automatisch ein. Dennoch sind sie eine durchaus vernünftige Alternative, wenn Docker lediglich als Verpackungsmittel und nicht zur Virtualisierung genutzt wird.
Insgesamt verwenden wir jetzt in einer sinnvollen Kombination sowohl deb-Pakete als auch Docker-Container, die wir möglicherweise in bestimmten Fällen durch Snap-Pakete ersetzen.
Nur registrierte Benutzer können an der Umfrage teilnehmen. Sind Sie an Contour interessiert?
Welche Methode verwenden Sie zur Bereitstellung?
Selbstgeschriebene Skripte
Wir kopieren manuell über FTP
deb-Pakete
rpm-Pakete
Snap-Pakete
Docker-Images
Virtuelle Maschinen
Wir klonen die gesamte HDD
Puppet
ansible
Sonstiges
109 Nutzer haben abgestimmt. 32 Nutzer haben sich enthalten.
Quelle: habr.com
