Wir haben DevOps so gut wie möglich entwickelt. Wir waren zu acht und Vasya war der coolste unter Windows. Plötzlich ging Vasya und ich hatte die Aufgabe, ein neues Projekt herauszubringen, das die Windows-Entwicklung unterstützt. Als ich den gesamten Stapel der Windows-Entwicklung auf den Tisch schüttete, wurde mir klar, dass die Situation schmerzhaft ist ...
So beginnt die Geschichte von
Alexander Sinchinov bei
DevOpsConf . Als der führende Windows-Spezialist das Unternehmen verließ, fragte sich Alexander, was er jetzt tun sollte. Wechseln Sie natürlich zu Linux! Alexander wird am Beispiel eines abgeschlossenen Projekts für 100.000 Endbenutzer erläutern, wie er es geschafft hat, einen Präzedenzfall zu schaffen und einen Teil der Windows-Entwicklung auf Linux zu übertragen.

Wie kann ein Projekt mit TFS, Puppet, Linux .NET Core einfach und mühelos an RPM geliefert werden? Wie kann die Versionierung der Projektdatenbank beibehalten werden, wenn die Entwicklung zuerst die Wörter Postgres und Flyway und die Frist übermorgen hört? Wie kann ich mich in Docker integrieren? Wie kann man .NET-Entwickler motivieren, Windows und Smoothies zugunsten von Puppet und Linux aufzugeben? Wie können ideologische Konflikte gelöst werden, wenn es keine Kräfte, keinen Wunsch und keine Ressourcen gibt, um Windows in der Produktion zu bedienen? Darüber sowie über Web Deploy, Testing, CI, über die Praktiken der Verwendung von TFS in bestehenden Projekten und natürlich über kaputte Krücken und Arbeitslösungen bei der Entschlüsselung des Berichts von Alexander.
Also, Vasya ist gegangen, die Aufgabe ist für mich, die Entwickler freuen sich
mit einer Heugabel . Als ich endlich merkte, dass Vasya nicht zurückgegeben werden konnte, machte ich mich an die Arbeit. Zunächst schätzte ich den Prozentsatz von Win VM in unserem Park. Die Punktzahl war nicht zugunsten von Windows.

Da wir DevOps aktiv entwickeln, wurde mir klar, dass beim Ansatz, eine neue Anwendung herauszunehmen, etwas geändert werden muss. Die Lösung war eine - wenn möglich, übertragen Sie alles auf Linux. Google hat mir geholfen - zu dieser Zeit war .Net bereits auf Linux portiert, und mir wurde klar, dass diese Lösung!
Warum wird .NET Core mit Linux gebündelt?
Dafür gab es mehrere Gründe. Zwischen "Geld bezahlen" und "Nicht bezahlen" wählt die Mehrheit die zweite - wie ich. Eine Lizenz für MSDB kostet ungefähr 1.000 US-Dollar, die Wartung einer virtuellen Windows-Maschinenflotte kostet Hunderte von US-Dollar. Für ein großes Unternehmen ist dies ein großer Aufwand. Daher ist das
Speichern der
erste Grund . Nicht das wichtigste, aber eines der wichtigsten.
Virtuelle Windows-Maschinen beanspruchen mehr Ressourcen als ihre Linux-Brüder -
sie sind schwer . Angesichts der Größe eines großen Unternehmens haben wir uns für Linux entschieden.
Das System wird einfach in das vorhandene CI integriert . Wir betrachten uns als progressive DevOps, wir verwenden Bamboo, Jenkins und GitLab CI, daher arbeiten wir hauptsächlich unter Linux.
Der letzte Grund ist
bequeme Begleitung. Wir mussten die Eintrittsschwelle für „Escorts“ senken - Leute, die den technischen Teil verstehen, sorgen für einen unterbrechungsfreien Betrieb und Serviceleistungen ab der zweiten Linie. Sie waren bereits mit dem Linux-Stack vertraut, sodass es für sie viel einfacher ist, das neue Produkt zu verstehen, zu warten und zu warten, als zusätzliche Ressourcen aufzuwenden, um die ähnliche Funktionalität der Software für die Windows-Plattform zu bewältigen.
Anforderungen
In erster Linie die
Bequemlichkeit einer neuen Lösung für Entwickler . Nicht alle von ihnen waren bereit für Veränderungen, insbesondere nach dem gesprochenen Wort Linux. Entwickler möchten ihr geliebtes Visual Studio, TFS, mit Build-Tests und Smoothies. Wie die Lieferung in der Produktion erfolgt - das ist ihnen egal. Aus diesem Grund haben wir beschlossen, den üblichen Prozess nicht zu ändern und alles für die Windows-Entwicklung unverändert zu lassen.
Das neue Projekt muss
in das vorhandene CI eingebettet werden . Die Schienen waren bereits vorhanden und alle Arbeiten mussten unter Berücksichtigung der Parameter des Konfigurationsmanagementsystems, der akzeptierten Lieferstandards und der Überwachungssysteme durchgeführt werden.
Einfachheit in Support und Betrieb als Voraussetzung für eine Mindesteintrittsschwelle für alle neuen Teilnehmer aus verschiedenen Abteilungen und Supportabteilungen.
Frist - gestern .
Entwicklungsgruppe gewinnen
Womit hat das Windows-Team damals gearbeitet?

