Docker + Laravel = ❀

Laravel-in-Docker


In diesem Artikel werde ich ĂŒber meine Erfahrungen beim „Verpacken“ einer Laravel-Anwendung in einen Docker-Container sprechen, damit Frontend- und Backend-Entwickler lokal damit arbeiten können und das Starten in der Produktion so einfach wie möglich war. Außerdem fĂŒhrt CI automatisch statische Code-Analysatoren, phpunit Tests und Build-Images aus.


"Und was ist eigentlich KomplexitĂ€t?" - Sie können sagen, und Sie werden teilweise recht haben. Tatsache ist, dass sich in der russisch- und englischsprachigen Gemeinschaft ziemlich viele Diskussionen mit diesem Thema befassen, und ich wĂŒrde fast alle untersuchten Themen bedingt in die folgenden Kategorien einteilen:


  • "Ich verwende Docker fĂŒr die lokale Entwicklung. Ich habe Laradock eingesetzt und kenne die Probleme nicht." Cool, aber was ist mit dem Automatisierungs- und Produktionsstart?
  • "Ich sammle einen Container (Monolith) basierend auf fedora:latest (~ 230 MB), lege alle Dienste (Nginx, DB, Cache usw.) darin ab und fĂŒhre alles im Supervisor aus." Auch ausgezeichnet, leicht zu starten, aber was ist mit der Ideologie von "einem Container - einem Prozess"? Was ist mit Balancing und Prozessmanagement? Wie groß ist das Bild?
  • "Hier sind einige KonfigurationsstĂŒcke, die mit AuszĂŒgen aus Sh-Skripten gewĂŒrzt, magische Env-Werte hinzugefĂŒgt und verwendet werden." Danke, aber was ist mit mindestens einem lebenden Beispiel, das ich gabeln und voll spielen könnte?

Alles, was Sie unten lesen, ist eine subjektive Erfahrung, die nicht vorgibt, die ultimative Wahrheit zu sein. Wenn Sie ErgÀnzungen oder Hinweise auf Ungenauigkeiten haben - willkommen zu Kommentaren.


FĂŒr Ungeduldige - ein Link zum Repository , Klon, mit dem Sie die Laravel-Anwendung mit einem Befehl starten können. Es ist auch nicht schwierig, es auf demselben Rancher auszufĂŒhren, die Container korrekt zu "verknĂŒpfen" oder die Lebensmittelversion docker-compose.yml als Ausgangspunkt zu verwenden.

Theoretischer Teil


Welche Werkzeuge werden wir in unserer Arbeit verwenden und worauf werden wir uns konzentrieren? ZunĂ€chst mĂŒssen wir auf dem Host installiert werden:


  • docker - 18.06.1-ce Zeitpunkt des Schreibens habe ich Version 18.06.1-ce
  • docker-compose - Es bewĂ€ltigt das VerknĂŒpfen von Containern und das Speichern der erforderlichen Umgebungswerte. Version 1.22.0
  • make - Sie werden vielleicht ĂŒberrascht sein, aber es passt perfekt in den Kontext der Arbeit mit Docker

Sie können docker auf debian Systemen mit dem Befehl curl -fsSL get.docker.com | sudo sh curl -fsSL get.docker.com | sudo sh , aber docker-compose besser mit pip zu installieren, da die neuesten Versionen in den Repositorys gespeichert sind ( apt Regel weit zurĂŒck).

Damit ist die Liste der AbhÀngigkeiten vervollstÀndigt. Was Sie verwenden, um mit dem Quellcode zu arbeiten - phpstorm , netbeans oder the dead vim - liegt ganz bei Ihnen.


