Docker + Laravel + RoadRunner = ❤

Bild


Dieser Beitrag wurde auf Anfrage von Mitarbeitern verfasst, die regelmäßig nach "Ausführen der Anwendung" Illuminate / Symfony / MyOwn Psr7 " im Docker fragen . Ich habe keine Lust, einen Link zu einem zuvor geschriebenen Beitrag zu geben , da sich meine Ansichten zur Lösung des Problems erheblich geändert haben.


Alles, was unten geschrieben wird, ist eine subjektive Erfahrung, die (wie immer) nicht behauptet, als die einzig richtige Entscheidung angesehen zu werden, aber einige Ansätze und Lösungen mögen Ihnen interessant und nützlich erscheinen.


Ich werde Laravel auch als Anwendung verwenden, da es mir am vertrautesten und weit verbreitet ist. Die Anpassung an andere PSR-7- basierte Frameworks / Komponenten ist möglich, aber in dieser Geschichte geht es nicht darum.

Fehlerbehandlung


Ich möchte mit dem beginnen, was sich im Kontext des vorherigen Artikels als "nicht die besten Praktiken" herausstellte:


  • Die Notwendigkeit, die Struktur von Dateien im Repository zu ändern
  • FPM verwenden. Wenn wir Leistung aus unseren Anwendungen herausholen möchten, besteht eine der besten Lösungen bereits in der Phase der Technologieauswahl darin, sie zugunsten von etwas Schnellerem aufzugeben und an die Tatsache anzupassen, dass Speicher verloren gehen kann. RoadRunner von lachezis ist hier wie nie zuvor
  • Ein separates Bild mit Quelle und Assets. Trotz der Tatsache, dass wir mit diesem Ansatz dasselbe Image wiederverwenden können, um ein komplexeres Routing eingehender Anforderungen zu erstellen (nginx an der Vorderseite, um statische Daten zurückzugeben; Anforderungen für die Dynamik werden von einem anderen Container bereitgestellt, in den das Volume mit denselben Quellen geworfen wird - z bessere Skalierung) - Dieses Schema hat sich im Produktbetrieb als ziemlich kompliziert erwiesen. Darüber hinaus rendert RR selbst die Statik perfekt. Wenn viele Statiken vorhanden sind (oder die Ressource benutzergenerierte Inhalte laden und anzeigen kann) , nehmen wir sie an CDN weiter (das S3 + CloudFront + CloudFlare-Bundle funktioniert einwandfrei) und vergessen dieses Problem im Prinzip
  • Komplexes CI. Dies wurde zu einem echten Problem, als die Phase des aktiven "Fleischbaus" in den Phasen der Montage und der automatischen Prüfung begann. Als Typ, der dieses CI zuvor nicht unterstützt hat, wird es sehr schwierig, Änderungen daran vorzunehmen, ohne Angst zu haben, irgendetwas zu beschädigen.

Da ich nun weiß, welche Probleme behoben werden müssen, und weiß, wie dies zu tun ist, schlage ich vor, mit ihrer Lösung fortzufahren. Der Satz von "Entwicklertools" hat sich nicht geändert - es ist das gleiche docker-ce , docker-compose und das leistungsstarke Makefile .


Als Ergebnis erhalten wir:


  • Ein eigenständiger Container mit einer Anwendung, ohne dass zusätzliches Volume bereitgestellt werden muss
  • Ein Beispiel für die Verwendung von Git-Hooks - Wir werden die erforderlichen Abhängigkeiten nach dem automatischen git pull von git pull und das Drücken des Codes verbieten, wenn die Tests fehlschlagen (Hooks werden natürlich unter dem Git gespeichert).
  • RoadRunner verarbeitet HTTP-Anfragen
  • Entwickler können weiterhin dd(..) und dump(..) zum Debuggen ausführen, während in ihrem Browser nichts abstürzt
  • Tests können direkt von der PHPStorm-IDE ausgeführt werden, während sie im Container mit der Anwendung ausgeführt werden
  • CI sammelt Bilder für uns, wenn ein neues Anwendungsversions-Tag veröffentlicht wird
  • Nehmen wir die strenge Regel, die Dateien CHANGELOG.md und CHANGELOG.md pflegen

Visuelle Einführung eines neuen Ansatzes


