Der Direktor für den Betrieb des Portals Banki.ru, Andrei Nikolsky, sprach auf der Konferenz im letzten Jahr über Waisendienste: wie man einen Waise im System erkennt, welche Probleme Waisendienste haben, was man mit ihnen tun kann und wie man vorgeht, wenn nichts hilft.
Unter dem Cut ist die textliche Version des Berichts.

Guten Tag, Kollegen! Ich heiße Andrei und leite den Betrieb bei Banki.ru.
Wir haben große Dienste, das sind Monolithdienste, es gibt Dienste im klassischeren Sinne, und es gibt ganz kleine. In meiner Arbeitssprache sage ich, dass ein Dienst einfach und klein ist, dann ist er Mikro, und wenn er nicht ganz einfach und nicht klein ist, dann ist es einfach ein Dienst.
Vorteile von Dienstleistungen
Ich werde schnell die Vorteile der Dienstleistungen durchgehen.

Erstens — Skalierbarkeit. Sie können schnell etwas auf dem Dienst machen und in die Produktion starten. Sie haben Traffic bekommen, Sie haben den Dienst klonen können. Ihr kommt noch mehr Traffic, Sie klonen nochmal und damit leben Sie. Das ist ein guter Bonus, und im Grunde genommen, als wir angefangen haben, galt das als das Wichtigste, warum wir das alles überhaupt machen.

Zweitens, isolierte Entwicklung, wenn Sie mehrere Entwicklungsteams haben, mehrere verschiedene Entwickler in jedem Team, und jedes Team entwickelt seinen eigenen Dienst.
Bei den Teams gibt es ein Nuance. Entwickler sind unterschiedlich. Und es gibt zum Beispiel . Ich habe das zum ersten Mal bei Maxim Dorofeev gesehen. Manchmal gibt es Schneeflockenmenschen in einigen Teams, in anderen gibt es sie nicht. Das macht die verschiedenen Dienste, die im Unternehmen verwendet werden, etwas ungleichmäßig.

Sehen Sie sich das Bild an: Das ist ein guter Entwickler, er hat große Hände, er kann viel tun. Das Hauptproblem ist, woher diese Hände kommen.

Dienste ermöglichen es, verschiedene Programmiersprachen zu verwenden, die besser für unterschiedliche Aufgaben geeignet sind. Ein Dienst ist in Go, ein anderer in Erlang, einer in Ruby, etwas in PHP, etwas in Python. Insgesamt kann man sich sehr breit entfalten. Auch hier gibt es Nuancen.

Service-orientierte Architektur bedeutet in erster Linie DevOps. Das heißt, wenn Sie keine Automatisierung haben, keinen Bereitstellungsprozess, wenn Sie es händisch einstellen, können sich Ihre Konfigurationen von Instanz zu Instanz unterscheiden, und Sie müssen dort hinein gehen und etwas machen, dann sind Sie im Chaos.
Zum Beispiel haben Sie 20 Services und müssen manuell deployen, haben 20 Konsolen und drücken gleichzeitig wie ein Ninja auf „Enter“. Das ist nicht ideal.
Wenn Sie einen Service nach dem Testen haben (sofern es Tests gibt) und ihn noch nachbearbeiten müssen, damit er in der Produktion funktioniert, habe ich leider auch schlechte Nachrichten für Sie.
Wenn Sie auf spezifische Amazon-Services angewiesen sind und dabei in Russland arbeiten, hatten Sie vor zwei Monaten auch „Alles brennt, mir geht's gut, alles ist super“.

Wir nutzen Ansible zur Automatisierung des Deployments, Puppet für die Konvergenz, Bamboo zur Automatisierung des Deployments und Confluence, um all dies irgendwie zu dokumentieren.
Ich werde nicht ausführlich darauf eingehen, da der Vortrag eher über Interaktionspraktiken und nicht über technische Implementierungen handelt.