Jetzt kann ich mit Sicherheit sagen, dass
IdentityServer4 eine coole, kostenlose Alternative zu ADFS mit ähnlichen Funktionen ist oder dass
Entity Framework Core ein Paradies für Entwickler ist, in dem Sie sich nicht die Mühe machen müssen, SQL-Skripte zu schreiben, sondern Abfragen in der Datenbank in OOP-Begriffen beschreiben. Bei der Erörterung des Aktionsplans betrachtete ich diesen Stapel jedoch als eine sumerische Keilschrift, die nur PostgreSQL und Git erkennt.
Zu dieser Zeit haben wir
Puppet aktiv als Konfigurationsmanagementsystem verwendet. In den meisten unserer Projekte verwendeten wir
GitLab CI ,
Elastic , ausgeglichene, hoch ausgelastete Dienste mit
HAProxy, überwachten alles mit
Zabbix , einer Reihe von
Grafana und
Prometheus ,
Jaeger , und all dies drehte sich auf
HP Hardware mit
ESXi auf
VMware . Jeder weiß - ein Klassiker des Genres.

Lassen Sie uns schauen und versuchen zu verstehen, was passiert ist, bevor wir mit all diesen Interventionen begonnen haben.
Was war
TFS ist ein ziemlich leistungsfähiges System, das nicht nur Code vom Entwickler an die endgültige Produktionsmaschine liefert, sondern auch eine Reihe für eine sehr flexible Integration mit verschiedenen Diensten bietet - um CI auf plattformübergreifender Ebene bereitzustellen.

Zuvor waren dies solide Fenster. TFS verwendete mehrere Build-Agenten, die viele Projekte sammelten. Jeder Agent hat 3-4 Mitarbeiter - a, um Aufgaben zu parallelisieren und den Prozess zu optimieren. Gemäß den Release-Plänen lieferte TFS den frisch gebackenen Build an den Windows-Anwendungsserver.
Zu was wir kommen wollten
Für die Bereitstellung und Entwicklung verwenden wir TFS und starten die Anwendung auf dem Linux-Anwendungsserver. Zwischen ihnen liegt eine Art Magie. Diese
Magic Box ist das Salz der bevorstehenden Arbeit. Bevor ich es in Teile zerlege, werde ich einen Schritt zur Seite treten und zwei Worte über die Anwendung sagen.
Projekt
Die Anwendung bietet Funktionen für den Umgang mit Prepaid-Karten.