Für eine visuelle Demonstration werde ich den gesamten Prozess in mehrere Phasen unterteilen, deren Änderungen als separate MRs vorgenommen werden (nach dem Zusammenschluss bleiben alle Brunchs an ihren Plätzen; Verweise auf MR in den Überschriften der „Schritte“). Der Ausgangspunkt ist das Laravel-Skelett einer Anwendung, die mit dem composer create-project laravel/laravel :


 $ docker run \ --rm -i \ -v "$(pwd):/src" \ -u "$(id -u):$(id -g)" \ composer composer create-project --prefer-dist laravel/laravel \ /src/laravel-in-docker-with-rr "5.8.*" 

Schritt 1 - Andocken + RR


Der erste Schritt besteht darin, der Anwendung das Ausführen im Container beizubringen. Dazu benötigen wir eine docker-compose.yml Dockerfile docker-compose.yml für die Beschreibung des docker-compose.yml und Verknüpfens von Containern und ein Makefile , um einen bereits vereinfachten Prozess auf ein oder zwei Befehle zu reduzieren.


Dockerfile


Das Grundbild, das ich benutze, ist php:XXX-alpine am einfachsten und enthält alles, was Sie zum Ausführen benötigen. Darüber hinaus werden alle nachfolgenden Aktualisierungen des Interpreters auf eine einfache Änderung des Werts in dieser Zeile reduziert (die Aktualisierung von PHP ist jetzt einfacher als je zuvor).


Composer und die RoadRunner-Binärdatei werden mehrstufig und COPY --from=... - an den Container COPY --from=... Dies ist sehr praktisch, und alle mit den Versionen verknüpften Werte sind nicht "verstreut", sondern befinden sich am Anfang der Datei. Dies funktioniert schnell und ohne Abhängigkeiten von curl / git clone / make build . 512k / Roadrunner-Bilder werden von mir unterstützt, wenn Sie möchten - Sie können die Binärdatei selbst zusammenstellen.


Eine interessante Geschichte ereignete sich mit der Umgebungsvariablen PS1 (die für die Eingabeaufforderung in der Shell verantwortlich ist) - es stellt sich heraus, dass Sie Emoji darin verwenden können und alles lokal funktioniert. Wenn Sie jedoch versuchen, das Bild mit einer Variablen zu starten, die Emoji beispielsweise in Rancher enthält, stürzt es ab (in Schwarm alles) funktioniert ohne Probleme).

In Dockerfile ich mit der Generierung eines selbstsignierten SSL-Zertifikats, um es für eingehende HTTPS-Anforderungen zu verwenden. Natürlich - nichts hindert die Verwendung eines "normalen" Zertifikats.


Ich möchte auch sagen über:


 COPY ./composer.* /app/ RUN set -xe \ && composer install --no-interaction --no-ansi --no-suggest --prefer-dist \ --no-autoloader --no-scripts \ && composer install --no-dev --no-interaction --no-ansi --no-suggest \ --prefer-dist --no-autoloader --no-scripts 

Hier ist die Bedeutung wie folgt: Die Dateien composer.lock und composer.json werden in einer separaten Ebene an das Image übergeben, wonach alle darin beschriebenen Abhängigkeiten installiert werden. Dies geschieht so, dass bei nachfolgenden Builds des Images mit --cache-from die composer install nicht ausgeführt wird, wenn sich die Zusammensetzung und Versionen der installierten Abhängigkeiten nicht geändert haben. Dadurch wird Build-Zeit und Datenverkehr gespart (danke für die Idee) jetexe ).


composer install wird zweimal ausgeführt (das zweite Mal mit --no-dev ), um den Cache der Dev-Abhängigkeiten "aufzuwärmen". Wenn wir also alle Abhängigkeiten auf das CI setzen, um die Tests auszuführen, werden sie aus dem Composer-Cache abgelegt, der sich bereits im Image befindet, nicht aus fernen Galaxien gestreckt.


Mit der letzten RUN Anweisung zeigen wir die Versionen der installierten Software und die Zusammensetzung der PHP-Module sowohl für den Verlauf in den Build-Protokollen als auch um sicherzustellen, dass "es mindestens vorhanden ist und irgendwie startet".


Ich verwende auch meinen Entrypoint, da ich vor dem Starten der Anwendung irgendwo im Cluster wirklich die Verfügbarkeit abhängiger Dienste überprüfen möchte - DB, Redis, Rabbit und andere.


Roadrunner