Wir hatten beispielsweise Probleme, dass Puppet auf dem Server mit Ruby 2 funktioniert, während eine bestimmte Anwendung für Ruby 1.8 geschrieben wurde, und zusammen funktionieren sie nicht. Da entsteht ein Problem. Wenn Sie mehrere Ruby-Versionen auf einem Rechner halten müssen, fangen die Probleme normalerweise an.
Zum Beispiel stellen wir jedem Entwickler eine Plattform zur Verfügung, auf der alles, was wir haben, verfügbar ist, alle Services, die entwickelt werden können, damit er eine isolierte Umgebung hat, in der er herumexperimentieren und aufbauen kann, wie er möchte.
Manchmal wird ein speziell kompilierter Paket benötigt, das etwas unterstützt. Das ist ziemlich strikt. Ich habe einen Vortrag gehört, in dem das Docker-Image 45 GB groß war. Unter Linux ist es natürlich einfacher, da ist alles kleiner, aber trotzdem wird der Platz knapp.
Es gibt auch widersprüchliche Abhängigkeiten, bei denen ein Teil des Projekts von einer Bibliothek einer Version abhängt, während ein anderer Teil von einer anderen Version abhängt, und diese Bibliotheken können überhaupt nicht zusammen installiert werden.

Wir haben Websites und Services auf PHP 5.6, wofür wir uns schämen, aber was sollen wir machen. Das ist unsere eine Plattform. Es gibt Websites und Services auf PHP 7, von denen es mehr gibt, und dafür schämen wir uns nicht. Und jeder Entwickler hat seine eigene Datenbank, in der er fröhlich arbeitet.
Wenn Sie in einem Unternehmen in einer einzigen Programmiersprache schreiben, klingt es normal, drei virtuelle Maschinen pro Entwickler zu haben. Wenn Sie verschiedene Programmiersprachen haben, wird die Situation schwieriger.

Sie haben Websites und Dienste dafür, dafür, dann noch eine Plattform für Go, eine Plattform für Ruby, noch ein Redis daneben. Am Ende verwandelt sich das alles in ein großes Feld für Support, und ständig kann etwas davon kaputtgehen.

Deshalb haben wir die Vorzüge der Programmiersprache durch die Nutzung unterschiedlicher Frameworks ersetzt, da Frameworks in PHP ziemlich unterschiedlich sind, sie haben unterschiedliche Möglichkeiten, unterschiedliche Communities, unterschiedlichen Support. Und man kann den Dienst so schreiben, dass man bereits etwas Fertiges dafür hat.
Jeder Dienst hat sein eigenes Team

Unser größter Vorteil, der sich über mehrere Jahre herauskristallisiert hat, ist, dass jeder Dienst sein eigenes Team hat. Das ist praktisch für ein großes Projekt, man kann Zeit bei der Dokumentation sparen, die Manager kennen ihr Projekt gut.
Support-Aufgaben können hervorragend einfließen. Zum Beispiel, wenn der Versicherungsdienst ausgefallen ist. Und sofort geht das Team, das sich um die Versicherung kümmert, ihn reparieren.
Neue Funktionen werden schnell umgesetzt, denn wenn Sie einen atomaren Dienst haben, kann man schnell etwas einbauen.
Und wenn Sie Ihren Dienst kaputt gemacht haben, was unvermeidlich passiert, haben Sie keine anderen Dienste beschädigt, und es kommen keine Entwickler mit Schlägen aus anderen Teams zu Ihnen, um zu sagen: "Oh-oh, das sollten Sie nicht tun."

Wie immer gibt es Nuancen. Wir haben stabile Teams, die Manager sind fest mit dem Team verbunden. Es gibt klare Dokumente, die Manager achten genau darauf. Jedes Team hat mit dem Manager mehrere Dienste und einen bestimmten Kompetenzbereich.
Wenn die Teams wechselhaft sind (was wir manchmal auch haben), gibt es eine gute Methode, die „Sternenkarte“ genannt wird.

Sie haben eine Liste von Diensten und Personen. Ein Sternchen bedeutet, dass die Person ein Experte in diesem Dienst ist, ein Buch bedeutet, dass die Person diesen Dienst studiert. Die Aufgabe der Person besteht darin, das Buch gegen ein Sternchen auszutauschen. Wenn gegenüber dem Dienst nichts geschrieben steht, beginnen Probleme, über die ich später berichten werde.
Wie entstehen Waise-Dienste?