Als nÀchstes folgt eine spontane QualitÀtssicherung im Zusammenhang mit (ich habe keine Angst vor diesem Wort) Bilddesign:


  • F: Grundbild - welches ist besser zu wĂ€hlen?


  • A: Derjenige, der "dĂŒnner" ist, ohne Schnickschnack. Auf der Grundlage von alpine (~ 5 Mb) können Sie sammeln, was Ihr Herz begehrt, aber höchstwahrscheinlich mĂŒssen Sie mit der Zusammenstellung von Diensten aus der Quelle spielen. Als Alternative - jessie-slim (~ 30 Mb) . Oder verwenden Sie diejenige, die in Ihren Projekten am hĂ€ufigsten verwendet wird.


  • F: Warum ist das Bildgewicht wichtig?


  • A: Verringerung des Verkehrsaufkommens, Verringerung der Wahrscheinlichkeit eines Fehlers beim Herunterladen (weniger Daten - weniger Wahrscheinlichkeit), Verringerung des verbrauchten Ortes. Die Regel "Schwerkraft ist zuverlĂ€ssig" (© "Snatch") funktioniert hier nicht sehr gut.


  • F: Aber mein Freund %friend_name% sagt, dass ein "monolithisches" Bild mit all-all-AbhĂ€ngigkeiten der beste Weg ist.


  • A: Lass uns einfach zĂ€hlen. Die Anwendung hat 3 AbhĂ€ngigkeiten - PG, Redis, PHP. Und Sie wollten testen, wie es sich in Bundles verschiedener Versionen dieser AbhĂ€ngigkeiten verhĂ€lt. PG - Versionen 9.6 und 10, Redis - 3.2 und 4.0, PHP - 7.0 und 7.2. Falls jede Sucht ein eigenes Bild ist - Sie benötigen 6 davon, die Sie nicht einmal sammeln mĂŒssen -, ist alles fertig und liegt auf hub.docker.com . Wenn aus ideologischen GrĂŒnden alle AbhĂ€ngigkeiten in einem Container "verpackt" sind, mĂŒssen Sie ihn mit Stiften wieder zusammensetzen ... 8 Mal? opcache Sie nun die Bedingung hinzu, dass Sie noch mit opcache spielen opcache . Im Falle einer Zerlegung ist dies einfach eine Änderung der Tags der verwendeten Bilder. Ein Monolith ist einfacher zu betreiben und zu warten, aber es ist der Weg ins Nirgendwo.


  • F: Warum ist der Supervisor im Container böse?


  • A: Weil PID 1 . Wenn Sie nicht viele Probleme mit Zombie-Prozessen haben möchten und bei Bedarf flexibel „KapazitĂ€t hinzufĂŒgen“ können, versuchen Sie, einen Prozess pro Container auszufĂŒhren. Eine besondere Ausnahme ist nginx mit seinen Arbeitern und php-fpm , die die FĂ€higkeit haben, Prozesse zu produzieren, dies aber php-fpm mĂŒssen (außerdem sind sie nicht schlecht darin, auf SIGTERM zu reagieren und ihre Arbeiter ganz richtig zu "töten"). Wenn Sie alle DĂ€monen als Supervisor starten, sind Sie mit ziemlicher Sicherheit zu Problemen verurteilt. In einigen FĂ€llen ist es zwar schwierig, darauf zu verzichten, aber dies sind bereits Ausnahmen.



Nachdem wir uns fĂŒr die wichtigsten AnsĂ€tze entschieden haben, fahren wir mit unserer Anwendung fort. Es muss in der Lage sein:


  • web|api - Geben Sie mit nginx statisch an und generieren Sie mit fpm dynamischen Inhalt
  • scheduler - FĂŒhren Sie den nativen Taskplaner aus
  • queue - Verarbeitet Jobs aus Warteschlangen

Ein Basissatz, der bei Bedarf erweitert werden kann. Kommen wir nun zu den Bildern, die wir sammeln mĂŒssen, damit unsere Anwendung „abhebt“ (ihre Codenamen sind in Klammern angegeben):


  • PHP + PHP-FPM ( App ) - die Umgebung, in der unser Code ausgefĂŒhrt wird. Da die Versionen von PHP und FPM fĂŒr uns gleich sind, sammeln wir sie in einem Bild. So ist die Verwaltung mit Konfigurationen einfacher und die Zusammensetzung der Pakete ist identisch. NatĂŒrlich - FPM- und Anwendungsprozesse werden in verschiedenen Containern ausgefĂŒhrt
  • nginx ( nginx ) - das wĂŒrde sich nicht um die Lieferung von Konfigurationen und optionalen Modulen fĂŒr nginx kĂŒmmern - wir werden ein separates Bild damit sammeln. Da es sich um einen separaten Dienst handelt, verfĂŒgt er ĂŒber eine eigene Docker-Datei und einen eigenen Kontext
  • Quellen der Anwendung ( Quellen ) - Die Quelle wird unter Verwendung eines separaten Abbilds geliefert, wobei das volume mit ihnen in einem Container mit der App bereitgestellt wird. Das Basis-Image ist alpine , im Inneren befinden sich nur Quellen mit installierten AbhĂ€ngigkeiten, die mithilfe von Webpack-Assets gesammelt wurden (Build-Artefakte).