Client
Es gab zwei Arten von Benutzern.
Der erste erhielt Zugriff, indem er sich mit dem SSL SHA-2-Zertifikat anmeldete. Der
zweite hatte Zugriff per Login und Passwort.
HAProxy
Ferner fiel die Kundenanfrage in HAProxy, das die folgenden Aufgaben löste:
- Erstgenehmigung;
- SSL-Beendigung
- Optimierung von HTTP-Anfragen;
- Broadcast-Anfragen.
Die Überprüfung des Client-Zertifikats durchlief die Kette. Wir sind
Autorität und können es uns leisten, da wir selbst Zertifikate für Servicekunden ausstellen.
Achten Sie auf den dritten Punkt, wenig später werden wir darauf zurückkommen.
Backend
Sie planten ein Backend unter Linux. Das Backend interagiert mit der Datenbank, lädt die erforderliche Liste der Berechtigungen und bietet dann, abhängig von den Berechtigungen des autorisierten Benutzers, den Zugriff zum Signieren und Senden von Finanzdokumenten oder zum Generieren eines Berichts.
Sparen mit HAProxy
Zusätzlich zu den beiden Kontexten, die jeder Client durchlief, gab es auch einen Identitätskontext.
Mit IdentityServer4 können Sie sich nur anmelden. Es ist ein kostenloses und leistungsstarkes Analogon für
ADFS -
Active Directory Federation Services .
Die Identitätsanfrage wurde in mehreren Schritten bearbeitet. Der erste Schritt - der
Client fiel in das Backend , das Daten mit diesem Server austauschte und das Vorhandensein eines Tokens für den Client überprüfte. Wenn ich es nicht gefunden habe, kehrte die Anfrage zu dem Kontext zurück, aus dem sie stammte, aber mit einer Weiterleitung und mit einer Weiterleitung ging sie zur Identität.
Der zweite Schritt - Die Anforderung wurde
an die Authentifizierungsseite in IdentityServer weitergeleitet, auf der der Client registriert war, und das sehr lang erwartete Token wurde in der IdentityServer-Datenbank angezeigt.
Der dritte Schritt - der
Kunde leitete zurück zu dem Kontext, aus dem er kam.