Das erste Problem und die erste Möglichkeit, einen Service-Waisen in Ihrer Infrastruktur zu erhalten, sind Entlassungen von Mitarbeitern. Hat jemand schon einmal erlebt, dass Fristen aus dem Unternehmen gesetzt werden, bevor die Aufgaben bewertet wurden? Manchmal sind die Fristen so eng, dass es nicht genug Zeit für die Dokumentation gibt. „Der Service muss in die Produktion übergeben werden, den Rest schreiben wir später.“
Wenn das Team klein ist, gibt es manchmal nur einen Entwickler, der alles schreibt, während die anderen unterstützend tätig sind. „Ich habe die Hauptarchitektur geschrieben, du kannst die Schnittstellen entwerfen.“ Dann verlässt irgendwann ein Manager das Team. In diesem Zeitraum, wenn der Manager nicht mehr da ist und kein neuer gefunden wurde, entscheiden die Entwickler selbst, wohin sich der Service bewegt und was dort passiert. Und wie wir wissen (wir erinnern uns an ein paar Folien zurück), gibt es in einigen Teams individuelle „Schneeflocken“-Mitglieder, manchmal ist eine Schneeflocke der Teamleiter. Dann kündigt er, und wir haben einen Service-Waisen.

Dabei verschwinden die Aufgaben von Support und Business nicht, sie sammeln sich im Backlog. Wenn bei der Entwicklung des Dienstes architektonische Fehler aufgetreten sind, bleiben sie ebenfalls im Backlog. Der Service degeneriert langsam.
Wie erkennt man einen Waisen-Service?
Diese Liste beschreibt die Situation ganz gut. Wer hat bei sich in der Infrastruktur etwas erkannt?

Zu dokumentierten Workarounds: Es gibt einen Service, der insgesamt funktioniert, und es gibt ein Handbuch von zwei Seiten, wie man damit umgeht, aber wie es intern funktioniert, weiß niemand.
Oder zum Beispiel gibt es einen Linkverkürzer. Bei uns zum Beispiel gibt es derzeit drei Linkverkürzer für unterschiedliche Zwecke in verschiedenen Services. Das sind die Folgen davon.

Jetzt werde ich der Kapitän der Offensichtlichkeit. Was sollte man unternehmen? Erstens, den Service an einen anderen Manager oder ein anderes Team übergeben. Wenn Ihr Teamleiter noch nicht gekündigt hat, sollten Sie in dieses andere Team, wenn Sie merken, dass der Service wie ein Waisen-Service aussieht, jemanden einbeziehen, der wenigstens ein bisschen davon versteht.
Das Wichtigste: Sie müssen schriftlich festgelegte Übergabeverfahren haben. In unserem Fall überwache ich das normalerweise, weil ich sicherstellen muss, dass alles funktioniert. Die Manager möchten, dass es schnell erledigt wird, und was danach mit dem Service passiert, ist ihnen nicht mehr so wichtig.

Eine weitere Möglichkeit, ein Waisenkind zu schaffen, ist: „Wir lagern es aus, das geht schneller, und dann geben wir es ins Team.“ Es ist klar, dass jeder in der Gruppe irgendwelche Pläne hat, eine Warteliste. Oft denkt der Geschäftskunde, dass die Auslagerer es genauso machen wie die Technikabteilung im Unternehmen. Aber ihre Motivationen sind unterschiedlich. Bei der Auslagerung gibt es oft merkwürdige technische Lösungen und seltsame algorithmische Entscheidungen.

Wir hatten zum Beispiel einen Service, in dem Sphinx an verschiedenen unerwarteten Orten war. Ich werde später erzählen, was wir tun mussten.
Auslagerer haben oft selbstgeschriebene Frameworks. Das ist einfach nur nacktes PHP, das vom vorherigen Projekt kopiert wurde, wo man allerlei finden kann. Große Krücken in den Deployment-Skripten, wenn Sie mit einigen komplizierten Bash-Skripten ein paar Zeilen in einer Datei ändern müssen, während diese Deployment-Skripte von einem dritten Skript aufgerufen werden. Am Ende ändern Sie das Deployment-System, wählen etwas anderes aus, und zack, der Service funktioniert nicht mehr. Weil man dafür noch 8 Links zwischen verschiedenen Ordnern setzen musste. Oder es kommt vor, dass tausend Einträge funktionieren, aber hunderttausend schon nicht mehr.
Ich mache weiter mit dem Kapitän. Die Abnahme des Services aus der Auslagerung ist ein Verfahren, das zwingend erforderlich ist. Wer hat schon einmal erlebt, dass ein Service aus der Auslagerung ankommt und nirgendwo angenommen wird? Das ist nicht so populär wie ein Waisenkind-Service, aber dennoch.