Um RoadRunner in eine Laravel-Anwendung zu integrieren, wurde ein Paket geschrieben, das die gesamte Integration auf einige Befehle in der Shell reduziert (durch Ausführen von docker-compose run app sh ):


 $ composer require avto-dev/roadrunner-laravel "^2.0" $ ./artisan vendor:publish --provider='AvtoDev\RoadRunnerLaravel\ServiceProvider' --tag=rr-config 

Fügen APP_FORCE_HTTPS=true der Datei ./docker/docker-compose.env APP_FORCE_HTTPS=true ./docker/docker-compose.env und geben Sie den Pfad zum SSL-Zertifikat im Container in den .rr*.yaml .


Um dump(..) und dd(..) und alles hat funktioniert, gibt es ein anderes Paket - avto-dev/stacked-dumper-laravel . Sie müssen diesen Helfern lediglich einen Pefix hinzufügen, nämlich \dev\dd(..) bzw. \dev\dump(..) . Ohne dies werden Sie einen Fehler des Formulars feststellen:
 worker error: invalid data found in the buffer (possible echo) 

Machen Sie nach all den Manipulationen docker-compose up -d und Voila:


Screenshot


Die Mitarbeiter von PostgeSQL-Datenbank, Redis und RoadRunner wurden erfolgreich in Containern ausgeführt.


Schritt 2 - Makefile und Tests


Wie ich bereits geschrieben habe, ist ein Makefile ein sehr unterschätzter Gegenstand. Abhängige Ziele, Ihr eigener syntaktischer Zucker, 99% ige Chance, dass er bereits auf der Linux / Mac-Entwicklungsmaschine steht, Autocomplete „out of the box“ - eine kleine Liste seiner Vorteile.


Wenn wir es zu unserem Projekt hinzufügen und make ohne Parameter ausführen, können wir Folgendes beobachten:


Screenshot


Um Unit-Tests auszuführen, können wir entweder einen make test oder eine Shell im Container mit der Anwendung ( make shell ) composer phpunit und composer phpunit . Um den Abdeckungsbericht zu erhalten, erstellen Sie einfach make test-cover . Bevor Sie die Tests ausführen, wird xdebug mit seinen Abhängigkeiten an den Container gesendet und die Tests werden gestartet (da dieses Verfahren nicht häufig und nicht von CI ausgeführt wird - scheint diese Lösung besser zu sein, als ein separates Image mit allen zu führen Dev-Lotionen).


Git Hooks


In unserem Fall erfüllen Hooks zwei wichtige Aufgaben: Sie dürfen keinen Code in den Ursprung verschieben, dessen Tests nicht erfolgreich sind. und setzen Sie automatisch alle erforderlichen Abhängigkeiten, wenn sich herausstellt, dass sich composer.lock geändert hat, wenn Sie die Änderungen auf Ihren Computer übertragen. Im Makefile gibt es dafür ein separates Ziel:


 cwd = $(shell pwd) git-hooks: ## Install (reinstall) git hooks (required after repository cloning) -rm -f "$(cwd)/.git/hooks/pre-push" "$(cwd)/.git/hooks/pre-commit" "$(cwd)/.git/hooks/post-merge" ln -s "$(cwd)/.gitlab/git-hooks/pre-push.sh" "$(cwd)/.git/hooks/pre-push" ln -s "$(cwd)/.gitlab/git-hooks/pre-commit.sh" "$(cwd)/.git/hooks/pre-commit" ln -s "$(cwd)/.gitlab/git-hooks/post-merge.sh" "$(cwd)/.git/hooks/post-merge" 

.gitlab/git-hooks einfach die vorhandenen Hooks entfernt und diese im .gitlab/git-hooks an ihre Stelle gesetzt. Ihre Quelle kann unter diesem Link eingesehen werden.


Ausführen von Tests mit PhpStorm


Trotz der Tatsache, dass es recht einfach und bequem ist - ich habe es lange Zeit verwendet ./vendor/bin/phpunit --group=foo anstatt nur den Hotkey direkt zu drücken, während ich einen Test oder Code dazu schreibe.


Klicken Sie auf File > Settings > Languages & Frameworks > PHP > CLI interpreter > [...] > [+] > From Docker, Vargant, VM, Remote . Wählen Sie Docker Compose und App Service Name.


Screenshot