Andere Entwicklungsdienste werden in Containern gestartet und von hub.docker.com . In der Produktion hingegen werden sie auf separaten Servern in Clustern ausgefĂŒhrt. Wir mĂŒssen der Anwendung nur noch mitteilen (ĂŒber die Umgebung), an welchen Adressen / Ports und mit welchen Details sie angeklopft werden mĂŒssen. Noch cooler ist es, Service-Discovery fĂŒr diesen Zweck zu verwenden, aber nicht um diese Zeit.


Nachdem ich mich fĂŒr den theoretischen Teil entschieden habe, schlage ich vor, mit dem nĂ€chsten Teil fortzufahren.


Der praktische Teil


Ich schlage vor, Dateien im Repository wie folgt zu organisieren:


 . ├── docker #    -   │  ├── app │  │  ├── Dockerfile │  │  └── ... │  ├── nginx │  │  ├── Dockerfile │  │  └── ... │  └── sources │    ├── Dockerfile │    └── ... ├── src #   │ ├── app │ ├── bootstrap │ ├── config │ ├── artisan │ └── ... ├── docker-compose.yml # Compose-    ├── Makefile ├── CHANGELOG.md └── README.md 

Sie können sich mit der Struktur und den Dateien vertraut machen, indem Sie auf diesen Link klicken.

Um einen Dienst zu erstellen, können Sie den folgenden Befehl verwenden:


 $ docker build \ --tag %local_image_name% \ -f ./docker/%service_directory%/Dockerfile ./docker/%service_directory% 

Der einzige Unterschied besteht in der Zusammenstellung des Bildes mit den Quellen - dafĂŒr muss der Zusammenstellungskontext (extremes Argument) auf ./src .


Die Regeln fĂŒr die Benennung von Bildern in der lokalen Registrierung empfehlen die Verwendung der Regeln, die docker-compose standardmĂ€ĂŸig verwendet: %root_directory_name%_%service_name% . Wenn das Projektverzeichnis my-awesome-project heißt und der Dienst my-awesome-project_redis ist es besser, den my-awesome-project_redis (lokal) fĂŒr my-awesome-project_redis zu wĂ€hlen.


Um den Erstellungsprozess zu beschleunigen, können Sie dem Docker anweisen, den Cache des zuvor zusammengestellten Images zu verwenden. Dazu wird die --cache-from %full_registry_name% . Daher wird der Docker-Daemon vor dem Starten einer bestimmten Anweisung in der Docker-Datei nachsehen - hat sich diese geĂ€ndert? Wenn nicht (der Hash konvergiert), wird die Anweisung ĂŒbersprungen, wobei die bereits vorbereitete Ebene aus dem Bild verwendet wird, die Sie als Cache verwenden sollen. Dieses Ding ist nicht schlecht, also wird es den Prozess neu aufbauen, besonders wenn sich nichts geĂ€ndert hat :)

ENTRYPOINT die ENTRYPOINT Skripte zum Starten von Anwendungscontainern.

Das Image der Umgebung zum Starten der Anwendung (App) wurde unter BerĂŒcksichtigung der Tatsache erfasst, dass sie nicht nur in der Produktion funktioniert, sondern auch lokal. Entwickler mĂŒssen effektiv mit ihr interagieren. Das Installieren und Entfernen von composer AbhĂ€ngigkeiten, das AusfĂŒhren von Komponententests, Endprotokollen und die Verwendung bekannter Aliase ( php /app/artisan → art , composer → c ) sollte ohne Probleme sein. DarĂŒber hinaus werden damit auch unit Tests und statische Code-Analysatoren (in unserem Fall phpstan ) auf CI ausgefĂŒhrt. Aus diesem Grund enthĂ€lt die xdebug Datei beispielsweise die xdebug Installationszeile, das Modul selbst ist jedoch nicht aktiviert (es wird nur mit CI aktiviert).


Auch fĂŒr composer das Paket hirak/prestissimo , was die Installation aller AbhĂ€ngigkeiten erheblich hirak/prestissimo .

WĂ€hrend der Produktion hĂ€ngen wir den Inhalt des Verzeichnisses /src aus dem Image mit den darin enthaltenen Quellen (Quellen) in das Verzeichnis /app . FĂŒr die Entwicklung "rollen" wir das lokale Verzeichnis mit Anwendungsquellen ( -v "$(pwd)/src:/app:rw" ).


Und hier liegt eine KomplexitĂ€t: Dies sind Zugriffsrechte auf Dateien , die aus dem Container erstellt werden. Tatsache ist, dass die im Container ausgefĂŒhrten Prozesse standardmĂ€ĂŸig vom Stammverzeichnis ( root:root ), den von diesen Prozessen erstellten Dateien (Cache, Protokolle, Sitzungen usw.) ausgehen und daher nichts „lokal“ mit ihnen zu tun haben Sie können dies tun, ohne sudo chown -R $(id -u):$(id -g) /path/to/sources auszufĂŒhren.