Der Service muss überprüft, der Service muss reviewed werden, die Passwörter müssen geändert werden. Wir hatten einen Fall, als uns ein Service zugeschoben wurde, wo im Code stand: „if login == 'admin' && password == 'admin'...“. Und das in 2018? Wir sitzen da und denken nach.
Die Testung des Speichervolumens ist ebenfalls notwendig. Man muss schauen, was bei hunderttausend Einträgen passiert, bevor man diesen Service in die Produktion gibt.

Es sollte nicht peinlich sein, den Service zur Nachbesserung zu senden. Wenn Sie sagen: „Wir nehmen diesen Service nicht an, wir haben 20 Aufgaben, machen Sie diese, dann nehmen wir ihn an“, ist das normal. Man sollte kein schlechtes Gewissen haben, dass man den Manager in die Bredouille bringt oder dass das Unternehmen Geld ausgibt. Das Unternehmen wird später mehr ausgeben.
Wir hatten einen Fall, als wir beschlossen, ein Pilotprojekt in der Auslagerung zu machen.

Es wurde rechtzeitig übergeben, und das war das einzige Qualitätskriterium. Daher wurde ein weiteres Pilotprojekt gestartet, das schon längst kein richtiges Pilotprojekt mehr war. Diese Services wurden bereitgestellt, es wurden administrative Mittel eingesetzt und gesagt, hier ist Ihr Code, hier ist das Team, hier ist Ihr Manager. Die Services haben tatsächlich schon begonnen, Gewinne zu erwirtschaften. Dennoch sind sie nach wie vor Waisen, niemand versteht, wie sie funktionieren, und die Manager weichen ihren Aufgaben irgendwie aus.

Es gibt noch ein hervorragendes Konzept – die Guerilla-Entwicklung. Wenn eine Abteilung, normalerweise die Marketingabteilung, eine Hypothese überprüfen möchte, beauftragt sie den gesamten Service im Outsourcing. Daraufhin fließt der Traffic ein, sie schließen die Abrechnungen ab, unterschreiben Verträge mit dem Auftragnehmer, gehen in Betrieb und sagen: „Jungs, wir haben hier einen Service, der bereits Traffic hat, er bringt uns Geld, lasst ihn annehmen.“ Wir sagen: „Wow, wie kann das sein?"

Eine weitere Möglichkeit, einen Waisendienst zu erhalten: Wenn ein Team plötzlich überlastet ist, sagt das Management: „Lass uns den Service dieses Teams an ein anderes Team übergeben, das weniger belastet ist.“ Und dann geben wir ihn an ein drittes Team weiter und wechseln den Manager. Am Ende haben wir wieder einen Waisen.
Was ist das Problem mit Waisen?

Wer es nicht weiß, das ist das in Schweden gehobene Schlachtschiff Wasa, das dafür berühmt ist, dass es 5 Minuten nach dem Stapellauf gesunken ist. Und der König von Schweden hat übrigens niemanden dafür hingerichtet. Es wurde von zwei Generationen von Ingenieuren erbaut, die nicht wussten, wie man solche Schiffe baut. Ein logischer Effekt.
Das Schiff hätte übrigens viel schlimmer untergehen können, zum Beispiel, wenn der König schon bei einem Sturm darauf gefahren wäre. So sank es sofort, was im Agilen Ansatz gut ist – früh zu scheitern.
Wenn wir früh gescheitert sind, gibt es normalerweise keine Probleme. Zum Beispiel, wenn wir bei der Abnahme zur Nachbearbeitung geschickt wurden. Wenn wir aber bereits in der Produktion gescheitert sind, wenn Geld investiert wurde, könnten Probleme auftreten. Die Folgen, wie sie im Geschäft genannt werden.
Was ist gefährlich an Waisenservices:
- Der Service kann plötzlich ausfallen.
- Der Service ist lange nicht repariert oder wird gar nicht repariert.
- Sicherheitsprobleme.
- Probleme mit Nachbesserungen und Updates.
- Wenn ein wichtiger Service ausfällt, leidet der Ruf des Unternehmens.
Was tun mit Waisendiensten?