IdentityServer4 hat eine Besonderheit:
Es gibt die Antwort auf die Rückgabeanforderung über HTTP zurück . Egal wie wir mit dem Server-Setup zu kämpfen hatten, egal wie wir mit der Dokumentation vertraut wurden, jedes Mal, wenn wir eine erste Client-Anfrage mit einer URL erhielten, die über HTTPS kam, gab IdentityServer denselben Kontext zurück, jedoch mit HTTP. Wir standen unter Schock! All dies wurde über den Identitätskontext an HAProxy übertragen, und in den Headern mussten wir das HTTP-Protokoll auf HTTPS ändern.
Was ist die Verbesserung und wo haben sie gespart?
Wir haben Geld gespart, indem wir eine kostenlose Lösung zum Autorisieren einer Gruppe von Benutzern und Ressourcen verwendet haben, da wir IdentityServer4 nicht als separate Notiz in einem separaten Segment verwendet haben, sondern zusammen mit einem Backend auf demselben Server verwendet haben, auf dem sich das Anwendungs-Backend dreht.
Wie es funktionieren soll
Also, wie ich versprochen habe - Magic Box. Wir verstehen bereits, dass wir garantiert auf Linux umsteigen werden. Formulieren wir spezifische Aufgaben, für die Lösungen erforderlich sind.
Marionette manifestiert sich. Um die Konfiguration des Dienstes und der Anwendung bereitzustellen und zu verwalten, mussten Sie coole Rezepte schreiben. Eine Bleistiftrolle zeigt eloquent, wie schnell und effizient dies geschehen ist.
Versandart. Der Standard ist RPM. Jeder versteht, dass es unter Linux keinen Weg ohne gibt, aber das Projekt selbst nach dem Zusammenbau bestand aus einer Reihe ausführbarer DLL-Dateien. Es waren ungefähr 150 von ihnen, das Projekt ist ziemlich schwierig. Die einzige harmonische Lösung besteht darin, diese Binärdateien in RPM zu packen und die Anwendung von dort aus bereitzustellen.
Versionierung Wir mussten sehr oft veröffentlichen und wir mussten entscheiden, wie wir den Namen des Pakets bilden sollten. Dies ist eine Frage der TFS-Integrationsstufe. Wir hatten einen Build Agent unter Linux. Wenn TFS die Aufgabe an den Handler - Worker - an den Build-Agenten sendet, sendet es auch eine Reihe von Variablen, die in die Umgebung des Handlerprozesses fallen. Diesen Umgebungsvariablen werden der Name Build, der Name der Version und andere Variablen übergeben. Weitere Informationen hierzu finden Sie im Abschnitt „Zusammenstellen eines RPM-Pakets“.
Beim Einrichten von TFS ging es darum, die Pipeline einzurichten. Früher haben wir alle Windows-Projekte auf Windows-Agenten gesammelt, und jetzt gibt es einen Linux-Agenten - einen Build-Agenten, der in die Assembly-Gruppe aufgenommen werden muss und mit einigen Artefakten angereichert ist. Geben Sie an, welche Art von Projekten auf diesem Build-Agenten erstellt werden sollen. und irgendwie Pipeline ändern.
IdentityServer. ADFS ist nicht unser Weg, wir ertrinken für Open Source.
Lassen Sie uns die Komponenten durchgehen.
Magic Box
Besteht aus vier Teilen.
Linux Build Agent. Linux ist logisch, weil wir es kompilieren. Dieser Teil wurde in drei Schritten durchgeführt.
- Konfigurieren Sie Mitarbeiter und mehrere, da davon ausgegangen wurde, dass die Arbeit am Projekt verteilt ist.
- Installieren Sie .NET Core 1.x. Warum 1.x, wenn 2.0 bereits im Standard-Repository verfügbar ist? Denn als wir mit der Entwicklung begannen, war die stabile Version 1.09 und es wurde beschlossen, das Projekt dafür durchzuführen.
- Git 2.x.
RPM-Repository. RPM-Pakete mussten irgendwo gespeichert werden. Es wurde angenommen, dass wir dasselbe Unternehmens-RPM-Repository verwenden würden, das allen Linux-Hosts zur Verfügung steht. Und so taten sie es. Auf dem Repository-Server
ist ein
Webhook konfiguriert, der das erforderliche RPM-Paket vom angegebenen Speicherort heruntergeladen hat. Die Version des Pakets wurde vom Build-Agenten an den Webhook gemeldet.
Gitlab Achtung! GitLab wird hier nicht von Entwicklern verwendet, sondern von der Betriebsabteilung, um Anwendungsversionen und Paketversionen zu steuern, den Status aller Linux-Computer zu überwachen und das Rezept zu speichern - alle Puppet-Manifeste.
Puppet - löst alle kontroversen Probleme und liefert genau die Konfiguration, die wir von Gitlab wollen.
Wir fangen an zu tauchen. Wie wird DLL in RPM geliefert?
DDL-Lieferung an RPM
Nehmen wir an, wir haben einen Rockstar für die .NET-Entwicklung. Es verwendet Visual Studio und erstellt einen Release-Zweig. Danach wird es in Git geladen, und Git ist hier eine TFS-Entität, dh es ist das Anwendungsrepository, mit dem der Entwickler arbeitet.

Danach sieht TFS, dass ein neues Commit eingetroffen ist. Welche Anwendung? In den TFS-Einstellungen gibt es eine Bezeichnung darüber, über welche Ressourcen ein bestimmter Build-Agent verfügt. In diesem Fall sieht er, dass wir ein .NET Core-Projekt erstellen und einen Linux-Build-Agenten aus dem Pool auswählen.
Der Build-Agent empfängt die Quellen, lädt die erforderlichen
Abhängigkeiten aus dem .NET, dem npm-Repository usw. herunter. Nach dem Erstellen der Anwendung selbst und dem anschließenden Packen wird das RPM-Paket an das RPM-Repository gesendet.
Auf der anderen Seite tritt Folgendes auf. Der Wartungstechniker ist direkt an der Einführung des Projekts beteiligt: Er ändert die Version der Pakete in
Hiera im Repository, in dem das Anwendungsrezept gespeichert ist. Anschließend löst Puppet
Yum aus , holt das neue Paket aus dem Repository ab und die neue Version der Anwendung ist einsatzbereit.