Verwenden Sie als eine Lösung fixuid , aber diese Lösung ist unkompliziert. Der beste Weg schien mir, USER_ID lokale USER_ID und ihre GROUP_ID im Container USER_ID und Prozesse mit diesen Werten zu starten . StandardmĂ€ĂŸig wurde durch Ersetzen der Werte 1000:1000 (die Standardwerte fĂŒr den ersten lokalen Benutzer) der Aufruf $(id -u):$(id -g) , und Sie können sie $ USER_ID=666 docker-compose up -d jederzeit ĂŒberschreiben ( $ USER_ID=666 docker-compose up -d ) oder .env die Docker-Compose-Datei in die .env Datei ein.


Wenn php-fpm lokal gestartet wird php-fpm vergessen php-fpm nicht, den opcache zu deaktivieren - andernfalls gibt es eine Menge "Ja, was zur Hölle!" Sie werden zur VerfĂŒgung gestellt.


FĂŒr eine "direkte" Verbindung zu Redis und Postgres habe ich zusĂ€tzliche Ports "out" ( 15432 bzw. 15432 ) 15432 , sodass es im Prinzip keine Probleme gibt, "zu verbinden und zu sehen, was und wie es wirklich ist".


Ich behalte den Container mit der Codenamen- app ( --command keep-alive.sh ), um bequem auf die Anwendung zugreifen zu können.


Hier einige Beispiele zur Lösung alltÀglicher Probleme mit docker-compose :


BedienungBefehl ausfĂŒhren
Installieren Sie das composer Paket$ docker-compose exec app composer require package/name
Phpunit ausfĂŒhren$ docker-compose exec app php ./vendor/bin/phpunit --no-coverage
Installieren Sie alle KnotenabhÀngigkeiten$ docker-compose run --rm node npm install
Installieren Sie das Knotenpaket$ docker-compose run --rm node npm i package_name
Starten eines Live-Wiederaufbaus von Assets$ docker-compose run --rm node npm run watch

Sie finden alle Startdetails in der Datei docker-compose.yml .


Choi make lebendig!


Nach dem zweiten Mal wird es langweilig, jedes Mal dieselben Befehle einzugeben. Da Programmierer von Natur aus faule Wesen sind, sollten wir uns mit ihrer "Automatisierung" befassen. Das sh einer Reihe von Skripten ist eine Option, aber nicht so attraktiv wie ein einzelnes Makefile , zumal seine Anwendbarkeit in der modernen Entwicklung stark unterschÀtzt wird.


Das vollstÀndige russischsprachige Handbuch finden Sie unter diesem Link .

Mal sehen, wie der make Lauf im Stammverzeichnis des Repositorys aussieht:


 [user@host ~/projects/app] $ make help Show this help app-pull Application - pull latest Docker image (from remote registry) app Application - build Docker image locally app-push Application - tag and push Docker image into remote registry sources-pull Sources - pull latest Docker image (from remote registry) sources Sources - build Docker image locally sources-push Sources - tag and push Docker image into remote registry nginx-pull Nginx - pull latest Docker image (from remote registry) nginx Nginx - build Docker image locally nginx-push Nginx - tag and push Docker image into remote registry pull Pull all Docker images (from remote registry) build Build all Docker images push Tag and push all Docker images into remote registry login Log in to a remote Docker registry clean Remove images from local registry --------------- --------------- up Start all containers (in background) for development down Stop all started for development containers restart Restart all started for development containers shell Start shell into application container install Install application dependencies into application container watch Start watching assets for changes (node) init Make full application initialization (install, seed, build assets) test Execute application tests Allowed for overriding next properties: PULL_TAG - Tag for pulling images before building own ('latest' by default) PUBLISH_TAGS - Tags list for building and pushing into remote registry (delimiter - single space, 'latest' by default) Usage example: make PULL_TAG='v1.2.3' PUBLISH_TAGS='latest v1.2.3 test-tag' app-push 

Er ist sehr gut in sĂŒchtig machenden Zielen. Um beispielsweise docker-compose run --rm node npm run watch zu starten ( docker-compose run --rm node npm run watch ), muss die Anwendung " docker-compose run --rm node npm run watch " werden - Sie mĂŒssen nur das up Ziel als abhĂ€ngig angeben - und Sie mĂŒssen sich nicht darum kĂŒmmern, dies zu vergessen, bevor Sie watch aufrufen - make sich alles fĂŒr Sie tun. Gleiches gilt zum Beispiel fĂŒr das AusfĂŒhren von Tests und statischen Analysatoren, bevor Änderungen vorgenommen werden. FĂŒhren Sie einen make test und die ganze Magie wird fĂŒr Sie geschehen!