Ich wiederhole, was zu tun ist. Erstens muss es eine Dokumentation geben. 7 Jahre bei Banki.ru haben mich gelehrt, dass Tester den Entwicklern nicht einfach glauben sollten und der Betrieb nicht jedem trauen sollte. Man muss überprüfen.

Zweitens sollten Interaktionsschemata geschrieben werden, denn es kann vorkommen, dass Services, die nicht gut angenommen werden, Abhängigkeiten enthalten, von denen niemand erzählt hat. Zum Beispiel haben die Entwickler einen Service an ihren Schlüssel für irgendwelche Yandex.Karten oder Dadata gebunden. Ihr kostenloses Kontingent ist erschöpft, alles ist kaputt gegangen und Sie wissen nicht, was überhaupt passiert ist. All diese Stolpersteine sollten dokumentiert werden: Im Service wird Dadata, Sms oder etwas anderes verwendet.

Drittens, die Arbeit mit technischem Schulden. Wenn Sie irgendwelche Notlösungen einführen oder einen Service übernehmen und sagen, dass etwas gemacht werden muss, müssen Sie darauf achten, dass das auch erledigt wird. Denn sonst kann es sein, dass ein kleines Loch nicht so klein ist und Sie da hineinfallen.
Mit architektonischen Aufgaben hatten wir eine Geschichte über Sphinx. In einem der Services wurde Sphinx verwendet, um Listen einzugeben. Einfach eine Liste mit Paginierung, aber sie wurde jede Nacht neu indiziert. Sie wurde aus zwei Indizes erstellt: Ein Index wurde jede Nacht groß indiziert und es gab einen kleinen Index, der daran angebunden war. Jeden Tag, mit einer Wahrscheinlichkeit von 50 %, gab es entweder einen Ausfall oder nicht, bei der Auslieferung fiel der Index aus und die Nachrichten auf unserer Startseite aktualisierten sich nicht mehr. Anfangs dauerte es 5 Minuten, bis der Index neu indiziert wurde, dann wuchs der Index und irgendwann begann es, 40 Minuten zu dauern. Als wir das herausnahmen, atmeten wir erleichtert auf, denn es war klar, dass es nicht lange dauern würde, bis unser Index während eines ganzen Arbeitstags neu indiziert werden müsste. Das wäre ein Misserfolg für unser Portal, acht Stunden keine Nachrichten – das würde das Geschäft lahmlegen.
Arbeitsplan für den Waisendienst

In der Tat ist es sehr schwierig, das zu tun, weil DevOps mit Kommunikation zu tun hat. Man möchte gute Beziehungen zu seinen Kollegen haben, und wenn man Kollegen und Manager mit Vorschriften überhäuft, können sie widersprüchliche Gefühle gegenüber denjenigen empfinden, die so handeln.
Neben all diesen Punkten gibt es noch eine wichtige Sache: Für jeden spezifischen Service und für jeden bestimmten Schritt im Deployment-Prozess müssen konkrete Personen verantwortlich sein. Wenn es keine solchen Personen gibt und man andere Leute hinzuziehen muss, um sich mit dem Ganzen auseinanderzusetzen, wird es schwierig.

Wenn all das nicht geholfen hat und der Service weiterhin ein Waise ist, den niemand haben möchte, die Dokumentation nicht geschrieben wird und das Team, das für diesen Service zusammengestellt wurde, sich weigert, etwas zu tun, gibt es einen einfachen Weg – alles neu machen.
Das heißt, Sie nehmen die Anforderungen an den Service neu auf und schreiben einen neuen, besseren Service auf einer besseren Plattform, ohne seltsame technologische Lösungen. Und Sie migrieren im laufenden Betrieb.