Mit anderen Worten, alles ist einfach, aber was passiert im Build-Agenten selbst?
Packaging RPM DLL
Die Projektquellen und die Build-Aufgabe von TFS wurden empfangen. Der Build Agent
beginnt mit der Erstellung
des Projekts aus dem Quellcode . Das zusammengestellte Projekt ist in Form vieler
DLL-Dateien verfügbar, die in einem Zip-Archiv gepackt sind, um die Belastung des Dateisystems zu verringern.
Das ZIP-Archiv wird
in das Build-Verzeichnis des RPM-Pakets geworfen
. Als Nächstes initialisiert das Bash-Skript die Umgebungsvariablen, findet die Build-Version, die Projektversion, den Pfad zum Build-Verzeichnis und startet RPM-Build. Am Ende der Assembly wird das Paket im
lokalen Repository veröffentlicht , das sich auf dem Build-Agenten befindet.
Außerdem
wird vom Build-Agenten eine
JSON-Anforderung mit dem Versionsnamen und dem Build an den Server im RPM-Repository
gesendet . Webhook, über den ich bereits gesprochen habe, lädt dasselbe Paket aus dem lokalen Repository des Build-Agenten herunter und stellt die neue Assembly für die Installation zur Verfügung.

Warum ein solches Schema für die Bereitstellung eines Pakets an ein RPM-Repository? Warum kann ich das zusammengestellte Paket nicht sofort an das Repository senden? Tatsache ist, dass dies eine Sicherheitsbedingung ist. Dieses Szenario schränkt die Möglichkeit des unbefugten Herunterladens von RPM-Paketen durch Außenstehende auf einen Server ein, auf den alle Linux-Computer zugreifen können.
DB-Versionierung
Bei der Konsultation mit der Entwicklung stellte sich heraus, dass die Jungs näher an MS SQL sind, aber in den meisten Nicht-Windows-Projekten haben wir PostgreSQL bereits mit Macht und Haupt verwendet. Da wir uns bereits entschlossen haben, alles, was bezahlt wurde, aufzugeben, haben wir hier begonnen, PostgreSQL zu verwenden.