Sie mĂŒssen sich --cache-from nicht darum kĂŒmmern, Bilder zusammenzustellen, herunterzuladen, --cache-from und so --cache-from alles anzugeben?


Sie können den Inhalt des Makefile unter diesem Link anzeigen.


Autoteil


Fahren wir mit dem letzten Teil dieses Artikels fort - dies ist die Automatisierung des Aktualisierungsprozesses von Bildern in der Docker-Registrierung. Obwohl in meinem Beispiel GitLab CI verwendet wird, um die Idee auf einen anderen Integrationsdienst zu ĂŒbertragen, denke ich, dass dies durchaus möglich sein wird.


ZunÀchst bestimmen wir die Benennung der verwendeten Bild-Tags:


Tag-NameZiel
latestBilder aus der Hauptniederlassung gesammelt.
Der Status des Codes ist der neueste, aber noch nicht bereit, in die Version einzusteigen
some-branch-nameBilder, die auf dem Brunch some-branch-name Zweignamens gesammelt wurden.
Auf diese Weise können wir Änderungen in jeder Umgebung "einfĂŒhren", die nur im Rahmen eines bestimmten Brunchs implementiert wurden, noch bevor sie mit dem master Light zusammengefĂŒhrt werden. Es reicht aus, die Bilder mit diesem Tag zu "strecken".
Und - ja, die Änderungen können sowohl den Code als auch die Bilder aller Dienste im Allgemeinen betreffen!
vX.XXEigentlich die Freigabe der Anwendung (zum Bereitstellen einer bestimmten Version)
stableAlias ​​fĂŒr das Tag mit der neuesten Version (zum Bereitstellen der neuesten stabilen Version)

Die Veröffentlichung erfolgt durch Veröffentlichung eines Tags in einem vX.XX Format.


Um den Build zu beschleunigen, wird das Caching der Verzeichnisse ./src/vendor und ./src/node_modules + --cache-from fĂŒr den docker build und besteht aus den folgenden Schritten:


KĂŒnstlernameZiel
prepareDie Vorbereitungsphase - die Zusammenstellung von Bildern aller Dienste außer dem Bild mit der Quelle
testTesten der Anwendung (AusfĂŒhren von phpunit , statische Code-Analysatoren) anhand von Bildern, die in der Vorbereitungsphase gesammelt wurden
buildInstallieren aller composer AbhĂ€ngigkeiten ( --no-dev ), Zusammenstellen von assets webpack und webpack Images mit dem Quellcode einschließlich der empfangenen Artefakte ( vendor/* , app.js , app.css )

Pipelines Screenshot


Die Baugruppe auf dem Hauptzweig erzeugt push mit den latest und master Tags

Im Durchschnitt dauern alle Phasen der Montage 4 Minuten , was ein ziemlich gutes Ergebnis ist (die parallele AusfĂŒhrung von Aufgaben ist unser Alles).


Unter diesem Link können Sie sich mit dem Inhalt der Konfiguration ( .gitlab-ci.yml ) des Kollektors vertraut machen.


Anstelle einer Schlussfolgerung


Wie Sie sehen können, ist die Organisation der Arbeit mit einer PHP-Anwendung (am Beispiel von Laravel ) mit Docker nicht so schwierig. Als Test können Sie das Repository tarampampam/laravel-in-docker und alle Vorkommen von tarampampam/laravel-in-docker durch Ihr eigenes ersetzen - probieren Sie alles "live" aus.


FĂŒr den lokalen Start - fĂŒhren Sie nur 2 Befehle aus:


 $ git clone https://gitlab.com/tarampampam/laravel-in-docker.git ./laravel-in-docker && cd $_ $ make init 

Öffnen Sie dann http://127.0.0.1:9999 in Ihrem bevorzugten Browser.


... die Gelegenheit nutzen


Im Moment arbeite ich am TL-Projekt "Autocode" und wir suchen talentierte PHP-Entwickler und Systemadministratoren (EntwicklungsbĂŒro befindet sich in Jekaterinburg). Wenn Sie sich als Erster oder Zweiter betrachten, schreiben Sie unseren HR-Brief mit dem Text "Ich möchte ein Entwicklungsteam sein, Lebenslauf:% link_on_summary%" an die E-Mail- hr@avtocod.ru . Wir helfen hr@avtocod.ru beim Umzug.

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


All Articles