Wir hatten die Situation, dass wir einen Service auf Yii 1 übernommen haben und festgestellt haben, dass wir ihn nicht weiterentwickeln können, weil uns die Entwickler ausgegangen sind, die gut mit Yii 1 arbeiten können. Alle Entwickler beherrschen gut Symfony 3. Was tun? Wir haben Zeit, ein Team und einen Manager eingeplant, das Projekt neu geschrieben und schrittweise den Traffic auf dieses Projekt umgeleitet.
Danach kann der alte Service gelöscht werden. Das ist mein liebstes Verfahren, wenn aus dem Konfigurationsmanagement-System ein Service entfernt werden muss, und man dann sicherstellen muss, dass alle Maschinen im Produktionsbetrieb abgeschaltet sind, damit die Entwickler keine Spuren hinterlassen. Das Repository bleibt im Git.
Das ist alles, was ich erzählen wollte. Ich bin bereit, darüber zu diskutieren; das Thema ist umstritten, viele haben sich damit beschäftigt.
In den Folien wurde erwähnt, dass Sie die Sprachen vereinheitlicht haben. Ein Beispiel war das Resizing von Bildern. Ist es wirklich notwendig, auf eine einzige Sprache festgelegt zu sein? Denn das Resizing von Bildern in PHP könnte man tatsächlich auch in Golang machen.
Tatsächlich ist das nicht unbedingt erforderlich, wie bei allen Praktiken. In einigen Fällen kann es sogar unerwünscht sein. Man muss jedoch verstehen, dass wenn in Ihrer Firma 50 Personen im Technikteam sind, von denen 45 PHP-Entwickler sind, 3 DevOps, die in Python, Ansible, Puppet und so weiter versiert sind, und nur einer von ihnen einen Dienst zum Resizen von Bildern in Go schreibt, dann geht mit ihm die Expertise verloren, wenn er geht. Zudem müssen Sie einen spezifischen Entwickler auf dem Markt suchen, der diese Sprache kennt, insbesondere wenn sie selten ist. Das bedeutet aus organisatorischer Sicht, dass es problematisch ist. Aus der Sicht eines DevOps müssen Sie nicht einfach irgendein vorhandenes Set von Playbooks klonen, die Sie zum Bereitstellen von Diensten verwenden, sondern Sie müssen sie von Grund auf neu schreiben.
Wir bauen gerade einen Dienst auf Node.js, und das wird der Raum für jeden Entwickler mit einer eigenen Sprache sein. Aber wir haben darüber nachgedacht, dass sich der Aufwand lohnt. Es ist also eine Frage des Nachdenkens.
Wie überwachen Sie Ihre Dienste? Wie sammeln und verfolgen Sie die Protokolle?
Wir sammeln die Protokolle in Elasticsearch und legen sie in Kibana ab, und je nachdem, ob es sich um Produktions- oder Testumgebungen handelt, verwenden wir unterschiedliche Sammler. An einigen Orten Lumberjack, an anderen noch etwas, ich erinnere mich nicht mehr. Und es gibt einige Stellen in bestimmten Diensten, wo wir Telegraf installieren und noch woanders separat hin schicken.
Wie lebt man mit Puppet und Ansible in einer Umgebung?
Tatsächlich haben wir jetzt zwei Umgebungen, eine mit Puppet, die andere mit Ansible. Wir arbeiten daran, sie zu hybridisieren. Ansible ist eine gute Umgebung für die Erstkonfiguration, Puppet ist eher ungeeignet dafür, weil es direkte manuelle Arbeit mit dem System erfordert, und Puppet gewährleistet die Konvergenz der Konfiguration. Das bedeutet, dass das System sich selbst in einem aktuellen Zustand hält, während Sie für eine Ansible-gestützte Maschine ständig Playbooks mit einer gewissen Regelmäßigkeit ausführen müssen, damit sie aktuell bleibt. Das ist der Unterschied.
Wie stellen Sie die Kompatibilität sicher? Haben Sie Konfigurationen sowohl in Ansible als auch in Puppet?
Das ist unser großes Problem; wir müssen die Kompatibilität selbst gewährleisten und überlegen, wie wir von all dem irgendwohin wechseln können. Es scheint, dass Puppet Pakete installiert und dort einige Links bereithält, während Ansible beispielsweise den Code bereitstellt und die aktuellen Anwendungs-Konfigurationen anpasst.
In der Präsentation ging es um verschiedene Ruby-Versionen. Was ist die Lösung?
Wir sind damit an einem Punkt konfrontiert worden, und wir müssen das ständig im Hinterkopf behalten. Wir haben einfach den Teil deaktiviert, der auf dem Ruby lief, das mit den Anwendungen nicht kompatibel war, und ihn getrennt gehalten.
In diesem Jahr findet die Konferenz am 7. Dezember im „Technopolis“ statt. Wir nehmen bis zum 11. November Anmeldungen für Vorträge entgegen. uns, wenn Sie sprechen möchten.
Die Registrierung für Teilnehmer ist offen, schließen Sie sich uns an!
Quelle: habr.com