Der zweite Schritt besteht darin, phpunit anzuweisen, den Interpreter aus dem Container zu verwenden: File > Settings > Test frameworks > [+] > PHPUnit by remote interpreter und wählen Sie den zuvor erstellten Remote-Interpreter aus. /app/vendor/autoload.php im /app/vendor/autoload.php Path to script /app/vendor/autoload.php , und in /app/vendor/autoload.php geben Sie das Stammverzeichnis des Projekts an, das in /app gemountet ist.


Screenshot


Und jetzt können wir Tests direkt von der IDE aus mit dem Interpreter im Image mit der Anwendung ausführen und dabei (standardmäßig Linux) Strg + Umschalt + F10 drücken.


Schritt 3 - Automatisierung


Wir müssen nur noch den Prozess der Ausführung von Tests und der Zusammenstellung des Images automatisieren. Erstellen .gitlab-ci.yml im Stammverzeichnis der Anwendung und füllen Sie sie mit den folgenden Inhalten . Die Hauptidee dieser Konfiguration ist es, so einfach wie möglich zu sein, aber nicht gleichzeitig die Funktionalität zu verlieren.


Das Bild wird bei jedem Brunch und bei jedem Commit zusammengestellt. Mit --cache-from Zusammenstellen eines Bildes beim erneuten --cache-from sehr schnell. Die Notwendigkeit eines Umbaus ist auf die Tatsache zurückzuführen, dass wir bei jedem Brunch ein Bild mit den Änderungen haben, die im Rahmen dieses Brunchs vorgenommen wurden, und daher hindert uns nichts daran, es auf swarm / k8s / etc auszurollen, um sicherzustellen, dass "live" dass alles funktioniert und funktioniert, wie es sollte, noch bevor es mit dem master .


Nach der Montage führen wir Komponententests durch und überprüfen den Start der Anwendung im Container. Dabei senden wir Curl-Th-Anforderungen an den Health-Check-Endpunkt (diese Aktion ist optional, aber dieser Test hat mir mehrmals sehr geholfen).


Für die "Veröffentlichung der Version" - veröffentlichen Sie einfach ein Tag des Formulars vX.XX (wenn Sie sich immer noch an die semantische Versionierung halten - es wird sehr cool sein) - sammelt CI das Image, führt die Tests aus und führt die Aktionen aus, die Sie bei der deploy to somewhere .


Vergessen Sie in den Projekteinstellungen nicht (falls möglich), die Möglichkeit zum Veröffentlichen von Tags nur auf Personen zu beschränken, die "Releases freigeben" dürfen.

CHANGELOG.md und ENVIRONMENT.md


Vor der Annahme des einen oder anderen MR muss der Inspektor unbedingt die Dateien CHANGELOG.md und CHANGELOG.md ENVIRONMENT.md überprüfen. Wenn der erste mehr und weniger klar ist , dann werde ich den relativen zweiten eine Erklärung geben. Diese Datei wird verwendet, um alle Umgebungsvariablen zu beschreiben, auf die der Container mit der Anwendung reagiert. Das heißt, Wenn der Entwickler die Unterstützung für die eine oder andere Umgebungsvariable hinzufügt oder entfernt, muss dies in dieser Datei berücksichtigt werden. Und in dem Moment, in dem die Frage „Wir müssen dies und das dringend neu definieren“ auftaucht - niemand beginnt verzweifelt, sich mit der Dokumentation oder den Quellcodes zu befassen -, sondern in einer einzigen Datei nachschaut. Sehr bequem.


Fazit


In diesem Artikel haben wir den ziemlich schmerzlosen Prozess der Übertragung der Anwendungsentwicklung und des Starts in die Docker-Umgebung untersucht, RoadRunner integriert und mithilfe eines einfachen CI-Skripts das Zusammenstellen und Testen des Images mit unserer Anwendung automatisiert.


Nach dem Klonen des Repositorys müssen die Entwickler make git-hooks && make install && make up erstellen und nützlichen Code schreiben. Genossen * ops-am - Nehmen Sie das Bild mit dem gewünschten Tag und rollen Sie es auf ihren Clustern.


Natürlich ist dieses Schema auch vereinfacht, und bei den "Kampf" -Projekten schließe ich viel mehr ab, aber wenn der im Artikel beschriebene Ansatz jemandem hilft, weiß ich, dass ich Zeit gut verbracht habe.

Source: https://habr.com/ru/post/de461687/


All Articles