In diesem Teil möchte ich darüber sprechen, wie wir die Datenbankversionierung implementiert haben und wie Sie zwischen Flyway und Entity Framework Core wählen können. Betrachten Sie ihre Vor- und Nachteile.
Nachteile
Flyway geht nur in eine Richtung, wir
können nicht zurückrollen - das ist ein deutliches Minus. Der Vergleich mit Entity Framework Core kann anhand anderer Parameter erfolgen - aus Sicht der Entwicklerfreundlichkeit. Sie erinnern sich, dass wir dies in den Vordergrund gestellt haben und das Hauptkriterium darin bestand, nichts für die Windows-Entwicklung zu ändern.
Für Flyway
brauchten wir
eine Art Wrapper, damit die Jungs keine
SQL-Abfragen schrieben. Sie sind in Bezug auf OOP viel näher am Betrieb. Wir haben Anweisungen zum Arbeiten mit Datenbankobjekten geschrieben, eine SQL-Abfrage erstellt und ausgeführt. Die neue Version der Datenbank ist fertig, gerollt - alles ist in Ordnung, alles funktioniert.
Entity Framework Core hat ein Minus - unter hoher Last werden
nicht optimale SQL-Abfragen erstellt , und der Datenbankabbau kann erheblich sein. Da wir jedoch keinen Hochlastservice haben, berechnen wir die Last nicht mit Hunderten von RPS. Wir sind diese Risiken eingegangen und haben das Problem an die Zukunft delegiert.
Vorteile
Entity Framework Core
funktioniert sofort und ist einfach zu entwickeln . Flyway lässt
sich nahtlos in vorhandene CI integrieren . Aber wir machen es bequem für Entwickler :)
Roll-up-Verfahren
Puppet sieht, dass sich die Version der Pakete ändert, unter denen die für die Migration verantwortliche ist. Zunächst wird ein Paket installiert, das Migrationsskripts und an die Datenbank gebundene Funktionen enthält. Danach wird die Anwendung, die mit der Datenbank arbeitet, neu gestartet. Als nächstes werden die restlichen Komponenten installiert. Die Reihenfolge, in der Pakete installiert und Anwendungen gestartet werden, ist im Puppet-Manifest beschrieben.
Anwendungen verwenden vertrauliche Daten wie Token und Kennwörter für die Datenbank. All dies wird mit dem Puppet-Master in die Konfiguration gezogen und dort verschlüsselt gespeichert.
TFS-Probleme
Nachdem wir entschieden und festgestellt hatten, dass wirklich alles für uns funktioniert, entschied ich mich zu sehen, was mit den Baugruppen in TFS als Ganzes für die Win-Entwicklungsabteilung für andere Projekte los war - schnell oder nicht, wir wollten / veröffentlichen und stellten erhebliche Probleme mit der Geschwindigkeit fest .
Eines der Hauptprojekte wird 12 bis 15 Minuten dauern - es ist eine lange Zeit, so kann man nicht leben. Eine schnelle Analyse ergab einen schrecklichen Rückgang der E / A, und dies gilt auch für Arrays.
Nachdem ich die Komponenten analysiert hatte, identifizierte ich drei Schwerpunkte. Das erste ist
Kaspersky Antivirus , das den Quellcode aller Windows Build-Agenten überprüft. Der zweite ist der
Windows- Indexer. Es wurde nicht getrennt, und auf Build-Agenten wurde in Echtzeit alles während des Bereitstellungsprozesses indiziert.
Der dritte ist die
Npm-Installation. Es stellte sich heraus, dass wir in den meisten Pipelines dieses spezielle Szenario verwendet haben. Warum ist er schlecht? Die Npm-Installationsprozedur beginnt, wenn der Abhängigkeitsbaum in
package-lock.json erstellt wird , in dem die Versionen der Pakete festgelegt sind, die zum Erstellen des Projekts verwendet werden. Das Minus ist, dass bei der Npm-Installation jedes Mal die neuesten Versionen von Paketen aus dem Internet abgerufen werden. Dies ist bei einem großen Projekt eine beträchtliche Zeit.
Entwickler experimentieren manchmal auf dem lokalen Computer, um die Funktionsweise eines einzelnen Teils oder Projekts als Ganzes zu testen. Manchmal stellte sich heraus, dass vor Ort alles cool war, aber zusammengebaut, ausgerollt - nichts funktionierte. Wir beginnen zu verstehen, wo das Problem liegt - ja, verschiedene Versionen von Abhängigkeitspaketen.
Lösung
- Quellen zu AV-Ausnahmen.
- Indizierung deaktivieren.
- Wechseln Sie zu npm ci .
Der Vorteil von npm ci besteht darin, dass wir
den Abhängigkeitsbaum einmal erfassen und dem Entwickler die Möglichkeit geben, eine
aktuelle Liste von Paketen bereitzustellen, mit denen er lokal so viel experimentieren kann, wie er möchte. Dies
spart Zeit für Entwickler, die Code schreiben.
Konfiguration
Nun ein wenig zur Repository-Konfiguration. In der Vergangenheit haben wir
Nexus zum Verwalten von Repositorys verwendet, einschließlich des
internen REPO . Alle Komponenten, die wir für interne Zwecke verwenden, z. B. die selbstgeschriebene Überwachung, werden an dieses interne Repository geliefert.

Wir verwenden auch
NuGet , da es besser zwischengespeichert wird als andere Paketmanager.
Ergebnis
Nachdem wir die Build-Agenten optimiert hatten, wurde die durchschnittliche Build-Zeit von 12 Minuten auf 7 Minuten reduziert.
, Windows, Linux , $10 000. , — .
Pläne
.
Docker- . TFS — , Pipeline, , , , Docker-.
package-lock.json . - , — Docker-. . , Kubernetes, .
Zusammenfassung
Windows, , . , Opensource- —
Linux- .
. , Open Source Linux .
GitHub .DevOps Conf — , . , ? , . DevOps Conf ++ 27 28 . . !